Java通过Html(ftl模板)生成PDF实战, 可支持商用

Java通过Html(freemarker模板)生成PDF实战, 可支持商用

技术架构

springboot + freemarker + [pdfbox] + flying-saucer-pdf

生成流程:

  1. freemarker: 根据数据填充ftl模板文件,得到包含有效数据的html文件(包含页眉页脚页码的处理,和解决中文渲染等问题)。
  2. flying-saucer-pdf: 将html转换成PDF文件。
  3. pdfbox: 操作PDF文件,完成加解密等操作。

依赖包

<!-- springboot版本, 2.2.x也是支持的 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.3.0</version>
</dependency>
<!-- load freemarker template file --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.30</version></dependency>
<!-- convert html to pdf --><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.4.1</version></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-core</artifactId><version>9.4.1</version></dependency><!-- operate pdf, such as encypt/decypt,不做加解密可不引用 --><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.24</version></dependency><!-- encrypt/decrypt zip --><dependency><groupId>net.lingala.zip4j</groupId><artifactId>zip4j</artifactId><version>2.11.5</version></dependency>
PS:网上很多文章使用 itext5/7来生成PDF的,用于个人学习或者开源项目确实没问题,但是不方便用于公司商业项目,因为itext是基于AGPL 的开源协议,商用是需要收费的,当然贵司愿意付费也是okay的。

1. 准备PDF的模板文件(freemarker文件)

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><title>PDF Demo Title</title><style>@page {size: A4;margin: 35mm 10mm 23mm 10mm;@top-center {content: element(headerTop);}@bottom-center {content: element(footerBottom);}}#pagenumber:before {content: counter(page);}#pagecount:before {content: counter(pages);}.headerTop {position: running(headerTop);color: white;}.footerBottom {color: #777E90;position: running(footerBottom);margin-top: 10mm;}* {padding: 0;margin: 0;}html,body {/*优先加载 Poppins英文字体,无法渲染则使用PingFang中文字体*/font-family: Poppins-Medium, PingFang, sans-serif;margin: 0 auto;}.content {color: #23262F;font-size: 8px;padding: 0 0;margin-top: 0;}.size16 {font-size: 16px;}.size12 {font-size: 12px;}.size10 {font-size: 10px;}.size8 {font-size: 8px;}.lineHeight18 {line-height: 18px;}.weight600 {font-weight: 600;}.weight500 {font-weight: 500;}.padding18 {padding: 18px;}.marginBottom8 {margin-bottom: 8px;}.marginLeft6 {margin-left: 6px;}.marginLeft24 {margin-left: 24px;}.marginLeft16 {margin-left: 16px;}.marginLeft13 {margin-left: 13px;}.marginLeft4 {margin-left: 4px;}.marginTop4 {margin-top: 4px;}.marginTop8 {margin-top: 8px;}.width140 {width: 140px;}.widthFull {width: 100%;}.heightFull {height: 100%;}.textAlignCenter {text-align: center;}.inlineCenter {display: inline-block;vertical-align: top;}.backGray {background-color: #F7F7F7;}.backBlue {background-color: #0E59F0;}.border {border: 1px solid #E5E5E5;}.colorBlue {color: #6E9BF6;}.colorDark {color: #777E90;}.colorBlack {color: #23262F;}.colorBottomLine {background-color: #6E9BF6;}.colorGray {color: #838CA4;}.table {border-spacing: 0;/*跨页表格标题*/-fs-table-paginate: paginate;}.table>thead>tr>th,.table>tbody>tr>td {padding: 16px 4px;text-align: left;word-break: break-all;word-wrap: break-word;white-space: normal;page-break-inside: avoid;page-break-after: auto;}.table>thead>tr>th {color: #838CA4;font-weight: 400;size: 12px;}.positionAbsolute {position: absolute;}.rightBottom8 {top: 80px;right: 30px;}.height8 {height: 8px;}.height6 {height: 6px;}.height16 {height: 16px;}.right0 {right: 0;}.lineGary {height: 1px;background-color: #E5E5E5;}</style>
</head><body>
<!-- 页眉 -->
<div class="headerTop"><img class="widthFull" style="margin-left: -1px" width="716px"src="data:image/jpeg;base64,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" /><div class="positionAbsolute rightBottom8 size12 weight500">${headerDate}</div>
</div><!-- 页脚 -->
<div class="footerBottom"><div class="height6 colorBottomLine"></div><div class="marginTop8 size10"><div class="inlineCenter">footer name<span class="marginLeft4 colorBlack"><![CDATA[${footerName}]]></span></div><!-- 页码 --><div class="inlineCenter colorBlack positionAbsolute right0"><span id="pagenumber"></span>/<span id="pagecount"></span></div></div>
</div><div class="content"><!-- 用户信息 --><div class="border padding18"><div class="colorBlue marginBottom8 weight500 size16"><![CDATA[${userName}]]></div><div class=""><span>Account Name</span><span class="colorBlue marginLeft6 weight500"><![CDATA[${accountName}]]></span><span class="marginLeft24">Account ID</span><span class="colorBlue marginLeft6 weight500"><![CDATA[${accountId}]]></span></div></div><div class="height8"></div><!-- 表格 --><section style="display: ${display}"><div class="height16"></div><div class=""><span class="colorBlack size16 weight600 marginLeft4 inlineCenter">Deposit</span></div><div class="marginTop8 weight500 colorBlack size10">Deposit History</div><div class="height16"></div><div class="lineGary"></div><div class="marginTop8"><table class="widthFull table"><thead><tr class="backGray"><th>Currency</th><th>Request ID</th><th>Bank Name</th><th>Bank Number</th><th>Amount</th><th>Time</th></tr></thead><tbody><#if list?? && (list?size> 0)><#list list as detail><tr><td>${detail.tokenCode}</td><td>${detail.orderNo}</td><td><#if detail.bankName??><![CDATA[${detail.bankName}]]><#else>-</#if></td><td><#if detail.bankNumber??><#-- 处理特殊字符的渲染 --><![CDATA[${detail.bankNumber}]]><#else>-</#if></td><td><#if detail.qty??>${detail.qty}<#else>-</#if></td><td><#if detail.updateTime??>${detail.updateTime}<#else>-</#if></td></tr></#list></#if></tbody></table></div><div class="height16"></div><div class="lineGary"></div></section>
</div>
</body>
</html>
freemarker模板注意事项:
  1. 图片需要通过base64的方式加载,直接加载图片路径可能无法渲染
  2. 字体名称需要和Java代码中加载的字体名称保持一致,中文无法渲染可能是没有设置别名
  3. 替换的变量,如果有null值需要在模板中判断
  4. 如果填充的变量中存在特殊字符,通过<![CDATA[${变量名}]]> 方式设置
  5. 部分高级的CSS样式或者标签可能不支持
  6. 页眉页脚采用running的方式处理

2. Java Code

2.1 FreeMarkerUtils

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;@Slf4j
public class FreeMarkerUtils {private static Template getTemplate(String templateFileName) {Configuration configuration = new Configuration(Configuration.VERSION_2_3_29);Template template = null;try {configuration.setObjectWrapper(new DefaultObjectWrapper());//设置编码格式configuration.setDefaultEncoding("UTF-8");//模板文件configuration.setClassForTemplateLoading(FreeMarkerUtils.class, "/templates");template = configuration.getTemplate(templateFileName + ".ftl", StandardCharsets.UTF_8.toString());} catch (IOException e) {e.printStackTrace();log.error("get template file failed, fileName:{}", templateFileName);}return template;}public static String generateHtmlStr(Map<String, Object> variables, String templateFileName) {Template template = getTemplate(templateFileName);StringWriter stringWriter = new StringWriter();template.setEncoding("UTF-8");try (BufferedWriter writer = new BufferedWriter(stringWriter)) {template.process(variables, writer);String htmlStr = stringWriter.toString();writer.flush();return htmlStr;} catch (TemplateException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}/*** 删除xml无法识别的非法字符* @param content* @return*/public static String removeIllegalChar(String content) {return content.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "");}
}

2.2 PdfTest Code


import com.janche.pdf.utils.FileUtil;
import com.janche.pdf.utils.FreeMarkerUtils;
import com.janche.pdf.vo.PdfVo;
import com.lowagie.text.pdf.BaseFont;
import freemarker.cache.ClassTemplateLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;/*** @Description:* @Auther: lirong* @Date: 2024/04/16*/
@Slf4j
public class PdfTest {public static void main(String[] args) {ITextRenderer renderer = new ITextRenderer();try {addPdfFont(renderer);String htmlStr = FreeMarkerUtils.generateHtmlStr(loadPdfData(), "pdfTemplate");renderer.setDocumentFromString(FreeMarkerUtils.removeIllegalChar(htmlStr));renderer.layout();} catch (IOException e) {throw new RuntimeException(e);}File pdfFile = new File("output/demo.pdf");File encryptPdfFile = new File("output/demo_encrypt.pdf");File zipFile = new File("output/demo.zip");try (OutputStream os = new FileOutputStream(pdfFile)) {renderer.createPDF(os);// encrypt pdf, if needn't encrypt pdf file, can remove pdfBox dependencyPDDocument document = PDDocument.load(pdfFile);StandardProtectionPolicy policy = new StandardProtectionPolicy("123456", "1234", new AccessPermission());policy.setEncryptionKeyLength(128);policy.setPermissions(new AccessPermission());document.protect(policy);document.save(encryptPdfFile);document.close();} catch (Exception e) {throw new RuntimeException(e);}log.info("PDF file generated successfully!");}private static void addPdfFont(ITextRenderer renderer) throws IOException {// add English fontClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(FreeMarkerUtils.class, "/static/font");String enFontPath = classTemplateLoader.getBasePackagePath() + "Poppins-Medium.ttf";ITextFontResolver fontResolver = renderer.getFontResolver();// the first font needn't set aliasfontResolver.addFont(enFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);// add Chinese font, the second font must set aliasString chFontPath = classTemplateLoader.getBasePackagePath() + "PingFang-Regular.ttf";fontResolver.addFont(chFontPath, "PingFang", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, null);}public static Map<String, Object> loadPdfData() {Map<String, Object> data = new HashMap<>();// 启用data.put("display", "block");// 隐藏
//        data.put("display", "none");data.put("footerName", "footer-龍");data.put("userName", "龍年發财");data.put("accountName", "Jackson-龍");data.put("accountId", "234324");LocalDateTime now = LocalDateTime.now();DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");data.put("headerDate", now.format(dateFormatter));ArrayList<PdfVo> tableList = new ArrayList<>();IntStream.range(1, 20).forEach(i -> {LocalDateTime dateTime = now.plusDays(i);PdfVo pdfVo = PdfVo.builder().tokenCode("USD" + i).bankName("HK Bank" + i).bankNumber("&23**94&" + i).qty(BigDecimal.TEN.add(new BigDecimal(i))).orderNo("ab123445" + i).updateTime(dateTime.format(dateFormatter)).build();tableList.add(pdfVo);});data.put("list", tableList);return data;}
}
PS:
  1. 关于中文字体不显示的问题: 一般是字体未正确加载,或者读取时字体名称不正确,完全不需要去更改什么源码class文件,高版本的flying-saucer-pdf 早已支持。
  2. 加载多个字体时,后面的字体需要设置别名,ftl模板中也需要使用设置的别名。
字体文件:(可在文章底部的github项目中获取)
  1. PingFang-Regular.ttf 中文字体
  2. Poppins-Medium.ttf 英文字体

3. PDF展示

在这里插入图片描述

Github源码下载:https://github.com/Janche/springboot-html2pdf-demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/16761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

InteractiveGraph图谱中vue项目中如何使用

InteractiveGraph图谱中vue项目中如何使用 一、下载js和css和字体二、vue2.0项目中引用三、grap组件 一、下载js和css和字体 //在这里面找 https://github.com/grapheco/InteractiveGraph/blob/master/dist/examples/example1.html二、vue2.0项目中引用 //main.js中全局引入$ …

导入 FDTD 仿真的 S 参数到 INTERCONNECT 的器件中

导入 FDTD 仿真的 S 参数到 INTERCONNECT 的器件中 正文正文 很多时候,仿真链路比较大时,我们可以将仿真的每个部分分隔开来,用 FDTD 计算出每一部分的 S 参数,然后将这些 S 参数导入 INTERCONNECT 中得到最终的仿真结果。这里我们来介绍一下这种方法。 首先,我们从右侧…

Linux系统安装AMH服务器管理面板并实现远程访问管理维护

目录 前言 1. Linux 安装AMH 面板 2. 本地访问AMH 面板 3. Linux安装Cpolar 4. 配置AMH面板公网地址 5. 远程访问AMH面板 6. 固定AMH面板公网地址 1. 部署Docker Registry 2. 本地测试推送镜像 3. Linux 安装cpolar 4. 配置Docker Registry公网访问地址 5. 公网远程…

【链表】Leetcode 92. 反转链表 II【中等】

反转链表 II 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#x…

敏捷开发最佳实践:发布管理实践案例之每周发布

发布管理是敏捷实践中交付价值的重要环节&#xff0c;本节敏捷实践案例将为大家分享&#xff1a;如何在增强业务感知的同时降低缺陷率&#xff0c;如何通过“缩短发布周期”解决需求交付时间较长和上线后缺陷较多的难题。 本实践节选自《2021中国企业敏捷实践白皮书》&#xf…

Solidity 教程01 Remix IDE 初次见面

访问链接地址&#xff1a; https://remix.ethereum.org/ 文件管理器 文件资源管理器用于管理工作区和文件。此插件还包含许多快捷方式和命令。要快速浏览&#xff0c;请右键单击文件以获取弹出菜单&#xff0c;并检查插件右上角的汉堡菜单。 要找到文件资源管理器模块 - 单…

SQL注入绕过技术深度解析与防御策略

引言 在Web安全领域&#xff0c;SQL注入攻击一直是一个棘手的问题。攻击者通过SQL注入手段获取敏感数据、执行恶意操作&#xff0c;甚至完全控制系统。尽管许多防御措施已被广泛采用&#xff0c;但攻击者仍不断开发新的绕过技术。本文将深度解析SQL注入的绕过技术&#xff0c;…

rust语言初识

程序设计实践课上水一篇ing 来源&#xff1a;rust基础入门-1.初识rust-酷程网 (kucoding.com) rust作为一名新兴语言&#xff0c;与go又有些许不同&#xff0c;因为它的目标是对标系统级开发&#xff0c;也就是C、C这两位在编程界的位置。比如我们最常用的windows系统&#x…

001.数据分析_NumPy

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

【ai】pycharm设置软件仓库编译运行基于langchain的chatpdf

联想笔记本 y9000p创建python工程: 使用langchain支持openai的向量化embedding安装软件包 发现没有openai ,添加软件仓库打开工具窗口 点击设置

优先队列的pop与push

前言 个人小记 一、代码 #include<stdio.h> #include <stdlib.h> #define MAX_LEN 10 #define father(n) ((n)/2) #define left(n) (2*(n)) #define right(n) (2*(n)1) typedef struct Prequeue {int * __data,*data;int size,count; }Prequeue;Prequeue* ini…

Jlink卡死 JFlash keil 盗版JLINK

现象&#xff1a;用Keil打开Jlink配置页&#xff0c;会卡死。 解决方法&#xff1a;用旧版本的Jlink软件&#xff0c;因为淘宝买的很多JLINK下载器是盗版的&#xff0c;不支持新版本的JLINK软件。到https://www.segger.com/downloads/jlink下载旧版本的软件。 如果必须要用新版…

SQLI-labs-第二十五关和第二十五a关

目录 第二十五关 1、判断注入点 2、判断数据库 3、判断表名 4、判断字段名 5、获取数据库的数据 第二十五a关 1、判断注入点 2、判断数据库 第二十五关 知识点&#xff1a;绕过and、or过滤 思路&#xff1a; 通过分析源码和页面&#xff0c;我们可以知道对and和or 进…

基于Kubeeasy安装Kubernetes-v1.22.1版本(安装报错已解决)

基础环境准备 将提供的安装包 chinaskills_cloud_paas_v2.0.2.iso 上传至 master 节点 /root 目录&#xff0c;并解压 到 /opt 目录&#xff1a; [rootlocalhost ~]# ll total 7446736 -rw-------. 1 root root 1579 Mar 7 22:46 anaconda-ks.cfg -rw-r--r--. 1 root …

链表经典题目—相交链表和链表倒数第k个节点

&#x1f389;&#x1f389;&#x1f389;欢迎莅临我的博客空间&#xff0c;我是池央&#xff0c;一个对C和数据结构怀有无限热忱的探索者。&#x1f64c; &#x1f338;&#x1f338;&#x1f338;这里是我分享C/C编程、数据结构应用的乐园✨ &#x1f388;&#x1f388;&…

艾体宝干货 | 教程:使用ntopng和nProbe监控网络流量

本教程旨在分享如何通过 ntopng 和 nProbe 这两款工具&#xff0c;深入了解和掌握网络流量监控的艺术。我们将提供从基本概念到高级应用的全面指导&#xff0c;涵盖了在多种平台和设备上的部署和配置步骤。不论您是专业人员还是技术爱好者&#xff0c;跟随本教程&#xff0c;都…

11thingsboard物联网网关接入ThingsBoard物联网平台的操作说明

本文包含关于如何配置ThingsBoard 平台和连接钡铼技术R40设备的说明。ThingsBoard平台是一个用于数据收集、处理、可视化和设备管理的开源物联网平台。它通过行业标准MQTT协议实现设备连接。ThingsBoard结合了可扩展性、容错性和性能&#xff0c;因此您永远不会丢失数据。 4G L…

【考研数学】线代除了「李永乐」,还能跟谁?

考研线代&#xff0c;除了利用了老师&#xff0c;我觉得还有一个宝藏老师的课程值得听&#xff01; 那就是喻老&#xff0c;这个是我在b站上面新发现的老师&#xff0c;听完他的课程发现真的喜欢 他不仅在B站上开设了课程&#xff0c;还编写了配套的线性代数辅导讲义&#xff…

Python图形界面(GUI)Tkinter笔记(十一):用【Entry()】实现单行文本输入(2)

Tkinter库中的单行文本输入框(Entry)与Pyhton中最常的get()方法有机结合在一起能实现各种各样的功能。get()不只是与Entry()方法配合使用,还可以与其它方法配合使用,例如还可以与前面的Button()方法使用等等。总之,不同的工具不同的组合产生的反应是充满各种梦幻与想象,妙趣…

NIO的ByteBuffer和Netty的ByteBuf的性能

在讨论Java NIO的ByteBuffer与Netty的ByteBuf的性能时&#xff0c;需要考虑几个主要的因素&#xff0c;因为性能表现并不是绝对的&#xff0c;而是依赖于具体的使用场景。Netty的ByteBuf设计更加现代&#xff0c;针对网络编程的需求进行了优化&#xff0c;包含了许多ByteBuffer…