jquick-pdf 超详细入门教程:Java 轻量级 HTML 模板生成 PDF 工具

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。

五步渲染链路

一次生成可拆成五个节点,排查问题也应沿这条链路定位:

  1. 读入:把模板字符串、classpath 资源或磁盘文件读入内存。
  2. 解析:解析器识别根节点、元素、文本与 style 属性,形成可遍历结构。
  3. 绑定:变量上下文替换 ${...},已注册资源替换 &{...}
  4. 布局与分页:按字体、边距与尺寸计算位置,并在超出页面时处理分页。
  5. 渲染输出:PDFBox 渲染器写入内存流,工厂取出最终字节。

工厂内部持有 JContextJPdfConfig,因此一次请求应创建一次工厂,不要跨请求复用带有业务变量的实例。

三种执行方式对比

方法 模板来源 适用场景
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 提供图表配置模型(如 JOptionJChart);jquick-pdf-font 提供内置 CJK 字体;jquick-pdf-css 提供 CSS 模型。文档层支持标题、段落、行内文本、块、列表、表格、图片、SVG、树与表单域,并提供 <areaBreak><htmlPageBreak><lineSeparator> 等布局元素;按需引入可避免基础导出携带全部图表代码。

关键细节

文本与变量语法

单引号是文本语法而非装饰,遗漏会导致解析异常;${name} 只做取值替换,&{name} 用于图表、模板、树或 SVG 等已注册资源。变量应通过绑定传入,禁止把用户输入拼进标签或 style,否则可能破坏语法并引入注入风险。

样式与单位要点

样式写在 style 属性中,以分号分隔,属性名支持驼峰与连字符互为别名,可在同一声明中混用。尺寸可用 pxptmmcminpx 按 96 DPI 折算(1px = 0.75pt)。常用属性包括 widthheightminHeightmaxWidthrelativePositionmargin*padding*verticalAlignmentbackgroundColorborderborderRadiusopacity;文字可用 fontFamilyNamesfontSizefontColorbolditalicunderlinetextAlignmentcharacterSpacingborder 采用"类型 宽度 颜色",如 solid 1px #999borderRadius 支持 1~4 个值。颜色支持颜色名、#RRGGBBrgb()/rgba()linear-gradient

需要提前知道的边界

模板循环指令(如 for/each)、rowSpan/colSpan 合并单元格、直接渲染网络 URL 图片、表单提交动作与提交 URL、全局分页背景或全局水印、keepTogether 绝对禁止分页,这些能力在本文基线下均未证实,不应写进方案假设。

实战说明

Maven 依赖

使用 JDK 8+、Maven 3.6+,pom.xml 只需加入核心依赖:

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

java 复制代码
import com.github.paohaijiao.executor.JQuickPdfFactory;
import java.nio.file.Files;
import java.nio.file.Paths;

public class QuickStartPdf {
    public static void main(String[] args) throws Exception {
        String template = ""
                + "<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 = new JQuickPdfFactory()
                .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 仓库

相关推荐
蛋先生DX1 小时前
Java和Go都拍胸脯说"内存我包了",可它们到底是怎么下手的?
java·go·编程语言
八年。。1 小时前
WSL学习(三)——搭建Linux + C++ + SOME/IP 基础开发环境
开发语言·笔记·ubuntu
SimonKing1 小时前
阅后即焚的加密便签:Cryptgeon,你口袋里的秘密信使
java·后端·程序员
孙启超1 小时前
【AI开发之Rust】第 6 课:结构体、枚举与方法 —— 开始定义自己的类型
开发语言·人工智能·后端·ai·rust
ss2731 小时前
Java全栈实战 | 1.3-03 索引原理:联合索引 (a,b,c) 只查 b 走不了索引?B+ 树一图讲透所有索引玄学
java·数据库
电商API_180079052471 小时前
电商平台数据分析实战_帖子
开发语言·爬虫·python·数据采集·京东
前端初见2 小时前
Java+AI零基础入门到大牛
java·spring boot·spring·java-ee·intellij-idea
猫猫不是喵喵.2 小时前
两个不同应用Session能互相访问解决
java
带多刺的玫瑰2 小时前
Leecode#35刷题之搜索插入位置
java·python·算法