jquick-pdf 超详细入门教程:Java 轻量级 HTML 模板生成 PDF 工具
引入
Java 后端做 PDF 导出,最先遇到的往往不是业务难题,而是排版难题:用底层 API 逐个创建页面、字体、段落与表格,代码会迅速膨胀成一套“坐标计算器”,改一个标题或间距就要重新编译;中文字体、长文本分页与服务端输出,又会变成隐性维护成本。jquick-pdf 把数据和版式分开:Java 准备数据,类 HTML 模板描述页面,渲染器输出 PDF。本文先走通一次完整生成。
核心讲解
版本基线与入口类
本文以仓库事实io.github.paohaijiao:jquick-pdfx:4.0.0和 JDK 8+ 为准。核心入口是com.github.paohaijiao.executor.JQuickPdfFactory,它接收模板、变量与页面配置,返回 PDF 的byte[]。模板常用<pdf><body>,固定文字必须用单引号(如'订单确认单'),变量用${customer},已注册资源用&{name}。它是元素与样式边界明确的模板语言,不是浏览器,也不承诺支持完整 CSS。
五步渲染链路
一次生成可拆成五个节点,排查问题也应沿这条链路定位:
- 读入:把模板字符串、classpath 资源或磁盘文件读入内存。
- 解析:解析器识别根节点、元素、文本与
style属性,形成可遍历结构。 - 绑定:变量上下文替换
${...},已注册资源替换&{...}。 - 布局与分页:按字体、边距与尺寸计算位置,并在超出页面时处理分页。
- 渲染输出:PDFBox 渲染器写入内存流,工厂取出最终字节。
工厂内部持有JContext与JPdfConfig,因此一次请求应创建一次工厂,不要跨请求复用带有业务变量的实例。
三种执行方式对比
| 方法 | 模板来源 | 适用场景 |
|---|---|---|
executeContent(String) | 内存字符串 | 单元测试、动态拼装的短模板 |
executeResource(String) | classpath 资源 | 随制品发布的版本化模板,如"report.txt" |
executeFile(String) | 磁盘文件 | 外置模板目录、需要单独更新的版式 |
三者都返回byte[],既可写文件,也可直接写 HTTP 响应流。
bind 与 bindAll 语义
bind(String,Object)绑定单变量,bindAll(Map<String,Object>)批量绑定;变量名必须与${name}完全一致,缺失变量不会自动补值。bind返回工厂自身,因此可链式调用。JQuickPdfFactory.create()与new JQuickPdfFactory()等价。配置页面可调用pageSize(...)、margins(top,right,bottom,left),也可构造JPdfConfig传入工厂。
模块构成
最小场景只需jquick-pdfx。矢量图表再引入jquick-pdf-svg(30+ 图表);jquick-pdf-data提供图表配置模型(如JOption、JChart);jquick-pdf-font提供内置 CJK 字体;jquick-pdf-css提供 CSS 模型。文档层支持标题、段落、行内文本、块、列表、表格、图片、SVG、树与表单域,并提供<areaBreak>、<htmlPageBreak>、<lineSeparator>等布局元素;按需引入可避免基础导出携带全部图表代码。
关键细节
文本与变量语法
单引号是文本语法而非装饰,遗漏会导致解析异常;${name}只做取值替换,&{name}用于图表、模板、树或 SVG 等已注册资源。变量应通过绑定传入,禁止把用户输入拼进标签或style,否则可能破坏语法并引入注入风险。
样式与单位要点
样式写在style属性中,以分号分隔,属性名支持驼峰与连字符互为别名,可在同一声明中混用。尺寸可用px、pt、mm、cm、in,px按 96 DPI 折算(1px = 0.75pt)。常用属性包括width、height、minHeight、maxWidth、relativePosition、margin*、padding*、verticalAlignment、backgroundColor、border、borderRadius、opacity;文字可用fontFamilyNames、fontSize、fontColor、bold、italic、underline、textAlignment、characterSpacing。border采用“类型 宽度 颜色”,如solid 1px #999;borderRadius支持 1~4 个值。颜色支持颜色名、#RRGGBB、rgb()/rgba()与linear-gradient。
需要提前知道的边界
模板循环指令(如for/each)、rowSpan/colSpan合并单元格、直接渲染网络 URL 图片、表单提交动作与提交 URL、全局分页背景或全局水印、keepTogether绝对禁止分页,这些能力在本文基线下均未证实,不应写进方案假设。
实战说明
Maven 依赖
使用 JDK 8+、Maven 3.6+,pom.xml只需加入核心依赖:
<dependency><groupId>io.github.paohaijiao</groupId><artifactId>jquick-pdfx</artifactId><version>4.0.0</version></dependency>运行时由 Maven 传递引入 Apache PDFBox 3.0.x、ANTLR4 runtime 与 SLF4J API;模板由 ANTLR4 解析、PDFBox 直接绘制,无浏览器、无 headless Chrome、无本地动态库。首次接入先执行mvn dependency:tree排除版本冲突,再运行静态模板验证链路。
QuickStartPdf 完整示例
以下类可作为普通 Java 程序直接运行,输出当前目录的quick-start.pdf:
importcom.github.paohaijiao.executor.JQuickPdfFactory;importjava.nio.file.Files;importjava.nio.file.Paths;publicclassQuickStartPdf{publicstaticvoidmain(String[]args)throwsException{Stringtemplate=""+"<pdf><body>"+"<h1 style=\"fontSize:24;textAlignment:center;fontColor:#1f4e79\">'订单确认单'</h1>"+"<p>'客户:'${customer}</p>"+"<p>'订单号:'${orderNo}</p>"+"<table style=\"width:520px;border:solid 1px #999\">"+"<tr><th>'商品'</th><th>'数量'</th><th>'金额'</th></tr>"+"<tr><td>'Java 技术书'</td><td>'2'</td><td>'98.00'</td></tr>"+"</table></body></pdf>";// 为本次文档绑定业务变量并执行模板byte[]pdf=newJQuickPdfFactory().bind("customer","张三").bind("orderNo","NO-2026001").executeContent(template);Files.write(Paths.get("quick-start.pdf"),pdf);}}结果预期与验证
执行后应得到可正常打开的quick-start.pdf:标题居中呈蓝色,变量替换为实际值,表格带 1px 灰色边框。验证顺序固定为“文件生成 → 可打开 → 中文正常 → 数据一致 → 分页符合预期”;失败时先查标签闭合与单引号,再查绑定键名与样式。
数据准备与选型边界
报表通常在服务层把金额、日期与状态格式化为展示值,再通过bind注入模板,从而不被 ORM 字段名、空值与业务枚举牵着走;列表用<list><li>,明细用<table>,图片用<image src="..." alt="...">,分页可用样例验证过的<htmlPageBreak>或<areaBreak>指定。选型上:相比 iText 与 PdfBox 的底层 API,它更适合结构化文档与快速迭代;相比浏览器截图,它无需 Chrome,部署更轻;但完整网页兼容与浏览器专属 CSS 仍需评估其他方案。
总结
- 五个节点决定成败:读入、解析、绑定、布局分页、渲染输出,排查应逐层验证。
- 语法固定:文本用单引号,变量用
${name},资源用&{name},样式以分号分隔且支持驼峰/连字符别名。 - 工程固定:先跑通静态模板,再依次加入绑定、样式、分页与真实数据。
适用边界与常见误区:它面向规则明确的结构化业务文档,不是完整 HTML/CSS 实现,也不能替代底层绘图库;最常见的错误是遗漏文本单引号、跨请求复用携带变量的工厂、把用户输入拼进模板,以及在服务器上忽略中文字体。
版本基线:jquick-pdfx 4.0.0、JDK 8+;许可证与版本强相关,上线前请核对 README 版本对照表;更多示例见 GitHub 仓库。