Guide to Using poi‑tl for Generating Word Reports with Custom Tags in Java
This article explains how to use the poi‑tl Java library to create Word report templates, define common tags such as text, images, tables, lists, and charts, encapsulate them with enums and factories, and generate complete reports through a reusable utility class.
The author needed a fast way to generate data reports containing text, images, tables, and charts, and chose the poi‑tl library—an open‑source Java template engine built on Apache POI—for its simplicity and extensibility.
1. Introduction to poi‑tl
poi‑tl is a lightweight, cross‑platform Java component that allows developers to define placeholders in a Word template similar to Freemarker syntax, then replace them with data objects.
2. Common Tag Types
2.1 Text
Placeholder format: {{var}} . Supported data models include plain String , TextRenderData (styled text) and HyperLinkTextRenderData (hyperlink).
put("name", "Sayi");
put("author", new TextRenderData("000000", "Sayi"));
put("link", new HyperLinkTextRenderData("website", "http://deepoove.com"));2.2 Image
Placeholder format: {{@var}} . The data model is PictureRenderData , which can handle local files, byte streams, or URLs.
// Local image
put("local", new PictureRenderData(80, 100, "./sayi.png"));
// Image stream
put("localbyte", new PictureRenderData(80, 100, ".png", new FileInputStream("./logo.png")));
// Network image (be aware of latency)
put("urlpicture", new PictureRenderData(50, 50, ".png", BytePictureUtils.getUrlBufferedImage("http://deepoove.com/images/icecream.png")));2.3 Table
Placeholder format: {{#var}} . The data model is MiniTableRenderData , built from a header row and a list of content rows.
2.4 List
Placeholder format: {{*var}} . The data model is NumbericRenderData , which supports various list styles such as bullets, Roman numerals, and ordered numbers.
FMT_DECIMAL //1. 2. 3.
FMT_DECIMAL_PARENTHESES //1) 2) 3)
FMT_BULLET //● ● ●
FMT_LOWER_LETTER //a. b. c.
FMT_LOWER_ROMAN //i ii iii
FMT_UPPER_LETTER //A. B. C.2.5 Single‑Series Chart
Placeholder format: {{val}} . The data model is ChartSingleSeriesRenderData , which holds categories, a title, and a single series.
ChartSingleSeriesRenderData singleSeriesRenderData = new ChartSingleSeriesRenderData();
singleSeriesRenderData.setCategories(new String[]{"俄罗斯","加拿大","美国","中国"});
singleSeriesRenderData.setChartTitle("测试");
pie.setSeriesData(new SeriesRenderData("countries", new Integer[]{17098242, 9984670, 9826675, 9596961}));2.6 Multi‑Series Chart
Placeholder format is the same as single‑series. The data model is ChartMultiSeriesRenderData , which can hold multiple series of different types.
ChartMultiSeriesRenderData chart = new ChartMultiSeriesRenderData();
chart.setChartTitle("MyChart");
chart.setCategories(new String[]{"中文","English"});
List
seriesRenderData = new ArrayList<>();
seriesRenderData.add(new SeriesRenderData("countries", new Double[]{15.0, 6.0}));
seriesRenderData.add(new SeriesRenderData("speakers", new Double[]{223.0, 119.0}));
chart.setSeriesDatas(seriesRenderData);3. Code Encapsulation
To simplify usage, the author wrapped each tag type into a unified framework:
WordContentTypeEnum : enum defining TEXT, PICTURE, TABLE, LIST, CHART.
LabelData : base class containing labelName and typeEnum .
GenerateWord interface: Object generateWord(LabelData data);
GenerateWordFactory : registers implementations for each WordContentTypeEnum and provides lookup.
Specific implementations (e.g., TextGenerateWord , PictureGenerateWord , TableGenerateWord , ListGenerateWord , ChartGenerateWord ) create the appropriate render data objects.
OperateWordManage : central utility that compiles the template, renders all placeholders using the factory, and writes the final Word file.
3.1 Example Enum
public enum WordContentTypeEnum {
TEXT,
PICTURE,
TABLE,
LIST,
CHART;
}3.2 Example Entity (Text)
@Data
@Accessors(chain = true)
public class TextContentData extends LabelData {
private String content; // plain text
private TextRenderData renderData; // styled text
private HyperLinkTextRenderData linkData; // hyperlink
}3.3 Example Factory Registration
@Component
public class TextGenerateWord implements GenerateWord {
@PostConstruct
public void init() {
GenerateWordFactory.register(WordContentTypeEnum.TEXT, this);
}
@Override
public Object generateWord(LabelData data) {
TextContentData d = (TextContentData) data;
return Objects.nonNull(d.getLinkData()) ? d.getLinkData()
: Objects.nonNull(d.getRenderData()) ? d.getRenderData()
: d.getContent();
}
}4. Testing and Report Generation
The author created a Word template with placeholders such as {{title}} , {{typeContent}} , {{picture}} , etc., placed it under resources/static/template , and then built a list of LabelData objects representing each section of the report.
Finally, the utility OperateWordManage.generateWordContent was called with the template file, output path, and the data list, producing a complete Word document that contains text, styled text, images, tables, lists, and various charts.
4.1 Sample Test Code
private static final String TEMPLATE_PATH = "static/template/demo_template.docx";
public void generateCharts() {
File templateFile = new ClassPathResource(TEMPLATE_PATH).getFile();
List
generates = new ArrayList<>();
// Text
TextContentData contentData = new TextContentData()
.setContent("2022年月通报函生成报告")
.setLabelName("title")
.setTypeEnum(WordContentTypeEnum.TEXT);
generates.add(contentData);
// Styled text
TextContentData typeData = new TextContentData()
.setRenderData(new TextRenderData("cc0000", "这是带样式的内容"))
.setLabelName("typeContent")
.setTypeEnum(WordContentTypeEnum.TEXT);
generates.add(typeData);
// Image
PictureContentData picData = new PictureContentData()
.setWidth(200).setHeight(160).setPicType(PicTypeEnum.JPG)
.setFile(new File("D:/down/java.jpg"))
.setLabelName("picture")
.setTypeEnum(WordContentTypeEnum.PICTURE);
generates.add(picData);
// ... (table, list, charts omitted for brevity) ...
OperateWordManage.generateWordContent(templateFile, "D:/down/output.docx", generates);
}The resulting document demonstrates that all placeholders have been correctly replaced, confirming the effectiveness of the encapsulated poi‑tl solution.
In summary, wrapping poi‑tl tag handling into a modular factory pattern simplifies Word report generation and makes the codebase easier to maintain and extend.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.