需求
为方便定制,项目有了通过html模板生成动态生成PDF的需求。本文使用的是flying-saucer-pdf-itext5 + freemarker的方案。
解决
关键的Maven依赖配置
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-itext5</artifactId> <version>9.1.5</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
|
关键Java代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public void generate(String templateContent, Map<String, String> dataMap, String fontPath, File file) { FileOutputStream outputStream = null; ITextRenderer renderer = new ITextRenderer(); try { Configuration cfg = new Configuration(); StringTemplateLoader stringLoader = new StringTemplateLoader(); stringLoader.putTemplate("myTemplate", templateContent); cfg.setTemplateLoader(stringLoader); Template template = cfg.getTemplate("myTemplate", "utf-8"); String htmlData = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap); outputStream = new FileOutputStream(file);
ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setDocumentFromString(htmlData);
renderer.layout(); renderer.createPDF(outputStream); } catch (DocumentException | IOException | TemplateException e) { log.error("生成失败", e); } finally { renderer.finishPDF(); IOUtils.closeQuietly(outputStream); } }
|
其中:
参数templateContent为freemarker模板内容,可直接从项目文件中读取或者定义在数据库中方便定制;
参数dataMap为freemark模板数据;
参数fontPath是为了解决生成的PDF乱码问题,将中文字体位置传入并进行配置同时在模板文件中也需要进行配置;
参数file则是为了将PDF生成至文件。
freemarker模板示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/> <title></title> <style type="text/css"> @page { size: 210mm 297mm; margin:0 auto;padding:0; } * {margin:0;padding:0;} body {background: #fff;font-family: 'Microsoft YaHei'} </style> </head> <body> <h1>${name!''}标题</h1> <#if isSigned?exists><span>内容</span></#if> <img style="width="60" height="60" src="data:image/png;base64,${stamp!''}" /> </body> </html>
|
一些需要注意的地方
style中font-family配合java程序中的字体设置可解决PDF中文乱码的问题。
${name!’’}的写法会将 dataMap中 key 为name的 value值渲染进freemarker模板。!’’的写法是为了设置空默认值,从而当dataMap中没有key为name时使用默认值且程序不会报错。
<#if isSigned?exists></#if>的写法让存在isSigned值时显示if里的内容。
img src的写法配合dataMap中图片转base64后stamp值 可用于显示图片。
页眉页脚及打印样式可通过css中的@page等打印属性进行设置。