1. 概述
在本教程中,我们将通过一个简洁实用的示例,学习如何以 Thymeleaf 作为模板引擎来生成 PDF 文件。
你可能已经用 Thymeleaf 做过服务端页面渲染,但其实它也能配合其他工具,简单粗暴地生成 PDF。这种方式特别适合生成合同、账单、报告等格式固定、样式丰富的文档,避免手动拼接 HTML 或硬编码布局。
核心思路是:
✅ 先用 Thymeleaf 渲染出 HTML 字符串
✅ 再通过支持 CSS 的 HTML 转 PDF 工具(如 flying-saucer)将 HTML 转为 PDF
整个过程灵活且易于维护,模板可复用,样式也能用 CSS 控制。
2. Maven 依赖
首先,引入 Thymeleaf 的基础依赖:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
⚠️ 注意:Thymeleaf 本身只是一个模板引擎,不能直接生成 PDF。我们需要借助第三方库将 HTML + CSS 转成 PDF。
这里我们选择 flying-saucer-pdf,它基于 iText,支持大部分 CSS2 和部分 CSS3,对中文也能较好支持(稍后可配字体)。
添加依赖:
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.5.1</version>
</dependency>
✅ 替代方案:你也可以用
thymeleaf-extras-pdf
或直接集成 iText+HTMLWorker,但 flying-saucer 更轻量、易上手,适合大多数场景。
3. 生成 PDF 实战
3.1 编写 Thymeleaf 模板
创建一个简单的 HTML 模板文件:src/main/resources/thymeleaf_template.html
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h3 style="text-align: center; color: green">
<span th:text="'Welcome to ' + ${to} + '!'"></span>
</h3>
</body>
</html>
这个模板很简单:
- 使用内联 CSS 控制样式
- 通过
${to}
变量动态填充内容
💡 提示:你可以在这里使用完整的 HTML 结构和 CSS 样式,甚至引入外部样式表(需确保 flying-saucer 能读取资源路径)
3.2 渲染模板为 HTML
接下来,写一个方法将 Thymeleaf 模板渲染成 HTML 字符串:
private String parseThymeleafTemplate() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
Context context = new Context();
context.setVariable("to", "Baeldung");
return templateEngine.process("thymeleaf_template", context);
}
关键点说明:
ClassLoaderTemplateResolver
:从类路径下加载模板TemplateMode.HTML
:设置为 HTML 模式Context
:传入变量上下文,支持 EL 表达式templateEngine.process(...)
:返回渲染后的 HTML 字符串
3.3 将 HTML 转为 PDF
最后一步,使用 flying-saucer 把 HTML 字符串转成 PDF 文件:
public void generatePdfFromHtml(String html) throws Exception {
String outputFolder = System.getProperty("user.home") + File.separator + "thymeleaf.pdf";
OutputStream outputStream = new FileOutputStream(outputFolder);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(outputStream);
outputStream.close();
}
✅ 调用流程:
setDocumentFromString(html)
:加载 HTML 字符串layout()
:执行布局计算(必须调用)createPDF(outputStream)
:生成 PDF 并输出
⚠️ 注意事项:
- 必须调用
layout()
,否则会抛异常 - 输出流使用完记得关闭,建议用 try-with-resources
- 不支持 JavaScript,但支持大部分 CSS(包括
@page
分页控制)
3.4 运行效果
运行上述代码后,在用户主目录下会生成一个名为 thymeleaf.pdf
的文件:
效果如下:
- 文字居中对齐
- 颜色为绿色
- 内容为 “Welcome to Baeldung!”
完全符合模板中的 CSS 定义。
✅ 这说明:Thymeleaf + flying-saucer 能正确解析内联样式并渲染到 PDF,这对复杂排版非常关键。
3.5 扩展建议
中文支持踩坑
默认情况下,flying-saucer 对中文显示可能乱码或方块字。解决方法:
- 在 HTML 中指定中文字体(如 SimSun、Microsoft YaHei)
- 注册字体到 ITextRenderer
示例代码:
ITextRenderer renderer = new ITextRenderer();
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("C:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // Windows 示例
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(outputStream);
⚠️ 生产环境建议将字体文件打包进 resources,避免路径依赖。
更复杂的模板
你可以构建更复杂的模板,比如包含表格、页眉页脚、分页符等:
<style>
@page {
size: A4;
margin: 1cm;
}
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
</style>
<table>
<tr>
<th>姓名</th>
<th>邮箱</th>
</tr>
<tr>
<td th:text="${user.name}">张三</td>
<td th:text="${user.email}">zhangsan@example.com</td>
</tr>
</table>
Thymeleaf 的强大之处就在于:模板可复用、数据可动态传入、结构清晰易维护。
4. 总结
本文演示了如何结合 Thymeleaf 和 flying-saucer 实现 PDF 自动生成:
- ✅ 使用 Thymeleaf 渲染 HTML 模板,支持变量、条件判断、循环等特性
- ✅ 利用 flying-saucer 将 HTML 转为高质量 PDF,保留 CSS 样式
- ✅ 整体结构解耦,未来可替换为 FreeMarker、Velocity 等其他模板引擎
🔗 所有示例代码已上传至 GitHub:https://github.com/baeldung/tutorials/tree/master/text-processing-libraries-modules/pdf
这种方案在实际项目中非常实用,尤其适合需要动态生成带样式的文档的场景,比如导出报表、生成合同、发票打印等。
只要 HTML 能画出来,PDF 就能生成出来 —— 这就是它的魅力所在。