当前位置: 首页 > news >正文

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. 技术思路

  1. 通过 Thymeleaf 渲染生成需要的页面内容;
  2. 通过 iText7 html2pdf 库将 Thymeleaf 渲染的结果转换成 PDF;
  3. 将 PDF 内容写入到接口输出流中返回给前端浏览器展示;

3. 实现过程

  1. 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>
    
  2. 编写 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>
    
  3. 添加中文字体资源 resources/fonts/simkai.ttf

  4. 编写 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();}
    }
    
  5. 编写 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;
    }
    
  6. 编写 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());}}
    }
    
  7. 编写 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&#xff08;2023/08/29&#xff09; 文章目录 SpringBoot Thymeleaf iText7 生成 PDF&#xff08;2023/08/29&#xff09;1. 前言2. 技术思路3. 实现过程4. 测试 1. 前言 近期在项目种遇到了实时生成复杂 PDF 的需求&#xff0c;经过一番…...

【核磁共振成像】并行采集MRI

目录 一、并行成像二、SENSE重建三、SMASH重建四、灵敏度校准五、AUTO-SMASH和VD-AUTO-SMASH六、GRAPPA重建七、SPACE RIP重建算法八、PILS重建算法九、PRUNO重建算法十、UNFOLD算法 一、并行成像 并行MR成像(pMRI):相位阵列接受线圈不但各有自己专用的接受通道&#xff0c;而且…...

深度图相关评测网站

文章目录 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正弦波和方波脉冲、斜披、三角波&#xff0c;噪声和直流波形14-bit,50MSa/s&#xff0c;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的优点有很多&#xff1a; API使用简单&#xff0c;学习成本低。功能强大…...

NVIDIA DLI 深度学习基础 答案 领取证书

最后一节作业是水果分类的任务&#xff0c;一共6类&#xff0c;使用之前学习的知识在代码段上进行填空。 加载ImageNet预训练的基础模型 from tensorflow import kerasbase_model keras.applications.VGG16(weights"imagenet",input_shape(224, 224, 3),include_t…...

axios模拟表单提交

axios默认是application/json方式提交&#xff0c;controller接收的时候必须以RequestBody的方式接收&#xff0c;有时候不太方便。如果axios以application/x-www-form-urlencoded方式提交数据&#xff0c;controller接收的时候只要保证名字应对类型正确即可。 前端代码&#…...

智安网络|探索物联网架构:构建连接物体与数字世界的桥梁

物联网是指通过互联网将各种物理设备与传感器连接在一起&#xff0c;实现相互通信和数据交换的网络系统。物联网架构是实现这一连接的基础和框架&#xff0c;它允许物体与数字世界之间的互动和协作。 一、物联网架构的概述 物联网架构是一种分层结构&#xff0c;它将物联网系…...

胡歌深夜发文:我对不起好多人

胡歌的微博又上了热搜。 8月29日01:18分&#xff0c;胡歌微博发文称&#xff1a;“我尽量保持冷静&#xff0c;我对不起好多人&#xff0c;我希望对得起这短暂的一生”&#xff0c;并配了一张自己胡子拉碴的图&#xff0c;右眼的伤疤清晰可见。 不少网友留言称“哥你又喝多了吗…...

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&#xff1a;适用于数据中心的超级产品 AFF A 系列中的 AFF A900 高端 NVMe 闪存存储功能强大、安全可靠、具有故障恢复能力&#xff0c;提供您为任务关键型企业级应用程序提供动力并保持数据始终可用且安全所需的一切。 产品功能与特性 AFF A900&#xff1a…...

入海排污口水质自动监测系统,助力把好入河入海“闸门”

随着经济社会的不断发展&#xff0c;污水的排放强度不断加大&#xff0c;大量的污水排入河流、湖泊和海洋中&#xff0c;造成了水体污染&#xff0c;严重影响着我国的用水安全、公众健康、经济发展与社会稳定。入河入海排污口是污染物进入河流和海洋的最后关口&#xff0c;也是…...

AUTOSAR知识点 之 ECUM (一):基础知识梳理(概念部分)

目录 1、概述 2、ECUM的工作状态 2.1、Startup状态 2.2、UP状态 2.3、RUN状态 2.4、SLEEP状态...

leetcode分类刷题:哈希表(Hash Table)(二、数组交集问题)

1、当需要快速判断某元素是否出现在序列中时&#xff0c;就要用到哈希表了。 2、本文针对的总结题型为给定两个及多个数组&#xff0c;求解它们的交集。接下来&#xff0c;按照由浅入深层层递进的顺序总结以下几道题目。 3、以下题目需要共同注意的是&#xff1a;对于两个数组&…...

[Mac软件]Adobe After Effects 2023 v23.5 中文苹果电脑版(支持M1)

After Effects是动画图形和视觉效果的行业标准。由运动设计师、平面设计师和视频编辑用于创建复杂的动画图形和视觉上吸引人的视频。 创建动画图形 使用预设样式为文本和图形添加动画效果&#xff0c;或逐帧调整它们。编辑、添加深度、制作动画或转换为可编辑的路径&#xff…...

范德波尔方程详细介绍与Python实现(附说明)

引言: 在研究真空管放大器的过程中,写下了一个振动微分方程。当时人们并没有混沌或是对初始条件敏感的概念。不过,当混沌理论有一定发展后,人们重新回顾这个方程时发现它其实是个混沌方程。当时,范德波尔在 Nature 杂志报告了基于这个微分方程的霓虹灯实验,发现当驱动信号…...

常用的GPT插件

0.简介 随着chatgpt爆火&#xff0c;这玩意并不对国内用户开放&#xff0c;如果想要使用的话还要需要进行翻墙以及国外手机号才能进行注册。 对于国内来说有很多国内免费的方法&#xff0c;这里就整理一下&#xff0c;方便大家开发 1. 网站类型 下面的网站无需注册即可免费…...

智慧校园用电安全解决方案

随着科技的不断发展&#xff0c;智慧校园建设逐渐成为了教育行业的一大趋势。在这个过程中&#xff0c;电力系统作为校园基础设施的重要组成部分&#xff0c;其安全、稳定、高效的运行显得尤为重要。下面小编来为大家介绍下智慧校园用电安全解决方案吧! 一、智慧校园电力系统现…...

【教程】DGL中的子图分区函数partition_graph讲解

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 函数形式 函数作用 函数内容 函数入参 函数返参 使用示例 实际上官方的函数解释中就已经非常详细了。 函数形式 def partition_graph(g, graph_name, num_parts, out_path, num_hops1, part…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...