告别 iText 繁杂配置:jquick-pdf 极简 PDF 生成实战(零基础上手)
引入
很多开发者第一次生成 PDF,会被 Document、Page、Font、Paragraph、Table 等对象包围:代码能生成文件,却很难一眼看懂最终版式;改文案要改 Java,改间距要重新定位。iText 与 PdfBox 都是成熟的底层工具,但"强大"不等于适合每一次业务导出。jquick-pdf 把常见文档表达为类 HTML 模板,让零基础开发者也能循序渐进地完成一份可维护的 PDF。本文以一个可直接运行的培训结业证书 Demo 为主线,说明它做了什么、没做什么,以及从零开始的推进顺序。
核心讲解
从底层 API 到模板表达
底层方案与模板方案的差别在抽象位置。底层 API 由开发者直接构造页面、字体与表格对象,控制粒度最细,但版式意图分散在大量方法调用里,评审时很难只看代码还原页面;模板方案把版式写成声明式文本,Java 只提供数据。两者都要求开发者理解页面结构,区别在于"页面结构用哪种形式表达、由谁维护"。需要客观说明:iText 在生态成熟度与底层对象控制上更强,PdfBox 给出最细的绘制控制;jquick-pdf 的取舍是用更少代码覆盖常见结构化文档,而不是覆盖全部 PDF 场景。
极简调用背后的完整链路
一行 new JQuickPdfFactory().bind(...).executeContent(template) 背后仍有完整流程:工厂保存变量上下文与 JPdfConfig;执行方法把模板交给解析器;解析器读取元素与 style 属性;布局引擎处理块、文本、表格与分页;最后由 PDFBox 渲染器输出字节数组。开发者不必手动管理页面坐标,但仍要尊重模板语言的约束,因为解析器只认识既定元素与属性。
模板与数据分离的收益
把固定文案留在模板、把姓名和日期留在绑定数据中,是这套架构最重要的分工,收益体现在三方面:
- 文案与版式调整不必改动业务逻辑,模板可以独立评审与版本化。
- 服务层只负责准备展示值,测试时构造一个 Map 即可验证渲染结果。
- 同一份模板可被多个数据源复用,更换查询逻辑不影响排版。
代价是引入了一层新语法,需要按样例确认元素与属性的可用边界,不能凭浏览器经验推断。
模块与能力边界
核心 jquick-pdfx 提供标题、段落、span、div、列表、表格、图片、SVG 与表单域等能力;jquick-pdf-svg 提供 30+ 图表,jquick-pdf-font 提供内置 CJK 字体,jquick-pdf-data 提供图表配置模型。零基础实战只引入核心模块,等文档版式跑通后再按需增加,排错范围会小很多。
关键细节
语法与样式要点
模板以 <pdf><body> 为根与正文,文本节点写成单引号形式,例如 <p>'课程:'${course}</p>;变量用 ${name},已注册资源用 &{name}。标题和段落可设置 fontSize、fontColor、bold、italic、underline、textAlignment;容器可设置 width、height、backgroundColor、border、borderRadius、opacity,边距与内边距使用 margin、padding 系列属性。尺寸单位支持 px、pt、mm、cm、in,样式以分号分隔,属性名可用驼峰或连字符别名。需要注意:文本值必须加单引号,样式值不要随意加引号,除非样例明确展示该形式。
渐进式学习路径
建议按五个台阶推进,每个台阶都能独立验收:
- 静态页面:先写只有固定文本的
<h1>与<p>,确认文件能生成、能打开、中文正常。 - 绑定变量:加入
${student}、${date},确认变量名与bind键一致,并明确缺失值策略。 - 表格:加入
<table>与表头,验证边框、列宽与较长文本的换行。 - 分页:用
<htmlPageBreak>明确开始新页,再测试自然分页是否稳定。 - 图片:加入
<image src="..." alt="...">,确认路径、尺寸与内存占用。
每一级只增加一个维度,并保留上一级可打开的 PDF;出现问题时就能判断是语法、数据还是布局变化,而不是在一个复杂模板里反复试错。
常见错误与工程约束
不要忘记文本单引号;不要把用户输入直接拼成标签或样式;不要把本机路径写死到模板中。入口方面,JQuickPdfFactory.create() 与无参构造在当前源码都存在且等价,执行方法仍使用 executeContent 等公开 API。模板应保持短小,大段固定内容放到资源文件;同一模板可以在请求间共享不可变文本,但每个请求都应创建自己的工厂与变量上下文。生成的 byte[] 可以写入 application/pdf 响应,也可以落盘或交给对象存储。
实战说明
Maven 依赖
准备 JDK 8+ 与 Maven,pom.xml 加入核心依赖:
xml
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-pdfx</artifactId>
<version>4.0.0</version>
</dependency>
刷新依赖后确认可以导入 com.github.paohaijiao.executor.JQuickPdfFactory。运行环境还需考虑中文字体,本机能显示不代表服务器一定能显示,正式部署应纳入字体回归(环境细节参见本系列入门篇)。
CertificateDemo 完整示例
这份证书 Demo 只使用真实入口与已验证样式:
java
import com.github.paohaijiao.executor.JQuickPdfFactory;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CertificateDemo {
public static void main(String[] args) throws Exception {
String template = "<pdf><body>"
+ "<h1 style=\"textAlignment:center;fontSize:26;fontColor:#244c6b\">'培训结业证书'</h1>"
+ "<p style=\"textAlignment:center;fontSize:16\">'兹证明:'${student}</p>"
+ "<p>'课程:'${course}</p>"
+ "<p>'结业日期:'${date}</p>"
+ "<div style=\"marginTop:20px;border:solid 1px #999;padding:12px\">"
+ "<p>'本证书用于证明学员完成规定课程并通过结业考核。'</p>"
+ "</div></body></pdf>";
// 绑定本次证书的数据并生成 PDF
byte[] pdf = new JQuickPdfFactory()
.bind("student", "李四")
.bind("course", "Java 后端开发")
.bind("date", "2026-09-14")
.executeContent(template);
Files.write(Paths.get("certificate.pdf"), pdf);
}
}
bind 接收 String 键与 Object 值,数字与格式化后的日期字符串都可以传入,批量场景使用 bindAll。executeContent 适合测试与动态模板,executeResource 读取 classpath 资源,executeFile 读取磁盘文件,三者都返回 byte[]。
效果 如下:

结果预期与扩展
运行后应得到 certificate.pdf:标题居中、字号 26、呈深蓝色;三行信息与变量值一一对应;说明块带 1px 灰色边框与内边距。若升级为培训成绩单,可在正文后增加 <table>;合同摘要可用 <div> 做分区;通知正文可用 <p> 与 <span> 混合强调;长内容用 <htmlPageBreak> 明确换页。每次只改一个维度,并保留上一次可打开的 PDF 作为对照。
典型场景
培训证书、入职通知、审批结果、报价单与订单确认单是最适合的第一批场景。实施时先选一个页面少、字段清晰的文档,建立"模板语法正确、数据绑定正确、PDF 可打开"三项验收,再逐步加入表格、分页与图片。产品调整版式时,模板评审可以与 Java 代码评审分开进行。
总结
- 极简调用不等于黑盒:工厂、解析器、布局引擎与 PDFBox 渲染器各司其职,掌握这条链路才能定位问题。
- 核心分工是模板与数据分离:固定文案留在模板,业务值通过
bind/bindAll注入,服务层负责格式化与空值策略。 - 学习路径建议为静态页面 → 绑定变量 → 表格 → 分页 → 图片,每一级独立验收。
适用边界与常见误区:jquick-pdf 适合结构化、版式稳定的业务文档,不适合需要完整浏览器布局或脚本交互的页面;模板语法有明确边界,未证实的循环指令、单元格合并与网络图片等能力不应预先写进设计。常见误区包括遗漏文本单引号、把本机路径写进模板、每个请求复用同一工厂,以及只在本机验证中文字体。
版本基线:jquick-pdfx 4.0.0、JDK 8+;许可证与版本强相关,上线前请核对 README 版本对照表;更多示例见 GitHub 仓库。