java 基于itext7 HTML 转 PDF
'# Java 基于iText7 HTML转PDF
一、背景与问题
在企业级应用中,将HTML内容转换为PDF文档是一项常见需求。例如:
- 生成电子发票
- 导出报表数据
- 将网页内容保存为可打印文档
- 创建动态PDF模板
传统方案通常需要使用第三方工具如wkhtmltopdf,但其存在以下问题:
- 需要依赖外部进程(Linux系统需安装依赖库)
- 在容器环境(如Docker)中部署复杂
- 对CSS样式的支持有限
- 对中文支持不够完善
iText7作为PDF生成领域的权威库,提供了更精细的控制能力。但其本身并未直接支持HTML解析,需要结合HTML解析工具实现。本文将深入探讨基于iText7的HTML转PDF技术实现,分析其原理、实现方式、常见问题及优化策略。
二、基本原理
iText7通过以下核心组件实现HTML转PDF:
- HTML解析引擎:使用Flying Saucer或iText自带的HTML解析器
- CSS样式处理器:处理CSS样式和布局
- PDF生成引擎:将解析后的内容渲染为PDF
关键流程:
HTML内容 -> HTML解析器 -> CSS样式处理 -> PDF渲染 -> PDF输出iText7的HtmlConverter类(需引入html2pdf模块)提供了直接转换能力,但其对CSS3的支持有限,需要特别注意样式兼容性。
三、环境准备
1. 依赖配置(Maven)
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>7.1.14</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>7.1.14</version>
</dependency>2. 字体资源准备
若需支持中文,需添加中文字体资源:
FontProvider fontProvider = new FontProvider();
fontProvider.addFont("path/to/simsun.ttf");四、核心实现
1. 基础转换示例
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.FontProvider;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class HtmlToPdfDemo {
public static void main(String[] args) throws Exception {
// 创建字体提供器
FontProvider fontProvider = new FontProvider();
fontProvider.addFont("path/to/simsun.ttf");
// 读取HTML内容
InputStream htmlStream = new FileInputStream("input.html");
InputStream fontStream = new FileInputStream("simsun.ttf");
// 转换为PDF
OutputStream pdfStream = new FileOutputStream("output.pdf");
HtmlConverter.convertToPdf(htmlStream, pdfStream, fontProvider);
}
}关键点说明:
FontProvider用于注册中文字体HtmlConverter.convertToPdf()方法支持流式处理- 需要确保字体文件路径正确
2. 复杂样式处理
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.FontProvider;
import com.itextpdf.html2pdf.resolver.font.FontResolver;
import com.itextpdf.html2pdf.resolver.font.IFontResolver;
import com.itextpdf.html2pdf.css.CssResolver;
import com.itextpdf.html2pdf.css.CssResolverFactory;
import com.itextpdf.html2pdf.css.CssStyle;
import com.itextpdf.html2pdf.css.CssStyleFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class StyleHandlingDemo {
public static void main(String[] args) throws Exception {
// 创建字体提供器
FontProvider fontProvider = new FontProvider();
fontProvider.addFont("path/to/simsun.ttf");
// 自定义CSS解析器
IFontResolver fontResolver = new FontResolver(fontProvider);
CssResolver cssResolver = CssResolverFactory.createDefault();
cssResolver.addCssStyle(CssStyleFactory.createDefault());
// 读取HTML内容
InputStream htmlStream = new FileInputStream("styled.html");
// 转换为PDF
OutputStream pdfStream = new FileOutputStream("styled_output.pdf");
HtmlConverter.convertToPdf(htmlStream, pdfStream, fontResolver, cssResolver);
}
}关键点说明:
- 自定义
IFontResolver处理字体 CssResolver用于处理CSS样式- 需要处理CSS样式兼容性问题
3. 复杂内容处理(表格/图片)
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.FontProvider;
import com.itextpdf.html2pdf.css.CssResolver;
import com.itextpdf.html2pdf.css.CssResolverFactory;
import com.itextpdf.html2pdf.css.CssStyle;
import com.itextpdf.html2pdf.css.CssStyleFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class ComplexContentDemo {
public static void main(String[] args) throws Exception {
// 创建字体提供器
FontProvider fontProvider = new FontProvider();
fontProvider.addFont("path/to/simsun.ttf");
// 配置CSS解析器
CssResolver cssResolver = CssResolverFactory.createDefault();
cssResolver.addCssStyle(CssStyleFactory.createDefault());
// 读取HTML内容
InputStream htmlStream = new FileInputStream("complex.html");
// 转换为PDF
OutputStream pdfStream = new FileOutputStream("complex_output.pdf");
HtmlConverter.convertToPdf(htmlStream, pdfStream, fontProvider, cssResolver);
}
}关键点说明:
- 支持表格、图片等复杂内容
- 需要处理图片路径问题
- 表格布局需要特别注意
五、完整案例
1. 项目结构
src/
├── main/
│ ├── java/
│ │ └── com.example/
│ │ └── PdfGenerator.java
│ └── resources/
│ └── templates/
│ └── invoice.html2. 前端页面(Thymeleaf模板)
<!-- templates/invoice.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>发票</title>
<style>
body { font-family: "SimSun"; }
.invoice { border: 1px solid #ccc; padding: 20px; }
</style>
</head>
<body>
<div class="invoice">
<h1>发票</h1>
<p>客户名称:张三</p>
<table>
<tr><th>商品</th><th>单价</th><th>数量</th></tr>
<tr><td>商品A</td><td>100</td><td>2</td></tr>
<tr><td>商品B</td><td>200</td><td>1</td></tr>
</table>
<p>总计:400元</p>
</div>
</body>
</html>3. 后端处理(Spring Boot)
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.FontProvider;
import com.itextpdf.html2pdf.css.CssResolver;
import com.itextpdf.html2pdf.css.CssResolverFactory;
import com.itextpdf.html2pdf.css.CssStyle;
import com.itextpdf.html2pdf.css.CssStyleFactory;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
import java.io.OutputStream;
@Service
public class PdfGenerator {
public byte[] generatePdf(String htmlContent) throws Exception {
// 创建字体提供器
FontProvider fontProvider = new FontProvider();
fontProvider.addFont("path/to/simsun.ttf");
// 配置CSS解析器
CssResolver cssResolver = CssResolverFactory.createDefault();
cssResolver.addCssStyle(CssStyleFactory.createDefault());
// 生成PDF
byte[] pdfBytes = new byte[0];
try (InputStream htmlStream = new ByteArrayInputStream(htmlContent.getBytes());
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) {
HtmlConverter.convertToPdf(htmlStream, pdfOutputStream, fontProvider, cssResolver);
pdfBytes = pdfOutputStream.toByteArray();
}
return pdfBytes;
}
}4. 控制器
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
import java.io.OutputStream;
@RestController
public class PdfController {
@GetMapping("/generate-pdf")
public void generatePdf() throws Exception {
// 读取HTML模板
InputStream htmlStream = getClass().getResourceAsStream("/templates/invoice.html");
String htmlContent = new String(htmlStream.readAllBytes());
// 生成PDF
byte[] pdfBytes = pdfGenerator.generatePdf(htmlContent);
// 返回PDF文件
// 这里需要根据实际需求处理文件流
}
}六、源码解析
以HtmlConverter.convertToPdf()方法为例,其核心流程如下:
- HTML解析:使用
XmlWorker解析HTML文档 - CSS处理:通过
CssResolver解析CSS样式 - PDF生成:使用
PdfWriter创建PDF文档 - 内容渲染:将解析后的元素转换为PDF内容
关键代码片段:
// HTML解析
Document document = new Document();
XmlWorker worker = new XmlWorker(document);
worker.parse(htmlStream);
// CSS处理
CssResolver cssResolver = CssResolverFactory.createDefault();
cssResolver.addCssStyle(CssStyleFactory.createDefault());
// PDF生成
PdfWriter writer = new PdfWriter(pdfOutputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document pdfDocument = new Document(pdfDoc);七、进阶使用
1. 自定义CSS样式
import com.itextpdf.html2pdf.css.CssStyle;
import com.itextpdf.html2pdf.css.CssStyleFactory;
public class CustomStyle {
public static void addCustomStyle() {
CssStyle style = new CssStyle();
style.setProperty("font-family", "SimSun");
style.setProperty("font-size", "12pt");
CssStyleFactory.getInstance().addStyle(style);
}
}2. 处理特殊字体
import com.itextpdf.html2pdf.resolver.font.FontProvider;
import com.itextpdf.html2pdf.resolver.font.FontResolver;
public class FontConfig {
public static void configureFonts(FontProvider fontProvider) {
fontProvider.addFont("path/to/simsun.ttf");
fontProvider.addFont("path/to/simsunb.ttf");
}
}3. 处理复杂表格
import com.itextpdf.html2pdf.html2pdf.HtmlTable;
import com.itextpdf.html2pdf.html2pdf.HtmlTableRow;
import com.itextpdf.html2pdf.html2pdf.HtmlTableCell;
public class TableHandler {
public static void handleTable(HtmlTable table) {
for (HtmlTableRow row : table.getRows()) {
for (HtmlTableCell cell : row.getCells()) {
cell.setPadding(10);
cell.setBorder(1);
}
}
}
}八、性能与工程实践
1. 性能优化
| 优化策略 | 说明 |
|---|---|
| 流式处理 | 避免一次性加载全部HTML内容 |
| 分页处理 | 合理设置页面大小和边距 |
| 内存管理 | 使用ByteArrayOutputStream代替FileOutputStream |
| 并行处理 | 使用线程池处理多个PDF生成任务 |
2. 异常处理
try {
HtmlConverter.convertToPdf(htmlStream, pdfStream, fontProvider, cssResolver);
} catch (Exception e) {
// 记录日志
logger.error("PDF生成失败: ", e);
// 返回错误响应
}3. 安全考虑
- XSS防护:过滤用户输入的HTML内容
- 字体安全:限制允许的字体类型
- 内容验证:检查HTML结构合法性
九、常见问题与踩坑
1. 常见错误
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 样式不生效 | CSS属性不兼容 | 使用@import引入CSS文件 |
| 中文字体乱码 | 字体未正确注册 | 使用FontProvider注册字体 |
| 页面布局异常 | CSS布局策略不兼容 | 使用display: block强制换行 |
| 内存溢出 | 处理大文档 | 使用ByteArrayOutputStream分块处理 |
2. 常见坑
CSS兼容性问题:
- 避免使用
@media查询 - 使用
!important覆盖默认样式
- 避免使用
字体缺失:
- 确保字体文件路径正确
- 使用
FontProvider注册字体
表格溢出:
- 设置
table-layout: fixed - 使用
width: 100%
- 设置
十、最佳实践
使用场景:
- 生成结构化文档(如发票、合同)
- 导出报表数据
- 创建动态PDF模板
避免使用场景:
- 需要处理复杂网页布局
- 需要高度兼容的PDF格式
- 需要跨平台支持(推荐使用wkhtmltopdf)
推荐策略:
- 对于结构化文档,使用iText7+CSS的组合
- 对于复杂网页,使用wkhtmltopdf
- 对于动态内容,使用Thymeleaf模板
十一、总结
基于iText7的HTML转PDF技术实现了对PDF文档的精细化控制,但需要结合HTML解析工具和CSS处理机制。通过合理配置字体、样式和布局策略,可以生成高质量的PDF文档。在实际应用中,需注意CSS兼容性、字体注册和性能优化等关键点。对于需要高度兼容性的场景,建议结合其他工具(如wkhtmltopdf)使用。掌握iText7的HTML转PDF技术,能够有效提升企业级应用的文档处理能力。
评论已关闭