我们知道,iTextPDF是一个强大PDF操作库,其中PdfHTML模块可以实现一键html格式转PDF(属于扩展付费业务),iTextPDF社区版(免费)只能使用itextcore里面的功能模块,所以为了实现免费的html转PDF功能,我们就可以利用Apache PDFBox®实现html转PDF,利用iTextPDF实现PDF操作。
通过组合使用 Apache PDFBox 和 iTextPDF,可以实现更加灵活且免费的 HTML 转 PDF 解决方案。Apache PDFBox 适用于简单的 PDF 生成,特别是我们不需要复杂布局或其他高级 PDF 操作时,而 iTextPDF 则可以在 PDF 创建完成后,进一步增强文档的功能和布局。这种方法可以让我们在不使用付费模块的情况下,依然高效地处理 PDF 文件。
知识储备:
iTextPDF:The Leading PDF Library for Developers | iText
iTextPDF社区版(免费)依赖下载网址:Installing iText Community for Java developers
itextcore模块
Kernel:这是 iText 的核心模块,包含所有基本的 PDF 操作功能,如创建、修改和读取 PDF 文件。几乎所有 PDF 操作都依赖于这个模块。
Layout:这个模块基于 Kernel 构建,提供更高级的布局功能,使开发人员可以创建更复杂的 PDF 文档,包含段落、表格、列表、图像等。
使用 Apache PDFBox 将 HTML 转换为 PDF
Apache PDFBox | A Java PDF LibraryApache PDFBox :Apache PDFBox | A Java PDF Library
Apache PDFBox 是一个功能强大的开源库,专门用于创建、操作和读取 PDF 文档。虽然它不像 iTextPDF 的付费扩展那样直接支持将 HTML 转换为 PDF,但我们可以使用它来创建 PDF 文档,然后手动将 HTML 的各个部分(如段落、标题、列表等)映射为 PDF 中的相应组件。
这里我们示例一个简单的 HTML 转 PDF 的实现,主要处理基础的 HTML 标签如 <p>
、<h1>
、<ul>
、<li>
等:
public class HtmlToPdfUsingPdfBox {
public static byte[] convertHtmlToPdf(String htmlContent) throws IOException {
// 创建一个新的 PDF 文档
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage();
document.addPage(page);
// 加载字体(此处为中文字体)
PDType0Font font = PDType0Font.load(document, new File("src/main/resources/fonts/NotoSansSC-VariableFont_wght.ttf"));
// 使用 Jsoup 解析 HTML
org.jsoup.nodes.Document htmlDoc = Jsoup.parse(htmlContent);
Elements elements = htmlDoc.body().select("p, h1, h2, h3, ul, li");
// 准备内容流
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.setLeading(14.5f); // 设置行高
contentStream.newLineAtOffset(25, 750);
// 将 HTML 内容写入 PDF
for (Element element : elements) {
String text = element.text();
// 处理不同的 HTML 标签
if (element.tagName().equals("h1")) {
contentStream.setFont(font, 18);
} else if (element.tagName().equals("h2")) {
contentStream.setFont(font, 16);
} else if (element.tagName().equals("h3")) {
contentStream.setFont(font, 14);
} else {
contentStream.setFont(font, 12);
}
contentStream.showText(text);
contentStream.newLine();
}
contentStream.endText();
}
// 将 PDF 文档写入字节数组
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
document.save(outputStream);
return outputStream.toByteArray();
}
}
}
}
代码总体概述
- 创建一个 PDF 文档。
- 使用 Jsoup 库解析 HTML 内容,并选择 HTML 文档中一些常用标签(如
<p>
、<h1>
、<h2>
等)。 - 通过 PDFBox 创建 PDF 页,并将解析出的 HTML 内容按顺序写入 PDF。
- 最终将生成的 PDF 写入字节数组,以便返回 PDF 文件数据。
- public class HtmlToPdfUsingPdfBox:
- 定义了一个包含将 HTML 转换为 PDF 的类。
- public static byte[] convertHtmlToPdf(String htmlContent) throws IOException:
- 这个方法是
static
,表示可以在不实例化类的情况下调用。方法接收一个htmlContent
参数,这是 HTML 文本内容,并返回一个byte[]
(字节数组),表示生成的 PDF 文件内容。如果处理过程中有文件 I/O 错误,会抛出IOException
。 - PDDocument document = new PDDocument():
- 创建了一个新的 PDF 文档对象。
PDDocument
是 PDFBox 用于表示 PDF 文档的类。通过try-with-resources
确保文档在使用完毕后自动关闭。 - PDPage page = new PDPage():
- 创建一个新的空白 PDF 页。
- document.addPage(page):将创建的页面添加到文档中。
- PDType0Font font = PDType0Font.load(document, new File(...)):
- 加载一个自定义字体(此处为 Noto Sans SC 字体),以确保生成的 PDF 支持中文字符的显示。
PDType0Font
是 PDFBox 中支持 Unicode 字符集的字体类。字体文件是一个外部文件,通过new File()
进行加载。 - org.jsoup.nodes.Document htmlDoc = Jsoup.parse(htmlContent):
- 使用 Jsoup 库将传入的 HTML 字符串解析为 Jsoup 的
Document
对象。Jsoup 是一个 Java 库,用于处理和操作 HTML。 - Elements elements = htmlDoc.body().select("p, h1, h2, h3, ul, li"):
- 从 HTML 文档中选择所有的
<p>
、<h1>
、<h2>
、<h3>
、<ul>
、<li>
标签,并存储在elements
集合中。这个集合中的元素是将要写入 PDF 的内容。 - PDPageContentStream contentStream = new PDPageContentStream(document, page):
- 创建一个
PDPageContentStream
对象,用于向页面中写入内容。它提供方法来写文本、绘制图形和插入图像。 - contentStream.beginText():
- 启动一个文本流,表示接下来会向 PDF 页面写入文本。
- contentStream.setFont(font, 12):
- 设置字体,字体大小为12。
- contentStream.setLeading(14.5f):
- 设置文本的行高,确保多行文本之间有足够的间距,行距为14.5个点。
- contentStream.newLineAtOffset(25, 750):
- 设置文本的起始位置,即从页面左侧 25 点、页面顶部 750 点处开始写入。
- for (Element element : elements):
- 遍历 HTML 文档中的元素(
<p>
、<h1>
、<h2>
等)。 - String text = element.text():
- 提取元素的文本内容。
- if (element.tagName().equals(...)):
- 根据元素的标签类型,设置不同的字体大小。标题标签
<h1>
对应较大的字体,<h2>
和<h3>
依次减小,普通段落<p>
使用默认字体大小 12。 - contentStream.showText(text):
- 将提取的文本写入到 PDF 文档中。
- contentStream.newLine():
- 每写一段文本后换行,准备写入下一行。
- contentStream.endText():
- 结束文本流。所有文本写入操作完成后调用此方法。
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream():
- 创建一个字节输出流,用于将生成的 PDF 内容保存为字节数组。
- document.save(outputStream):
- 将 PDF 文档保存到字节流中。
- return outputStream.toByteArray():
- 返回生成的 PDF 的字节数组,这可以用于生成 PDF 文件或通过网络发送。
- 结束 try-with-resources 块:
PDDocument
自动关闭,确保资源被正确释放。
通过上面的代码,我们可以将 HTML 文本的基本内容转换为 PDF 文档。然而,Apache PDFBox 主要适用于基本的 PDF 创建和编辑任务,而在某些复杂的场景下(如页面布局、表格、图片插入等),我们可以结合 iTextPDF 提供的功能来增强 PDF 的处理能力。
假设你需要将 PDF 文档生成后进一步处理,比如给 PDF 增加页眉、页脚、电子签名等高级功能,你可以用 iTextPDF 进行二次处理。以下是一个简单的例子:
public class PdfEnhancementUsingItext {
public static byte[] addFooterToPdf(byte[] pdfBytes) throws IOException {
// 创建 iTextPDF 读取器
PdfReader reader = new PdfReader(new ByteArrayInputStream(pdfBytes));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(byteArrayOutputStream);
// 读取 PDF 文档
PdfDocument pdfDocument = new PdfDocument(reader, writer);
Document document = new Document(pdfDocument);
// 添加页脚
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
pdfDocument.getPage(i).setIgnorePageRotationForContent(true);
document.showTextAligned(new Paragraph("页脚文本 - 第 " + i + " 页"),
297.5f, 20, i, TextAlignment.CENTER, VerticalAlignment.BOTTOM, 0);
}
// 关闭文档
document.close();
return byteArrayOutputStream.toByteArray();
}
}