本文内容从Poi-tl官方文档摘录,poi-tl是一个基于Java POI的Word模板引擎,有着非常强大的功能
此处只摘录了一些我现在需要用到的功能
文章目录
一、简单示例
1.准备一个模板文档 template.docx
在文档中写上{{title}}
2.写一个测试方法
public class PoiTlTest01 {
@Test
public void testStart() throws IOException {
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(new HashMap<String,Object>(){{
put("title","Hi,poi-tl Word模板引擎");
}});
template.writeAndClose(new FileOutputStream("output.docx"));
}
}
二、标签
1.文本
{{var}}
数据模型:
String 文本
TextRenderData 有样式的文本
HyperlinkTextRenderData 超连接文本
Object 调用toString()方法转换为文本
代码示例:
put("name", "Sayi");
put("author", new TextRenderData("000000", "Sayi"));
put("link", new HyperlinkTextRenderData("website", "http://deepoove.com"));
put("anchor", new HyperlinkTextRenderData("anchortxt", "anchor:appendix1"));
除了使用上面这这种new
方法,还可以使用链式调用,使用更优雅的Text
来构建文本模型
put("name", "Sayi");
put("author", Texts.of("Sayi").color("000000").create());
put("link", Texts.of("website").link("http://deepoove.com").create());
put("anchor", Texts.of("anchortxt").anchor("appendix1").create());
TextRenderData的结构体
{
"text": "Sayi",
"style": {
"strike": false, // 删除线
"bold": true, // 粗体
"italic": false, // 斜体
"color": "00FF00", // 颜色
"underLine": false, // 下划线
"fontFamily": "微软雅黑", // 字体样式
"fontSize": 12, // 字体大小
"highlightColor": "green", // 背景高亮色
"vertAlign": "superscript", // 上标或者下标
"characterSpacing" : 20 // 间距
}
}
文本之间换行使用\n
2.列表
列表标签以*开始:{{*var}}
数据模型:
- NumberingReanderData
推荐使用工厂Numberings构建编号模型
示例代码:
put("list", Numberings.create("Plug-in grammar",
"Supports word text, pictures, table...",
"Not just templates"));
编号样式支持罗马字符、有序无序等,可以通过Numberings.os(NumberingFormat)
来指定
DECIMAL //1. 2. 3.
DECIMAL_PARENTHESES //1) 2) 3)
BULLET //● ● ●
LOWER_LETTER //a. b. c.
LOWER_ROMAN //i ⅱ ⅲ
UPPER_LETTER //A. B. C.
3.区块对
区块对由前后两个标签组成,开始标签以?
标识,结束标签以/
标识,如:
{{?sections}}{{/sections}}
区块对应用范围
区块对开始和结束标签中间可以包含以下内容:
- 多个图片
- 表格
- 段落
- 列表
- 图表
- 等…
其中开始和结束标签可以跨多个段落,也可以在同一个段落,但是如果在表格中使用区块对,开始和结束标签必须在同一个单元格内,因为跨多个单元格的渲染行为是未知的
。
区块对在处理一些列文档元素的时候非常有用,位于区块对中的文档元素可以被渲染零次,一次或N次,这取决于区块对的取值。
False或空集合
隐藏区块中的所有文档元素
如果区块对的值是null、false或者空的集合,位于区块中的所有文档元素将不会显示,等同于if
语句的条件为false
如下:
data-model
{
"announce": false
}
------
template.docx
Made it,Ma!{{?announce}}Top of the world!{{/announce}}
Made it,Ma!
{{?announce}}
Top of the world!🎋
{{/announce}}
----
output.docx
Made it,Ma!
Made it,Ma!
非False且不是集合
显示区块中的文档元素 渲染一次
如果区块对的值不为null、false,且不是集合,位于区块中的所有文档元素会被渲染一次,等同于if语句的条件为true
data-model
{
"person": { "name": "Sayi" }
}
-----
template.docx
{{?person}}
Hi {{name}}!
{{/person}}
-----
output.docx
Hi Sayi!
区块对中标签的作用域会被限定在当前区块对内,当且仅当区块对的值是 boolean 类型且为 true 时,这些标签作用域才不会改变。
非空集合
根据集合的大小,循环渲染区块中的文档元素
如果区块对的值是一个非空集合,区块中的文档元素会被迭代渲染一次或者N次,这取决于集合的大小,类似foreach
语句
data-model
{
"songs": [
{ "name": "Memories" },
{ "name": "Sugar" },
{ "name": "Last Dance" }
]
}
----
template.docx
{{?songs}}
{{name}}
{{/songs}}
----
output.docx
Memories
Sugar
Last Dance
上述的集合是根据值的类型是否实现了Iterable
迭代器接口来判断
循环内置变量
在循环中提供了一些内置banliang,这些内置变量只能用于区块对中
变量 | 类型 | 说明 |
---|---|---|
_index | int | 返回当前迭代从0开始的索引 |
_is_first | boolean | 判别循环项是否是当前迭代的第一项 |
_is_last | boolean | 判别循环项是否是当前迭代的最后一项 |
_has_next | boolean | 判别循环项是否是有下一项 |
_is_even_item | boolean | 判别循环项是否是当前迭代间隔1的奇数项 |
_is_odd_item | boolean | 判别循环项是否是当前迭代间隔1的偶数项 |
#this | object | 引用当前对象,由于#和已有表格标标识的冲突,所以在文本标签中需要使用= 号 |
示例数据:
{
"produces": [
"application/json",
"application/xml"
]
}
-----
template.docx:
{{?produces}}
{{_index}}. {{=#this}}
{{/produces}}
----
output.docx:
0. application/json
1. application/xml
4.嵌套
嵌套又称为导入
、包含或者合并,以+
标识:{{+var}}
数据模型:
- DocxRenderData
推荐使用工厂Includes
构建嵌套模型
List<SegmentData> subData = new ArrayList<SegmentData>();
SegmentData s1 = new SegmentData();
s1.setTitle("Monday");
s1.setContent("Two parallel lines will intersect one day.");
subData.add(s1);
SegmentData s2 = new SegmentData();
s2.setTitle("Friday");
s2.setContent("Confidence is not necessarily successful.");
subData.add(s2);
//主模板包含嵌套标签{{+docx_word}}
//segment.docx是一个包含了{{title}}和{{content}}的子模板,使用subData集合渲染后合并到主模板
put("docx_word", Includes.ofLocal("segment.docx").setRenderModel(subData).create());
三、配置
poi-tl提供了配置类Configure
来存储常用的设置,配置的使用方式如下:
ConfigureBuilder builder = Configure.builder();
XWPFTemplate.compile("template.docx", builder.buid());
1.前后缀
可以使用builder.buildGramer()方法来修改标签的前后缀
2.标签类型
默认的图片标签是以@
开始,也可以使用%
开始作为图片标签
builder.addPlugin('%', new PictureRenderPolicy());
也可以根据自己的需求 对我标签标识类型进行更改
builder.addPlugin('@', new TableRenderPolicy());
builder.addPlugin('#', new PictureRenderPolicy());
这样{{@var}}
就ibanchengle表格标签,{{#var}}
就变成了图片标签,虽然不建议对默认标签标识进行修改,但是从中可以看到poi-tl插件的灵活度
3.标签正则
标签默认支持中文、字母、数字、下划线
的组合,可以通过正则表达式来配置标签的规则,比如不允许中:
builder.buildGrammerRegex("[\\w]+(\\.[\\w]+)*");
比如允许除了标签前后缀以外的任意字符:
builder.buildGrammerRegex(RegexUtils.createGeneral("{{", "}}"));
4.计算标签值
计算标签值是指如何在数据中索引标签的值,可以完全自定义计算的方式:
builder.setRenderDataComputeFactory(new RenderDataComputeFactory());
RenderDataComputeFactory是一个抽象工厂,你可以定义自己的工厂提供标签表达式计算接口 RenderDataCompute 的实现。
5.Spring表达式
Spring Expression Language 是一个强大的表达式语言,支持在运行时查询和操作对象图。在使用前需要引入相应的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
为了在模板标签中使用SpringEL表达式,需要将标签配置为SpringEL模式:
builder.useSpringEL();
6.SpringEL作为区块对的条件
Spring表达式与区块对结合可以实现更强大的功能
data-model
{
"desc": "",
"summary": "Find A Pet",
"produces": [
"application/xml"
]
}
----
template.docx
{{?desc == null or desc == ''}}{{summary}}{{/}}
{{?produces == null or produces.size() == 0}}无{{/}}
----
output.docx
Find A Pet
使用SpringEL时区块对的结束标签可以是:{{/}}
7.错误处理
poi-tl支持在发生错误的时候定制引擎的行为
标签无法被计算
标签无法被计算的场景有几种,比如模板中引用了一个不存在的变量,或者级联的前置结果不是一个哈希,如 {{author.name}} 中author的值为null,此时就无法计算name的值。
poi-tl可以在发生这种错误时对计算结果进行配置,默认会认为标签值为 null 。当我们需要严格校验模板是否有人为失误时,可以抛出异常:
builder.useDefaultEL(true);
注意的是,如果使用SpringEL表达式,可以通过参数来配置异常处理的方式:
builder.useSpringEL(true);
标签数据类型不合法
我们知道渲染图片、表格等标签时对数据模型是有要求的,如果数据不合法(为空或者是一个错误的数据类型),可以配置模板标签的渲染行为。
poi-tl默认的行为会清空标签,如果希望保留标签:
builder.setValidErrorHandler(new DiscardHandler());
如果希望执行严格的校验,直接抛出异常:
builder.setValidErrorHandler(new AbortHandler());
8.模板生成模板
模板引擎不仅仅可以生成文档,也可以生成新的模板,比如我们把原先的一个模板标签分成两个模板标签:
put("title", "{{title}}\n{{subtitle}}");