期望 :能够通过编写某种模板,把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 实现。
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
12
compile('org.freemarker:freemarker:2.3.28')compile('org.xhtmlrenderer:flying-saucer-pdf:9.1.16')
分页媒体格式模型中,文档被转移到一个或多个页面框。 该页框是映射到一个矩形平面。 这大致类似于css盒子模型: CSS Paged Media Module Level 3
123456789101112131415161718192021222324252627
/** * freemarker 引擎渲染 html * * @param dataMap 传入 html 模板的 Map 数据 * @param ftlDir html 模板文件存放包的相对路径(相对于resources路径的包路径) eg: /templates * @param ftlFile html 模板文件名 eg: pdf_export_demo.ftl * * @return */public static String freemarkerRender(Object dataMap, String ftlDir, String ftlFile) { Writer out = new StringWriter(); configuration.setDefaultEncoding("UTF-8"); configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); try { configuration.setClassForTemplateLoading(PDFUtil.class, ftlDir); configuration.setLogTemplateExceptions(false); configuration.setWrapUncheckedExceptions(true); Template template = configuration.getTemplate(ftlFile); template.process(dataMap, out); out.flush(); return out.toString(); } catch (IOException | TemplateException e) { e.printStackTrace(); log.error("pdf模板资源文件载入失败", e); throw new IllegalArgumentException("pdf模板资源文件载入失败", e); }}
关于资源文件路径问题 : 用类加载器去找资源文件下的路径 其实这个方法是根据类加载路径来判断的,最终会执行以下代码:
1
FreemarkerUtil.class.getClassLoader().getResource("/template/");
这里注意一下 第二个参数 需要以 "/" 开头。 Freemarker提供了3种加载模板目录的方法。 它使用Configuration类加载模板: 3种方法分别是:
第二个参数
"/"
12345678910111213141516171819202122232425262728
/** * 使用 iText 生成 PDF 文档 * * @param htmlTmpStr html 模板文件字符串 * @param fontFile 所需字体文件(相对路径+文件名) */ public static byte[] createPDF(String htmlTmpStr, String fontFile) { ByteArrayOutputStream outputStream; byte[] result; try { outputStream = new ByteArrayOutputStream(); ITextFontResolver fontResolver = renderer.getFontResolver(); // 解决中文支持问题,需要所需字体(ttc)文件 fontResolver.addFont(fontFile, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setDocumentFromString(htmlTmpStr); renderer.layout(); renderer.createPDF(outputStream); result = outputStream.toByteArray(); outputStream.flush(); outputStream.close(); } catch (DocumentException | IOException e) { e.printStackTrace(); log.error("pdf生成失败", e); throw new IllegalArgumentException("pdf生成失败", e); } return result; }
相关问题:
1234
ITextFontResolver fontResolver = renderer.getFontResolver();// 解决中文支持问题,需要所需字体(ttc)文件fontResolver.addFont(fontFile, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
遇到项目build打包成jar,资源路径无法找到的问题。 本地可通过 new ClassPathResource(pdfFont).getPath(); 获取资源文件的全路径。但是jar包里是无法进入jar包内查找文件路径。
new ClassPathResource(pdfFont).getPath();
@page{size:297mm 210mm;margin:0;padding:0;margin:0}
.index { /* 后面内容归到下一页 */ page-break-after: always;}
关于引入的css样式是否需要加绝对路径问题 很多时候,前端的样式引入是可能用相对路径的,但pdf转换的时候是必须用绝对路径的,那就要么让前端把根路径加上,又或者我们在程序里面把link标签拎出来遍历给它们加上根路径,但其实还有一种方式,也就是flying-saucer-pdf提供的一个方法 renderer.setDocumentFromString(html,baseUrl) 也是能达到效果,不需要我们事先加好根路径。
renderer.setDocumentFromString(html,baseUrl)
解决图片相对路径问题 renderer.getSharedContext().setBaseURL("http://localhost:8080");
renderer.getSharedContext().setBaseURL("http://localhost:8080");