目录

  • 一、序言
  • 二、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(多页固定头尾)相关推荐

    1. 如何在Flutter上实现高性能的动态模板渲染

      背景 最近小组在尝试使用一套阿里dinamicX的DSL,通过动态模板下发,实现Flutter端的动态化模板渲染:本来以为只是DSL到Widget的简单映射和数据绑定,但实际跑起来的效果出乎意料的差, ...

    2. java根据Freemarker模板渲染出Excel文件并在浏览器中下载

      **java根据Freemarker模板渲染出Excel文件并在浏览器中下载** 准备工作 1.导入的依赖 2.创建模板 Freemrker语法大全: [Freemarker语法使用请点击该链接跳转学 ...

    3. Freemarker - 根据模板动态生成word文档

      文章目录 Freemarker 根据模板动态生成word文档 Freemarker 介绍: Freemarker 使用: freemarker加载模板目录的方法 参考资料 Freemarker 根据模 ...

    4. 利用freemarker 在模板里面写入动态数据,动态表格,图片插入并生成word文档

      利用freemarker 在模板里面写入动态数据,动态表格,图片插入. 以下测试代码图片(image.jpg)和模板(template.xml)是直接放到src目录下面的,可以根据自己需求调整 废话不 ...

    5. gulp-jade 动态数据渲染编译模板 热更新案例开发

      导读 我们今天来聊一聊如何使用gulp编译生成jade模板:如果你想快速编写html,就可以使用node下很火的一款模板语法jade, 但你便写完代码后,想要立即看到效果就可以使用gulp生态里面的g ...

    6. freemarker根据静态模板和动态模板生成PDF与Word

      背景介绍:最近在做老旧项目的二次开发,所以用到了freemarker去生成PDF和Word,涉及到打印静态模板与动态模板.查看了这方面的资料发现大都不全,似是而非.废话不多说,上案例. freemar ...

    7. SpringBoot中使用Freemarker邮件模板生成邮件

      SpringBoot中使用Freemarker邮件模板生成邮件 当邮件内容比较简单的时候,我们可能一行字符串就能表达所有意思了,但是大部分情况下,我们的邮件内容都比较复杂需要用HTML来组织邮件内容, ...

    8. freemarker ftl模板_Web开发人员必会的模板引擎技术之Freemarker

      曾几何时,Web开发是个多么高大上的名字,程序猿们都以能搞定Web技术为荣,此时还没有前后端之说.然而随着互联网的发展,社会分工进一步细化,职业岗位也更加细分,慢慢开始有了前端攻城狮和后端攻城狮,技术 ...

    9. Springboot中使用freemarker动态生成word文档

      文章目录 freemarker模板动态生成word文档 前言 准备 简单模板准备 <一> `word 2003` 新建`.doc` 模板 <二> 另存为`.xml` 文件,格式 ...

    最新文章

    1. jbpm 4.3 与 spring 集成
    2. CentOS6.5通过jdk8.rpm文件安装JDK8
    3. Android OkHttp完全解析 是时候来了解OkHttp了
    4. Python random模块常用方法的使用
    5. 32岁了学python来的及吗_为什么每个人都应该在2020年学习Python?
    6. Java hibernate假外键_JAVA基础:Hibernate外键关联与HQL语法
    7. HTML之表格的基本知识
    8. 矩阵分析相关证明(一) —— 正交与投影
    9. 现代软件工程第五周作业
    10. CDOJ--1012
    11. 【洛谷 P2764】 最小路径覆盖问题(最大流)
    12. linux命令行用户登录,Linux终端以及用户登录相关命令
    13. mysql查看表空间占用情况
    14. SPSS数据分析常见问题(差异性研究)
    15. 罗振宇2021跨年演讲6:山村小学的豆腐课到底在玩啥?
    16. 西安非全日制计算机研究生哪所学校好,报考陕西非全日制研究生有哪些学校可以选择?...
    17. 【博学谷学习记录】超强总结,用心分享 | 产品经理必备技能之Axure RP9(持续更新)
    18. 易基因|3文聚焦:宏病毒组测序在肠病中的应用研究
    19. 服务器存储视频文件夹在哪里找,微信视频文件夹存储在什么位置?在哪里能找到...
    20. Win10 Docker 安装mysql8.0

    热门文章

    1. java 二维数组排序_java – 如何对二维ArrayList进行排序
    2. 【电商】订单拆单的流程中,系统需要做哪些工作?
    3. Ubuntu 安装 Code Blocks 16.01
    4. weui实现移动端地址三级联动,基于js
    5. 英文名字大全(女篇) 1
    6. 2023年5月产品经理认证NPDP线上班,我要报名学习
    7. 2017华为实习生招聘面试经历(IT应用软件 c++)
    8. 【C++】 内存空间布局、new/delete、malloc/free
    9. 普通人怎么能在一年内赚到20万?
    10. Fat32 无法存放4G文件 文件分割