01 引言
你是否因为Html转PDF发愁?又是否因为中文乱码而心烦意乱?又是否因为格式导致PDF排版错乱而苦恼?......
在企业里,我们经常会遇到下载PDF报告的场景,前端展示还好,可以使用pdf.js类似的框架处理,但是很多时候,需要服务端自己渲染数据然后,上传或者邮件发送。好好的页面,转成PDF直接错乱了,只能一点点的调试,才能完成。过程费时费力。
这不,前两天正好遇到Html转PDF的需求,调整了许久的样式才完成PDF的完美转化。
02 框架选型
将Html转化成PDF的框架有很多,小编也试了好几种,将觉得好用的分享给大家。
2.1 io.woo.htmltopdf
Maven
<dependency>
<groupId>io.woogroupId>
<artifactId>htmltopdfartifactId>
<version>1.0.4version>
dependency>
官网地址:jar-download.com
GitHub地址:github.com/wooio/htmlt…
2.2 openhtmltopdf
Maven
<dependency>
<groupId>com.openhtmltopdfgroupId>
<artifactId>openhtmltopdf-coreartifactId>
<version>1.0.10version>
dependency>
<dependency>
<groupId>com.openhtmltopdfgroupId>
<artifactId>openhtmltopdf-pdfboxartifactId>
<version>1.0.10version>
dependency>
GitHub地址:github.com/danfickle/o…
2.3 Flying Saucer
Maven
<dependency>
<groupId>org.xhtmlrenderergroupId>
<artifactId>flying-saucer-pdfartifactId>
<version>10.0.0version>
dependency>
GitHub地址:github.com/flyingsauce…
03 Springboot集成thymeleaf
3.1 Maven依赖
这里暂时忽略Html转PDF的依赖,后面按需引入。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
3.2 页面
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
3.4 渲染效果
左右没有留白。
04 Html转PDF
上面已经完成Html的渲染和展示。我们只需要拿到Html并通过工具,转化成PDF即可。
Html转PDF不需要通过浏览器渲染,需要服务端直接渲染并拿到渲染之后的Html。渲染的代码块:
Map 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可能更加方便,本身就支持中文,而其他两个则需要单独配置才能支持中文。你们更喜欢哪一种呢?