Backend Development 19 min read

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.

Architect's Guide
Architect's Guide
Architect's Guide
Guide to Using poi‑tl for Generating Word Reports with Custom Tags in Java

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.

backendJavaTemplate Enginewordpoi-tlreport-generation
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.