Freemarker动态模板渲染flyingsaucer将html转PDF(多页固定头尾)
目錄
- 一、序言
- 二、CSS樣式控制打印模板
- 三、代碼示例
- 1、pom.xml
- 2、application.yml
- 3、PdfGenerationController
- 4、Freemarker模板內容
- 四、展示效果
一、序言
一般正常來說,生成PDF的操作都是通過將HTML轉成PDF,HTML動態渲染可以借助模板引擎,如常用的Thymeleaf或者Freemarker。
HTML轉PDF可以通過flyingsaucer來實現,可以參考之前博主寫的一篇文章《flyingsaucer進行html文件轉圖片和pdf》,至于PDF樣式,我們可以通過CSS打印樣式來控制。
今天這篇文章主要分享模板引擎動態渲染以及結合flyingsaucer通過CSS打印樣式控制PDF的內容呈現,固定每頁PDF的頭和尾部。
二、CSS樣式控制打印模板
在PrintCSS上有一篇文章: Running Headers and Footers ,里面會介紹CSS運行時元素以及如何控制打印PDF時的頭部和尾部。
這里介紹一個在線工具:PrintCSS.live,里面可以在線預覽pdf打印效果,如下:
三、代碼示例
1、pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.1.22</version></dependency>2、application.yml
spring:# freemarker configurationfreemarker:cache: truesuffix: .ftlcharset: UTF-8template-loader-path: classpath:templates/備注:template-loader-path為.ftl模板加載路徑,這里我們指定了類路徑下的templates目錄。
3、PdfGenerationController
import com.itextpdf.text.pdf.BaseFont; import com.universe.wonderful.pojo.model.AccountProofModel; import freemarker.template.Configuration; import freemarker.template.Template; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.xhtmlrenderer.pdf.ITextRenderer;import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.time.LocalDate;/*** @author Nick Liu* @date 2023/3/1*/ @Slf4j @RestController @RequiredArgsConstructor public class PdfGenerationController {private final Configuration configuration;@RequestMapping("/pdf/preview")public ResponseEntity<byte[]> downloadPdfWithFixedHeaderAndFooter() {AccountProofModel accountProofModel = AccountProofModel.builder().generationDate(LocalDate.now().toString()).memberName("Nick Liu").memberAddress("Nanshan District, Shenzhen city, Guangdong Province").accountNo("88888888888888").bankName("ICBC").bankSwiftCode("ABCDEFG").bankAddress("Shenzhen city of Guangdong Province").countryName("China").build();ByteArrayOutputStream os = new ByteArrayOutputStream();try {// 不建議直接創建Template實例,開銷比較大,可以直接通過Configuration實例獲取,有緩存機制Template template = configuration.getTemplate("personalAccountProof.ftl");String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, accountProofModel);ITextRenderer renderer = new ITextRenderer();// 如果內容有中文則需要添加支持中文的字體renderer.getFontResolver().addFont("/fonts/calibri.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);renderer.setDocumentFromString(content);renderer.layout();renderer.createPDF(os);renderer.finishPDF();} catch (Exception e) {log.error("Fail to generate pdf: {}", e.getMessage(), e);return ResponseEntity.internalServerError().body(null);}HttpHeaders respHeaders = new HttpHeaders();respHeaders.setContentType(MediaType.APPLICATION_PDF);respHeaders.setContentDisposition(ContentDisposition.inline().filename("accountProof.pdf", StandardCharsets.UTF_8).build());return new ResponseEntity<>(os.toByteArray(), respHeaders, HttpStatus.OK);} }備注:字體會從類路徑下加載,底層通過ClassLoader#getResourceAsStream()讀取。
字體目錄和freemarker模板目錄如下圖:
4、Freemarker模板內容
<!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>Running Headers and Footers</title><style>@page {size: A4;margin: 40mm 10mm 50mm 10mm;@top-left {content: element(headerLeft);}@bottom-center {content: element(footerCenter);}}* {padding: 0;margin: 0;}body {font-family: Calibri, serif;}.headerLeft {position: running(headerLeft);}.titleWrapper > div {margin: 2px 0;}.footerCenter {text-align: center;position: running(footerCenter);}.footerTipsWrapper {color: #C1A97D;margin-top: 10px;border-top: 2px solid #EFE7DA;}.footerTipsWrapper > div {font-size: 12px;margin-top: 12px;}.contentWrapper {margin-top: -10px;}.paddingWrapper {padding: 10px;}.accountIntroduction {margin-top: 60px;background-color: #EFE7DA;border: 1px solid #EFE7DA;border-radius: 10px;}.accountDetailsWrapper {margin-top: 50px;border: 3px solid #EFE7DA;border-radius: 10px;}.subTitle {font-weight: bold;border-bottom: 2px solid #EFE7DA;padding-bottom: 10px;}.accountDetails > div {margin-top: 8px;}</style> </head> <body><div class="headerLeft paddingWrapper"><img src="http://localhost:8080/images/proof/head_logo.png" /></div><div class="footerCenter"><div class="footerLogoWrapper"><img src="http://localhost:8080/images/proof/footer_logo.png" alt="logo" /></div><div class="footerTipsWrapper"><div>www.aletaplanet.com | account@aletaplanet.com</div><div>MPHK Management Company Limited | Suite 615, 6/F, Ocean Centre, Harbour City, Tsim Sha Tsui, Tsim Sha Tsui, Kowloon |<br/>License No.: 21-10-03068</div></div></div><div class="contentWrapper"><div class="titleWrapper paddingWrapper"><div><b>Proof of Account Details</b></div><div>Generated on: ${generationDate}</div></div><div class="tips paddingWrapper">To whom it may concern,</div><div class="accountIntroduction paddingWrapper"><div><b>Personal account of ${memberName}</b></div><div style="margin-top: 10px;word-break: break-word">This letter confirms the below account details allow ${memberName} residing at ${memberAddress} to receive payments into his/ her AP-1 Account:</div></div><div class="accountDetailsWrapper paddingWrapper"><div class="subTitle">Business account details</div><div class="accountDetails"><div>Account Name: ${memberName}</div><div>Account Number: ${accountNo}</div><div>Bank Name: ${bankName}</div><div>Bank SWIFT/BIC: ${bankSwiftCode}</div><div>Bank Country: ${countryName}</div><div>Bank Address: ${bankAddress}</div></div></div></div> </body> </html>在@page{}代碼塊中我們指定了打印頁面的大小為A4、上下左右的邊緣分別為40毫米、50毫米、10毫米、10毫米,同時在頁面左上角指定了logo,以及在頁面底部居中指定了logo和描述。
實際上@top-left和@bottom-center的效果類似于固定定位。
備注:關于@page、@top-left、@bottom-center的介紹可以參考:https://www.w3.org/TR/css-page-3/#margin-boxes。
四、展示效果
啟動項目,打開瀏覽器,輸入http://localhost:8080/pdf/preview,可以預覽生成的PDF,如下:
備注:如果有多頁,頭部和尾部的logo也會在同樣的地方顯示。
總結
以上是生活随笔為你收集整理的Freemarker动态模板渲染flyingsaucer将html转PDF(多页固定头尾)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 坐标系转换分析
- 下一篇: 本地rdm连接服务器redis