排名第一的word模板引擎,到底有多神仙

图片

hello大家好,今天我要给大家推荐一个非常棒的Word专用的模板引擎,Poi-tl(Poi-template-language)。这款引擎基于Apache Poi,可以根据用户输入的内容直接生成相应的word文档,很是方便。

前言

作为一名开发者,模板引擎,大家都应该用过吧,模板中有一系列占位符,模板+数据,传递给模板引擎,会输出一份新的文档,如下图,模板可以启动重用的作用。

图片

常见的模板基本上都是文本格式的,比如 html 格式的、text 格式的,这种格式的模板处理起来相对比较容易,对应的模板引擎有很多,比如 java 中的 freemarker,velocity,thymeleaf。

如果我们的模板是 word 呢?

比如开发、测试、项目管理人员,通常需要写文档,而多数文档基本上格式都差不多,如果能够有一款模板引擎可以对 word 进行处理,传入不同的数据,就输出不同的 word 文档,那咱们的功能效率将大大提高。

Poi-tl

poi-tl(poi template language)是 Word 模板引擎,基于 Microsoft Word 模板和数据生成新的文档。

Word 模板拥有丰富的样式,poi-tl 在生成的文档中会完美保留模板中的样式,还可以为标签设置样式,标签的样式会被应用到替换后的文本上,因此你可以专注于模板设计。

这个是 java 开发的一款 word 模板引擎,如果你是搞 java 的,想必你肯定用过 POI,java 中最强大的 office 处理工具库,可以通过 java 代码轻松的操作 word、excel、ppt 中的所有内容,而 Poi-tl 底层用的就是 POI。

POI-TL 功能清单

引擎功能描述
文本将标签渲染为文本
图片将标签渲染为图片
表格将标签渲染为表格
列表将标签渲染为列表
图表条形图(3D 条形图)、柱形图(3D 柱形图)、面积图(3D 面积图)、折线图(3D 折线图)、雷达图、饼图(3D 饼图)等图表渲染
If Condition 判断隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop 循环循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop 表格行循环渲染表格的某一行
Loop 表格列循环渲染表格的某一列
Loop 有序列表支持有序列表的循环,同时支持多级列表
图片替换将原有图片替换成另一张图片
书签、锚点、超链接支持设置书签,文档内锚点和超链接功能
强大的表达式完全支持 SpringEL 表达式,可以扩展更多的表达式:OGNL, MVEL…
标签定制支持自定义标签前后缀
文本框文本框内标签支持
样式模板即样式,同时代码也可以设置样式
模板嵌套模板包含子模板,子模板再包含子模板
合并Word 合并 Merge,也可以在指定位置进行合并
用户自定义函数(插件)在文档任何位置执行函数

Poi-tl整体设计采用了Template + data-model = output模式.

图片

Configure提供了模板配置功能,比如语法配置和插件配置:


/**
 * 插件化配置
 * 
 * @author Sayi
 * @version 1.0.0
 */
public class Configure {

    // defalut expression
    private static final String DEFAULT_GRAMER_REGEX = "[\\w\\u4e00-\\u9fa5]+(\\.[\\w\\u4e00-\\u9fa5]+)*";

    // Highest priority
    private Map<String, RenderPolicy> customPolicys = new HashMap<String, RenderPolicy>();
    // Low priority
    private Map<Character, RenderPolicy> defaultPolicys = new HashMap<Character, RenderPolicy>();

    /**
     * 引用渲染策略
     */
    private List<ReferenceRenderPolicy<?>> referencePolicies = new ArrayList<>();

    /**
     * 语法前缀
     */
    private String gramerPrefix = "{{";
    /**
     * 语法后缀
     */
    private String gramerSuffix = "}}";

    /**
     * 默认支持中文、字母、数字、下划线的正则
     */
    private String grammerRegex = DEFAULT_GRAMER_REGEX;

    /**
     * 模板表达式模式,默认为POI_TL_MODE
     */
    private ELMode elMode = ELMode.POI_TL_STANDARD_MODE;

    /**
     * 渲染数据校验不通过时的处理策略
     * <ul>
     * <li>DiscardHandler: 什么都不做</li>
     * <li>ClearHandler: 清空标签</li>
     * <li>AbortHandler: 抛出异常</li>
     * </ul>
     */
    private ValidErrorHandler handler = new ClearHandler();

    private Configure() {
        plugin(GramerSymbol.TEXT, new TextRenderPolicy());
        plugin(GramerSymbol.IMAGE, new PictureRenderPolicy());
        plugin(GramerSymbol.TABLE, new MiniTableRenderPolicy());
        plugin(GramerSymbol.NUMBERIC, new NumbericRenderPolicy());
        plugin(GramerSymbol.DOCX_TEMPLATE, new DocxRenderPolicy());
    }

    /**
     * 创建默认配置
     * 
     * @return
     */
    public static Configure createDefault() {
        return newBuilder().build();
    }

    /**
     * 构建器
     * 
     * @return
     */
    public static ConfigureBuilder newBuilder() {
        return new ConfigureBuilder();
    }

    /**
     * 新增或变更语法插件
     * 
     * @param c
     *            语法
     * @param policy
     *            策略
     */
    public Configure plugin(char c, RenderPolicy policy) {
        defaultPolicys.put(Character.valueOf(c), policy);
        return this;
    }

    /**
     * 新增或变更语法插件
     * 
     * @param symbol
     *            语法
     * @param policy
     *            策略
     * @return
     */
    Configure plugin(GramerSymbol symbol, RenderPolicy policy) {
        defaultPolicys.put(symbol.getSymbol(), policy);
        return this;
    }

    /**
     * 自定义模板和策略
     * 
     * @param tagName
     *            模板名称
     * @param policy
     *            策略
     */
    public void customPolicy(String tagName, RenderPolicy policy) {
        customPolicys.put(tagName, policy);
    }

    /**
     * 新增引用渲染策略
     * 
     * @param policy
     */
    public void referencePolicy(ReferenceRenderPolicy<?> policy) {
        referencePolicies.add(policy);
    }

    /**
     * 获取标签策略
     * 
     * @param tagName
     *            模板名称
     * @param sign
     *            语法
     */
    // Query Operations

    public RenderPolicy getPolicy(String tagName, Character sign) {
        RenderPolicy policy = getCustomPolicy(tagName);
        return null == policy ? getDefaultPolicy(sign) : policy;
    }

    public List<ReferenceRenderPolicy<?>> getReferencePolicies() {
        return referencePolicies;
    }
    
    private RenderPolicy getCustomPolicy(String tagName) {
        return customPolicys.get(tagName);
    }

    private RenderPolicy getDefaultPolicy(Character sign) {
        return defaultPolicys.get(sign);
    }

    public Map<Character, RenderPolicy> getDefaultPolicys() {
        return defaultPolicys;
    }

    public Map<String, RenderPolicy> getCustomPolicys() {
        return customPolicys;
    }

    public Set<Character> getGramerChars() {
        return defaultPolicys.keySet();
    }

    public String getGramerPrefix() {
        return gramerPrefix;
    }

    public String getGramerSuffix() {
        return gramerSuffix;
    }

    public String getGrammerRegex() {
        return grammerRegex;
    }

    public ELMode getElMode() {
        return elMode;
    }

    public ValidErrorHandler getValidErrorHandler() {
        return handler;
    }

    public static class ConfigureBuilder {
        private boolean regexForAll;
        private Configure config;

        public ConfigureBuilder() {
            config = new Configure();
        }

        public ConfigureBuilder buildGramer(String prefix, String suffix) {
            config.gramerPrefix = prefix;
            config.gramerSuffix = suffix;
            return this;
        }

        public ConfigureBuilder buildGrammerRegex(String reg) {
            config.grammerRegex = reg;
            return this;
        }

        public ConfigureBuilder supportGrammerRegexForAll() {
            this.regexForAll = true;
            return this;
        }

        public ConfigureBuilder setElMode(ELMode mode) {
            config.elMode = mode;
            return this;
        }

        public ConfigureBuilder setValidErrorHandler(ValidErrorHandler handler) {
            config.handler = handler;
            return this;
        }

        public ConfigureBuilder addPlugin(char c, RenderPolicy policy) {
            config.plugin(c, policy);
            return this;
        }

        public ConfigureBuilder customPolicy(String tagName, RenderPolicy policy) {
            config.customPolicy(tagName, policy);
            return this;
        }

        public ConfigureBuilder referencePolicy(ReferenceRenderPolicy<?> policy) {
            config.referencePolicy(policy);
            return this;
        }

        public ConfigureBuilder bind(String tagName, RenderPolicy policy) {
            config.customPolicy(tagName, policy);
            return this;
        }

        public Configure build() {
            if (config.elMode == ELMode.SPEL_MODE) {
                regexForAll = true;
            }
            if (regexForAll) {
                config.grammerRegex = RegexUtils.createGeneral(config.gramerPrefix,
                        config.gramerSuffix);
            }
            return config;
        }
    }
}


Visitor提供了模板解析功能:

/**
 * 模板解析器
 * 
 * @author Sayi
 * @version 1.4.0
 */
public class TemplateVisitor implements Visitor {
    private static Logger logger = LoggerFactory.getLogger(TemplateVisitor.class);

    private Configure config;
    private List<ElementTemplate> eleTemplates;

    private Pattern templatePattern;
    private Pattern gramerPattern;

    static final String FORMAT_TEMPLATE = "{0}{1}{2}{3}";
    static final String FORMAT_GRAMER = "({0})|({1})";

    public TemplateVisitor(Configure config) {
        this.config = config;
        initPattern();
    }

    @Override
    public List<ElementTemplate> visitDocument(XWPFDocument doc) {
        if (null == doc) return null;
        this.eleTemplates = new ArrayList<ElementTemplate>();
        logger.info("Visit the document start...");
        visitParagraphs(doc.getParagraphs());
        visitTables(doc.getTables());
        visitHeaders(doc.getHeaderList());
        visitFooters(doc.getFooterList());
        logger.info("Visit the document end, resolve and create {} ElementTemplates.",
                this.eleTemplates.size());
        return eleTemplates;
    }

    void visitHeaders(List<XWPFHeader> headers) {
        if (null == headers) return;
        for (XWPFHeader header : headers) {
            visitParagraphs(header.getParagraphs());
            visitTables(header.getTables());
        }
    }

    void visitFooters(List<XWPFFooter> footers) {
        if (null == footers) return;
        for (XWPFFooter footer : footers) {
            visitParagraphs(footer.getParagraphs());
            visitTables(footer.getTables());
        }
    }

    void visitParagraphs(List<XWPFParagraph> paragraphs) {
        if (null == paragraphs) return;
        for (XWPFParagraph paragraph : paragraphs) {
            visitParagraph(paragraph);
        }
    }

    void visitTables(List<XWPFTable> tables) {
        if (null == tables) return;
        for (XWPFTable tb : tables) {
            visitTable(tb);
        }
    }

    void visitTable(XWPFTable table) {
        if (null == table) return;
        List<XWPFTableRow> rows = table.getRows();
        if (null == rows) return;
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            if (null == cells) continue;
            for (XWPFTableCell cell : cells) {
                visitParagraphs(cell.getParagraphs());
                visitTables(cell.getTables());
            }
        }
    }

    void visitParagraph(XWPFParagraph paragraph) {
        if (null == paragraph) return;
        RunningRunParagraph runningRun = new RunningRunParagraph(paragraph, templatePattern);
        List<XWPFRun> refactorRun = runningRun.refactorRun();
        if (null == refactorRun) return;
        for (XWPFRun run : refactorRun) {
            visitRun(run);
        }
    }

    void visitRun(XWPFRun run) {
        String text = null;
        if (null == run || StringUtils.isBlank(text = run.getText(0))) return;
        ElementTemplate elementTemplate = parseTemplateFactory(text, run);
        if (null != elementTemplate) eleTemplates.add(elementTemplate);
    }

    private <T> ElementTemplate parseTemplateFactory(String text, T obj) {
        logger.debug("Resolve where text: {}, and create ElementTemplate", text);
        // temp ,future need to word analyze
        if (templatePattern.matcher(text).matches()) {
            String tag = gramerPattern.matcher(text).replaceAll("").trim();
            if (obj.getClass() == XWPFRun.class) {
                return TemplateFactory.createRunTemplate(tag, config, (XWPFRun) obj);
            } else if (obj.getClass() == XWPFTableCell.class)
                // return CellTemplate.create(symbol, tagName, (XWPFTableCell)
                // obj);
                return null;
        }
        return null;
    }

    private void initPattern() {
        String signRegex = getGramarRegex(config);
        String prefixRegex = RegexUtils.escapeExprSpecialWord(config.getGramerPrefix());
        String suffixRegex = RegexUtils.escapeExprSpecialWord(config.getGramerSuffix());

        templatePattern = Pattern.compile(MessageFormat.format(FORMAT_TEMPLATE, prefixRegex,
                signRegex, config.getGrammerRegex(), suffixRegex));
        gramerPattern = Pattern
                .compile(MessageFormat.format(FORMAT_GRAMER, prefixRegex, suffixRegex));
    }

    private String getGramarRegex(Configure config) {
        List<Character> gramerChar = new ArrayList<Character>(config.getGramerChars());
        StringBuilder reg = new StringBuilder("(");
        for (int i = 0;; i++) {
            Character chara = gramerChar.get(i);
            String escapeExprSpecialWord = RegexUtils.escapeExprSpecialWord(chara.toString());
            if (i == gramerChar.size() - 1) {
                reg.append(escapeExprSpecialWord).append(")?");
                break;
            } else reg.append(escapeExprSpecialWord).append("|");
        }
        return reg.toString();
    }

}

RenderPolicy是渲染策略扩展点,Render模块提供了RenderDataCompute表达式计算扩展点,通过RenderPolicy对每个标签进行渲染。

Poi-tl提供详细的示例代码讲解和官方文档.

图片

图片

点击下方卡片/微信搜索,关注公众号“天宇文创意乐派”(ID:gh_cc865e4c536b)

回复“poi-tl”获取仓库地址和官方文档

往期推荐

[

Win10系统电脑怎么关闭右下方任务栏的广告新闻

](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487189&idx=1&sn=5fa56554c6cd3ba3018729ab076db49a&chksm=ebb6edb6dcc164a0eb8ba38bb388c78694f46e5c57c69a28a52c17e1b50744548dcc28f7f0d2&scene=21#wechat_redirect)

[

官方出手!百度网盘有望免费提速了!

](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487189&idx=2&sn=93eb8e7458319c118e525716cdf775e7&chksm=ebb6edb6dcc164a00914102750e3694fe630b10bbed1b2f7c12d8ef82314e29686be5425ae9c&scene=21#wechat_redirect)

[

Linux常用命令!

](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487189&idx=3&sn=13b305957439725a2b18c28cd5148c5e&chksm=ebb6edb6dcc164a0a1dd3f05a744137653af64cbf9f5ed2b6668f190f156922ed8bc678ebab3&scene=21#wechat_redirect)

[

EA财报:有史以来最好的Q2《2042》备受玩家欢迎

](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487189&idx=4&sn=45380c3ad82afa2649f80b17d8b39883&chksm=ebb6edb6dcc164a0e6d17e2942c992f20098f73fe988f9a1696c81d226ef1a53f900b5e552f5&scene=21#wechat_redirect)

[

GitHub AI 编程工具 Copilot 已支持 IntelliJ IDEA、PyCharm

](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487088&idx=1&sn=2ed7921517ac3ee058e0a2cd23650249&chksm=ebb6ed13dcc164057b5e780d9d6187c9ce423c31ac6088762a238f0f216a7c91a4ebf11bccba&scene=21#wechat_redirect)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过使用 Apache POI 库来实现在已有的 Word 模板中插入多张图片: 1. 首先需要导入 Apache POI 库的 jar 包,可以从官方网站下载。 2. 打开 Word 模板文件,使用 `XWPFDocument` 类加载 Word 文档。 ```java FileInputStream fis = new FileInputStream(new File("template.docx")); XWPFDocument doc = new XWPFDocument(fis); ``` 3. 获取 Word 文档中的表格,可以使用 `XWPFTable` 类。 ```java List<XWPFTable> tables = doc.getTables(); XWPFTable table = tables.get(0); //获取第一个表格 ``` 4. 对于表格中需要插入图片的单元格,使用 `XWPFParagraph` 类来创建一个新的段落,并将图片插入到段落中。 ```java XWPFParagraph para = table.getRow(row).getCell(cell).addParagraph(); XWPFRun run = para.createRun(); run.addPicture(new FileInputStream(imageFile), XWPFDocument.PICTURE_TYPE_PNG, "image.png", Units.toEMU(width), Units.toEMU(height)); ``` 其中,`row` 和 `cell` 分别代表插入图片的单元格的行和列;`imageFile` 是要插入的图片文件;`width` 和 `height` 是图片的宽度和高度。 5. 最后将修改后的 Word 文档保存到磁盘。 ```java FileOutputStream fos = new FileOutputStream(new File("output.docx")); doc.write(fos); fos.close(); ``` 完整代码如下: ```java import java.io.*; import java.util.List; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.*; public class InsertImageToTable { public static void main(String[] args) throws Exception { //打开 Word 模板文件 FileInputStream fis = new FileInputStream(new File("template.docx")); XWPFDocument doc = new XWPFDocument(fis); //获取第一个表格 List<XWPFTable> tables = doc.getTables(); XWPFTable table = tables.get(0); //插入图片到单元格 int row = 0; int cell = 0; int width = 200; int height = 200; File imageFile1 = new File("image1.png"); File imageFile2 = new File("image2.png"); XWPFParagraph para1 = table.getRow(row).getCell(cell).addParagraph(); XWPFRun run1 = para1.createRun(); run1.addPicture(new FileInputStream(imageFile1), XWPFDocument.PICTURE_TYPE_PNG, "image1.png", Units.toEMU(width), Units.toEMU(height)); XWPFParagraph para2 = table.getRow(row).getCell(cell + 1).addParagraph(); XWPFRun run2 = para2.createRun(); run2.addPicture(new FileInputStream(imageFile2), XWPFDocument.PICTURE_TYPE_PNG, "image2.png", Units.toEMU(width), Units.toEMU(height)); //保存文档 FileOutputStream fos = new FileOutputStream(new File("output.docx")); doc.write(fos); fos.close(); } } ``` 注意事项: - 图片文件必须存在,且路径正确。 - 插入图片的单元格必须存在。 - 如果需要插入多张图片,可以在同一个单元格中插入多个段落。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值