一、方案概述
本方案基于iText7 开源PDF操作库实现动态PDF生成服务 ,支持通过接口传入结构化参数,自动生成包含标题、正文段落、动态表格、分页的标准A4格式PDF文件,完美解决中文乱码问题,提供轻量化、可扩展、高可用的PDF生成能力,适用于报表导出、文档生成、电子凭证等业务场景。
二、技术选型
| 技术组件 | 版本 | 选型说明 |
|---|---|---|
| iText7 Core | 7.2.5 | PDF核心操作库,负责文档创建、渲染、页面管理 |
| iText7 Layout | 7.2.5 | 布局渲染核心,支持段落、表格、样式排版 |
| iText7 Font-Asian | 7.2.5 | 亚洲字体支持包,解决PDF中文/日文/韩文乱码问题 |
三、核心功能
-
基础文档生成:创建标准A4尺寸PDF,自定义页面边距、文档元数据
-
中文完美支持:内置中文字体加载,无乱码、无字体缺失问题
-
动态内容渲染
-
自定义标题:居中显示、可配置字号、边距
-
正文段落:支持自定义字号、常规/加粗样式、段落间距
-
动态表格:自适应列数、100%宽度占比、表头+数据渲染
-
强制分页:支持手动触发新页面生成
-
-
服务化输出:提供REST接口,直接返回PDF字节流,支持浏览器下载
-
异常容错:统一异常捕获,抛出友好的业务异常
四、技术实现
核心流程:前端传入JSON参数 → Controller接收请求 → 调用工具类生成PDF → 内存流输出字节数组 → 响应PDF文件下载
1. 引入依赖
xml
<!-- iText 7 核心包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.5</version>
</dependency>
<!-- 布局渲染核心 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.5</version>
</dependency>
<!-- 中文支持(解决中文乱码必备)-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>7.2.5</version>
</dependency>
2. 数据模型
java
@Data
public class PdfRequest {
// 文档标题
private String title;
// 作者(可选)
private String author;
// 内容元素
private List<Element> elements;
@Data
public static class Element {
// paragraph / table / newPage
private String type;
// 文本内容
private String content;
// 字体大小
private Integer fontSize;
// 是否加粗
private Boolean bold;
// 表头
private List<String> headers;
// 表数据
private List<List<String>> rows;
}
3. 工具类
PdfGeneratorUtil :iText7封装工具类,核心方法generatePdf()
核心步骤:
-
初始化PDF文档:创建PdfWriter、PdfDocument、Document,设置A4尺寸+页面边距
-
加载中文字体:使用
STSong-Light字体+UniGB-UCS2-H编码,解决中文乱码 -
渲染文档标题:居中、18号字体、底部边距20
-
遍历渲染内容元素:
-
段落:自定义字号、段落间距
-
表格:动态列数、100%宽度、表头+数据逐行渲染
-
分页:手动新增PDF页面
-
-
关闭文档,返回PDF字节数组
java
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import java.io.ByteArrayOutputStream;
import java.util.List;
/**
* iText 7 标准排版 PDF 生成工具
* 包含:标题、正文、表格、分页、中文支持
*/
public class PdfGeneratorUtil {
public static byte[] generatePdf(PdfRequest request) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc, PageSize.A4);
// 设置页面边距(上下左右)
document.setMargins(50, 50, 50, 50);
// ========== 1. 加载中文字体(核心:解决中文乱码 【正常 + 加粗】) ==========
PdfFont fontNormal = PdfFontFactory.createFont(
"STSong-Light",
"UniGB-UCS2-H",
PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED
);
// ========== 2. 标题 ==========
Paragraph title = new Paragraph(request.getTitle())
.setFont(fontNormal)
.setFontSize(18)
.setTextAlignment(TextAlignment.CENTER)
.setMarginBottom(20);
document.add(title);
// ===================== 遍历内容元素 ======================
for (PdfRequest.Element element : request.getElements()) {
switch (element.getType()) {
// ========== 3. 正文段落 ==========
case "paragraph" -> {
Paragraph p = new Paragraph(element.getContent())
.setFont(fontNormal)
.setFontSize(element.getFontSize() == null ? 12 : element.getFontSize());
document.add(p.setMarginBottom(10));
}
// ========== 4. 标准表格(4列) ==========
case "table" -> {
List<String> headers = element.getHeaders();
List<List<String>> rows = element.getRows();
Table table = new Table(headers.size())
.setWidth(UnitValue.createPercentValue(100))
.setMarginBottom(15);
// 表头(加粗)
for (String header : headers) {
// table.addHeaderCell(new Cell()
// .add(new Paragraph(header).setFont(fontNormal).setBold())
// .setTextAlignment(TextAlignment.CENTER)
// .setPadding(8));
table.addHeaderCell(new Paragraph(header).setFont(fontNormal));
}
// 表数据(正常)
for (List<String> row : rows) {
for (String val : row) {
// table.addCell(new Cell()
// .add(new Paragraph(val).setFont(fontNormal))
// .setTextAlignment(TextAlignment.CENTER)
// .setPadding(6));
table.addCell(new Paragraph(val).setFont(fontNormal));
}
}
document.add(table);
}
case "newPage" -> {
pdfDoc.addNewPage();
}
}
}
document.close();
return outputStream.toByteArray();
} catch (Exception e) {
throw new RuntimeException("PDF生成失败");
}
}
}
4. 接口层
java
@RestController
public class MarkDownToPdfController {
@PostMapping("/generate")
public ResponseEntity<byte[]> generatePdf(@RequestBody PdfRequest request) {
byte[] pdfBytes = PdfGeneratorUtil.generatePdf(request);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=document.pdf");
return ResponseEntity.status(HttpStatus.OK)
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(pdfBytes);
}
}
五、接口请求示例
- 请求地址
POST localhost:8080/generate
- 请求体(JSON)
json
{
"title": "员工信息报表",
"author": "管理员",
"elements": [
{
"type": "paragraph",
"content": "以下是本公司员工详细信息列表,数据统计日期:2025年12月",
"fontSize": 14
},
{
"type": "table",
"headers": ["员工ID", "姓名", "部门", "职位"],
"rows": [
["1001", "张三", "技术部", "Java开发"],
["1002", "李四", "产品部", "产品经理"]
]
},
{
"type": "newPage"
},
{
"type": "paragraph",
"content": "第二页:报表备注:本数据仅供内部使用",
"fontSize": 12
}
]
}
- 响应结果
浏览器自动下载名为document.pdf的PDF文件。