Java 动态模板转 PDF 并输出高清晰度图片
企业级方案:Java 动态模板转 PDF 并输出高清晰度图片
(Freemarker + iText7 + PDFBox 深度实战)
1. 为什么选择这个组合?
在生产环境中,我们需要解决三大痛点:
-
样式还原度:iText7 配合 html2pdf 对 CSS3 的支持远超 iText5。
-
中文乱码:通过自定义 FontProvider 完美解决。
-
动态图片:支持将 Base64 或网络图片直接注入模板。
2. 依赖管理 (Maven)
引入最新的稳定版本,确保功能最全。
code Xml
downloadcontent_copy
expand_less
4.0.5
2.0.29
org.freemarker
freemarker
2.3.31
com.itextpdf
html2pdf
${itext.version}
org.apache.pdfbox
pdfbox
${pdfbox.version}
org.projectlombok
lombok
1.18.24
true
3. 高级 HTML 模板设计 (report.ftl)
为了体现专业性,我们加入表格、Base64图片、分页符和页眉页脚区域。
code Html
play_circledownloadcontent_copy
expand_less
${title}
尊敬的 ${userName}:
以下是您的季度消费明细:
日期
项目
金额
<#list items as item>
${item.date}
${item.name}
¥${item.price}
#list>
附件:详细条款
此处为第二页内容...
4. 核心工具类封装
4.1 资源路径与字体处理
在生成PDF时,最麻烦的是字体。建议将 .ttf 字体文件放在 resources/fonts 下。
code Java
downloadcontent_copy
expand_less
@Slf4j
public class PdfGeneratorUtil {
/**
* 执行HTML转PDF
* @param htmlContent 填充后的HTML字符串
* @param destFile 目标文件
*/
public static void createPdf(String htmlContent, File destFile) throws IOException {
ConverterProperties props = new ConverterProperties();
FontProvider fontProvider = new FontProvider();
// 1. 自动注册系统字体(Windows/Linux下存在的字体)
fontProvider.addSystemFonts();
// 2. 显式注册自定义字体(解决Linux环境无宋体的问题)
// String fontPath = "src/main/resources/fonts/simsun.ttf";
// fontProvider.addFont(fontPath);
props.setFontProvider(fontProvider);
// 3. 设置基础路径,方便读取相对路径的图片
// props.setBaseUri("src/main/resources/static/");
try (FileOutputStream os = new FileOutputStream(destFile)) {
HtmlConverter.convertToPdf(htmlContent, os, props);
}
}
}
4.2 PDF 转多张高清图片
如果 PDF 有多页,我们需要循环渲染并拼接或单独保存。
code Java
downloadcontent_copy
expand_less
public class ImageGeneratorUtil {
/**
* PDF转图片
* @param pdfFile 源文件
* @param outputDir 输出目录
* @param dpi 渲染精度,建议300
*/
public static List pdfToImages(File pdfFile, String outputDir, int dpi) throws IOException {
List imagePaths = new ArrayList<>();
try (PDDocument document = PDDocument.load(pdfFile)) {
PDFRenderer renderer = new PDFRenderer(document);
for (int i = 0; i < document.getNumberOfPages(); i++) {
BufferedImage image = renderer.renderImageWithDPI(i, dpi);
String fileName = "page_" + (i + 1) + ".png";
File outFile = new File(outputDir, fileName);
ImageIO.write(image, "PNG", outFile);
imagePaths.add(outFile.getAbsolutePath());
}
}
return imagePaths;
}
}
5. 业务逻辑整合
code Java
downloadcontent_copy
expand_less
@Service
public class DocumentService {
public void generateReport(String userId) throws Exception {
// 1. 模拟数据准备
Map data = new HashMap<>();
data.put("title", "2023年度消费报告");
data.put("userName", "王小明");
data.put("logoBase64", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."); // 实际使用时替换为真实Base64
List
6. 避坑指南(进阶必看)
-
Linux 环境部署:
-
现象:本地 Windows 正常,发到服务器中文全乱码。
-
根因:Linux 默认没有 SimSun (宋体)。
-
对策:必须在 Java 代码中通过 fontProvider.addFont("path/to/simsun.ttf") 手动加载字体文件。
-
-
图片加载失败:
-
现象:
标签找不到图片。
-
对策:推荐将图片转为 Base64 字符串通过模板注入,这样不需要处理复杂的文件路径问题,也不会受限于内网防火墙。
-
-
内存溢出:
-
现象:生成超大型 PDF(几百页)时 OOM。
-
对策:iText7 支持 PdfDocument 的流式写入。对于 PDF 转图片,避免一次加载过大的 PDF 文件,或者分批处理页面。
-
-
CSS 支持限制:
-
iText7 虽然强大,但不支持 flex 和 grid 布局。请回归到 float 布局或 table 布局。
-
7. 总结
本文展示了从模板引擎渲染到PDF高保真转换,再到高清图像输出的完整闭环。这套方案不仅适用于简单的证书生成,也能胜任复杂的金融报表导出。
源码获取/讨论:欢迎在评论区交流,点赞收藏不迷路!








