跳转到内容

JQuick-Pdf

纯 Java · Apache-2.0 · 零浏览器依赖

纯 Java 生成带图表的 PDF 报表不需要浏览器,不需要手写图表绘制代码

一份 HTML/CSS 风格的模板,加一份 ECharts 风格的图表配置,绑定一次数据,直接拿到 byte[] —— 版式、矢量图表、自动分页全部在单个 JVM 进程内完成。

上面代码的真实输出 · 第 1 页 / 共 2 页 — JQuick-Pdf
上面代码的真实输出 · 第 1 页 / 共 2 页

非设计稿:由示例代码运行生成,可下载 PDF 与模板源文件对照

  • 0 行图表绘制代码
  • 30+内置矢量图表类型
  • 0 依赖浏览器 / WebKit / 原生库
  • 1 个JVM 进程跑完全链路
9 行生成这份报告的全部核心代码
JOption option = new JOption();
option.title().text("2024年月度营收统计");
JCategoryAxis x = new JCategoryAxis();
x.data("1月", "2月", "3月", "4月", "5月", "6月");
option.xAxis(x); option.yAxis(new JValueAxis());
option.series(new JLine().name("营业收入").data(1860, 1720, 2140));
String svg = JChartRendererFactory.renderChart(JChartType.LINE, option);
byte[] pdf = JQuickPdfFactory.create().bind("svg", svg)
.executeResource("report/monthly.txt"); // 模板里:<svg>${svg}</svg>
Files.write(Paths.get("monthly.pdf"), pdf);

省略 import 与 main 方法;模板中图表只占一行 <svg>${svg}</svg>,图表按 ECharts 风格用 JOption 配置。

和 iText 7 比,代码差在哪?

下面这个对照不是贬低 iText —— 它是好库。只是当你想「像写网页一样写报表、图表开箱即用」时,两者的工作量确实差出一个量级。

关注点JQuick-PdfiText 7
上手方式HTML/CSS 风格模板 + 数据绑定,前端心智逐元素调用 Java API 构建文档对象
图表内置 30+ 矢量图表,JOption(ECharts 风格)配置即出图无内置图表引擎,需引入 JFreeChart 等先出位图再插入
中文渲染内置 CJK 字体,零配置自行寻找并注册字体文件
运行环境一个 JVM,无浏览器、无原生库纯 Java 可运行(图表链路额外依赖)
许可Apache-2.0(4.0.0+)社区版 AGPL-3.0,闭源商用需购买授权

9 行核心代码 vs 三段式胶水代码,差距在图表进入文档的那一段。

JQuick-Pdf 是 JQuick 生态中的 PDF 工具包,面向 Java 端的 生成、渲染与导出:写一份类似 HTML + 内联 CSS 的模板,绑定数据,即可拿到 PDF —— 可以是 byte[]、流或文件。

模板由自研语法解析器解析,并通过 Apache PDFBox 直接绘制到页面上,整条链路 没有浏览器、没有 Headless Chrome、没有本地库,非常适合嵌入服务端、定时任务或桌面应用。

模块 坐标 说明
jquick-pdfx io.github.paohaijiao:jquick-pdfx 解析器、渲染器、布局引擎与入口 JQuickPdfFactory,多数场景只需引入它
jquick-pdf-css io.github.paohaijiao:jquick-pdf-css CSS 模型:样式属性、单位与颜色
jquick-pdf-svg io.github.paohaijiao:jquick-pdf-svg SVG / ECharts 风格图表生成(30+ 图表类型)
jquick-pdf-data io.github.paohaijiao:jquick-pdf-data 图表配置模型(JOption、JChart、JTitle、JLegend 等)
jquick-pdf-font io.github.paohaijiao:jquick-pdf-font 内置中文字体资源,中文渲染零配置
  • 纯 Java,无外部引擎:不需要 WebKit、Chrome 或任何操作系统级依赖,一个 JVM 进程即可完成渲染。
  • HTML/CSS 风格模板:14 种元素,每个元素都支持 style="..." 内联样式。
  • 两种命名约定:minHeight(驼峰)与 min-height(标准 CSS)等价,可在同一声明中混用。
  • 数据绑定:${variable} 绑定普通值,&{resource} 引用已注册资源(图表、子模板、树形结构)。
  • 30+ 图表类型:柱状图、折线图、饼图、雷达图、散点图、箱线图、热力图、K 线、漏斗图、仪表盘、甘特图、词云、地图、旭日图、矩形树图、拓扑图等,全部为矢量绘制。
  • 完整文档能力:自动分页、目录(TOC)、页眉页脚、页码、水印、PDF 加密。
  • 中文友好:内置 CJK 字体,中文内容开箱即用。
  • 模块化产物:只依赖解析与渲染核心,图表、CSS、字体按需追加。
  • Java 8 兼容:发布产物面向 Java 8 字节码。
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-pdfx</artifactId>
<version>4.1.0</version>
</dependency>

图表能力为可选依赖:

<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-pdf-svg</artifactId>
<version>4.1.0</version>
</dependency>
import com.github.paohaijiao.executor.JQuickPdfFactory;
import java.nio.file.Files;
import java.nio.file.Paths;
public class HelloDemo {
public static void main(String[] args) throws Exception {
String template = ""
+ "<pdf>"
+ " <body>"
+ " <h1 style=\"fontSize:24;fontColor:blue\">'Hello jquick-pdf'</h1>"
+ " <p>'Customer: '${customer}</p>"
+ " </body>"
+ "</pdf>";
// 绑定数据并渲染为 PDF 字节数组 / bind data and render to PDF bytes
byte[] pdf = JQuickPdfFactory.create()
.bind("customer", "Martin")
.executeContent(template);
Files.write(Paths.get("hello.pdf"), pdf);
}
}

以下方法均返回 byte[]:

方法 模板来源
executeContent(String) 内存中的模板文本
executeResource(String) classpath 资源,例如 "report.txt"
executeFile(String) 磁盘文件,例如 "D:/templates/report.txt"

模板从 classpath 读取,支持 flex 布局、表格、列表与内联 SVG 图表:

import com.github.paohaijiao.adaptor.JAdaptor;
import com.github.paohaijiao.config.JPdfConfig;
import com.github.paohaijiao.executor.JQuickPdfFactory;
import com.github.paohaijiao.resouce.JReader;
import com.github.paohaijiao.resouce.impl.JReSourceFileReader;
import java.io.FileOutputStream;
public class CreditReportDemo {
public static void main(String[] args) throws Exception {
// 开启 flex 布局:雷达图与右侧指标面板并排显示
JPdfConfig config = new JPdfConfig();
config.getLayoutConfig().setFlexLayout(true);
JReader fileReader = new JReSourceFileReader("report.txt"); // 文档模板
JAdaptor adaptor = new JAdaptor(fileReader);
JReader svgReader = new JReSourceFileReader("radar.txt"); // 内联 SVG 图表
JAdaptor svgAdaptor = new JAdaptor(svgReader);
JQuickPdfFactory factory = new JQuickPdfFactory(config);
factory.bind("svg", svgAdaptor.getRuleContent()); // 绑定 ${svg} 占位符
byte[] bytes = factory.executeContent(adaptor.getRuleContent());
try (FileOutputStream out = new FileOutputStream("credit-report.pdf")) {
out.write(bytes);
}
}
}

对应的模板片段(节选自 report.txt):

<pdf>
<body>
<div style="textAlignment:center; marginBottom:5px; background:#3E6B9D; color:white; borderRadius:4px">
<h1 style="textAlignment:center;color:white; fontSize:20">'企业信用评级报告'</h1>
<p style="textAlignment:center;color:rgba(255,255,255,0.9); fontSize:11">'报告编号: CR-2023-08975'</p>
</div>
<table style="width:600px; fontSize:10">
<tr>
<td style="backgroundColor:#f8f9fa;padding:5px;width:150px;">'统一信用代码'</td>
<td style="backgroundColor:#f8f9fa;padding:5px;width:150px;">'91110108MA01XX1234'</td>
</tr>
</table>
<list style="list-style-type:none; paddingLeft:0; fontSize:11">
<li style="marginBottom:8px; padding:10px; backgroundColor:#f8f9fa; borderLeft:4px solid #4CAF50">
'营收增长率: 连续三年保持 40% 以上增长'
</li>
</list>
<div style="display:flex; justifyContent:space-between; marginTop:10px">
<svg style="width:400px;height:400px">${svg}</svg>
<div style="width:300px">
<h3 style="fontSize:12">'评级指标说明'</h3>
</div>
</div>
</body>
</pdf>

图表通过 JOption 配置,配置思路与 ECharts 一致,渲染结果是矢量图形,可直接写入 .svg 文件或嵌入文档。

import com.github.paohaijiao.axis.JCategoryAxis;
import com.github.paohaijiao.axis.JValueAxis;
import com.github.paohaijiao.bar.JBarChartsRenderer;
import com.github.paohaijiao.code.JTrigger;
import com.github.paohaijiao.series.JBar;
JOption option = new JOption();
option.title().text("销售数据").subtext("2023 年度");
option.tooltip().trigger(JTrigger.axis);
JCategoryAxis xAxis = new JCategoryAxis();
xAxis.data("衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子");
option.xAxis(xAxis);
option.yAxis(new JValueAxis());
JBar bar = new JBar();
bar.name("销量").data(5, 20, 36, 10, 10, 20);
option.series(bar);
// 渲染为 SVG 文件,或渲染为字符串后嵌入模板
JBarChartsRenderer renderer = new JBarChartsRenderer();
renderer.render(option, "barchart.svg");
String svg = renderer.renderToString(option);
元素 用途
<h1> … <h6> 六级标题
<p> / <span> / <br> / <tab> 段落、行内文本、换行与制表
<div> 块容器:背景、边框、内边距、尺寸、旋转、flex
<list> / <li> 列表容器与列表项
<table> / <tr> / <th> / <td> 表格与单元格样式
<image src="..." alt="..."> 位图图片
<svg> 矢量图,配合 ${svg} / &{svg}
<tree> 树形结构
<button> / <checkbox> / <comboBoxField> / <textArea> 交互式表单控件
<areaBreak> / <htmlPageBreak> / <lineSeparator> 强制分页 / HTML 风格分页 / 分隔线
<template> 可复用模板片段

样式写在任意元素的 style="..." 上,minHeight(驼峰)与 min-height(标准 CSS)等价,可混用;单位支持 px(96 DPI,1px = 0.75pt)、pt、mm、cm、in;颜色支持颜色名、#RRGGBB、rgb() / rgba() 与 linear-gradient(...)。

JQuick-Pdf 模板语法共定义 18 种元素,元素名称及支持状态如下:

元素名称 说明 支持状态
paragraph 段落容器(模板中写作 <p>),承载一段文本及其行内样式 支持
heading 标题元素,对应 <h1> … <h6> 六级标题 支持
list 列表容器,配合 <li> 渲染有序或无序列表项 支持
table 表格,配合 <tr> / <th> / <td> 绘制行列与单元格样式 支持
image 位图图片,通过 src 指定图片来源 支持
svg 矢量图形,配合 ${svg} / &{svg} 嵌入图表 支持
div 块级容器,支持背景、边框、内边距、尺寸、旋转与 flex 布局 支持
span 行内容器,用于为段落内的局部文本单独设置样式 支持
areaBreak 强制分页符,此后内容从下一页开始绘制 支持
button 按钮控件,用于生成可交互的 PDF 表单 支持
checkbox 复选框控件,用于生成可交互的 PDF 表单 支持
comboBoxField 下拉选择框控件,用于生成可交互的 PDF 表单 支持
htmlPageBreak HTML 风格分页符,按声明位置分页 支持
lineSeparator 水平分隔线 支持
tab 制表符,配合制表位实现对齐排版 支持
textArea 多行文本域控件,用于生成可交互的 PDF 表单 支持
tree 树形结构,用于渲染层级数据 支持
value 文本值节点:单引号包裹的字面文本,或 ${...} 绑定的数据值 支持

下面给出每种元素的最小模板片段(为节省篇幅均省略 <pdf><body> 根结构)。

段落使用 <p>,是承载普通文本的块级元素:

<p style="fontSize:12; lineHeight:1.5">'这是一个段落,用于承载一段普通文本。'</p>

标题使用 <h1> … <h6>,数字越小级别越高:

<h1 style="fontSize:20; fontColor:#1f2937">'一级标题'</h1>
<h2 style="fontSize:16; fontColor:#374151">'二级标题'</h2>

列表使用 <list> + <li>,通过 list-style-type 控制项目符号:

<list style="list-style-type:disc; fontSize:12">
<li>'无序列表第一项'</li>
<li>'无序列表第二项'</li>
</list>

表格按 <table> → <tr> → <th> / <td> 的层级书写:

<table style="width:100%; fontSize:11">
<tr>
<th style="backgroundColor:#f8f9fa; padding:5px">'姓名'</th>
<th style="backgroundColor:#f8f9fa; padding:5px">'年龄'</th>
</tr>
<tr>
<td style="padding:5px">'张三'</td>
<td style="padding:5px">'28'</td>
</tr>
</table>

图片使用 <image>,通过 src 指定来源,可设置宽高:

<image src="logo.png" style="width:120px; height:60px" />

矢量图使用 <svg>,通常通过 ${svg} 绑定渲染好的图表字符串:

<svg style="width:400px; height:300px">${svg}</svg>

<div> 是块级容器,用于分区并统一样式(背景、边框、内边距、flex 布局等):

<div style="padding:10px; backgroundColor:#f8f9fa; borderRadius:4px">
<p style="fontSize:12">'这是容器内的段落。'</p>
</div>

<span> 是行内容器,用于为段落内的局部文本单独设置样式:

<p style="fontSize:12">'合计:'<span style="fontColor:red; fontWeight:bold">'¥99.00'</span></p>

<areaBreak> 强制分页,此后的内容从下一页开始绘制:

<p>'第一页内容'</p>
<areaBreak />
<p>'第二页内容'</p>

<button> 生成可交互的按钮表单控件,通过 name 标识字段:

<button name="confirm" style="width:80px; height:28px">'提交'</button>

<checkbox> 生成复选框表单控件:

<checkbox name="agreed" />'我已阅读并同意协议'

<comboBoxField> 生成下拉选择框表单控件,通过 name 标识字段:

<comboBoxField name="city" style="width:200px; height:24px" />

<htmlPageBreak> 在声明位置插入 HTML 风格分页符:

<p>'分页前的内容'</p>
<htmlPageBreak />
<p>'分页后的内容'</p>

<lineSeparator> 绘制一条水平分隔线:

<lineSeparator style="width:100%; color:#cccccc" />

<tab> 插入制表符,配合制表位实现同行对齐:

<p style="fontSize:12">'姓名:'<tab />'张三'</p>

<textArea> 生成多行文本域表单控件:

<textArea name="remark" style="width:300px; height:80px" />

<tree> 渲染树形结构,树数据通过绑定变量传入:

<tree style="width:100%; fontSize:12">${deptTree}</tree>

value 是语法层的文本值节点:单引号包裹字面文本,${...} 绑定动态数据,二者都会渲染为最终文本:

<p>'客户:'${customer}</p>

样式统一写在元素的 style="..." 上,标准 CSS 的短横线命名(font-size)与驼峰命名(fontSize)等价、可混用;尺寸单位支持 px、pt、mm、cm、in。常用样式属性如下:

属性名 说明 适用元素
width 元素宽度 div、table、image 等块级元素
height 元素高度 div、table、image 等块级元素
margin 外边距,控制元素与相邻元素之间的距离 全部元素
padding 内边距,控制内容与边框之间的距离 div、table、td、list 等容器元素
border 边框,含宽度、线型与颜色,如 1px solid #cccccc div、table、td 等
background-color 背景颜色,支持颜色名、#RRGGBB、rgb() / rgba() div、table、td、list 等容器元素
color 文本(前景)颜色 paragraph、value、表格单元格等含文本元素
font-size 字号 含文本元素
font-family 字体,内置 CJK 字体可直接渲染中文 含文本元素
text-align 水平对齐方式:left / center / right paragraph、value、td、div
vertical-align 垂直对齐方式:top / middle / bottom td 等表格单元格及容器元素
line-height 行高(行距) paragraph、value 等文本元素
font-weight 字重,如 normal / bold 含文本元素