Freemarker动态模板渲染&flyingsaucer将html转PDF(多页固定头尾)
一、序言
一般正常来说,生成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 configuration freemarker: cache: true suffix: .ftl charset: UTF-8 template-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 ResponseEntitybyte[]> 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也会在同样的地方显示。
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Freemarker动态模板渲染&flyingsaucer将html转PDF(多页固定头尾)
本文地址: https://pptw.com/jishu/296322.html