基于SpringBoot实现HTML转PDF功能的常用方式

 更新时间:2025年10月12日 11:38:13   作者:智_永无止境  
你是否因为Html转PDF发愁?又是否因为中文乱码而心烦意乱?又是否因为格式导致PDF排版错乱而苦恼?所以本文小编给大家介绍了基于SpringBoot实现HTML转PDF功能的常用方式,需要的朋友可以参考下

01 引言

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

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

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

02 框架选型

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

2.1 io.woo.htmltopdf

Maven

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

官网地址:https://jar-download.com

GitHub地址:https://github.com/wooio/htmltopdf-java

2.2 openhtmltopdf

Maven

<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地址:https://github.com/danfickle/openhtmltopdf

2.3 Flying Saucer

Maven

<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>10.0.0</version>
</dependency>

GitHub地址:https://github.com/flyingsaucerproject/flyingsaucer

03 Springboot集成thymeleaf

3.1 Maven依赖

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

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

<!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 数据渲染

@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。渲染的代码块:

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

核心方法:

InputStream convert = HtmlToPdf.create()
                .object(HtmlToPdfObject.
                        forHtml(htmlContent)
                        .defaultEncoding("utf-8"))
                .convert();

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

效果

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

应该怎么去调整呢?

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

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

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

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

4.2 openhtmltopdf

核心方法

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需要明确指出

font-family: "SimHei";

页面元素必须要有闭合标签,否则报错

调整

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

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

调正页面长度:

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

效果非常完美:

4.3 Flying Saucer

核心方法

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可能更加方便,本身就支持中文,而其他两个则需要单独配置才能支持中文。你们更喜欢哪一种呢?

以上就是基于SpringBoot实现HTML转PDF功能的常用方式的详细内容,更多关于SpringBoot实现HTML转PDF的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot结合Maven项目依赖版本冲突问题解决

    SpringBoot结合Maven项目依赖版本冲突问题解决

    本文主要介绍了SpringBoot结合Maven项目依赖版本冲突问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • springmvc实现跨服务器文件上传功能

    springmvc实现跨服务器文件上传功能

    这篇文章主要为大家详细介绍了springmvc实现跨服务器文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • SSH框架网上商城项目第1战之整合Struts2、Hibernate4.3和Spring4.2

    SSH框架网上商城项目第1战之整合Struts2、Hibernate4.3和Spring4.2

    SSH框架网上商城项目第1战之整合Struts2、Hibernate4.3和Spring4.2,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • IDEA中用maven连接数据库的教程

    IDEA中用maven连接数据库的教程

    这篇文章主要介绍了IDEA中用maven连接数据库的教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Spring中的@PostConstruct注解使用方法解析

    Spring中的@PostConstruct注解使用方法解析

    这篇文章主要介绍了Spring中的@PostConstruct注解使用方法解析,@PostConstruct注解是用来处理在@Autowired注入属性后init()方法之前,对一些零散的属性进行赋值的注解,需要的朋友可以参考下
    2023-11-11
  • 浅谈Maven环境隔离应用

    浅谈Maven环境隔离应用

    这篇文章主要介绍了浅谈Maven环境隔离应用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • springboot之如何获取项目目录路径

    springboot之如何获取项目目录路径

    这篇文章主要介绍了springboot之如何获取项目目录路径问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • 利用java判断质数的3种方法代码示例

    利用java判断质数的3种方法代码示例

    这篇文章主要给大家介绍了关于利用java判断质数的3种方法,在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数(素数),文中给出了详细的代码示例,需要的朋友可以参考下
    2023-07-07
  • java web实现网上手机销售系统

    java web实现网上手机销售系统

    这篇文章主要为大家详细介绍了java web实现网上手机销售系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java HashMap源码深入分析讲解

    Java HashMap源码深入分析讲解

    在java开发中,HashMap是最常用、最常见的集合容器类之一,下面一起温故一下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08

最新评论