期望:能够通过编写某种模板,把PDF的大概样子确定下来,然后把数据和模板做一次整合,得到最终的结果,生成PDF导出。
最终方案:freemarker + flying-saucer-pdf + iText
iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。但iText html转PDF对中文和css的支持都不是很好,后来调研到了flying-saucer-pdf这个工具库,用它生成pdf解决了大部分的问题。它依赖于iText实现。
- 图片显示问题。
- 中文显示问题,css样式问题。
- 表格跨行问题。
关于Flying Saucer
Flying Saucer是一个纯Java开源项目库,它使用CSS2.1进行布局渲染呈现格式良好的XML或XHTML,导出到Swing面板、PDF或图像。
New releases of Flying Saucer are distributed through Maven. The available artifacts are:
org.xhtmlrenderer:flying-saucer-core - Core library and Java2D rendering
org.xhtmlrenderer:flying-saucer-pdf - PDF output using iText 2.x
org.xhtmlrenderer:flying-saucer-pdf-itext5 - PDF output using iText 5.x
org.xhtmlrenderer:flying-saucer-pdf-openpdf - PDF output using OpenPDF
org.xhtmlrenderer:flying-saucer-swt - SWT output
org.xhtmlrenderer:flying-saucer-log4j - Logging plugin for log4j
GitHub:https://github.com/flyingsaucerproject/flyingsaucer
流程实现:
1. build.gradle
1 | compile('org.freemarker:freemarker:2.3.28') |
2. 编写Freemarker模板(或者使用其他模板引擎),打造HTML,绘制出PDF的模板。
- css渲染:
用css控制- ①图片、PDF模块防断裂 ②PDF纸张大小、方向、换页、page模型边距设置。
分页媒体格式模型中,文档被转移到一个或多个页面框。
该页框是映射到一个矩形平面。
这大致类似于css盒子模型:
CSS Paged Media Module Level 3
- freemarker模板语言
介绍和语法:FreeMarker手册
3. 引入Freemarker的引擎,将数据和模板使用引擎生成最终的内容(htmlStr)。
1 | /** |
关于资源文件路径问题:
用类加载器去找资源文件下的路径
其实这个方法是根据类加载路径来判断的,最终会执行以下代码:
1 | FreemarkerUtil.class.getClassLoader().getResource("/template/"); |
这里注意一下第二个参数
需要以 "/"
开头。
Freemarker提供了3种加载模板目录的方法。 它使用Configuration类加载模板:
3种方法分别是:
- public void setClassForTemplateLoading(Class clazz, String pathPrefix); –基于类路径
- public void setDirectoryForTemplateLoading(File dir) throws IOException; –基于文件系统
- public void setServletContextForTemplateLoading(Object servletContext, String path); –Servlet Context
4. 利用ITextRenderer解析生成的HTML模板,创建PDF。
1 | /** |
相关问题:
- 关于中文不换行的问题
在flying-saucer-pdf-itext5的9.0.6及以下版本,中文是不会换行的。 - 关于中文字体问题
使用itext转pdf是需要安装中文字体库的,不然中文显示不出来,在itext里面有多种引入字体的方式,其中html通过字符串转pdf的,使用以下方式引入字体库。并且在前端样式中加入font-family:SimSun;,即可显示中文。
1 | ITextFontResolver fontResolver = renderer.getFontResolver(); |
遇到项目build打包成jar,资源路径无法找到的问题。
本地可通过new ClassPathResource(pdfFont).getPath();
获取资源文件的全路径。但是jar包里是无法进入jar包内查找文件路径。
- 关于纸张大小、换页
指定纸张大小为a4横向排版、并且边距为0的样式:@page{size:297mm 210mm;margin:0;padding:0;margin:0}
,
换页样式:
1 | .index { |
关于引入的css样式是否需要加绝对路径问题
很多时候,前端的样式引入是可能用相对路径的,但pdf转换的时候是必须用绝对路径的,那就要么让前端把根路径加上,又或者我们在程序里面把link标签拎出来遍历给它们加上根路径,但其实还有一种方式,也就是flying-saucer-pdf提供的一个方法renderer.setDocumentFromString(html,baseUrl)
也是能达到效果,不需要我们事先加好根路径。解决图片相对路径问题
renderer.getSharedContext().setBaseURL("http://localhost:8080");
- 其他问题
该转换方式的html必须是静态化的,该转换方式对html的检查非常严格,必须使用闭合的标签,否则报错。