在电商、金融等场景中,经常需要把一些合同、账单等生成PDF文件,相比使用一些PDF的类库直接进行创建和生成,使用html进行转换有以下两个优点:
-
样式美观,可以通过熟悉的html和css直接生成相对整齐美观的样式
-
后期修改和维护简单,html的生成可以使用freemarker等模板引擎,做到数据和样式的分离
下面的方式为面向普通内部项目免费合规的使用方式,iText(>=5.0)及需要其他商业授权的方案不在考虑范围。
核心类库
FlyingSaucer (
https://github.com/flyingsaucerproject/flyingsaucer
))
支持输入XML/XHtml + CSS2.1,除转换为PDF外,还支持转换为图片等其他格式。生成PDF支持选择使用不同的类库,这里选择openpdf(基于iText4版本,授权协议为LGPL和MPL)。
准备工作
-
maven依赖
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-openpdf</artifactId>
<version>9.1.22</version>
</dependency>
因为flyingsaucer只接受xml/xhtml,所以如果不能确保html内容严格遵守xml或者xhtml,需要使用JSoup或者JTidy进行解析和转化。
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.4</version>
</dependency>
或者
<dependency>
<groupId>com.github.jtidy</groupId>
<artifactId>jtidy</artifactId>
<version>1.0.3</version>
</dependency>
2. 需要转换html的文件
src/main/resources/doc.html:
注意:样式中设置的字体名称(font-family),需要与实际字体一致。
<html>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<style>
body{
font-family: MiSans;
</style>
</head>
<body style="text-align: center">
<h1>订单信息</h1>
<table align="center">
<tr><td>订单号</td> <td>金额</td></tr>
<tr><td>200323323344</td><td>1004.32</td></tr>
<tr><td>200323323345</td><td>1004.32</td></tr>
</table>
</body>
</html>
3. 中文字体
这里使用小米的可以免费商用的MiSans(
下载地址
)
解压后,复制MiSans-Regular.ttf到 src/main/resources 下。
代码实现
1. 转换普通html到xhtml。
因为FlyingSaucer只支持xml和xhtml,而我们的html一般不会完全遵守xhtml,所以先进行格式转换。
使用JTidy
public static String convertHtmlToXhtmlUsingJTidy(String html){
Tidy tidy = new Tidy();
tidy.setXHTML(true);
tidy.setQuiet(true);
tidy.setShowWarnings(false);
StringWriter stringWriter =new StringWriter();
tidy.parse(new StringReader(html), stringWriter);
return stringWriter.toString();
}
也可以使用Jsoup
public static String convertHtmlToXHtmlUsingJsoup(String html){
Document doc = Jsoup.parse(html);
doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
return doc.html();
}
2. 转换xhtml到PDF
在这里指定了使用的中文字体,输入为格式化后的xhtml,输出PDF格式的byte数组。
public static byte[] convertHtmlToPDFData(String xhtml) throws IOException {
ITextRenderer renderer = new ITextRenderer();
renderer.getFontResolver().addFont("/MiSans-Regular.ttf", BaseFont.IDENTITY_H,true);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
renderer.setDocumentFromString(xhtml);
renderer.layout();
renderer.createPDF(outputStream);
return outputStream.toByteArray();
}
3. 把html文件保存生成到一个pdf文件。
String htmlFileName = "/doc.html";
String pdfFileName = "result.pdf";
String html = IOUtils.resourceToString(htmlFileName, StandardCharsets.UTF_8);
String xhtml = HtmlUtils.convertHtmlToXhtmlUsingJTidy(html);
byte[] pdfData = convertHtmlToPDFData(xhtml);
FileUtils.writeByteArrayToFile(new File(pdfFileName), pdfData);
文件效果预览:
补充说明
-
包含以上功能的完整代码示例:
https://gitee.com/maceve_demo/html2pdf
, 有问题可以随时
创建issue
。
-
在添加字体时,也可以使用指定的字体名称覆盖字体原来的名称,便于在html中指定字体,具体可以参考ITextRenderer.addFont的其他签名方法。