1. 概述

在这个快速教程中,我们将学习如何使用OpenPDF在Java中实现HTML生成PDF文件。

2. OpenPDF

OpenPDF 是一个用于创建和编辑PDF文件的Java库,遵守LGPL和MPL协议。它是iText项目的分支。实际上,在版本5之前,使用OpenPDF生成PDF的代码几乎与iText API相同。它是一个维护良好的Java生成PDF的解决方案。

3. 使用Flying Saucer库实现

Flying Saucer是一个Java库,底层封装了OpenPDF,允许我们使用XML/XHTML + CSS 技术渲染生成为PDF文件。

3.1. Maven依赖

首先,我们需要添加Maven依赖:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.17.2</version>
</dependency>
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf-openpdf</artifactId>
    <version>9.5.1</version>
</dependency>

我们将使用库jsoup来解析HTML文件、输入流、URL和字符串。它提供了DOM(文档对象模型)遍历功能、CSS以及类似jQuery的选择器,以从HTML中提取数据。

flying-saucer-pdf-openpdf库接受HTML文件的XML表示,应用CSS格式和样式,并输出PDF。

3.2. HTML转PDF

在这个教程中,我们将讨论HTML到PDF转换的一些常见情况,如HTML中的图像和样式,使用Flying Saucer和OpenPDF。我们还将讨论如何定制代码以接受外部样式、图像和字体。

让我们看一个示例HTML代码:

<html>
    <head>
        <style>
            .center_div {
                border: 1px solid gray;
                margin-left: auto;
                margin-right: auto;
                width: 90%;
                background-color: #d0f0f6;
                text-align: left;
                padding: 8px;
            }
        </style>
        <link href="style.css" rel="stylesheet">
    </head>
    <body>
        <div class="center_div">
            <h1>Hello Baeldung!</h1>
            <img src="Java_logo.png">
            <div class="myclass">
                <p>This is the tutorial to convert html to pdf.</p>
            </div>
        </div>
    </body>
</html>

要将HTML转换为PDF,首先从定义的位置读取HTML文件:

File inputHTML = new File(HTML);

接下来,我们将使用jsoup将上述HTML文件转换为jsoup Document,以便渲染XHTML。

以下是XHTML输出:

Document document = Jsoup.parse(inputHTML, "UTF-8");
document.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
return document;

最后一步,我们将使用ITextRenderer从上一步生成的XHTML文档创建PDF文件。请注意,我们使用了try-with-resources块来确保输出流在结束后关闭:

try (OutputStream outputStream = new FileOutputStream(outputPdf)) {
    ITextRenderer renderer = new ITextRenderer();
    SharedContext sharedContext = renderer.getSharedContext();
    sharedContext.setPrint(true);
    sharedContext.setInteractive(false);
    renderer.setDocumentFromString(xhtml.html());
    renderer.layout();
    renderer.createPDF(outputStream);
}

3.3. 为外部样式自定义

我们可以将HTML输入文档中使用的额外字体注册到ITextRenderer,以便在生成PDF时包含它们:

renderer.getFontResolver().addFont(getClass().getClassLoader().getResource("fonts/PRISTINA.ttf").toString(), true);

ITextRenderer可能需要注册相对URL,以便访问外部样式:

String baseUrl = FileSystems.getDefault()
  .getPath("src/main/resources/")
  .toUri().toURL().toString();
renderer.setDocumentFromString(xhtml, baseUrl);

通过实现ReplacedElementFactory,我们可以自定义与图像相关的属性:

public ReplacedElement createReplacedElement(LayoutContext lc, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {
    Element e = box.getElement();
    String nodeName = e.getNodeName();
    if (nodeName.equals("img")) {
        String imagePath = e.getAttribute("src");
        try {
            InputStream input = new FileInputStream("src/main/resources/"+imagePath);
            byte[] bytes = IOUtils.toByteArray(input);
            Image image = Image.getInstance(bytes);
            FSImage fsImage = new ITextFSImage(image);
            if (cssWidth != -1 || cssHeight != -1) {
                fsImage.scale(cssWidth, cssHeight);
            } else {
                fsImage.scale(2000, 1000);
            }
            return new ITextImageElement(fsImage);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    return null;
}

注意:以上代码会在图像路径前添加基路径,并在未提供时设置默认图像大小。

然后,我们将自定义的ReplacedElementFactory添加到SharedContext

sharedContext.setReplacedElementFactory(new CustomElementFactoryImpl());

4. 使用Open HTML库实现

Open HTML to PDF 是另一个基于Flying Saucer + Apache PDF-BOX2 实现的Java库

4.1. Maven依赖

除了上述的jsoup库外,我们还需要在pom.xml文件中添加几个Open HTML to PDF库:

<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-core</artifactId>
    <version>1.0.10</version>
</dependency>
<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-pdfbox</artifactId>
    <version>1.0.10</version>
</dependency>

openhtmltopdf-core用于渲染规范的XML/XHTML,而*openhtmltopdf-pdfbox*则从渲染后的XHTML表示中生成PDF文档。

4.2. HTML转PDF

在这个程序中,使用Open HTML将HTML转换为PDF,我们将使用第3.2节中的相同HTML。首先,我们将像前面的例子一样将HTML文件转换为jsoup Document。

在最后一步,从XHTML文档创建PDF,PdfRendererBuilder会接收这个XHTML文档并创建输出文件。同样,我们使用try-with-resources来包裹我们的逻辑:

try (OutputStream os = new FileOutputStream(outputPdf)) {
    PdfRendererBuilder builder = new PdfRendererBuilder();
    builder.withUri(outputPdf);
    builder.toStream(os);
    builder.withW3cDocument(new W3CDom().fromJsoup(doc), "/");
    builder.run();
}

4.3. 为外部样式自定义

我们也可以将HTML输入文档中使用的额外字体注册到PdfRendererBuilder,以便在生成PDF时包含它们:

builder.useFont(new File(getClass().getClassLoader().getResource("fonts/PRISTINA.ttf").getFile()), "PRISTINA");

与先前的示例类似,PdfRendererBuilder库可能也需要注册相对URL以访问外部样式:

String baseUrl = FileSystems.getDefault()
  .getPath("src/main/resources/")
  .toUri().toURL().toString();
builder.withW3cDocument(new W3CDom().fromJsoup(doc), baseUrl);

5. 结论

在这篇文章中,我们学习了如何使用Flying Saucer和Open HTML将HTML转换为PDF。我们还讨论了如何注册外部字体、样式和自定义选项。

如惯例,本文中所有使用的代码示例可在GitHub上找到这里