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
