乐于分享
好东西不私藏

被动态PDF导出折磨?对比了4种方案,找到了最优解

被动态PDF导出折磨?对比了4种方案,找到了最优解

前言

在业务系统中,动态生成 PDF 基本是高频需求。无论是合同单据、对账报表、电子凭证,还是合规归档文件,都离不开稳定、可控的 PDF 生成能力。

最近在一个核心业务场景中,我们需要把报表动态生成 PDF 文件。这个需求看似常见,实际约束非常多:不仅要求文本可复制、内容可检索、图文清晰不失真,还要求页眉页脚精准控制、百页级文档稳定导出、跨平台部署方便。为了找到真正适合业务落地的方案,我们对四种主流技术路径进行了调研和实际验证:

  • html2pdf
  • wkhtmltopdf
  • DOCX 模板 + LibreOffice
  • XML 模板 + LibreOffice 本文将结合真实业务需求,从实现方式、生成质量、性能表现、维护成本等多个维度,对这四种方案进行系统对比,并给出最终的选型结论与落地经验。

业务背景

本次需求主要用于生成报表PDF文件,单个文件页数最高可达 50 页。这意味着方案不仅要“能生成”,还要在质量、性能和维护性上都满足业务要求。 核心诉求总结为以下几点:

  • 格式精准度 支持自定义页眉页脚,适配企业LOGO、页码、文件编号、生成时间等固定信息,并确保不同页数下排版稳定、不发生错位;

  • 展示效果可靠 文字、表格、图标和矢量图形在导出后保持清晰,放大后无明显模糊或锯齿,满足打印和归档场景;

  • 文档具备可用性 生成后的 PDF 中文本应支持复制和检索,避免输出为纯图片式 PDF,影响后续查阅和数据提取;

  • 性能与部署可接受 对于 50 页级别的大文件,生成耗时需要可控;同时方案应支持跨平台部署,具备合理的研发成本与长期维护成本。

基于以上诉求,我们没有停留在理论层面的选型,而是对每种方案进行了实际调研与验证,希望最终方案既能满足当前需求,也能兼顾后续迭代的可持续性。

四种技术方案

一、纯前端:html2pdf

核心原理:通过 HTML/CSS 构建文档结构,利用 Canvas 渲染可视化内容,再通过 PDF 库合成文件。

  • 实现步骤
  1. 编写含页眉页脚的 HTML 模板;

  2. 使用 CSS 控制页眉、页脚和分页样式;

  3. 调用 html2canvas 捕获页面内容;

  4. 用 jsPDF 注入 Canvas 图像,通过addPage()实现多页拼接。

// 页眉页脚动态渲染function createPdf() {    var element = document.getElementById('element-print');    var elementHeader = document.getElementById('header');    var elementFooter = document.getElementById('footer');    html2pdf().from(elementHeader).toImg().get('img').then(function(headerImg) {        html2pdf().from(elementFooter).toImg().get('img').then(function(footerImg) {            // opt config            html2pdf().set(opt).from(element).toPdf().get('pdf').then(function(pdf){                var totalPages = pdf.internal.getNumberOfPages();                for (i = 1; i <= totalPages; i++) {                    pdf.setPage(i);                    pdf.addImage(headerImg, 'JPEG'0.108.20.8);                    pdf.addImage(footerImg, 'JPEG'0.122.18.11.3);                }            }).save();        });    });}

二、前后端结合方案:wkhtmltopdf

核心原理:基于Qt WebKit渲染引擎,将HTML/CSS页面直接转换为PDF。

  • 实现步骤
  1. 引入 Java 封装库(如wkhtmltopdf-java);

  2. 编写 HTML 模板(支持 CSS3 分页、页眉页脚);

  3. 配置引擎参数实现高质量转换。

Pdf pdf = new Pdf();// 页眉页脚配置pdf.addParam("--header-html""http://localhost/header.html");pdf.addParam("--footer-center""页码 \[page]/\[topage]");// 矢量输出与文本可复制pdf.addParam("--dpi""300"); // 高清渲染pdf.addParam("--disable-smart-shrinking"""); // 保持文本矢量属性// 转换执行byte[] pdfBytes = pdf.convert("http://xxx.domain.name/report.html");

三、DOCX模板+libreoffice

核心原理:以 DOCX 作为模板文件,在模板中预留占位符,业务侧完成数据替换后,再通过 LibreOffice 命令行将 DOCX 转换为 PDF。

  • 实现步骤
  1. 创建 DOCX 模板,预先设计文档结构、页眉页脚及样式;
  2. 在模板中预留 ${orderNo} 等占位符;
  3. 使用 Apache POI 等工具替换模板变量;
  4. 调用 LibreOffice 命令行将 DOCX 转换为 PDF。
XWPFDocument doc = new XWPFDocument(new FileInputStream("template.docx"));// 替换段落占位符doc.getParagraphs().forEach(p -> {    p.getRuns().forEach(r -> {        String text = r.getText(0);        if (text.contains("${orderNo}")) {            r.setText(text.replace("${orderNo}""ORD2025001"), 0);        }    });});

执行转换

soffice --headless --convert-to pdf template.docx

四、XML模板+libreoffice

核心原理:DOCX 文件本质上是基于 Open XML 标准的压缩包。可以先将 DOCX 模板解压,直接修改其中的 XML 内容完成变量替换,再重新打包生成 DOCX,最后通过 LibreOffice 转换为 PDF。

  • 实现步骤
  1. 制作 DOCX模板(预留${orderNo}等占位符,设置页眉页脚样式);
  2. 解压DOCX模板;
  3. 替换XML中占位符;
  4. 重新压缩为DOCX,执行libreoffice命令生成PDF文件。
// 解压获取document.xml文件unzip template_V5.docx -d /template/// 省略 替换XML中占位符// 压缩并转为DOCXzip -r  /template/ ./cp template.zip template.docxsoffice --headless --convert-to pdf template.docx

方案选定

方案对比

对比维度
html2pdf
wkhtmltopdf
DOCX模板+libreoffice
XML模板+libreoffice
实现复杂度
页眉页脚支持
矢量内容保真
文本可复制/检索
跨平台部署
50页性能
模板维护性
研发成本
复杂文档稳定性

说明:本次 50 页 PDF 生成性能对比,不含业务数据下载与数据填充环节;各方案资源与耗时如下:

  • html2pdf:内存占用(JS 堆)66.5MB,生成耗时 22 秒;
  • wkhtmltopdf:内存占用 59.5MB,生成耗时 4 秒;
  • LibreOffice:内存占用 130MB,生成耗时 10 秒。

方案选择

综上对比:html2pdf 虽支持页眉页脚,但不兼容矢量内容保真输出,且生成文本无法复制;wkhtmltopdf 在 Linux 环境部署后,页面渲染样式与前端原生 HTML 差异较大,表格样式尤其难以满足业务规范;DOCX 模板方案则存在开发实现复杂度高、维护成本大的问题。

综合评估后,最终选定 XML 模板 + LibreOffice 作为动态 PDF 生成的核心方案,选型依据如下:

  1. 文档质量更稳定该方案能够保留文字、表格和矢量图形的清晰度,生成后的 PDF 支持复制与检索,能够满足打印、归档和合规类场景对文档质量的要求。

  2. 更适合复杂模板场景相比基于 Apache POI 的对象替换方式,直接处理底层 XML 可以减少 Word 文档 run 拆分、样式继承等问题带来的影响,对于复杂页眉页脚、动态表格和多页文档场景更友好。

  3. 在大页数场景下更可控对于单文件页数较多的场景,XML 模板方案在生成链路上更直接,问题定位也更清晰,更适合作为稳定的服务端文档生成方案。

  4. 长期维护成本更低尽管初期需要理解 DOCX 的 Open XML 结构,但在模板规则稳定后,后续扩展和排查都会更聚焦,适合长期演进和多模板并行维护。

  5. 良好的扩展能力该方案便于支持动态表格、签章、水印、多模板切换等后续需求,能够较好适配业务迭代。

遇到的问题

  • html2pdf方案生成 PDF 单页内容少、页面利用率低。

规避浏览器最小字体限制,采用 transform: scale() 整体缩放内容容器,压缩整体排版,增加单页展示内容。

  • wkhtmltopdf 不支持 JS 异步接口请求,生成的 PDF 仅包含空白模板,无业务数据。

改造为静态 HTML 生成方案,将业务数据、模板、CSS 直接合并为完整页面,让 PDF 生成脱离 JS 依赖,保证数据正常渲染。

总结

动态生成 PDF 的技术选型,本质上是在 生成质量、实现成本、运行性能、维护复杂度 之间寻找平衡。

  • 如果需求简单、开发周期短,可以优先考虑 html2pdf
  • 如果现有系统以 HTML 模板为主,且文档复杂度适中,可以考虑 wkhtmltopdf
  • 如果希望通过可视化模板快速落地,DOCX 模板 + LibreOffice 是一个可行方案;
  • 而对于 格式要求高、页数多、需要长期维护和扩展 的业务场景,XML 模板 + LibreOffice 更适合作为长期方案。

本次选型过程中,我们不仅关注了方案的实现难度,也重点验证了其在真实业务场景中的稳定性和可维护性。最终选择XML模板方案,本质上是为了在满足当前需求的同时,为后续同类场景复用提供更稳健的技术基础。

作者简介

招聘信息

往期精彩内容指路

把7个页面变成1段对话:AI如何重构借款流程
打破“知识孤岛”:微服务架构下的自动化业务图谱构建
信也AI赋能慢SQL治理的探索与实践
海外支付路由探索与实践
基石Redis实例自动化调度之路