需求
为方便定制,项目有了通过html模板生成动态生成PDF的需求。本文使用的是flying-saucer-pdf-itext5 + freemarker的方案。
解决
关键的Maven依赖配置
1
2
3
4
5
6
7
8
9
10
org.xhtmlrenderer
flying-saucer-pdf-itext5
9.1.5
org.freemarker
freemarker
2.3.23
关键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 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();
// 解决中文乱码问题,fontPath为中文字体地址
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
@page {
size: 210mm 297mm;
margin:0 auto;padding:0;
}
* {margin:0;padding:0;}
body {background: #fff;font-family: 'Microsoft YaHei'}
${name!''}标题
内容#if>
一些需要注意的地方
style中font-family配合java程序中的字体设置可解决PDF中文乱码的问题。
${name!’’}的写法会将 dataMap中 key 为name的 value值渲染进freemarker模板。!’’的写法是为了设置空默认值,从而当dataMap中没有key为name时使用默认值且程序不会报错。
#if>的写法让存在isSigned值时显示if里的内容。
img src的写法配合dataMap中图片转base64后stamp值 可用于显示图片。
页眉页脚及打印样式可通过css中的@page等打印属性进行设置。