基于freemarker生成pdf
生活随笔
收集整理的這篇文章主要介紹了
基于freemarker生成pdf
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
環境準備
開發環境
java8,SpringBoot 2.1.4,字符集GBK
字體
宋體–simsun.ttf
pom依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency><groupId>com.itextpdf</groupId><artifactId>kernel</artifactId><version>7.0.3</version> </dependency> <dependency><groupId>com.itextpdf</groupId><artifactId>io</artifactId><version>7.0.3</version> </dependency> <dependency><groupId>com.itextpdf</groupId><artifactId>forms</artifactId><version>7.0.3</version> </dependency> <!-- 解決中文字體問題 --> <dependency><groupId>com.itextpdf</groupId><artifactId>font-asian</artifactId><version>7.0.3</version> </dependency> <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version> </dependency> <dependency><groupId>com.itextpdf.tool</groupId><artifactId>xmlworker</artifactId><version>5.5.13</version> </dependency>模板生成PDF
將模板轉換為html
從文件讀取模板
/*** 初始化freemarker配置* templateRoot:模板文件根目錄*/ Configuration freemarkerCfg = initFreemarkerCfg(templateRoot); /*** 將模板轉換為HTML字符串*/ String content = freeMarkerRender(data, freemarkerCfg, htmlTemplate, charSet); private static Configuration initFreemarkerCfg(String templateRoot) {Configuration freemarkerCfg = new Configuration();try {freemarkerCfg.setDirectoryForTemplateLoading(new File(templateRoot));} catch (IOException e) {log.error("模板根路徑獲取失敗!" + templateRoot, e);throw new RuntimeException("模板根路徑獲取失敗!" + templateRoot,e);}return freemarkerCfg; } /*** data 需要注入模板的數據* freemarkerCfg freemarker配置* htmlTmp 模板名稱* charSet 字符集 linux下使用UTF-8,windows下使用GBK,否則會出現中文亂碼,模板文件的文件編碼和聲明編碼同樣需要保持一致*/ private static String freeMarkerRender(Map<String, Object> data, Configuration freemarkerCfg, String htmlTmp,String charSet) {try (Writer out = new StringWriter();) {Template template = freemarkerCfg.getTemplate(htmlTmp);template.setEncoding(charSet);template.process(data, out);out.flush();return out.toString();} catch (Exception e) {log.error("HTML加載數據失敗!", e);throw new RuntimeException("HTML加載數據失敗!", e);} }從流讀取模板
/*** data 需要注入模板的數據* fileName 文件名稱* inputStream 模板文件流* charSet 字符集 linux下使用UTF-8,windows下使用GBK,否則會出現中文亂碼,模板文件的文件編碼和聲明編碼同樣需要保持一致*/ private static String freeMarkerRender(Map<String, Object> data, String fileName, InputStream inputStream,String charSet) {try (Writer out = new StringWriter();InputStreamReader inputStreamReader = new InputStreamReader(inputStream);) {Configuration configuration = new Configuration();Template template = new Template(fileName, inputStreamReader, configuration);template.setEncoding(charSet);template.process(data, out);out.flush();return out.toString();} catch (Exception e) {log.debug("HTML加載數據失敗!", e);throw new RuleException(ErrCodeFile.CO_HTML_TEMPLATE_CONVERT_ERROR);} }生成PDF
/*** htmlContent 通過freemarker生成的html* fontPath 字體文件路徑* ByteArrayOutputStream pdf文件流*/ private static ByteArrayOutputStream htmlToPdf(String htmlContent, String fontPath) {try {ByteArrayOutputStream output = new ByteArrayOutputStream();ITextRenderer render = new ITextRenderer();ITextFontResolver fontResolver = render.getFontResolver();fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);render.setDocumentFromString(htmlContent);render.getSharedContext().setBaseURL(BASE_URL);render.layout();render.createPDF(output);return output;} catch (Exception e) {log.debug("html轉換pdf失敗!", e);throw new RuntimeException("html轉換pdf失敗!", e);} }添加水印和頁碼
從文件獲取水印
/*** outputStream 生成的pdf流* waterMarkPath 水印文件路徑* fontPath 字體路徑* OutputStream pdf文件流*/ private static OutputStream addWaterImage(ByteArrayOutputStream outputStream, String waterMarkPath,String fontPath) {BaseFont baseFont = createFont(fontPath);try (InputStream input = new ByteArrayInputStream(outputStream.toByteArray());) {ByteArrayOutputStream output = new ByteArrayOutputStream();PdfReader reader = new PdfReader(input);PdfStamper stamp = new PdfStamper(reader, output);PdfContentByte contentByte = null;int n = reader.getNumberOfPages();Image logo = null;if(StringUtils.isNotBlank(waterMarkPath)){logo = Image.getInstance(waterMarkPath);}for (int i = 1; i <= n; i++) {contentByte = stamp.getUnderContent(i);Rectangle rectangle = reader.getPageSize(i);float width = rectangle.getWidth();float height = rectangle.getHeight();if(logo != null){logo.setAbsolutePosition(width / 2 - logo.getWidth() / 2, height / 2);contentByte.addImage(logo);contentByte.saveState();}String text = "第 " + i + " 頁 /共 " + n + " 頁";contentByte.beginText();contentByte.setFontAndSize(baseFont, 12);contentByte.showTextAligned(Element.ALIGN_CENTER, text, (width / 2) - 6, 15, 0);contentByte.endText();}reader.close();stamp.close();return output;} catch (Exception e) {log.debug("添加水印和頁碼失敗," + waterMarkPath, e);throw new RuntimeException("添加水印和頁碼失敗," + waterMarkPath, e);} } private static BaseFont createFont(String fontPath) {try {return BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);} catch (Exception e) {log.debug("字體讀取失敗," + fontPath, e);throw new RuntimeException("字體讀取失敗," + fontPath, e);} }從流獲取水印
/*** outputStream 生成的pdf流* waterMarkPath 水印流* fontPath 字體路徑* OutputStream pdf文件流*/ private static OutputStream addWaterImage(ByteArrayOutputStream outputStream, InputStream waterMarkPath,String fontPath) {BaseFont baseFont = createFont(fontPath);try (InputStream input = new ByteArrayInputStream(outputStream.toByteArray());) {ByteArrayOutputStream output = new ByteArrayOutputStream();PdfReader reader = new PdfReader(input);PdfStamper stamp = new PdfStamper(reader, output);PdfContentByte contentByte = null;int n = reader.getNumberOfPages();Image logo = null;if(waterMarkPath != null){byte[] waterMarkBytes = IOUtils.toByteArray(inputStream);logo = Image.getInstance(waterMarkBytes);}for (int i = 1; i <= n; i++) {contentByte = stamp.getUnderContent(i);Rectangle rectangle = reader.getPageSize(i);float width = rectangle.getWidth();float height = rectangle.getHeight();if(logo != null){logo.setAbsolutePosition(width / 2 - logo.getWidth() / 2, height / 2);contentByte.addImage(logo);contentByte.saveState();}String text = "第 " + i + " 頁 /共 " + n + " 頁";contentByte.beginText();contentByte.setFontAndSize(baseFont, 12);contentByte.showTextAligned(Element.ALIGN_CENTER, text, (width / 2) - 6, 15, 0);contentByte.endText();}reader.close();stamp.close();return output;} catch (Exception e) {log.debug("添加水印和頁碼失敗," + waterMarkPath, e);throw new RuntimeException("添加水印和頁碼失敗," + waterMarkPath, e);} }pdf加密碼及權限設置
權限說明
| ALLOW_PRINTING | 文檔允許打印 |
| ALLOW_DEGRADED_PRINTING | 允許用戶打印文檔,但不提供allow_printing質量(128位加密) |
| ALLOW_MODIFY_CONTENTS | 允許用戶修改內容,例如 更改頁面內容,或插入或刪除頁 |
| ALLOW_ASSEMBLY | 允許用戶插入、刪除和旋轉頁面和添加書簽。頁面的內容不能更改,除非也授予allow_modify_contents權限,(128位加密) |
| ALLOW_COPY | 允許用戶復制或以其他方式從文檔中提取文本和圖形,包括使用輔助技術。例如屏幕閱讀器或其他可訪問設備 |
| ALLOW_SCREENREADERS | 允許用戶提取文本和圖形以供易訪問性設備使用,(128位加密) |
| ALLOW_MODIFY_ANNOTATIONS | 允許用戶添加或修改文本注釋和交互式表單字段 |
| ALLOW_FILL_IN | 允許用戶填寫表單字段,(128位加密) |
需要多個權限時,用|*(或符號)拼接即可
無水印頁碼
在生成PDF時添加權限及密碼
有水印頁碼
在生成PDF時無需添加權限及密碼,在添加水印及頁碼時添加
模板記錄
1、字符集導致的亂碼問題
<meta http-equiv="Content-Type" content="text/html; charset=GBK"/>
(歷史原因導致開發使用GBK字符集,UTF-8的情況暫時未知)在windows上開發時,項目字符集,ftl文件,header的charset 均為GBK
部署在linux服務器時,ftl文件,header的charset均為UTF-8,否則生成PDF亂碼
2、img標簽
<img src="${logoImage}" width="204"/>
img標簽支持base64格式data:image/png;base64,
3、強制分頁
<p style="margin: 0pt"><div style="page-break-before: always; clear: both"/> </p>4、head記錄
<head><meta http-equiv="Content-Type" content="text/html; charset=GBK"/><meta http-equiv="Content-Style-Type" content="text/css"/><title>xxxx</title><style type='text/css'>body {font-family: SimSun;padding-top: 50px;}@page {size: a4;@top-center {content: element(header);}@bottom-center {content: element(footer);}}div.header {display: block;/*text-align: center;*/position: running(header);width: 100%;}div.footer {display: block;text-align: center;position: running(footer);width: 100%;}.custom-page-start {margin-top: 50px;}table {border-collapse: collapse;margin: 0 auto;width: 100%;}td {border: #000000 solid 0.75pt;vertical-align: top;padding: 5pt;}p {line-height: 18pt;margin: 0pt 0pt 4pt;}span {font-size: 10pt;}@media print {table {page-break-after: auto}tr {page-break-inside: avoid;page-break-after: auto}td {page-break-inside: avoid;page-break-after: auto}thead {display: table-header-group}tfoot {display: table-footer-group}} </style> </head>字體損壞
原因:maven打包時對字體文件進行編譯,導致字體文件損壞在pom.xml的build標簽中添加如下插件,使相關文件不被編譯
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>2.7</version><configuration><nonFilteredFileExtensions><nonFilteredFileExtension>ttf</nonFilteredFileExtension><nonFilteredFileExtension>ftl</nonFilteredFileExtension><nonFilteredFileExtension>html</nonFilteredFileExtension></nonFilteredFileExtensions></configuration><dependencies><dependency><groupId>org.apache.maven.shared</groupId><artifactId>maven-filtering</artifactId><version>1.3</version></dependency></dependencies> </plugin>總結
以上是生活随笔為你收集整理的基于freemarker生成pdf的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过Servlet的response绘制
- 下一篇: kubenetes中port、targe