在sping boot中常用的操作PDF的的有以下三种
- PDFBox:Apache2.0,底层 PDF 操作库,适合解析、提取、合并、签名 PDF;手写坐标绘制 PDF,不支持 HTML 转 PDFApache PDF...
- iTextPDF(iText7):AGPL 协议,强大的编程式 PDF 生成库,支持表格、页眉页脚、表单、签名;HTML 转 PDF 需要额外`pdfHTML`插件,商用闭源项目要购买商业授权iText
- Flying Saucer(xhtmlrenderer):LGPL 协议,专门 XHTML+CSS2.1 转 PDF,底层依赖 iText5;适合报表、发票模板,不是通用 PDF 编辑库
一:PDFBox介绍
1:优点
- Apache 2.0 协议,商业友好,闭源产品随便用,无传染性开源约束
- 纯 Java,Apache 顶级项目,社区稳定;功能:文本提取、PDF 合并 / 拆分、PDF 转图片、表单填充、数字签名、PDF/A 校验Apache PDF
- 底层 API,可以直接操作 PDF 页、文字、图片、坐标;适合对已有 PDF 做修改、解析
- 自带命令行工具,可离线处理 PDF 文件
2:缺点
- 没有高层排版 API:没有自动换行、自动分页、表格组件,写文字必须手动指定 x/y 坐标;复杂报表写起来极其麻烦,需要第三方扩展(Boxable/easytable)Apache PDF
- 原生不支持 HTML 转 PDF
- 默认字体无中文,必须手动加载中文字体
- 超大 PDF 性能一般;表格提取弱,需要额外 Tabula 等工具辅助
3:依赖包安装
<!-- PDFBox 核心 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>3.0.5</version>
</dependency>
4:操作示例
package com.example.demo.controller;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.apache.pdfbox.pdmodel.PDDocument;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@RestController
@RequestMapping("/pdf")
public class PdfController {
@GetMapping("/pdfbox")
public String pdfbox()
{
try (PDDocument doc = new PDDocument()) {
PDPage page = new PDPage();
doc.addPage(page);
try (PDPageContentStream stream = new PDPageContentStream(doc, page)) {
// 加载中文字体
PDType0Font font = PDType0Font.load(doc, new FileInputStream("fonts/simhei.ttf"));
stream.setFont(font, 14);
stream.beginText();
stream.newLineAtOffset(100, 700);
stream.showText("Hello PDFBox 中文测试");
stream.endText();
}
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String nowStr = LocalDateTime.now().format(fmt);
String savePath = "file/" + nowStr + "/";
File dir = new File(savePath);
if (!dir.exists()) {
// 创建多级目录,父目录不存在也自动创建
boolean success = dir.mkdirs();
if (!success) {
throw new RuntimeException("目录创建失败,检查权限");
}
}
doc.save(savePath + "pdfbox-demo.pdf");
} catch (Exception e) {
//PDF生成异常
throw new RuntimeException(e);
}
return "PDF生成完成";
}
}
二:iTextPDF介绍
1:优点
- 高层排版 API:内置段落、表格、图片、页眉页脚、页码、水印、表单、PDFUA 无障碍文档,开发复杂报表非常方便iText
- 性能优秀,高并发批量生成 PDF 稳定;支持数字签名、加密、各种 PDF 高级特性
- 配套`pdfHTML`插件,可以直接 HTML 转 PDF,CSS 支持比 Flying Saucer 更强
2:缺点
- AGPL 协议大坑:如果你把 iText7 部署在 Web 服务(SaaS)并且不对外开源全部代码,必须购买商业授权,否则违反协议;内部离线使用也要评估法务风险iText
- iText7 API 和 iText5 改动巨大,升级成本高
- 中文同样需要手动嵌入字体
3:依赖包安装
<!--itextpdf核心包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>8.0.4</version>
<type>pom</type>
</dependency>
<!--html转PDF核心插件-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>5.0.4</version>
</dependency>
<!-- 中文支持 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>8.0.4</version>
</dependency>
4:操作示例
package com.example.demo.controller;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.io.font.FontProgramFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@RestController
@RequestMapping("/pdf")
public class PdfController {
@GetMapping("/itextpdf")
public String itextpdf() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String nowStr = LocalDateTime.now().format(fmt);
String savePath = "file/" + nowStr + "/";
File dir = new File(savePath);
if (!dir.exists()) {
// 创建多级目录,父目录不存在也自动创建
boolean success = dir.mkdirs();
if (!success) {
throw new RuntimeException("目录创建失败,检查权限");
}
}
String file = savePath + "itext-html-demo.pdf";
try (FileOutputStream out = new FileOutputStream(file)) {
ConverterProperties converterProperties = new ConverterProperties();
//第三个参数 true:加载系统字体
DefaultFontProvider fontProvider = new DefaultFontProvider(false, false, true);
fontProvider.addFont(FontProgramFactory.createFont("fonts/simhei.ttf"));
converterProperties.setFontProvider(fontProvider);
String html = """
<html>
<head>
<meta charset="UTF-8"/>
</head>
<body>
<h1>测试PDF</h1>
<p>中文报表</p>
</body>
</html>
""";
HtmlConverter.convertToPdf(html, out, converterProperties);
} catch (Exception e) {
throw new RuntimeException(e);
}
return "PDF生成完成";
}
}
三:Flying Saucer介绍
1:优点
- HTML/CSS 开发 PDF,前端写模板,后端填充数据(Thymeleaf/Freemarker),开发报表速度极快;支持`@page`、`page-break`分页控制,适合打印样式报表、发票
- LGPL 协议,商用友好;纯 Java,无需浏览器、无头 Chrome,服务器部署简单,内存占用低
- 支持自定义字体解决中文,支持图片、表格、页码
2:缺点
- 仅支持 CSS2.1,少量 CSS3,不支持 flex、grid,现代 CSS 基本不可用;必须是合法 XHTML,普通 HTML5 直接渲染会报错,需要 Jsoup 清洗转 XHTML
- 不能修改、解析已有 PDF,只能把 XHTML 渲染生成新 PDF;不支持 PDF 签名、表单填充等高级 PDF 能力
- 项目维护活跃度一般;复杂嵌套样式容易排版错乱,需要严格控制模板
3:依赖包安装
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.7.2</version>
</dependency>
4:操作示例
package com.example.demo.controller;
import com.itextpdf.text.pdf.BaseFont;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@RestController
@RequestMapping("/pdf")
public class PdfController {
@GetMapping("/flying")
public String flying()
{
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String nowStr = LocalDateTime.now().format(fmt);
String savePath = "file/" + nowStr + "/";
File dir = new File(savePath);
if (!dir.exists()) {
// 创建多级目录,父目录不存在也自动创建
boolean success = dir.mkdirs();
if (!success) {
throw new RuntimeException("目录创建失败,检查权限");
}
}
String file = savePath + "flying-saucer-pdf.pdf";
try (FileOutputStream out = new FileOutputStream(file)) {
ITextRenderer renderer = new ITextRenderer();
// 设置字体,解决中文
renderer.getFontResolver().addFont("fonts/simhei.ttf", BaseFont.IDENTITY_H, true);
String xhtml = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
body {
font-family: SimHei;
}
</style>
</head>
<body>
<h1>报表标题</h1>
<p>Flying Saucer XHTML转PDF,中文测试</p>
</body>
</html>
""";
renderer.setDocumentFromString(xhtml);
renderer.layout();
renderer.createPDF(out);
} catch (Exception e) {
throw new RuntimeException(e);
}
return "PDF生成完成";
}
}