需求
为方便定制,项目有了通过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等打印属性进行设置。