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();
}

✅ 调用流程:

  1. setDocumentFromString(html):加载 HTML 字符串
  2. layout():执行布局计算(必须调用)
  3. createPDF(outputStream):生成 PDF 并输出

⚠️ 注意事项:

  • 必须调用 layout(),否则会抛异常
  • 输出流使用完记得关闭,建议用 try-with-resources
  • 不支持 JavaScript,但支持大部分 CSS(包括 @page 分页控制)

3.4 运行效果

运行上述代码后,在用户主目录下会生成一个名为 thymeleaf.pdf 的文件:

baeldung pdf

效果如下:

  • 文字居中对齐
  • 颜色为绿色
  • 内容为 “Welcome to Baeldung!”

完全符合模板中的 CSS 定义。

✅ 这说明:Thymeleaf + flying-saucer 能正确解析内联样式并渲染到 PDF,这对复杂排版非常关键。


3.5 扩展建议

中文支持踩坑

默认情况下,flying-saucer 对中文显示可能乱码或方块字。解决方法:

  1. 在 HTML 中指定中文字体(如 SimSun、Microsoft YaHei)
  2. 注册字体到 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 就能生成出来 —— 这就是它的魅力所在。


原始标题:Generating PDF Files Using Thymeleaf