根据模板动态生成word(三)使用poi-tl生成word
@
一、前言
1、什么是poi-tl
poi-tl是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库。同类型的FreeMarker或Velocity基于文本模板和数据生成新的html页面或配置文件。而poi tl是一个基于Word模板和数据生成新文档的Word模板引擎。
Word模板具有丰富的样式。Poi-tl将在生成的文档中完美地保留模板中的样式。也可以设置标记的样式。标记的样式将应用于替换的文本,因此您可以专注于模板设计。
poi-tl是一个“无逻辑”模板引擎。没有复杂的控制结构和变量分配,只有标签,有些标签可以用文本、图片、表格等代替,有些标签会隐藏某些文档内容,而另一些标签会循环一系列文档内容。
像变量赋值或条件语句这样的“强大”构造可以很容易地在模板系统中专门修改应用程序的外观。。。然而,以分离为代价,将模板本身变成应用程序逻辑的一部分。
poi-tl支持自定义函数(插件),函数可以在Word模板的任何地方执行,在文档的任何地方做任何事情都是poi-tl的目标。
2、官方信息
2.1 源码仓库
GitHub - Sayi/poi-tl: Generate awesome word(docx) with template
2.2 中文文档
Poi-tl Documentation (deepoove.com)
2.3 开源协议
Apache License 2.0
3、poi-tl的优势
3.1 poi-tl和其他模板引擎的对比
下面表格是官方文档中提供的与其他模板引擎的对比
| 方案 | 移植性 | 功能性 | 易用性 |
|---|---|---|---|
| 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.2 poi-tl Word模板引擎支持的功能
| Word模板引擎功能 | 描述 |
|---|---|
| 文本 | 将标签渲染为文本 |
| 图片 | 将标签渲染为图片 |
| 表格 | 将标签渲染为表格 |
| 列表 | 将标签渲染为列表 |
| 图表 | 条形图(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,也可以在指定位置进行合并 |
| 用户自定义函数(插件) | 插件化设计,在文档任何位置执行函数 |
二、基本的使用配置
1、引入依赖
1.1 Maven
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.1</version>
</dependency>
1.2 Gradle
implementation 'com.deepoove:poi-tl:1.12.1'
2、配置
2.1 新建配置
ConfigureBuilder builder = Configure.builder();
2.2 标签前后缀替换
poi-tl所有的标签都是以{{开头,以}}结尾,这是为了致敬Google CTemplate。标签可以出现在任何位置,包括页眉,页脚,表格内部,文本框等,表格布局可以设计出很多优秀专业的文档,推荐使用表格布局。
当然如果你更偏爱freemarker ${} 的方式,也可以添加如下配置修改标签的前后缀配置:
builder.buildGramer("${", "}");
2.3 加载模板
XWPFTemplate template = XWPFTemplate.compile("template.docx", builder.buid());
poi-tl加载使用XWPFTemplate.compile方法来加载模板,支持模板以绝对路径(String),File、InputStream、XWPFDocument四种格式传入。
2.4 填充数据
poi-tl数据类似于哈希或者字典,可以是Map结构(key是标签名称):
Map<String, Object> data = new HashMap<>();
data.put("name", "Sayi");
data.put("start_time", "2019-08-04");
template.render(dataMap);
2.5 输出文件
poi-tl以流的方式进行输出:
template.write(OutputStream stream);
可以写到任意输出流中,比如文件流:
template.write(new FileOutputStream("output.docx"));
如网络流:
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");
// HttpServletResponse response
OutputStream out = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, bos, out);
三、各类模板标签替换和填充
1 文本
1.1 文本的标签如下
{{var}}
1.2 支持数据类型
String:文本TextRenderData:有样式的文本HyperlinkTextRenderData:超链接和锚点文本Object:调用 toString() 方法转化为文本
1.3 文本数据填充方式如下
代码示例
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操作符,还提供了更加优雅的工厂 Texts 和链式调用的方式轻松构建文本模型。
链式代码示例
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());
2 图片
2.1 图片的标签如下:
图片标签以@开始:{{@var}}
2.2 支持数据类型
String:图片url或者本地路径,默认使用图片自身尺寸PictureRenderDataByteArrayPictureRenderDataFilePictureRenderDataUrlPictureRenderData
2.3 图片数据填充方式如下
// 指定图片路径
put("image", "logo.png");
// svg图片
put("svg", "https://img.shields.io/badge/jdk-1.6%2B-orange.svg");
// 设置图片宽高
put("image1", Pictures.ofLocal("logo.png").size(120, 120).create());
// 图片流
put("streamImg", Pictures.ofStream(new FileInputStream("logo.jpeg"), PictureType.JPEG)
.size(100, 120).create());
// 网络图片(注意网络耗时对系统可能的性能影响)
put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png")
.size(100, 100).create());
// java图片
put("buffered", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
.size(100, 100).create());
3 表格
3.1 表格的标签如下:
表格标签以#开始:{{#var}}
3.2 支持数据类型
TableRenderData
3.3 表格数据填充方式如下
- 基础表格示例
// 一个2行2列的表格
put("table0", Tables.of(new String[][] {
new String[] { "00", "01" },
new String[] { "10", "11" }
}).border(BorderStyle.DEFAULT).create());
- 表格样式示例
// 第0行居中且背景为蓝色的表格
RowRenderData row0 = Rows.of("姓名", "学历").textColor("FFFFFF")
.bgColor("4472C4").center().create();
RowRenderData row1 = Rows.create("李四", "博士");
put("table1", Tables.create(row0, row1));
- 表格合并示例
// 合并第1行所有单元格的表格
RowRenderData row0 = Rows.of("列0", "列1", "列2").center().bgColor("4472C4").create();
RowRenderData row1 = Rows.create("没有数据", null, null);
MergeCellRule rule = MergeCellRule.builder().map(Grid.of(1, 0), Grid.of(1, 2)).build();
put("table3", Tables.of(row0, row1).mergeRule(rule).create());
4、列表
4.1 列表的标签如下:
列表标签以*开始:{{*var}}
4.2 支持数据类型
List<String>NumberingRenderData
4.3 列表数据填充方式如下
put("list", Numberings.create("Plug-in grammar",
"Supports word text, pictures, table...",
"Not just templates"));
四、验证
1、准备模板
首先我们建立一个word文件,在word文件里填充一下内容。

2、准备测试代码
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.*;
import dto.Qiankuan;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.*;
public class PoitlTest {
public static void main(String[] args) throws IOException {
ConfigureBuilder builder = Configure.builder();
//获取模板的文件流
FileInputStream fileInputStream = new FileInputStream("D:\\文章\\word生成\\poi-tl\\qiantiao.docx");
HashMap<String, Object> dataMap = new HashMap<>();
//添加文本
LocalDate currentDate = LocalDate.now();
LocalDate endDate = currentDate.plusYears(1L);
dataMap.put("debtor", "陈有楚");
dataMap.put("nowYear", String.valueOf(currentDate.getYear()));
dataMap.put("nowMonth", String.valueOf(currentDate.getMonthValue()));
dataMap.put("nowDay", String.valueOf(currentDate.getDayOfMonth()));
//验证换行的情况
dataMap.put("arrears", "\n一顿老魏,\n贵州大黄牛,\nv我50");
dataMap.put("endYear", String.valueOf(endDate.getYear()));
dataMap.put("endMonth", String.valueOf(endDate.getMonthValue()));
dataMap.put("endDay", String.valueOf(endDate.getDayOfMonth()));
//添加列表
List<String> list = Arrays.asList("阿大", "阿二", "阿三");
Numberings.NumberingBuilder numberingBuilder = Numberings.of(NumberingFormat.DECIMAL);
for (String s : list) {
numberingBuilder.addItem(s);
}
dataMap.put("witness", numberingBuilder.create());
//添加图片,考虑到实际生产环境图片大都都从文件服务获取,所以这里用图片流做例子
PictureRenderData pictureRenderData = Pictures.ofStream(Files.newInputStream(Paths.get("D:\\picture\\其他\\24-05-23-142418.png")), PictureType.JPEG)
.size(300, 220).create();
dataMap.put("image1", pictureRenderData);
List<Qiankuan> qiankuanList = getQiankuanList();
//添加表格
//填充表头,表格的第一行
RowRenderData row0 = Rows.of("拖欠物品", "拖欠次数", "偿还期限").center().bgColor("4472C4").create();
Tables.TableBuilder tableBuilder = Tables.of(row0);
//填充表格内容
for (Qiankuan qiankuan : qiankuanList) {
RowRenderData row = Rows.create(qiankuan.getName(), String.valueOf(qiankuan.getCount()), qiankuan.getQixian());
tableBuilder.addRow(row);
}
//MergeCellRule rule = MergeCellRule.builder().map(MergeCellRule.Grid.of(1, 0), MergeCellRule.Grid.of(1, 2)).build();
//tableBuilder.mergeRule(rule);
dataMap.put("table1", tableBuilder.create());
ChartMultiSeriesRenderData chart = Charts
.ofMultiSeries("ChartTitle", new String[] { "中文", "English" })
.addSeries("countries", new Double[] { 15.0, 6.0 })
.addSeries("speakers", new Double[] { 223.0, 119.0 })
.create();
dataMap.put("barChart", chart);
XWPFTemplate template = XWPFTemplate.compile(fileInputStream, builder.build())
.render(dataMap);
template.writeAndClose(Files.newOutputStream(Paths.get("D:\\test\\qiantiao-poitl.docx")));
System.out.println("success");
}
static List<Qiankuan> getQiankuanList() {
List<Qiankuan> list = new ArrayList<>();
Qiankuan q1 = new Qiankuan();
q1.setName("一顿老魏");
q1.setCount(1);
q1.setQixian("三月内");
list.add(q1);
Qiankuan q2 = new Qiankuan();
q2.setName("一顿大黄牛");
q2.setCount(1);
q2.setQixian("半年内");
list.add(q2);
Qiankuan q3 = new Qiankuan();
q3.setName("特一特");
q3.setCount(3);
q3.setQixian("一周内");
list.add(q3);
Qiankuan q4 = new Qiankuan();
q4.setName("v我50");
q4.setCount(5);
q4.setQixian("一周内");
list.add(q4);
return list;
}
}
3、生成效果

根据模板动态生成word(三)使用poi-tl生成word的更多相关文章
- 【POI word】使用POI实现对Word的读取以及生成
项目结构如下: 那第一部分:先是读取Word文档 package com.it.WordTest; import java.io.FileInputStream; import java.io.Fil ...
- Freemaker基于word模板动态导出压缩文件汇总整理
Freemaker基于word模板动态导出压缩文件汇总整理 Freemaker基于word模板动态导出单个文件思路和代码详情见连接: https://www.cnblogs.com/lsy-blogs ...
- C# 处理Word自动生成报告 三、设计模板
C# 处理Word自动生成报告 一.概述 C# 处理Word自动生成报告 二.数据源例子 C# 处理Word自动生成报告 三.设计模板 C# 处理Word自动生成报告 四.程序处理 既然是模板就少不了 ...
- Freemaker基于word模板动态导出汇总整理
Freemaker基于word模板动态导出汇总整理 一.使用的jar包: 二.Word模板动态导出的基本思路: 1.首先通过自己在word中创建好需要导出的word文本+表格的模板,模板中需要填写内容 ...
- C#读取Word模板替换相应的字符串(标签)生成新的Word
在平常工作中,生成word的方式主要是C#读取html的模板文件处理之后保存为.doc文件,这样的好处是方便,快捷,能满足大部分的需求.不过有些特殊的需求并不能满足,如要生成的Word为一个表格,只是 ...
- 利用html模板生成Word文件(服务器端不需要安装Word)
利用html模板生成Word文件(服务器端不需要安装Word) 由于管理的原因,不能在服务器上安装Office相关组件,所以只能采用客户端读取Html模板,后台对模板中标记的字段数据替换并返回给客户端 ...
- 使用T4模板动态生成NPoco实体类
这是一个妥妥的NPoco类,这是我们在工作开发中,手动去写这个实体类,属实非常心累,字段少无所谓一次两次,数量多了,字段多了,就心态裂开
- 三种C#.net生成静态页面的方法
ASP.NET生成静态页面方法主要有三种 第一种方法:向服务器的动态页面发送请求,获取页面的html代码.这种方法缺点显而易见:速度慢.另外如果请求的动态页面有验证控件的话,返回的html页面却无 ...
- ASP.NET实现二维码 ASP.Net上传文件 SQL基础语法 C# 动态创建数据库三(MySQL) Net Core 实现谷歌翻译ApI 免费版 C#发布和调试WebService ajax调用WebService实现数据库操作 C# 实体类转json数据过滤掉字段为null的字段
ASP.NET实现二维码 using System;using System.Collections.Generic;using System.Drawing;using System.Linq;us ...
- WIN 下的超动态菜单(三)代码
WIN 下的超动态菜单(一)简介 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/ 超动态 ...
随机推荐
- 使用CodeArts发布OBS,函数工作流刷新CDN缓存
摘要:上次通过OBS和CDN部署来Hexo网站,但是每次我们不可能都自己编译然后在上传到OBS,不然太麻烦了,所以我们需要构建流水线,通过PUSH Markdown来发布文章. 本文分享自华为云社区& ...
- Node.js入门学习笔记
NodeJs是js的运行时,意味着可以在浏览器外运行js.可以使用nodejs来构建服务器端应用.CLI应用.Web API,甚至用electron构建桌面端应用. 使用nvm来管理node版本. 在 ...
- Linux 内存管理 pt.1
哈喽大家好,我是咸鱼 今天我们来学习一下 Linux 操作系统核心之一:内存 跟 CPU 一样,内存也是操作系统最核心的功能之一,内存主要用来存储系统和程序的指令.数据.缓存等 关于内存的学习,我会尽 ...
- win系统安装wordcloud报错解决方案
问题: ① 在命令行下执行pip install wordcloud出现报错如图: ②下载了错误的whl文件, 出现wordcloud-1.4.1-cp27-cp27m-win_amd64.whl i ...
- cf1774f解题报告
Magician and Pigs 分析一下三个操作分别干了些什么 新添一只猪 使血量为 \(x\) 的猪血量变为 \(\max(x-v,0)\) 设前面操作后猪总共会受到 \(s\) 的伤害,复制一 ...
- Finalshell
使用VMware可以得到Linux虚拟机,但是在VMware中操作Linux的命令行页面不太方便 1.内容的复制.粘贴跨越VMware不方便 2.文件的上传.下载跨越VMware不方便 3.也就是和L ...
- 2023-03-25:若两个正整数的和为素数,则这两个正整数称之为“素数伴侣“。 给定N(偶数)个正整数中挑选出若干对,组成“素数伴侣“, 例如有4个正整数:2,5,6,13, 如果将5和6分为一组的
2023-03-25:若两个正整数的和为素数,则这两个正整数称之为"素数伴侣". 给定N(偶数)个正整数中挑选出若干对,组成"素数伴侣", 例如有4个正整数:2 ...
- 2020-12-03:mysql中,Heap 表是什么?
福哥答案2020-12-04:[答案来自此链接:](http://bbs.xiangxueketang.cn/question/605) Heap表,即使用MEMORY存储引擎的表,这种表的数据存储在 ...
- 2022-01-20: 矩形区域不超过 K 的最大数值和。 给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。 题目数据保证总会存在一
2022-01-20: 矩形区域不超过 K 的最大数值和. 给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和. 题目数据保证总会存在一 ...
- Python中json.dump()和json.dumps()的区别
一.图解 json.dumps(dict, indent):将Python对象转换成json字符串 json.dump(dict, file_pointer):将Python对象写入json文件 二. ...