poi-tl(poi template language)是基于Apache POI的Word模板引擎,纯Java组件,跨平台,代码短小精悍,通过插件机制使其具有高度扩展性。
poi-tl分为两个版本,其中v1.5.x支持Apache poi3.16+版本和JDK1.6+。
Apache poi4.0.0+要求JDK1.8以上,需要引入最新的poi-tl版本:
{{title}}
Word文档支持DOCX格式,所有的模板标签都是以 {{ 开头,以 }} 结尾,模板标签可以出现在任何非文本框的位置,包括页眉,页脚,表格内部等等。
poi-tl的一个核心特点是 数据模型与样式的分离,同样的数据模型可以用来渲染各种不同样式的模板。
文档的样式继承模板标签的样式,即如果模板{{title}}是蓝色微软雅黑加粗四号字体,则替换后的文本也是蓝色微软雅黑加粗四号字体。
数据源的结构体是一个标签名称和数据模型映射的集合,即[{标签名称, 数据模型}]。数据源可以是一个Map,其中key是标签名称;可以是一个JavaBean,field名称是标签名称,也可以通过注解@Name设置标签名称。
数据模型实现了接口 public interface RenderData {} , 有以下几种数据模型:
TextRenderData、HyperLinkTextRenderData
PictureRenderData
MiniTableRenderData
NumbericRenderData
DocxRenderData
可以将最终结果渲染到任意输出流中,比如输出到文件流FileOutputStream生成新文档,输出到网络流ServletOutputStream供浏览器下载。
poi-tl內建了五种模板。
{{var}}
TextRenderData 或 String 数据模型。
除了继承模板标签样式,也提供了通过代码设定文本样式的方式。
{{@var}}
PictureRenderData 数据模型。
可以指定图片的宽度和高度,也支持 BufferedImage,这样我们可以利用Java生成任意图表插入到word文档中。
{{#var}}
poi-tl默认实现了N行N列的样式(如下图),同时提供了当数据为空时,展示一行空数据的文案(如下图中的No Data Descs),数据模型是 MiniTableRenderData 。
需求的丰富多彩往往是默认表格样式无法满足的,我们通常会遇到以下两个场景:
{{*var}}
NumbericRenderData 数据模型。
{{+var}}
DocxRenderData 数据模型,可以是另一个docx文档的合并,或者是数据集合针对同一个模板的多个渲染结果的合并。
插件机制是poi-tl的核心,默认的五大內建模板语法是通过插件方式加载的。插件的核心逻辑是在模板的基础上通过poi-tl和poi提供的API操作word文档,实现插件就是实现自己的渲染策略。
內建策略是poi-tl自带的一些渲染策略,用来处理文本、图片、列表、表格、文档合并等:
TextRenderPolicy
PictureRenderPolicy
NumbericRenderPolicy
MiniTableRenderPolicy
DocxRenderPolicy
我们可以通过实现 RenderPolicy 接口扩展自己的渲染策略:
AbstractRenderPolicy是一个抽象模板类,定义了一些骨架步骤并且将数据模型的校验和渲染逻辑分开,新的策略继承AbstractRenderPolicy类不是必须的。
所有的插件都是通过如下构建器来配置:
当我们有个模板标签为{{report}},它本身是一个文本模板,如果希望在这个位置做些不一样或者更复杂的事情,我们可以通过构建器设定模板的渲染策略:
比如增加%语法:{{%var}},对应自定义的渲染策略 PercentRenderPolicy,加载插件代码如下:
由于內建模板也是通过插件方式加载的,我们甚至可以改变它们的语法:
这样{{@var}}就变成了表格模板,{{#var}}变成了图片模板,虽然不建议改变內建模板,但是从中可以看到poi-tl插件的设计思想,深藏功与名。
在內建策略中,通常会提供一个静态Helper辅助类,在我们实现自己的RenderPolicy时,也可以通过这些辅助类操作文档。
Spring Expression Language (SpEL)是一个强大的表达式语言,支持在运行时查询和操作对象图。poi-tl的表达式模板支持切换到SpEL模式。
关于SpEL的写法可以参见官网,下面给出一些典型的示例。
高度扩展性表现在其本身的语法也可以自定义,如果你不喜欢 {{}} 的方式,更偏爱freemarker ${} 的方式:
模板引擎不仅仅可以生成文档,也可以生成新的模板,比如我们想构造这样的新模板:把原先的一个模板标签分成两个模板标签:
接下来的示例采取三段式output+template+data-model来说明,首先直接展示生成后的文档,然后一览模板的样子,最后我们对数据模型做个介绍。
output
template
使用poi-tl语法制作模板,可以看到模板标签不仅仅是模板,同样也是样式标签。
这个示例向我们展示了poi-tl最基本的能力,它在模板标签位置,插入基本的数据模型。同时也向我们展示了无需编码设置样式:模板,不仅仅是标签模板,还是样式模板。
output
template
使用{{#order}}生成poi-tl提供的默认样式的表格,设置{{detail_table}}为自定义模板渲染策略(继承抽象表格策略DynamicTableRenderPolicy),自定义已有表格中部分单元格的渲染。
这个示例向我们展示了poi-tl在表格操作上的一些思考。示例中货物明细和人工费的表格就是一个相当复杂的表格,货物明细是由7列组成,行数不定,人工费是由4列组成,行数不定。
默认表格数据模型(MiniTableRenderData)实现了最基本的样式,当需求中的表格更加复杂的时候,我们完全可以设计好那些固定的部分,将需要动态渲染的部分单元格交给自定义模板渲染策略。
poi-tl提供了抽象表格策略DynamicTableRenderPolicy来实现这样的功能,{{detail_table}}标签可以在表格内的任意单元格内,DynamicTableRenderPolicy会获取XWPFTable对象进而获得操作整个表格的能力。
新建渲染策略DetailTablePolicy,继承于抽象表格策略。
将模板标签{{detail_table}}设置成此策略。
output
template
这个示例充分展示了poi-tl的文档模板和循环功能。当有一段固定样式的段落,根据集合数据循环填充后展示。示例中标题+文字+图片就是这样的可重复段落。
output
template
看起来很复杂的简历,其实对于模版引擎来说,和普通的Word文档没有什么区别,我们只需要制作好一份简历,将需要替换的内容用模版标签代替。
因为模版即样式,模版引擎无需考虑样式,只关心数据,我们甚至可以制作10种不同样式的简历模板,用同一份数据去渲染。
Apache License 2.0
poi-tl依赖的apache-poi版本是3.16+,如果你的项目引用了低版本,请升级或删除。
不支持,表格布局可以设计出几乎所有优秀专业的文档,请使用表格。
暂不支持,参考原生POI API自行扩展。
未知,有些朋友尝试成功,但我尚未在Android环境中验证过。
不能够直接设置,目前支持设置文字字体大小等,或者在已经应用了标题样式的模板中替换文本。
暂不支持,如果是简单的图表,可以考虑通过Java提供的 BufferedImage 类创建图片后插入。