SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)
SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)
文章目录
- SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)
- 1. 前言
- 2. 技术思路
- 3. 实现过程
- 4. 测试
1. 前言
近期在项目种遇到了实时生成复杂 PDF 的需求,经过一番调研和测试,最终选择了采用 Thymeleaf 和 iText7 来实现需求,本文将详细介绍实现过程。
2. 技术思路
- 通过 Thymeleaf 渲染生成需要的页面内容;
- 通过 iText7 html2pdf 库将 Thymeleaf 渲染的结果转换成 PDF;
- 将 PDF 内容写入到接口输出流中返回给前端浏览器展示;
3. 实现过程
-
Maven 引入依赖;
<!-- Thymeleaf --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency><!-- iText html2pdf --> <dependency><groupId>com.itextpdf</groupId><artifactId>html2pdf</artifactId><version>5.0.0</version> </dependency><!-- lombok --> <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId> </dependency><!-- 获取资源文件 --> <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.21</version> </dependency> -
编写 Thymeleaf 模板
resources/templates/demo.html;<!DOCTYPE html> <html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>PDF Demo</title><style>body {padding-top: 50px;padding-left: 60px;padding-right: 60px;font-family: 'KaiTi', serif;}.title {color: red;text-align: center;margin-bottom: 50px;}table {width: 100%;border: 1px solid black;border-spacing: 0;}th {border: 1px solid black;background-color: rgb(128, 128, 128);}td {border: 1px solid black;}</style> </head><body> <h1 class="title" th:text="${title}"></h1><table><thead><tr><th>序号</th><th>姓名</th><th>年龄</th><th>性别</th></tr></thead><tbody th:each="student, studentStat : ${students}"><tr><td th:text="${studentStat.count}"></td><td th:text="${student.name}"></td><td th:text="${student.age}"></td><td th:text="${student.sex}"></td></tr></tbody> </table></body> </html> -
添加中文字体资源
resources/fonts/simkai.ttf; -
编写 PDF 页码事件处理
handler/PageEventHandler;package com.xiaoqqya.itextpdf.handler;import com.itextpdf.kernel.events.Event; import com.itextpdf.kernel.events.IEventHandler; import com.itextpdf.kernel.events.PdfDocumentEvent; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.layout.Canvas; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.properties.TextAlignment;/*** 页码事件处理.** @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>* @since 2023/08/29*/ public class PageEventHandler implements IEventHandler {@Overridepublic void handleEvent(Event event) {PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;PdfDocument document = documentEvent.getDocument();PdfPage page = documentEvent.getPage();Rectangle pageSize = page.getPageSize();PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);Canvas canvas = new Canvas(pdfCanvas, pageSize);float x = (pageSize.getLeft() + pageSize.getRight()) / 2;float y = pageSize.getBottom() + 15;Paragraph paragraph = new Paragraph("-- " + document.getPageNumber(page) + " --").setFontSize(10);canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);canvas.close();} } -
编写 Student 实体类
model/domain/Student;package com.xiaoqqya.itextpdf.model.domain;import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;/*** 学生.** @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>* @since 2023/08/29*/ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Student {/*** 姓名*/private String name;/*** 、* 年龄*/private Integer age;/*** 性别*/private String sex; } -
编写 Service
service/PdfService生成 PDF;package com.xiaoqqya.itextpdf.service.impl;import cn.hutool.core.io.resource.ResourceUtil; import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; import com.itextpdf.io.font.FontProgramFactory; import com.itextpdf.kernel.events.PdfDocumentEvent; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.font.FontProvider; import com.xiaoqqya.itextpdf.exception.CustomException; import com.xiaoqqya.itextpdf.handler.PageEventHandler; import com.xiaoqqya.itextpdf.model.domain.Student; import com.xiaoqqya.itextpdf.service.PdfService; import org.springframework.stereotype.Service; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context;import javax.annotation.Resource; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List;/*** PDF Service.** @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>* @since 2023/08/29*/ @Service public class PdfServiceImpl implements PdfService {@Resourceprivate TemplateEngine templateEngine;/*** 生成 PDF.** @param outputStream 输出流*/@Overridepublic void generatePdf(OutputStream outputStream) {// 模拟数据List<Student> students = new ArrayList<>();students.add(Student.builder().name("小红").age(18).sex("女").build());students.add(Student.builder().name("小强").age(21).sex("男").build());students.add(Student.builder().name("熊大").age(19).sex("男").build());// 生成 Thymeleaf 上下文Context context = new Context();context.setVariable("title", "PDF Demo");context.setVariable("students", students);String demo = templateEngine.process("demo", context);// 生成 PDF, 并添加页码try (PdfWriter pdfWriter = new PdfWriter(outputStream); PdfDocument pdfDocument = new PdfDocument(pdfWriter)) {pdfDocument.setDefaultPageSize(PageSize.A4);pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, new PageEventHandler());ConverterProperties converterProperties = new ConverterProperties();FontProvider fontProvider = new DefaultFontProvider(true, true, false);fontProvider.addFont(FontProgramFactory.createFont(ResourceUtil.readBytes("fonts/simkai.ttf")));converterProperties.setFontProvider(fontProvider);HtmlConverter.convertToPdf(demo, pdfDocument, converterProperties);} catch (IOException e) {throw new CustomException(e.getMessage());}} } -
编写 Controller
controller/PdfController返回给前端浏览器展示;package com.xiaoqqya.itextpdf.controller;import com.xiaoqqya.itextpdf.service.PdfService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException;/*** PDF Controller.** @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>* @since 2023/08/29*/ @RestController @RequestMapping(value = "/pdf") public class PdfController {@Resourceprivate PdfService pdfService;/*** 生成 PDF.*/@GetMappingpublic void generatePdf(HttpServletResponse response) throws IOException {pdfService.generatePdf(response.getOutputStream());} }
4. 测试
浏览器访问 http://localhost:8080/pdf 查看效果。
参考文章:
- 使用itext7将HTML转为pdf · Issue #12 · ydq/blog (github.com);
相关文章:
SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)
SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29) 文章目录 SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)1. 前言2. 技术思路3. 实现过程4. 测试 1. 前言 近期在项目种遇到了实时生成复杂 PDF 的需求,经过一番…...
【核磁共振成像】并行采集MRI
目录 一、并行成像二、SENSE重建三、SMASH重建四、灵敏度校准五、AUTO-SMASH和VD-AUTO-SMASH六、GRAPPA重建七、SPACE RIP重建算法八、PILS重建算法九、PRUNO重建算法十、UNFOLD算法 一、并行成像 并行MR成像(pMRI):相位阵列接受线圈不但各有自己专用的接受通道,而且…...
深度图相关评测网站
文章目录 1 单目/Stereo相关测评网站介绍12 单目/Stereo相关测评网站介绍23 单目/Stereo相关测评网站介绍3 1 单目/Stereo相关测评网站介绍1 https://vision.middlebury.edu/stereo/eval3/ 2 单目/Stereo相关测评网站介绍2 http://www.cvlibs.net/datasets/kitti/eval_stereo…...
本地部署 CodeLlama 并在 VSCode 中使用 CodeLlama
本地部署 CodeLlama 并在 VSCode 中使用 CodeLlama 1. CodeLlama 是什么2. CodeLlama Github 地址3. 下载 CodeLlama 模型4. 部署 CodeLlama5. 在 VSCode 中使用 CodeLlama6. 使用WSGI启动服务7. 创建 start.sh 启动脚本 1. CodeLlama 是什么 Code Llama 是一个基于 Llama 2 的…...
Agilent33220A任意波形发生器
20MHz正弦波和方波脉冲、斜披、三角波,噪声和直流波形14-bit,50MSa/s,64K点任意波形AM、FM、PM、FSK和PWM凋制线性和对数扫描及脉冲串模式10mVpp至10Vpp幅苗范围图形化界面可以对信号设置进行可视化验证通过USB、GPIB和LAN连接 性能优异的各种函数的波形…...
springboot第37集:kafka,mqtt,Netty,nginx,CentOS,Webpack
image.png binzookeeper-server-start.shconfigzookeeper.properties.png image.png image.png 消费 image.png image.png image.png image.png image.png image.png image.png image.png image.png Netty的优点有很多: API使用简单,学习成本低。功能强大…...
NVIDIA DLI 深度学习基础 答案 领取证书
最后一节作业是水果分类的任务,一共6类,使用之前学习的知识在代码段上进行填空。 加载ImageNet预训练的基础模型 from tensorflow import kerasbase_model keras.applications.VGG16(weights"imagenet",input_shape(224, 224, 3),include_t…...
axios模拟表单提交
axios默认是application/json方式提交,controller接收的时候必须以RequestBody的方式接收,有时候不太方便。如果axios以application/x-www-form-urlencoded方式提交数据,controller接收的时候只要保证名字应对类型正确即可。 前端代码&#…...
智安网络|探索物联网架构:构建连接物体与数字世界的桥梁
物联网是指通过互联网将各种物理设备与传感器连接在一起,实现相互通信和数据交换的网络系统。物联网架构是实现这一连接的基础和框架,它允许物体与数字世界之间的互动和协作。 一、物联网架构的概述 物联网架构是一种分层结构,它将物联网系…...
胡歌深夜发文:我对不起好多人
胡歌的微博又上了热搜。 8月29日01:18分,胡歌微博发文称:“我尽量保持冷静,我对不起好多人,我希望对得起这短暂的一生”,并配了一张自己胡子拉碴的图,右眼的伤疤清晰可见。 不少网友留言称“哥你又喝多了吗…...
C++二级题
数字放大 #include<iostream> #include<string.h> #include<stdio.h> #include<iomanip> #include<cmath> #include<bits/stdc.h> int a[2000][2000]; int b[2000]; char c[2000]; long long n; using namespace std; int main() {cin>…...
NetApp AFF A900:适用于数据中心的超级产品
NetApp AFF A900:适用于数据中心的超级产品 AFF A 系列中的 AFF A900 高端 NVMe 闪存存储功能强大、安全可靠、具有故障恢复能力,提供您为任务关键型企业级应用程序提供动力并保持数据始终可用且安全所需的一切。 产品功能与特性 AFF A900:…...
入海排污口水质自动监测系统,助力把好入河入海“闸门”
随着经济社会的不断发展,污水的排放强度不断加大,大量的污水排入河流、湖泊和海洋中,造成了水体污染,严重影响着我国的用水安全、公众健康、经济发展与社会稳定。入河入海排污口是污染物进入河流和海洋的最后关口,也是…...
AUTOSAR知识点 之 ECUM (一):基础知识梳理(概念部分)
目录 1、概述 2、ECUM的工作状态 2.1、Startup状态 2.2、UP状态 2.3、RUN状态 2.4、SLEEP状态...
leetcode分类刷题:哈希表(Hash Table)(二、数组交集问题)
1、当需要快速判断某元素是否出现在序列中时,就要用到哈希表了。 2、本文针对的总结题型为给定两个及多个数组,求解它们的交集。接下来,按照由浅入深层层递进的顺序总结以下几道题目。 3、以下题目需要共同注意的是:对于两个数组&…...
[Mac软件]Adobe After Effects 2023 v23.5 中文苹果电脑版(支持M1)
After Effects是动画图形和视觉效果的行业标准。由运动设计师、平面设计师和视频编辑用于创建复杂的动画图形和视觉上吸引人的视频。 创建动画图形 使用预设样式为文本和图形添加动画效果,或逐帧调整它们。编辑、添加深度、制作动画或转换为可编辑的路径ÿ…...
范德波尔方程详细介绍与Python实现(附说明)
引言: 在研究真空管放大器的过程中,写下了一个振动微分方程。当时人们并没有混沌或是对初始条件敏感的概念。不过,当混沌理论有一定发展后,人们重新回顾这个方程时发现它其实是个混沌方程。当时,范德波尔在 Nature 杂志报告了基于这个微分方程的霓虹灯实验,发现当驱动信号…...
常用的GPT插件
0.简介 随着chatgpt爆火,这玩意并不对国内用户开放,如果想要使用的话还要需要进行翻墙以及国外手机号才能进行注册。 对于国内来说有很多国内免费的方法,这里就整理一下,方便大家开发 1. 网站类型 下面的网站无需注册即可免费…...
智慧校园用电安全解决方案
随着科技的不断发展,智慧校园建设逐渐成为了教育行业的一大趋势。在这个过程中,电力系统作为校园基础设施的重要组成部分,其安全、稳定、高效的运行显得尤为重要。下面小编来为大家介绍下智慧校园用电安全解决方案吧! 一、智慧校园电力系统现…...
【教程】DGL中的子图分区函数partition_graph讲解
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 目录 函数形式 函数作用 函数内容 函数入参 函数返参 使用示例 实际上官方的函数解释中就已经非常详细了。 函数形式 def partition_graph(g, graph_name, num_parts, out_path, num_hops1, part…...
日志分析 Elasticsearch 和 logstach.filebeat.
一、Elasticsearch 到底是啥?简单说,ES 就是一个能飞速搜索和分析海量数据的搜索引擎。类似百度、谷歌,但它是给你公司内部的数据用的。比如:淘宝搜商品,输入“手机 拍照好”,毫秒级给你结果——背后就是 E…...
保姆级教程:用S32K344的FlexCAN模块实现CAN FD通信(附代码解析)
从零构建S32K344的CAN FD通信系统:硬件连接、寄存器配置与实战代码解析 在汽车电子和工业控制领域,CAN FD协议正逐步取代传统CAN成为主流总线标准。NXP S32K344微控制器内置的FlexCAN模块完美支持CAN FD协议,其最高8Mbps的数据传输速率和64字…...
深入STM32F103定时器:用TIM2输入捕获精准测量脉冲宽度与频率
深入STM32F103定时器:用TIM2输入捕获精准测量脉冲宽度与频率 在嵌入式开发中,精确测量外部信号的脉冲宽度和频率是一项常见但极具挑战性的任务。无论是工业控制中的旋转编码器、消费电子中的红外遥控信号,还是无人机领域的PPM控制信号&#x…...
手把手教你创建CST自定义材料:以吸波材料为例,导入厂家S参数曲线
手把手教你创建CST自定义材料:以吸波材料为例,导入厂家S参数曲线 在电磁仿真领域,材料参数的精确建模往往是决定仿真结果可靠性的关键因素。当我们需要模拟特殊频段的吸波材料、频率色散介质或各向异性材料时,仅依赖CST内置材料库…...
Stateflow实战:构建LKA系统状态机的模块化建模与数据管理
1. 从零理解LKA系统与Stateflow建模 第一次接触车道保持辅助系统(LKA)时,我盯着那个能在高速上自动修正方向的方向盘看了半天。这玩意儿到底怎么判断什么时候该介入?后来才知道,核心就是藏在控制器里的状态机逻辑。Sta…...
推荐几款实测有效的降重工具,要求同时对付查重系统和AIGC检测
毕业季论文两大 “生死关”—— 知网 / 维普 / 格子达等查重标红、AIGC 疑似率超标,已成为无数学生的噩梦。普通降重工具仅能降重复率,改写后仍难逃 AI 检测;AI 写作工具生成内容流畅度高,却自带明显 AI 痕迹,双检极易…...
STM32MP25x嵌入式Linux平台:集成XFCE、VNC、TSN的工业边缘计算解决方案
1. 项目概述:一个面向工业边缘的“瑞士军刀”级嵌入式平台最近,我们团队基于STM32MP25x系列核心板,成功构建并发布了一套完整的Debian系统镜像。这个项目的目标非常明确:打造一个开箱即用、功能全面、且能无缝覆盖从传统工业控制到…...
CANN/asc-devkit SIMD排序函数文档
Sort 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址: https://gitcode.com/cann/…...
Windows缩略图加载太慢?这款智能预加载工具让文件浏览快如闪电
Windows缩略图加载太慢?这款智能预加载工具让文件浏览快如闪电 【免费下载链接】WinThumbsPreloader-V2 WinThumbsPreloader is a powerful open source tool for quickly preloading thumbnails in Windows Explorer. 项目地址: https://gitcode.com/gh_mirrors/…...
企业级应用如何利用Taotoken的容灾与路由能力保障AI服务高可用
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 企业级应用如何利用Taotoken的容灾与路由能力保障AI服务高可用 对于将大模型能力深度集成到关键业务流程的企业应用而言࿰…...
