一、简介
在Java开发中,有时我们需要将HTML内容转换为PDF文件。iText 7是这方面的一个不错选择,它功能强大且灵活。这篇文章我会分享如何使用iText 7来实现HTML转PDF的过程,包括一些关键概念和实现细节。
二、核心组件
使用iText 7转换HTML到PDF,主要涉及这几个核心组件:
- ConverterProperties:用于配置转换过程中的各种参数
- FontProvider:负责处理字体渲染,特别是对中文等非ASCII字符的支持
- PdfWriter:将内容写入到输出流
- PdfDocument:代表整个PDF文档的结构
- HtmlConverter:真正执行HTML到PDF转换的类
三、转换流程详解
0. 引入依赖
首先需要在pom.xml中添加相关依赖:
<!-- iText 7 Core -->
<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>
<!-- HTML 转换模块 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>4.0.4</version>
</dependency>
1. 创建转换配置
我们需要创建并配置ConverterProperties对象,其中最关键的是配置字体:
private static ConverterProperties createConverterProperties() {
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new FontProvider();
// 添加系统所有字体,支持中文等多语言
fontProvider.addSystemFonts();
properties.setFontProvider(fontProvider);
return properties;
}
这一步很重要,尤其是处理中文内容时。通过addSystemFonts()
方法,我们可以加载系统中所有可用字体,确保PDF能正确显示各种语言。
2. 准备PDF输出
接下来创建PdfWriter和PdfDocument对象:
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
这里的outputStream可以是文件流、内存流或其他任何输出流。
3. 执行转换
有了前面的准备工作,现在可以用HtmlConverter来进行实际的转换了:
// 从HTML字符串转换
HtmlConverter.convertToPdf(htmlContent, pdfDocument, properties);
// 或从HTML文件转换
HtmlConverter.convertToPdf(Files.newInputStream(htmlFile.toPath()), pdfDocument, properties);
4. 资源释放
别忘了在完成后释放资源:
// 使用try-with-resources自动处理资源释放
try (OutputStream outputStream = new FileOutputStream("output.pdf")) {
// 转换过程
}
四、高级功能与配置
1. 字体处理
字体处理是HTML转PDF中最头疼的问题之一,特别是对中文支持。除了前面提到的addSystemFonts()
,我们还可以:
// 添加特定字体
fontProvider.addFont("/path/to/font.ttf");
// 指定默认字体
fontProvider.addFont("SimSun"); // 宋体通常对中文支持较好
2. 图片处理
处理图片时,需要正确设置基础URI:
// 设置基础URI,用于解析HTML中相对路径的图片
properties.setBaseUri("file:///path/to/images/");
3. CSS支持
iText的HTML转PDF支持大部分CSS特性:
// 添加外部CSS
properties.setCssAppliers(new DefaultCssApplierFactory());
// 添加自定义CSS文件
properties.addCssFile("/path/to/style.css");
五、常见问题及解决方案
在实际使用过程中,我遇到过一些常见问题,这里分享解决方案:
1. 中文显示问题
如果PDF中的中文显示为方块或问号,通常是字体问题:
// 确保添加了支持中文的字体
fontProvider.addFont("SimSun"); // 使用宋体
// 或者
fontProvider.addSystemFonts(); // 添加所有系统字体
2. 布局问题
HTML到PDF的转换可能存在布局差异,特别是复杂布局:
// 设置文档大小
pdfDocument.setDefaultPageSize(PageSize.A4);
// 设置页边距(上右下左)
properties.setMargins(new float[]{36, 36, 36, 36});
3. 图片路径问题
图片无法显示通常是因为路径解析问题:
// 设置正确的基础URI
properties.setBaseUri("file:///path/to/html/directory/");
六、性能优化
在处理大量文档或大型HTML文件时,性能优化很重要:
1. 内存管理
处理大型HTML文件时:
// 设置处理大型HTML的缓冲区大小
properties.setBufferSize(8192);
// 分页处理大型文档
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
pdfDocument.setTagged();
pdfDocument.setDefaultPageSize(PageSize.A4);
2. 并发处理
对于批量处理场景,可以使用线程池:
// 使用线程池并发处理多个文档
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<?>> futures = new ArrayList<>();
for(String html : htmlList) {
futures.add(executor.submit(() -> {
convertHtmlToPdf(html, new FileOutputStream("output_" + count.getAndIncrement() + ".pdf"));
return null;
}));
}
七、实际应用示例
下面是一些我在实际项目中使用的示例:
1. 基本转换
public static void simpleConversion(String html, String outputPath) {
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
PdfWriter writer = new PdfWriter(fos);
PdfDocument pdf = new PdfDocument(writer);
ConverterProperties properties = createConverterProperties();
HtmlConverter.convertToPdf(html, pdf, properties);
} catch (IOException e) {
log.error("转换失败", e);
}
}
2. 生成并返回Base64编码
这在Web应用中特别有用,可以直接将PDF作为Base64字符串返回给前端:
public static String generatePdfBase64(String html) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
PdfWriter writer = new PdfWriter(baos);
PdfDocument pdf = new PdfDocument(writer);
ConverterProperties properties = createConverterProperties();
HtmlConverter.convertToPdf(html, pdf, properties);
return Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (IOException e) {
log.error("生成PDF Base64失败", e);
return null;
}
}
3. 处理模板文件
结合模板引擎使用非常方便:
public static void processTemplate(Map<String, Object> data, String templatePath, String outputPath) {
// 先使用模板引擎处理HTML模板
String processedHtml = processTemplate(templatePath, data); // 使用Thymeleaf或其他模板引擎
// 再转换为PDF
convertHtmlToPdf(processedHtml, new FileOutputStream(outputPath));
}
4. 完整的工具类
这是我整合的一个完整工具类,可以直接拿去用:
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.file.Files;
import java.util.Base64;
/**
* HTML转PDF工具类
*/
@Slf4j
public class HtmlToPdfUtil {
/**
* 创建转换属性
*/
private static ConverterProperties createConverterProperties() {
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new FontProvider();
// 添加中文字体支持
fontProvider.addSystemFonts();
properties.setFontProvider(fontProvider);
return properties;
}
/**
* 将HTML内容转换为PDF
*/
private static void convertToPdf(String htmlContent, OutputStream outputStream) {
try {
// 创建PDF文档
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
// 转换HTML到PDF
HtmlConverter.convertToPdf(htmlContent, pdfDocument, createConverterProperties());
} catch (Exception e) {
log.error("HTML转PDF失败", e);
throw new RuntimeException("HTML转PDF失败", e);
}
}
/**
* 将HTML文件转换为PDF
*/
private static void convertFileToPdf(File htmlFile, OutputStream outputStream) {
try {
// 创建PDF文档
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
// 转换HTML文件到PDF
HtmlConverter.convertToPdf(Files.newInputStream(htmlFile.toPath()),
pdfDocument, createConverterProperties());
} catch (Exception e) {
log.error("HTML文件转PDF失败", e);
throw new RuntimeException("HTML文件转PDF失败", e);
}
}
// 公开API方法
public static void convertHtmlToPdf(String htmlContent, OutputStream outputStream) {
convertToPdf(htmlContent, outputStream);
}
public static void convertHtmlFileToPdf(File htmlFile, OutputStream outputStream) {
convertFileToPdf(htmlFile, outputStream);
}
public static void convertHtmlToPdfFile(String htmlContent, File outputFile) {
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
convertToPdf(htmlContent, outputStream);
} catch (Exception e) {
log.error("HTML转PDF文件失败", e);
throw new RuntimeException("HTML转PDF文件失败", e);
}
}
public static String convertHtmlToPdfBase64(String htmlContent) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
convertToPdf(htmlContent, outputStream);
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
log.error("HTML转PDF Base64失败", e);
throw new RuntimeException("HTML转PDF Base64失败", e);
}
}
public static String convertHtmlFileToPdfBase64(File htmlFile) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
convertFileToPdf(htmlFile, outputStream);
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
log.error("HTML文件转PDF Base64失败", e);
throw new RuntimeException("HTML文件转PDF Base64失败", e);
}
}
}
八、对比其他解决方案
在选择HTML转PDF方案时,我对比了几种主流技术:
- iText 7: 功能全面,性能很好,支持复杂格式,但商业使用需要授权费
- PDFBox: Apache开源项目,完全免费,但HTML渲染能力比较弱
- wkhtmltopdf: 基于WebKit的命令行工具,渲染质量高,但需要系统安装
- Flying Saucer: 开源纯Java实现,通常结合iText使用,不支持现代CSS特性
根据实际需求和场景,iText 7在大多数情况下是一个不错的选择,特别是对于复杂的HTML内容转换需求。
九、总结
通过iText 7实现HTML到PDF的转换并不复杂,关键是正确配置ConverterProperties和FontProvider。在实际项目中,最容易遇到的问题是字体和布局问题,只要掌握了本文介绍的技巧,基本都能解决。
如果你有复杂的报表或文档需求,使用iText 7配合HTML模板是一个高效且灵活的方案。希望这篇文章对你有所帮助!