Java使用poi-tl1.9.1生成Word文档的技巧分享
作者:夜郎king
前言
也许在您的工作当中会碰到如下的一些场景,比如您需要组织一个活动,主办方需要对每个报名参加的单位进行报名通知书的生成。也许您会说,参加活动的不多,可以采取人工的方式进行信息填写,然后再发送给报名人员就好。如果仅是几个人或者几十个人还好,如果是几百人,您还会选择这种方式么?
针对这种批量动态个性化的word生成需求,有没有什么技术可以进行辅助生成呢?一定是有的,在Java的世界中,实现这种需求有好几种实现方案,本文推荐一种简单快捷的实现方式,基于开源的poi-tl的实现机制。本文将简单介绍poi-tl的相关知识,通过一个实际的案例实践,充分介绍如何利用poi-tl进行目标文档的生成,同时分享几个不同的office版本如何进行图表生成的解决方案。如果刚好您也在进行相关技术选型,本文可以作为使用参考。
一、poi-tl简介
1、什么是poi-tl
poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档。poi-tl是一款采用Apache License 2.0开源协议的开源产品,poi-tl github地址。
2、常见的word生成对比
方案 | 移植性 | 功能性 | 易用性 |
---|---|---|---|
Poi-tl | Java跨平台 | Word模板引擎,基于Apache POI,提供更友好的API | 低代码,准备文档模板和数据即可 |
Apache POI | Java跨平台 | Apache项目,封装了常见的文档操作,也可以操作底层XML结构 | 文档不全,这里有一个教程:Apache POI Word快速入门 |
Freemarker | XML跨平台 | 仅支持文本,很大的局限性 | 不推荐,XML结构的代码几乎无法维护 |
OpenOffice | 部署OpenOffice,移植性较差 | - | 需要了解OpenOffice的API |
HTML浏览器导出 | 依赖浏览器的实现,移植性较差 | HTML不能很好的兼容Word的格式,样式糟糕 | - |
Jacob、winlib | Windows平台 | - | 复杂,完全不推荐使用 |
3、poi-tl功能点
d模板引擎功能 | 描述 |
---|---|
文本 | 将标签渲染为文本 |
图片 | 将标签渲染为图片 |
表格 | 将标签渲染为表格 |
列表 | 将标签渲染为列表 |
图表 | 条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)、散点图等图表渲染 |
If Condition判断 | 根据条件隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等) |
Foreach Loop循环 | 根据集合循环某些文档内容(包括文本、段落、图片、表格、列表、图表等) |
Loop表格行 | 循环复制渲染表格的某一行 |
Loop表格列 | 循环复制渲染表格的某一列 |
Loop有序列表 | 支持有序列表的循环,同时支持多级列表 |
Highlight代码高亮 | word中代码块高亮展示,支持26种语言和上百种着色样式 |
Markdown | 将Markdown渲染为word文档 |
Word批注 | 完整的批注功能,创建批注、修改批注等 |
Word附件 | Word中插入附件 |
SDT内容控件 | 内容控件内标签支持 |
Textbox文本框 | 文本框内标签支持 |
图片替换 | 将原有图片替换成另一张图片 |
书签、锚点、超链接 | 支持设置书签,文档内锚点和超链接功能 |
Expression Language | 完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL… |
样式 | 模板即样式,同时代码也可以设置样式 |
模板嵌套 | 模板包含子模板,子模板再包含子模板 |
合并 | Word合并Merge,也可以在指定位置进行合并 |
用户自定义函数(插件) | 插件化设计,在文档任何位置执行函数 |
二、poi-tl文档生成
通常来说,我们会先制作好一个标准的参考模板,让后将需要替换的数据替换到目标参数中,完成相应参数的替换,所以刚开始先来准备一分word模板。
1、模板准备
在电脑的任意盘符,这里以D盘为例,创建一个文件输入.docx的word文档,打开文档编辑
在这个模板中定义了文本标签以及图片的定义。这里注意的是,{{}}这对标准参数符必须是英文状态下输入,之前有朋友就是没注意中英文,导致程序没有按照预期出来。同时注意图片的引用符在参数名称前面加上英文@符号。
在模板中加入一些图表信息,丰富素材,这里需要设置。我个人电脑上的图表参数设置如下(尤其注意不同的office版本,对应的编辑处理方法不一致):
2、目标参数填充
首先在工程中进行pom.xml引入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yelang</groupId> <artifactId>poi-demo2</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.1</version> </dependency> </dependencies> </project>
参数填充:这里为了演示方便,直接构造演示数据,真实项目中可以使从数据库或者其它接口获取相应的数据来进行参数的设置。
package com.yelang.test; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.data.ChartMultiSeriesRenderData; import com.deepoove.poi.data.Charts; import com.deepoove.poi.data.PictureRenderData; import com.deepoove.poi.data.PictureType; import com.deepoove.poi.data.Pictures; import com.deepoove.poi.data.SeriesRenderData; public class TestMain2 { public static void main(String[] args) throws IOException { Map<String, Object> map = new HashMap<String,Object>(); map.put("score", "28"); map.put("title", "测试任务"); map.put("type", "上半年综合测评"); map.put("status", "已完成"); map.put("time", "2023-07-18"); map.put("locpicture", new PictureRenderData(400, 300, "D:/image.jpg")); map.put("urlImg", Pictures.ofUrl("https://p1.itc.cn/images01/20230418/5d13ab4a86c04a8dac668bf4129e1f0c.png", PictureType.PNG).size(400, 300).create()); ChartMultiSeriesRenderData sbqk = Charts .ofMultiSeries("十六市区县情况", new String[] { "济南","青岛","烟台","威海"}) .addSeries("上报情况", new Double[] { 15.0,20.6,42.6,90.1}) .addSeries("查出情况", new Double[] { 12.0,15.3,28.6,80.1}) .create(); map.put("sbqk", sbqk); ChartMultiSeriesRenderData sjzlpm = Charts .ofMultiSeries("医院综合排名", new String[] { "山东大学齐鲁医院","山东省泰山医院","山东省第二人民医院","山东省第三医院"}) .addSeries("数据质量排名", new Double[] { 70.5,40.6,22.7,85.4}) .addSeries("价格质量排名", new Double[] { 80.5,75.6,72.7,85.4}) .create(); map.put("sjzlpm", sjzlpm); ChartMultiSeriesRenderData qst = Charts .ofMultiSeries("任务趋势", new String[] { "06-10","06-11","06-12","06-13","06-14","06-15"}) .addSeries("微信端", new Double[] { 70.5,40.6,22.7,85.4,700.0,40.8}) .addSeries("PC端", new Double[] { 80.5,50.6,62.7,45.4,200.0,140.8}) .addSeries("小程序端", new Double[] { 120.5,520.6,362.7,405.4,300.0,340.8}) .create(); map.put("qst", qst); //柱状图、折线图共存 List<SeriesRenderData> seriesRenderData = new ArrayList<SeriesRenderData>(3); SeriesRenderData series1 = new SeriesRenderData("GDP", new Double[] {70.5,40.6,22.7,85.4,700.0,40.8}); series1.setComboType(SeriesRenderData.ComboType.BAR); seriesRenderData.add(series1); SeriesRenderData series2 = new SeriesRenderData("人口", new Double[] {80.5,50.6,62.7,45.4,200.0,140.8}); series2.setComboType(SeriesRenderData.ComboType.BAR); seriesRenderData.add(series2); SeriesRenderData series3 = new SeriesRenderData("指数", new Double[] {0.6,0.6,0.7,0.4,0.7,0.8}); series3.setComboType(SeriesRenderData.ComboType.LINE); seriesRenderData.add(series3); ChartMultiSeriesRenderData hntb = Charts .ofMultiSeries("某省社会排名", new String[] { "城市1","城市2","城市3","城市4","城市5","城市6"}) .create(); hntb.setSeriesDatas(seriesRenderData); map.put("hntb", hntb); File file = new File("D:/文件输入.docx"); XWPFTemplate template = XWPFTemplate.compile(file).render(map); FileOutputStream out = new FileOutputStream(new File("D:\\文件输出.docx")); template.write(out); out.flush(); out.close(); template.close(); System.out.println("完成"); } }
3、生成效果
将以上代码运行后,可以在D盘目录中看到以下的输出结果。
三、可能会遇到的问题
1、混合图表生成报错
如果在代码运行过程中遇到以下异常:
Exception in thread "main" com.deepoove.poi.exception.RenderException: Combo chart must set comboType field of series! at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.validate(MultiSeriesChartTemplateRenderPolicy.java:122) at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.doRender(MultiSeriesChartTemplateRenderPolicy.java:56) at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.doRender(MultiSeriesChartTemplateRenderPolicy.java:48) at com.deepoove.poi.policy.reference.AbstractTemplateRenderPolicy.render(AbstractTemplateRenderPolicy.java:38) at com.deepoove.poi.render.processor.ElementProcessor.visit(ElementProcessor.java:70) at com.deepoove.poi.render.processor.ElementProcessor.visit(ElementProcessor.java:56) at com.deepoove.poi.template.ChartTemplate.accept(ChartTemplate.java:117) at com.deepoove.poi.render.processor.DocumentProcessor.visit(DocumentProcessor.java:104) at com.deepoove.poi.template.ChartTemplate.accept(ChartTemplate.java:117) at com.deepoove.poi.render.processor.DocumentProcessor.lambda$process$0(DocumentProcessor.java:58) at java.util.ArrayList.forEach(Unknown Source) at com.deepoove.poi.render.processor.DocumentProcessor.process(DocumentProcessor.java:58) at com.deepoove.poi.render.DefaultRender.renderTemplate(DefaultRender.java:82) at com.deepoove.poi.render.DefaultRender.render(DefaultRender.java:64) at com.deepoove.poi.XWPFTemplate.render(XWPFTemplate.java:127) at com.yelang.test.TestMain2.main(TestMain2.java:87)
Combo chart must set comboType field of series!这个问题大概率是因为图表生成没有指定图表类型。只需要的代码中指定图表类型即可,错误参考代码如下:
ChartMultiSeriesRenderData hntb = Charts .ofMultiSeries("title", new String[] { "06-10","06-11","06-12","06-13","06-14","06-15"}) .addSeries("系列1", new Double[] { 70.5,40.6,22.7,85.4,700.0,40.8}) .addSeries("系列1", new Double[] { 80.5,50.6,62.7,45.4,200.0,140.8}) .addSeries("系列1", new Double[] { 120.5,520.6,362.7,405.4,300.0,340.8}) .create();
正确参考代码,通过series3.setComboType(SeriesRenderData.ComboType.LINE)指定:
List<SeriesRenderData> seriesRenderData = new ArrayList<SeriesRenderData>(3); SeriesRenderData series1 = new SeriesRenderData("GDP", new Double[] {70.5,40.6,22.7,85.4,700.0,40.8}); series1.setComboType(SeriesRenderData.ComboType.BAR); seriesRenderData.add(series1); SeriesRenderData series2 = new SeriesRenderData("人口", new Double[] {80.5,50.6,62.7,45.4,200.0,140.8}); series2.setComboType(SeriesRenderData.ComboType.BAR); seriesRenderData.add(series2); SeriesRenderData series3 = new SeriesRenderData("指数", new Double[] {0.6,0.6,0.7,0.4,0.7,0.8}); series3.setComboType(SeriesRenderData.ComboType.LINE); seriesRenderData.add(series3); ChartMultiSeriesRenderData hntb = Charts .ofMultiSeries("某省社会排名", new String[] { "城市1","城市2","城市3","城市4","城市5","城市6"}) .create(); hntb.setSeriesDatas(seriesRenderData);
2、图表参数设置技巧
不同版本的office,对于参数的替换设置界面不一样,尤其需要注意。这里提供两种环境的设置参考。第一种是office2021。
office2021的图表参数设置是鼠标点击图表右键,出现查看可选文字,在这里进行设置。
在以wps为例,本机安装版本为:wps11.1.0
双击图表空白区,在文字选项中的大小与属性一栏进行设置
总结
以上就是本文的主要内容,本文将简单介绍poi-tl的相关知识,通过一个实际的案例实践,充分介绍如何利用poi-tl进行目标文档的生成,同时分享几个不同的office版本如何进行图表生成的解决方案。如果刚好您也在进行相关技术选型,本文可以作为使用参考。行文仓促,如有不当之处,欢迎批评指正。
poi-tl 参考技术网站:https://deepoove.com/poi-tl/
以上就是Java使用poi-tl1.9.1生成Word文档的技巧分享的详细内容,更多关于Java使用poi-tl1.9.1生成Word的资料请关注脚本之家其它相关文章!