日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于freemarker生成pdf

發布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于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時添加權限及密碼

private static ByteArrayOutputStream htmlToPdf(String htmlContent, String fontPath, String password, String adminPassword) {try {ByteArrayOutputStream output = new ByteArrayOutputStream();ITextRenderer render = new ITextRenderer();ITextFontResolver fontResolver = render.getFontResolver();setPDFEncryption(password, adminPassword, render);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 RuleException(ErrCodeFile.CO_HTML_TO_PDF_FAILED);} } /** 用戶權限,根據需求自己設置*/ private static final int PERMIT = PdfWriter.ALLOW_PRINTING; /** pdf加密類型*/ private static final int ENCRYPTION_TYPE = PdfWriter.STANDARD_ENCRYPTION_128; private static void setPDFEncryption(String password, String adminPassword,ITextRenderer render) {PDFEncryption pdfEncryption = new PDFEncryption();// 用戶密碼pdfEncryption.setUserPassword(password.getBytes());// 管理員密碼pdfEncryption.setOwnerPassword(adminPassword.getBytes());// 用戶權限pdfEncryption.setAllowedPrivileges(PERMIT);// 加密類型pdfEncryption.setEncryptionType(ENCRYPTION_TYPE);render.setPDFEncryption(pdfEncryption); }

有水印頁碼


在生成PDF時無需添加權限及密碼,在添加水印及頁碼時添加

private static OutputStream addWaterImage(ByteArrayOutputStream outputStream, InputStream waterMarkPath,String fontPath, String password, String adminPassword) {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);// 用戶密碼,管理員密碼,權限,加密類型stamp.setEncryption(password.getBytes(), adminPassword.getBytes(), PERMIT, ENCRYPTION_TYPE);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);} }

模板記錄

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,

data:,文本數據 data:text/plain,文本數據 data:text/html,HTML代碼 data:text/html;base64,base64編碼的HTML代碼 data:text/css,CSS代碼 data:text/css;base64,base64編碼的CSS代碼 data:text/javascript,Javascript代碼 data:text/javascript;base64,base64編碼的Javascript代碼 data:image/gif;base64,base64編碼的gif圖片數據 data:image/png;base64,base64編碼的png圖片數據 data:image/jpeg;base64,base64編碼的jpeg圖片數據 data:image/x-icon;base64,base64編碼的icon圖片數據

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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。