SpringBoot集成:5分钟实现HTML转PDF功能

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

你是否因为HtmlPDF发愁?又是否因为中文乱码而心烦意乱?又是否因为格式导致PDF排版错乱而苦恼?......

在企业里,我们经常会遇到下载PDF报告的场景,前端展示还好,可以使用pdf.js类似的框架处理,但是很多时候,需要服务端自己渲染数据然后,上传或者邮件发送。好好的页面,转成PDF直接错乱了,只能一点点的调试,才能完成。过程费时费力。

这不,前两天正好遇到HtmlPDF的需求,调整了许久的样式才完成PDF的完美转化。

02 框架选型

Html转化成PDF的框架有很多,小编也试了好几种,将觉得好用的分享给大家。

2.1 io.woo.htmltopdf

Maven

xml 复制代码
<dependency>
	<groupId>io.woo</groupId>
	<artifactId>htmltopdf</artifactId>
	<version>1.0.4</version>
</dependency>
<!-- Thanks for using https://jar-download.com -->

官网地址:jar-download.com

GitHub地址:github.com/wooio/htmlt...

2.2 openhtmltopdf

Maven

xml 复制代码
<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>

GitHub地址:github.com/danfickle/o...

2.3 Flying Saucer

Maven

xml 复制代码
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>10.0.0</version>
</dependency>

GitHub地址:github.com/flyingsauce...

03 Springboot集成thymeleaf

3.1 Maven依赖

这里暂时忽略HtmlPDF的依赖,后面按需引入。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- html 2 pdf 工具 -->

3.2 页面

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>评估报告</title>
</head>
<body>
<div class="total-container">
    <div class="container" th:each="item:${list}">
        <input type="hidden" th:value="${item.licenseCode}" name="licenseCode" />
        <div class="img-container">
            <img th:src="${item.image1}" />
            <img th:src="${item.image2}" />
        </div>
        <div class="title" th:text="${item.vehicleName}"></div>
        <div>
            <ul>
                <li>
                    <label>品牌:</label>
                    <span th:text="${item.brandName}"></span>
                </li>
                <li>
                    <label>车系:</label>
                    <span th:text="${item.seriesName}"></span>
                </li>
                <li>
                    <label>年款:</label>
                    <span th:text="${item.modelYear}"></span>
                </li>
                <li>
                    <label>上牌年月:</label>
                    <span th:text="${item.licenseYear}"></span>
                </li>
                <li>
                    <label>里程(公里):</label>
                    <span th:text="${item.displayMileage}"></span>
                </li>
                <li>
                    <label>成交日期:</label>
                    <span th:text="${item.dealDate}"></span>
                </li>
                <li style="border-bottom: none;">
                    <label>成交价(元):</label>
                    <span th:text="${item.dealPrice}"></span>
                </li>
            </ul>
        </div>
    </div>
</div>
</body>
</html>

这里描述了车辆的信息

3.3 数据渲染

java 复制代码
@RequestMapping("index")
public String index(Model model) {
    List<Map<String, Object>> list = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        Map<String, Object> item = new HashMap<>();
        item.put("image1", "http://img.example.com/a.jpg");
        item.put("image2", "http://img.example.com/b.jp");
        item.put("vehicleName", "路虎Defender[卫士](进口)2023款Defender130 48V[卫士 130 48V] 3.0T手自-体P400 HSE");
        item.put("brandName", "路虎");
        item.put("seriesName", "Defender[卫士]");
        item.put("modelYear", "2023款");
        item.put("licenseYear", SDF.format(new Date()));
        item.put("displayMileage", DF.format(new BigDecimal("9657")));
        item.put("dealDate", SDF.format(new Date()));
        item.put("dealPrice", DF.format(new BigDecimal("1000000")));

        list.add(item);
    }
    model.addAttribute("list", list);
    return "index";
}

3.4 渲染效果

左右没有留白。

04 Html转PDF

上面已经完成Html的渲染和展示。我们只需要拿到Html并通过工具,转化成PDF即可。

HtmlPDF不需要通过浏览器渲染,需要服务端直接渲染并拿到渲染之后的Html。渲染的代码块:

java 复制代码
Map<String, Object> map = new HashMap<>();
map.put("list", list);

Context context = new Context(Locale.getDefault(), map);
String html = templateEngine.process("index", context);
System.out.println(html);

4.1 io.woo.htmltopdf

核心方法:

java 复制代码
InputStream convert = HtmlToPdf.create()
                .object(HtmlToPdfObject.
                        forHtml(htmlContent)
                        .defaultEncoding("utf-8"))
                .convert();

IOUtils.copyLarge(convert, response.getOutputStream());

效果

我们会发现转成PDF之后,左右会自动留白。现在的问题是,图片需要调整一下,调整到左右留白差不多,这个是难点。

应该怎么去调整呢?

因为生成的PDF默认都是基于A4的尺寸:21cm*29.7cm。所以我们直接将宽度设置为21cm

css 复制代码
.container{
    width: 21cm;
    /*设置边框方便查看*/
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}

转成PDF之后右边距还是有较大的空隙的:

我们继续加大宽度,最终结果:宽度设置23cm比较合适。此时,就可在页面上愉快的调整图片或者其他与元素了。

4.2 openhtmltopdf

核心方法

java 复制代码
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.withHtmlContent(htmlContent, null);
builder.toStream(response.getOutputStream());

// 配置字体支持中文
builder.useFont(new File("C:\\Windows\\Fonts\\simhei.ttf"), "SimHei");

builder.run();

注意事项

  • 需要设置支持中的字体,且页面的font-family需要明确指出

    css 复制代码
    font-family: "SimHei";
  • 页面元素必须要有闭合标签,否则报错

调整

调整依然是难点。经过测试,该框架的的PDFA4纸的大小基本一致。而内容则是去除留白的长度。

经过测量:PDF长度794px,两边留白56px,所以页面长度:794-56-56 = 682px

调正页面长度:

css 复制代码
.container{
    font-family: "SimHei";
    width: 682px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}

效果非常完美:

4.3 Flying Saucer

核心方法

java 复制代码
ITextRenderer renderer = new ITextRenderer();
renderer.getFontResolver().addFont("C:\\Windows\\Fonts\\simh	ei.ttf", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
renderer.setDocumentFromString(htmlContent);
renderer.layout();
renderer.createPDF(response.getOutputStream());

这里同样要设置支持中文,和openhtmltopdf配置类似。

调整方式同4.2openhtmltopdf本身就是基于Flying Saucer,所以这里就不在演示,两个同宗同源。

05 小结

每一款软件都是其特定标准,调整的顺序也不一样。无论如何,解决问题的思路是一致的。相比较io.woo.htmltopdf可能更加方便,本身就支持中文,而其他两个则需要单独配置才能支持中文。你们更喜欢哪一种呢?

相关推荐
wuxuanok7 小时前
苍穹外卖 —— 公共字段填充
java·开发语言·spring boot·spring·mybatis
Asthenia04127 小时前
技术复盘:从 Interceptor 到 Filter —— 正确修改 HTTP Request 字段的探索之路
后端
JaguarJack8 小时前
别再用 PHP 动态方法调用了!三个坑让你代码难以维护
后端·php
mudtools8 小时前
打造.NET平台的Lombok:实现构造函数注入、日志注入、构造者模式代码生成等功能
后端·.net
串串店藕片打孔员8 小时前
把List<T>构建一颗树封装工具类
java
用户0332126663678 小时前
自动创建 Word 文档——Java 实现
java
江上月5138 小时前
django与vue3的对接流程详解(下)
后端·python·django
Cikiss8 小时前
图解 bulkProcessor(调度器 + bulkAsync() + Semaphore)
java·分布式·后端·elasticsearch·搜索引擎
Mintopia8 小时前
Next.js 与 Serverless 架构思维:无状态的优雅与冷启动的温柔
前端·后端·全栈