此页内容
Java工具

将HTML格式文本转换为PDF

Memory

819字约3分钟

JavaPDFHTML

2024-04-20

若是有类似需求,可以看看这篇文章。起因是之前的一个需求,要求一次请求访问生成5种样式不同的PDF文件,并打包压缩,接着发送到指定邮箱。一开始我考虑的是PdfBox,简单用了一下,发现实在不方便,需要的样式不是靠调偏移量可以正常调过来的。后面发现了这个 openhtmltopdf 开源库,我开始编写原生HTML及CSS,用 openhtmltopdf 来转换为PDF(适合样式复杂的PDF)。

本文只描述简易用法,更多请查看 指南

开始

首先导入Maven依赖(可使用Jsoup,用于解析Dom):

pom.xml
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.15.4</version>
</dependency>

<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>

解析HTML

String html = "......";
Document document = Jsoup.parse(html);;
document.outputSettings().syntax(Document.OutputSettings.Syntax.html);

设置资源目录

这个主要针对于你有图片,或把CSS摘出来放在外面的情况,需要设置资源目录。

当你在HTML中引用CSS、图片等资源时,需指定 URL格式 的目录路径。
且指定的路径必须是 HTML 引用资源的路径的父目录。

我们假设资源目录设置为 file:///D:/JavaProjects/spring-new-demo/src/main/resources,这个URL指向着 resources 这个目录,而假设在 resources 目录下,存在着以下的目录结构。

# 目录结构
# resources/
#   print/
#     |-- css/
#       |-- test.css
#     |-- images/
#       |-- test.jpg

那么在HTML中的引用就该这样写:

test.html
<!-- 引入CSS -->
<link rel="stylesheet" href="print/css/test.css">

<!-- 引入图片 -->
<img src="print/images/test.jpg" alt="">

资源目录 /resourcesprint/css/print/images/ 目录的父级目录,所以按照 HTML 中定义引入:print/css/test.css,表示引用 /resources/print/css/test.css 文件。

注意

Jar包中,URL格式路径是不一样的,你需要以这种格式作为资源目录:
jar:file:/....../spring-new-demo.jar!/BOOT-INF/classes!/
因为打完包后,/print/ 目录会放在 /BOOT-INF/classes/


获取Jar包中的资源目录路径:

继续刚才的目录结构,先找到Jar包内某文件的URL形式,再通过截取获取Jar包内 resources 目录的URL路径。

private static String getPath(String templateName) {
    String resultPath;

    try {
        String templateLoaderPath = "classpath:/print/css/";
        // classpath:/print/css/
        templateLoaderPath = templateLoaderPath.replace(ResourceUtils.CLASSPATH_URL_PREFIX + "/", ResourceUtils.CLASSPATH_URL_PREFIX);

        URL url = ResourceUtils.getURL(templateLoaderPath + "test.css");
        String path = url.toExternalForm();

        String[] pathArray = path.split("/");
        pathArray = ArrayUtil.sub(pathArray, 0, pathArray.length - 3);
        resultPath = ArrayUtil.join(pathArray, "/") + "/";

    } catch (FileNotFoundException e) {

    }

    return resultPath;
}

准备字体

注意

如果不设置字体,用默认字体的话,HTML内的中文会变成###(建议把字体文件放在Jar包外)。

需要准备一个中文字体,例如宋体,建议ttf格式。

hero

转换

转换比较简单,只要准备好前面的东西,可一键生成。

注意

需注意的是,生成出的PDF效果可能会与HTML展示的效果有细微差距,可能需要自己慢慢调节。

// 解析HTML
Document document = Jsoup.parse(html);
document.outputSettings().syntax(Document.OutputSettings.Syntax.html);

// Pdf文件
String savePath = "....";
File pdfFile = new File(savePath);

// 字体资源
String fontPath = "....";
File fontFile = new File(fontPath);

try (OutputStream os = FileUtil.getOutputStream(pdfFile)) {
    PdfRendererBuilder builder = new PdfRendererBuilder();
    // 快速模式
    builder.useFastMode();
    builder.toStream(os);
    // 资源路径
    builder.withW3cDocument(new W3CDom().fromJsoup(document), resourcesPath);
    // 使用字体
    builder.useFont(fontFile, fontFamily);
    // 执行
    builder.run();
}