7. 示例

接下来的示例采取三段式output+template+data-model来说明,首先直接展示生成后的文档,然后一览模板的样子,最后我们对数据模型做个介绍。

7.1. 软件说明文档

output

需要生成这样的一份软件说明书:拥有封面和页眉,正文含有不同样式的文本,还有表格,列表和图片。下载最终生成的文件poi_tl.docx

example poitl output

template

使用poi-tl语法制作模板,可以看到模板标签不仅仅是模板,同样也是样式标签。

example poitl template

这个示例向我们展示了poi-tl最基本的能力,它在模板标签位置,插入基本的数据模型。同时也向我们展示了无需编码设置样式:模板,不仅仅是标签模板,还是样式模板。

源码参见Junit XWPFTemplateTest

7.2. 付款通知书

output

需要生成这样的一份流行的通知书:大部分数据是由表格构成的,需要创建一个订单的表格,还需要在一个已有表格中,填充货物明细和人工费数据。下载最终生成的文件payment.docx

example payment output

template

使用{{#order}}生成poi-tl提供的默认样式的表格,设置{{detail_table}}为自定义模板渲染策略(继承抽象表格策略DynamicTableRenderPolicy),自定义已有表格中部分单元格的渲染。

example payment template

这个示例向我们展示了poi-tl在表格操作上的一些思考。示例中货物明细和人工费的表格就是一个相当复杂的表格,货物明细是由7列组成,行数不定,人工费是由4列组成,行数不定。

默认表格数据模型(MiniTableRenderData)实现了最基本的样式,当需求中的表格更加复杂的时候,我们完全可以设计好那些固定的部分,将需要动态渲染的部分单元格交给自定义模板渲染策略。

poi-tl提供了抽象表格策略DynamicTableRenderPolicy来实现这样的功能,{{detail_table}}标签可以在表格内的任意单元格内,DynamicTableRenderPolicy会获取XWPFTable对象进而获得操作整个表格的能力。

  1. public abstract class DynamicTableRenderPolicy implements RenderPolicy {
  2. public abstract void render(XWPFTable table, Object data);
  3. }

新建渲染策略DetailTablePolicy,继承于抽象表格策略。

  1. public class DetailTablePolicy extends DynamicTableRenderPolicy {
  2. // 货品填充数据所在行数
  3. int goodsStartRow = 2;
  4. // 人工费填充数据所在行数
  5. int laborsStartRow = 5;
  6. @Override
  7. public void render(XWPFTable table, Object data) {
  8. if (null == data) return;
  9. DetailData detailData = (DetailData) data;
  10. // 人工费循环渲染
  11. List<RowRenderData> labors = detailData.getLabors();
  12. if (null != labors) {
  13. table.removeRow(laborsStartRow);
  14. // 循环插入行
  15. for (int i = 0; i < labors.size(); i++) {
  16. XWPFTableRow insertNewTableRow = table.insertNewTableRow(laborsStartRow);
  17. for (int j = 0; j < 7; j++) insertNewTableRow.createCell();
  18. // 合并单元格
  19. TableTools.mergeCellsHorizonal(table, laborsStartRow, 0, 3);
  20. // 渲染单行人工费数据
  21. MiniTableRenderPolicy.Helper.renderRow(table, laborsStartRow, labors.get(i));
  22. }
  23. }
  24. // 货品明细
  25. List<RowRenderData> goods = detailData.getGoods();
  26. if (null != goods) {
  27. table.removeRow(goodsStartRow);
  28. for (int i = 0; i < goods.size(); i++) {
  29. XWPFTableRow insertNewTableRow = table.insertNewTableRow(goodsStartRow);
  30. for (int j = 0; j < 7; j++) insertNewTableRow.createCell();
  31. // 渲染单行货品明细数据
  32. MiniTableRenderPolicy.Helper.renderRow(table, goodsStartRow, goods.get(i));
  33. }
  34. }
  35. }
  36. }

将模板标签{{detail_table}}设置成此策略。

  1. Configure config = Configure.newBuilder().customPolicy("detail_table", new DetailTablePolicy()).build();
源码参见Junit PaymentExample

7.3. 一篇文章

output

需要生成这样的一系列文章:除了标题作者之外,它的内容是有规律的,内容是由一行蓝色的标题,一段文字,一张图片构成。下载最终生成的文件story.docx

example story output

template

文章的内容是个典型的文档模板类型,我们制作一个待合并的文档模板segment.docx(下图右侧),主模板story.docx看起来很简单,其中{{+segment}}标签将会被文档模板循环合并。

example story template

这个示例充分展示了poi-tl的文档模板和循环功能。当有一段固定样式的段落,根据集合数据循环填充后展示。示例中标题+文字+图片就是这样的可重复段落。

基本原理是后台提供数据模型的集合,不断渲染segment.docx,将渲染结果合并到story.docx文档中。

源码参见Junit StoryExample

7.4. 个人简历

output

需要生成这样的一份流行的个人简历:左侧是个人的基本信息,技术栈是个典型的列表,右侧是个人的工作经历,数量不定。下载最终生成的文件resume.docx

example resume output

template

工作经历是个典型的文档模板类型,我们制作两个模板,一套主模板简历.docx(下图左侧),一套为文档模板segment.docx(下图右侧)。

example resume template

看起来很复杂的简历,其实对于模版引擎来说,和普通的Word文档没有什么区别,我们只需要制作好一份简历,将需要替换的内容用模版标签代替。

因为模版即样式,模版引擎无需考虑样式,只关心数据,我们甚至可以制作10种不同样式的简历模板,用同一份数据去渲染。

源码参见Junit ResumeExample