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(多页固定头尾)相关推荐
- 如何在Flutter上实现高性能的动态模板渲染
背景 最近小组在尝试使用一套阿里dinamicX的DSL,通过动态模板下发,实现Flutter端的动态化模板渲染:本来以为只是DSL到Widget的简单映射和数据绑定,但实际跑起来的效果出乎意料的差, ...
- java根据Freemarker模板渲染出Excel文件并在浏览器中下载
**java根据Freemarker模板渲染出Excel文件并在浏览器中下载** 准备工作 1.导入的依赖 2.创建模板 Freemrker语法大全: [Freemarker语法使用请点击该链接跳转学 ...
- Freemarker - 根据模板动态生成word文档
文章目录 Freemarker 根据模板动态生成word文档 Freemarker 介绍: Freemarker 使用: freemarker加载模板目录的方法 参考资料 Freemarker 根据模 ...
- 利用freemarker 在模板里面写入动态数据,动态表格,图片插入并生成word文档
利用freemarker 在模板里面写入动态数据,动态表格,图片插入. 以下测试代码图片(image.jpg)和模板(template.xml)是直接放到src目录下面的,可以根据自己需求调整 废话不 ...
- gulp-jade 动态数据渲染编译模板 热更新案例开发
导读 我们今天来聊一聊如何使用gulp编译生成jade模板:如果你想快速编写html,就可以使用node下很火的一款模板语法jade, 但你便写完代码后,想要立即看到效果就可以使用gulp生态里面的g ...
- freemarker根据静态模板和动态模板生成PDF与Word
背景介绍:最近在做老旧项目的二次开发,所以用到了freemarker去生成PDF和Word,涉及到打印静态模板与动态模板.查看了这方面的资料发现大都不全,似是而非.废话不多说,上案例. freemar ...
- SpringBoot中使用Freemarker邮件模板生成邮件
SpringBoot中使用Freemarker邮件模板生成邮件 当邮件内容比较简单的时候,我们可能一行字符串就能表达所有意思了,但是大部分情况下,我们的邮件内容都比较复杂需要用HTML来组织邮件内容, ...
- freemarker ftl模板_Web开发人员必会的模板引擎技术之Freemarker
曾几何时,Web开发是个多么高大上的名字,程序猿们都以能搞定Web技术为荣,此时还没有前后端之说.然而随着互联网的发展,社会分工进一步细化,职业岗位也更加细分,慢慢开始有了前端攻城狮和后端攻城狮,技术 ...
- Springboot中使用freemarker动态生成word文档
文章目录 freemarker模板动态生成word文档 前言 准备 简单模板准备 <一> `word 2003` 新建`.doc` 模板 <二> 另存为`.xml` 文件,格式 ...
最新文章
- jbpm 4.3 与 spring 集成
- CentOS6.5通过jdk8.rpm文件安装JDK8
- Android OkHttp完全解析 是时候来了解OkHttp了
- Python random模块常用方法的使用
- 32岁了学python来的及吗_为什么每个人都应该在2020年学习Python?
- Java hibernate假外键_JAVA基础:Hibernate外键关联与HQL语法
- HTML之表格的基本知识
- 矩阵分析相关证明(一) —— 正交与投影
- 现代软件工程第五周作业
- CDOJ--1012
- 【洛谷 P2764】 最小路径覆盖问题(最大流)
- linux命令行用户登录,Linux终端以及用户登录相关命令
- mysql查看表空间占用情况
- SPSS数据分析常见问题(差异性研究)
- 罗振宇2021跨年演讲6:山村小学的豆腐课到底在玩啥?
- 西安非全日制计算机研究生哪所学校好,报考陕西非全日制研究生有哪些学校可以选择?...
- 【博学谷学习记录】超强总结,用心分享 | 产品经理必备技能之Axure RP9(持续更新)
- 易基因|3文聚焦:宏病毒组测序在肠病中的应用研究
- 服务器存储视频文件夹在哪里找,微信视频文件夹存储在什么位置?在哪里能找到...
- Win10 Docker 安装mysql8.0