SpringBoot集成:5分钟实现HTML转PDF功能
01 引言
你是否因为Html
转PDF
发愁?又是否因为中文乱码而心烦意乱?又是否因为格式导致PDF
排版错乱而苦恼?…
在企业里,我们经常会遇到下载PDF
报告的场景,前端展示还好,可以使用pdf.js
类似的框架处理,但是很多时候,需要服务端自己渲染数据然后,上传或者邮件发送。好好的页面,转成PDF
直接错乱了,只能一点点的调试,才能完成。过程费时费力。
这不,前两天正好遇到Html
转PDF
的需求,调整了许久的样式才完成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依赖
这里暂时忽略Html
转PDF
的依赖,后面按需引入。
<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
即可。
Html
转PDF
不需要通过浏览器渲染,需要服务端直接渲染并拿到渲染之后的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";
-
页面元素必须要有闭合标签,否则报错
调整
调整依然是难点。经过测试,该框架的的PDF
和A4
纸的大小基本一致。而内容则是去除留白的长度。
经过测量: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.2
。openhtmltopdf
本身就是基于Flying Saucer
,所以这里就不在演示,两个同宗同源。
05 小结
每一款软件都是其特定标准,调整的顺序也不一样。无论如何,解决问题的思路是一致的。相比较io.woo.htmltopdf
可能更加方便,本身就支持中文,而其他两个则需要单独配置才能支持中文。你们更喜欢哪一种呢?