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

图片

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

前言

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

排名第一的word模板引擎,到底有多神仙_第1张图片

常见的模板基本上都是文本格式的,比如 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模式.

排名第一的word模板引擎,到底有多神仙_第2张图片

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 customPolicys = new HashMap();
    // Low priority
    private Map defaultPolicys = new HashMap();

    /**
     * 引用渲染策略
     */
    private List> 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;

    /**
     * 渲染数据校验不通过时的处理策略
     * 
         * 
  • DiscardHandler: 什么都不做
  •      * 
  • ClearHandler: 清空标签
  •      * 
  • AbortHandler: 抛出异常
  •      * 
     */     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> getReferencePolicies() {         return referencePolicies;     }          private RenderPolicy getCustomPolicy(String tagName) {         return customPolicys.get(tagName);     }     private RenderPolicy getDefaultPolicy(Character sign) {         return defaultPolicys.get(sign);     }     public Map getDefaultPolicys() {         return defaultPolicys;     }     public Map getCustomPolicys() {         return customPolicys;     }     public Set 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 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 visitDocument(XWPFDocument doc) {
        if (null == doc) return null;
        this.eleTemplates = new ArrayList();
        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 headers) {
        if (null == headers) return;
        for (XWPFHeader header : headers) {
            visitParagraphs(header.getParagraphs());
            visitTables(header.getTables());
        }
    }

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

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

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

    void visitTable(XWPFTable table) {
        if (null == table) return;
        List rows = table.getRows();
        if (null == rows) return;
        for (XWPFTableRow row : rows) {
            List 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 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  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 gramerChar = new ArrayList(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提供详细的示例代码讲解和官方文档.

排名第一的word模板引擎,到底有多神仙_第3张图片

图片

点击下方卡片/微信搜索,关注公众号“天宇文创意乐派”(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)

你可能感兴趣的:(开发工具,github)