国产化PDF处理控件Spire.PDF教程:如何在 Java 中通过模板生成 PDF

在企业级应用开发中,生成 PDF 文档是一项非常常见的需求。无论是发票、报告、合同,还是其他业务文档,开发人员通常都需要一种高效、稳定的方式来创建 PDF。与其逐行绘制 PDF 内容,不如直接利用 模板 ——常见的模板形式包括 HTML 模板 和 PDF 模板 ,开发者只需将动态数据填充进去,就能快速生成所需文档。

E-iceblue旗下Spire系列产品,是文档处理领域的佼佼者,支持国产化信创在本文中,我们将介绍如何使用 Spire.PDF for Java 通过模板生成 PDF 文件。文章不仅涵盖 HTML 模板和 PDF 模板的使用方法,还会提供一些高效生成文档的最佳实践和常见问题解答,帮助你在实际项目中更好地应用。

Spire.PDF for Java免费试用下载 

Spire.PDF for Java 简介

Spire.PDF for Java是一个功能强大的 PDF 库,提供用于创建、读取、编辑和转换 PDF 文件的完整 API。它支持以下功能:

  • 从零生成 PDF :可以从头开始创建全新的 PDF 文档。
  • 将 HTML 、图片和文本转换为 PDF :支持将网页内容、图片或文本内容快速生成 PDF。
  • 修改或编辑现有 PDF :可以对已有 PDF 文件进行内容更新或调整。
  • 操作文本、图片、表格和注释 :提供丰富的文档处理能力,满足多样化需求。

安装:

  1. 从官网下载 Spire.PDF for Java,并将 JAR 文件添加到项目的构建路径中。
  2. 如果你使用 Maven 管理项目,可以在 pom.xml 中添加如下依赖:
<repositories><repository><id>com.e-iceblue</id><name>e-iceblue</name><url>https://repo.e-iceblue.cn/repository/maven-public/</url></repository>
</repositories>
<dependencies><dependency><groupId>e-iceblue</groupId><artifactId>spire.pdf</artifactId><version>11.8.3</version></dependency>
</dependencies>

从 HTML 模板创建 PDF

HTML 模板非常灵活,允许你通过 CSS 样式 自定义文档布局和外观。在模板中,你可以定义 占位符 (使用 {{ }} 包裹),在运行时动态替换成实际数据,从而生成个性化的 PDF 文档。

安装 HTML 转 PDF 渲染引擎

Spire.PDF 依赖外部引擎来将 HTML 渲染为 PDF,可以选择 Qt WebEngine或 Google Chrome 。在本指南中,我们将使用 Qt WebEngine 。

  1. 下载适用于操作系统的 Qt WebEngine 插件:

    • Windows x86
    • Windows x64
    • Linux x64
    • Mac x64
  2. 将下载的文件解压到本地文件夹,并找到 plugins 目录,例如:C:\plugins-windows-x64\plugins

  3. 在代码中配置插件路径:

HtmlConverter.setPluginPath("C:\\plugins-windows-x64\\plugins");

完成插件配置后,Spire.PDF 就可以将 HTML 完整渲染为 PDF,并支持 CSS 样式,实现高保真的文档转换。

示例:从 HTML 模板生成发票 PDF

import com.spire.pdf.graphics.PdfMargins;
import com.spire.pdf.htmlconverter.LoadHtmlType;
import com.spire.pdf.htmlconverter.qt.HtmlConverter;
import com.spire.pdf.htmlconverter.qt.Size;import java.util.HashMap;
import java.util.Map;public class CreatePdfFromHtmlTemplate {public static void main(String[] args) {// HTML 模板,使用双大括号 {{}} 作为占位符变量String htmlTemplate = "<!DOCTYPE html>\n" +"<html lang=\"zh\">\n" +"<head>\n" +"    <meta charset=\"UTF-8\">\n" +"    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +"    <title>发票</title>\n" +"    <style>\n" +"        body {\n" +"            font-family: 宋体, sans-serif;\n" +"            margin: 20px;\n" +"            padding: 20px;\n" +"            font-size: 12pt;\n" +"        }\n" +"        h1 {\n" +"            text-align: left;\n" +"            font-size: 30pt;\n" +"        }\n" +"        h2 {\n" +"            font-size: 18pt;\n" +"        }\n" +"        .invoice-header, .invoice-footer {\n" +"            text-align: left;\n" +"            margin-bottom: 20px;\n" +"        }\n" +"        .invoice-details {\n" +"            margin-bottom: 20px;\n" +"        }\n" +"        table {\n" +"            width: 100%;\n" +"            border-collapse: collapse;\n" +"        }\n" +"        th, td {\n" +"            border: 1px solid #ccc;\n" +"            padding: 8px;\n" +"            text-align: left;\n" +"        }\n" +"        th {\n" +"            background-color: #f2f2f2;\n" +"        }\n" +"        .total {\n" +"            font-weight: bold;\n" +"        }\n" +"    </style>\n" +"</head>\n" +"<body>\n" +"    <div class=\"invoice-header\">\n" +"        <h1>发票</h1>\n" +"        <p>发票编号: {{INVOICE_NUMBER}}</p>\n" +"        <p>日期: {{INVOICE_DATE}}</p>\n" +"    </div>\n" +"    <div class=\"invoice-details\">\n" +"        <h2 style=\"margin-top: 50px;\">开票给:</h2>\n" +"        <p>姓名: {{BILLER_NAME}}</p>\n" +"        <p>地址: {{BILLER_ADDRESS}}</p>\n" +"        <p>邮箱: {{BILLER_EMAIL}}</p>\n" +"    </div>\n" +"    <table>\n" +"        <thead>\n" +"            <tr>\n" +"                <th>描述</th>\n" +"                <th>数量</th>\n" +"                <th>单价</th>\n" +"                <th>总计</th>\n" +"            </tr>\n" +"        </thead>\n" +"        <tbody>\n" +"            <tr>\n" +"                <td>{{ITEM_DESCRIPTION}}</td>\n" +"                <td>{{ITEM_QUANTITY}}</td>\n" +"                <td>{{ITEM_UNIT_PRICE}}</td>\n" +"                <td>{{ITEM_TOTAL}}</td>\n" +"            </tr>\n" +"            <!-- 如有需要,可添加更多项目 -->\n" +"        </tbody>\n" +"    </table>\n" +"    <div class=\"total\" style=\"text-align: right;\">\n" +"        <p>小计: {{SUBTOTAL}}</p>\n" +"        <p>税率 ({{TAX_RATE}}%): {{TAX}}</p>\n" +"        <p>总计: {{TOTAL}}</p>\n" +"    </div>\n" +"    <div class=\"invoice-footer\">\n" +"        <p>感谢您的惠顾!</p>\n" +"    </div>\n" +"</body>\n" +"</html>";// 发票示例数据 - 与模板占位符对应的键值对Map<String, String> invoiceData = new HashMap<>();invoiceData.put("INVOICE_NUMBER", "12345");invoiceData.put("INVOICE_DATE", "2025-08-25");invoiceData.put("BILLER_NAME", "张三");invoiceData.put("BILLER_ADDRESS", "北京市朝阳区123号");invoiceData.put("BILLER_EMAIL", "zhangsan@example.comdocument.getElementById('cloak05a0bc07693e7fd0df1ec689a1a701df').innerHTML = '';var prefix = '&#109;a' + 'i&#108;' + '&#116;o';var path = 'hr' + 'ef' + '=';var addy05a0bc07693e7fd0df1ec689a1a701df = 'zh&#97;ngs&#97;n' + '&#64;';addy05a0bc07693e7fd0df1ec689a1a701df = addy05a0bc07693e7fd0df1ec689a1a701df + '&#101;x&#97;mpl&#101;' + '&#46;' + 'c&#111;m';var addy_text05a0bc07693e7fd0df1ec689a1a701df = 'zh&#97;ngs&#97;n' + '&#64;' + '&#101;x&#97;mpl&#101;' + '&#46;' + 'c&#111;m';document.getElementById('cloak05a0bc07693e7fd0df1ec689a1a701df').innerHTML += '<a ' + path + '\'' + prefix + ':' + addy05a0bc07693e7fd0df1ec689a1a701df + '\'>'+addy_text05a0bc07693e7fd0df1ec689a1a701df+'<\/a>';");invoiceData.put("ITEM_DESCRIPTION", "咨询服务");invoiceData.put("ITEM_QUANTITY", "10");invoiceData.put("ITEM_UNIT_PRICE", "¥100");invoiceData.put("ITEM_TOTAL", "¥1000");invoiceData.put("SUBTOTAL", "¥1000");invoiceData.put("TAX_RATE", "5");invoiceData.put("TAX", "¥50");invoiceData.put("TOTAL", "¥1050");// 用实际数据填充 HTML 模板String populatedInvoice = populateInvoice(htmlTemplate, invoiceData);// 指定生成 PDF 的输出路径String outputFile = "output/HtmlToPdf.pdf";// 指定 HTML 转 PDF 转换器插件路径(QT 插件)String pluginPath = "C:\\plugins-windows-x64\\plugins";// 设置 HTML 转 PDF 所需的插件路径HtmlConverter.setPluginPath(pluginPath);// 将 HTML 字符串转换为 PDF 并指定相关设置HtmlConverter.convert(populatedInvoice, outputFile,true, // 启用 JavaScript100000, // 超时时间(毫秒)new Size(595, 842), // A4 页面大小(595x842磅)new PdfMargins(20), // 四边 20 点边距LoadHtmlType.Source_Code); // 从 HTML 源代码字符串加载}// 辅助方法:用数据 Map 中的实际值替换模板占位符private static String populateInvoice(String template, Map<String, String> data) {String result = template;for (Map.Entry<String, String> entry : data.entrySet()) {result = result.replace("{{" + entry.getKey() + "}}", entry.getValue());}return result;}
}

工作原理

  1. 定义一个 HTML 模板,使用占位符 {{PLACEHOLDER_NAME}}。
  2. 将实际数据存储在 Map<String, String> 中。
  3. 在运行时用实际数据替换占位符。
  4. 使用 HtmlConverter.convert 生成带样式的 PDF。

这种方法非常适合 发票、收据和报表 等对格式有要求的文档生成场景。

效果图:

基于现有 PDF 模板生成 PDF

如果你已经有一个预先设计好的 PDF 表单或模板,可以直接在 PDF 内部 替换占位符 来生成新的文档。

示例:替换 PDF 模板中的文本

import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.texts.PdfTextReplaceOptions;
import com.spire.pdf.texts.PdfTextReplacer;
import com.spire.pdf.texts.ReplaceActionType;import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;public class CreatePdfFromPdfTemplate {public static void main(String[] args) {// 创建 PdfDocument 对象PdfDocument doc = new PdfDocument();// 加载现有 PDF 文件doc.loadFromFile("C:\\Users\\Administrator\\Desktop\\Template.pdf");// 创建 PdfTextReplaceOptions 对象并指定替换选项PdfTextReplaceOptions textReplaceOptions = new PdfTextReplaceOptions();textReplaceOptions.setReplaceType(EnumSet.of(ReplaceActionType.WholeWord));// 获取指定页面(此处为第一页)PdfPageBase page = doc.getPages().get(0);// 基于页面创建 PdfTextReplacer 对象PdfTextReplacer textReplacer = new PdfTextReplacer(page);textReplacer.setOptions(textReplaceOptions);// 定义旧字符串和新字符串的字典Map<String, String> replacements = new HashMap<>();replacements.put("{PROJECT_NAME}", "新网站开发");replacements.put("{PROJECT_NO}", "2023-001");replacements.put("{PROJECT MANAGER}", "王五");replacements.put("{PERIOD}", "2023年第3季度");replacements.put("{START_DATE}", "2023年7月1日");replacements.put("{END_DATE}", "2023年9月30日");// 遍历字典,替换占位符文本for (Map.Entry<String, String> pair : replacements.entrySet()) {textReplacer.replaceText(pair.getKey(), pair.getValue());}// 保存修改后的 PDF 到新的文件doc.saveToFile("output/ModifyTemplate.pdf");doc.dispose();}
}

工作原理

  1. 加载现有 PDF 模板。
  2. 使用 PdfTextReplacer 查找并替换占位符文本。
  3. 将更新后的文件保存为新的 PDF。

注意事项

  • 此方法仅适用于 行内文本替换 ,适合替换 短文本 ,例如姓名、日期、ID 或项目编号。
  • 如果需要插入 多行文本 (例如长描述、条款或表格行),此方法不适用,因为 PDF 文本替换不会自动调整页面布局。

多行文本的替代方案

  • 设计 HTML 或 Word 模板,然后转换为 PDF。
  • 使用 Spire.PDF 的文本绘制 API 编程生成新的文本块到 PDF 页面上。

效果图:

使用模板生成 PDF 的最佳实践

  • 使用 HTML 模板以获得灵活性 :适合发票、收据和报表等场景,尤其是需要处理长文本块、表格和 CSS 样式的文档。
  • 使用 PDF 模板以保证严格布局 :适合简单的占位符替换,如项目名称、日期或编号等固定内容。
  • 在适当情况下使用 Word 模板 :如果你的模板是 Word 格式,可以使用 Spire.Doc for Java。它支持在 Word 文件中修改占位符,并直接导出为 PDF。
  • 保持占位符唯一性 :使用清晰的标记,例如 {START_DATE} 或 {END_DATE},以避免意外替换。
  • 集中管理模板 :将模板存放在代码外部,以便于更新和维护。
  • 使用真实数据进行测试 :在投入生产使用前,务必验证格式、对齐方式和文本换行效果。

常见问题与解答

Q1:我可以在 Java 中通过 Word 模板创建 PDF 文件吗?

可以,但需要使用 Spire.Doc for Java。它允许你替换 Word 文档中的占位符,然后将结果导出为 PDF。

Q2:在通过模板生成 PDF 时,我可以添加图片或图表吗?

可以。使用 Spire.PDF,无论是基于 HTML 模板生成,还是修改现有 PDF 模板,都可以嵌入图片、图表和形状。

Q3:HTML 转 PDF 是否需要 Qt WebEngine 或 Google Chrome?

是的,需要插件来准确渲染带样式和 CSS 的 HTML。

  • Qt WebEngine :易于配置,轻量级,且在 Spire.PDF 示例中常用。
  • Google Chrome :对于复杂的现代网页(尤其是包含高级 CSS 或 JavaScript 的页面)提供更高保真度。

建议:简单文档使用 Qt WebEngine,要求与最新浏览器渲染效果一致时使用 Google Chrome。

Q4:Spire.PDF for Java 支持模板中的多语言文本吗?

支持。Spire.PDF 完全支持 Unicode,可以生成包含多种语言(如英文、中文、阿拉伯语或印地语)的 PDF,且不会丢失格式。

总结

通过将 Spire.PDF集成到 Java 项目中,你可以从 HTML 模板 或 PDF 模板 高效生成专业的 PDF 文档。根据不同场景的需求:

  • 使用 PDF 模板 :适合轻量级文本替换。
  • 使用 HTML 模板 :当需要丰富的格式、表格、图片或多行内容时更合适。
  • 配置 Qt WebEngine 或 Google Chrome :确保 HTML 转 PDF 的渲染准确无误。

如果你的工作流程已经依赖 Word 模板,可以考虑使用 Spire.Doc for Java ,它能够:

  • 轻松替换 Word 文档中的占位符。
  • 保留所有 Word 格式、样式和布局。
  • 无损导出为 PDF,确保文档高保真。

这种灵活性让你可以根据模板格式选择合适的工具 —— Spire.PDF 或 Spire.Doc ,从而确保 PDF 输出既高效又高质量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/96249.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/96249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Gateway WebFlux现cvss10分高危漏洞,可导致环境属性篡改

漏洞概述Spring官方披露了Spring Cloud Gateway Server WebFlux组件中存在一个高危漏洞&#xff08;编号CVE-2025-41243&#xff09;&#xff0c;该漏洞在特定配置下允许攻击者篡改Spring环境属性。该漏洞已获得CVSS 10.0的最高严重性评级。根据安全公告&#xff0c;该漏洞被描…

嵌入式 SQLite 数据库开发笔记

嵌入式 SQLite 数据库开发入门笔记在嵌入式开发中&#xff0c;数据存储与管理是不可或缺的环节。对于资源有限的系统&#xff0c;轻量级数据库 SQLite 是一个非常理想的选择。它无需独立服务进程&#xff0c;直接嵌入到应用中即可使用&#xff0c;既能满足数据持久化需求&#…

Spark面试题及详细答案100道(71-80)-- 配置与部署

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

Redis 面试

1、主从集群1、构建主从集群单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离。主写从读&#xff0c;主可以读也可以写&#xff0c;从只能读利用docker-compose文件来构建主从集群&#xff1a;…

如何使用PostgreSQL数据库进行数据挖掘与预测分析

如何使用PostgreSQL数据库进行数据挖掘与预测分析 关键词:PostgreSQL,数据挖掘,预测分析,数据库,机器学习 摘要:本文旨在深入探讨如何利用PostgreSQL数据库进行数据挖掘与预测分析。首先介绍了使用PostgreSQL进行此类操作的背景信息,包括目的、预期读者、文档结构等。接…

ZooKeeper vs Redis:分布式锁的实现与选型指南

一、Redis 分布式锁&#xff1a;追求极致的性能 Redis 分布式锁基于内存操作&#xff0c;其核心思想是在内存中设置一个唯一的键值对来表示锁的持有。 1. 基础实现&#xff08;SETNX Lua&#xff09; 最简单的实现是使用 SETNX&#xff08;SET if Not eXists&#xff09;命令&…

vue基于Springboot框架的考研咨询平台系统实现

目录前言-本系统介绍已开发项目效果实现截图开发技术详细介绍核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码获取详细视频演示或者查看其他版本&#xff1a;文章底部获取博主联系方式&…

苹果用户速更新!macOS存严重漏洞,用户隐私数据面临泄露风险

漏洞概况近日&#xff0c;macOS系统发现一个CVSS评分高达 9.8 的高危漏洞&#xff0c;该漏洞可能允许应用程序绕过系统保护机制&#xff0c;非法访问受保护的用户数据。该漏洞编号为 CVE-2025-24204&#xff0c;目前已有概念验证&#xff08;PoC&#xff09;代码公开。漏洞影响…

海盗王64位dx9客户端修改篇之五

在海盗王3.0客户都升级64位dx9版本的过程中&#xff0c;因为特效的问题&#xff0c;被卡壳了很久。 开始是精灵草的粒子效果、白银城的烟囱烟雾效果、篝火的效果、阳光透射效果、海浪效果等&#xff0c;修了很长的时间&#xff0c;才找到窍门弄好。 然后是精灵效果、角色阴影。…

Linux学习——管理网络安全(二十一)

一、管理服务器防火墙&#xff08;firewalld&#xff09;RHEL 默认使用 firewalld 作为防火墙管理工具&#xff0c;它通过 “区域&#xff08;zone&#xff09;” 和 “服务&#xff08;service&#xff09;” 的概念简化规则配置&#xff0c;支持动态更新规则而无需重启服务。…

leetcode-python-1941检查是否所有字符出现次数相同

题目&#xff1a; 给你一个字符串 s &#xff0c;如果 s 是一个 好 字符串&#xff0c;请你返回 true &#xff0c;否则请返回 false 。 如果 s 中出现过的 所有 字符的出现次数 相同 &#xff0c;那么我们称字符串 s 是 好 字符串。 示例 1&#xff1a; 输入&#xff1a;s “…

Snort的介绍

当然可以。以下是对 Snort 的全面介绍&#xff0c;涵盖其定义、核心功能、三种运行模式、工作原理、规则系统以及应用场景等内容。 Snort 网络入侵检测系统&#xff08;NIDS&#xff09;详解 一、Snort 简介 Snort 是一款开源的、轻量级但功能强大的 网络入侵检测与防御系统&…

滴滴二面准备(一)

结合你的简历内容和技术面试问题&#xff0c;以下是一个结构化的回答建议&#xff0c;突出你的技术深度和项目经验&#xff1a;2. 项目与实习经历 得物低代码落地页编辑器&#xff08;核心项目&#xff09; 背景&#xff1a;解决软广落地页开发周期长、迭代慢问题。技术方案&am…

socket通信在Windows和Linux上的区别

前言 笔者在将socket通信的自定义类从Linux移植到Windows时遇到一些问题&#xff0c;整理下来希望帮助到需要的人&#xff0c;同时也加深自己的理解。 差异 头文件 #ifdef _WIN32 #include <ws2tcpip.h> #define inet_pton InetPton #define SHUT_RDWR SD_BOTH #define M…

一款将PDF转化为机器可读格式的工具介绍

ps:以下内容来自MinerU项目 MinerU 项目简介 MinerU是一款将PDF转化为机器可读格式的工具&#xff08;如markdown、json&#xff09;&#xff0c;可以很方便地抽取为任意格式。 MinerU诞生于书生-浦语的预训练过程中&#xff0c;我们将会集中精力解决科技文献中的符号转化问…

代码随想录算法训练营第三十九天|62.不同路径 63.不同路径ll

62.不同路径&#xff1a; 文档讲解&#xff1a;代码随想录|62.不同路径 视频讲解&#xff1a;https://www.bilibili.com/video/BV1ve4y1x7Eu 状态&#xff1a;已做出 一、题目要求&#xff1a; 一个二维数组里&#xff0c;将(0&#xff0c;0)位置下标作为起点&#xff0c;计算…

openEuler2403安装部署Prometheus和Grafana

文章目录openEuler2403安装部署Prometheus和Grafana一、前言1.简介2.环境二、正文1.环境准备1&#xff09;JDK 安装部署&#xff08;可选&#xff09;2&#xff09;关闭防火墙2.安装 Prometheus1&#xff09;下载和安装2&#xff09;启动3&#xff09;systemd服务管理3.安装 Gr…

乐吾乐大屏可视化组态软件【SQL数据源】

乐吾乐大屏可视化组态软件&#xff08;大屏可视化设计器 - 乐吾乐Le5le&#xff09;支持直接对接SQL数据源功能&#xff0c;目前仅对企业源码客户开放。 配置SQL数据源 管理员进入可视化管理中心&#xff0c;点击SQL数据源&#xff0c;配置添加SQL数据源。 创建SQL数据源连接 …

Django高效查询:values_list实战详解

Django 实战案例 讲解 values_list 的用法。 values_list("field", flatTrue) → 获取单字段的一维列表。values_list("f1", "f2") → 获取多个字段&#xff0c;返回元组。搭配 filter / distinct / in / 外键查询 非常高效。适合用于 导出数据 …

Java数据结构——树

一、树型结构1.1 概念我们之前提到的数组&#xff0c;单链表&#xff0c;栈和队列都是一种线性结构&#xff0c;每个元素都有最多一个后继节点。而树型结构是一种非线性结构&#xff0c;它是由n&#xff08;n>0&#xff09;节点组成的一个具有层次关系的集合。它之所以叫做树…