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);

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

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

相关文章

ceph中PGLog处理流程

ceph的PGLog是由PG来维护&#xff0c;记录了该PG的所有操作&#xff0c;其作用类似于数据库里的undo log。PGLog通常只保存近千条的操作记录(默认是3000条&#xff0c; 由osd_min_pg_log_entries指定)&#xff0c;但是当PG处于降级状态时&#xff0c;就会保存更多的日志&#x…

地铁+铁路系统防雷接地应用解决方案

地铁作为城市轨道交通的一种&#xff0c;是一种高效、安全、环保的公共交通方式。然而&#xff0c;地铁也面临着雷电灾害的威胁&#xff0c;尤其是在雷暴多发的地区。 雷电对地铁系统的影响主要有以下几个方面&#xff1a; 直接雷击&#xff1a;雷电直接击中地铁系统的设备或…

RISC-V公测平台发布 · 在SG2042上配置Jupiter+Octave科学计算环境

简介 JupyterHub是一个开源的共享计算平台&#xff0c;它为每个用户管理一个单独的 Jupyter 环境&#xff0c; 可以用于学生班级、企业数据科学小组或科学研究小组。它是一个多用户中心&#xff0c;可以生成、管理和代理多个单用户Jupyter笔记本服务器的实例。 GNU Octave是一…

WebSocket- 前端篇

官网代码 // 为了浏览器兼容websocketconst WebSocket window.WebSocket || window.MozWebSocket// 创建连接 this.socket new WebSocket(ws://xxx)// 连接成功this.socket.onopen (res)>{console.log(websocket 连接成功)this.socket.send(入参字段) // 传递的参数字段}…

zookeeper集群 数据一致性测试

搭建zk集群 准备 3台ubuntu 20机器每台机器提前安装配置好jdk-8apache-zookeeper-3.8.2-bin.tar.gz 开始 # 上传 scp -P 22 -r C:\Users\xcrj\Downloads\apache-zookeeper-3.8.2-bin.tar.gz root192.168.1.102:/root/zk/ # 解压 tar -zxvf apache-zookeeper-3.8.2-bin.tar.…

【前端】 Layui点击图片实现放大、关闭效果

实现效果&#xff1a;点击图片实现放大&#xff0c;点击空白处关闭效果。下图。 实现逻辑&#xff1a;二维码是使用JQ插件生成的&#xff0c;点击二维码&#xff0c;获取图片路径&#xff0c;通过Layui的弹窗显示放大后的图片。 Html <div id"qrcode" class&quo…

Java学习笔记31——字符流

字符流 字符流为什么出现字符流编码表字符串中的编码解码问题字符流写数据的5中方式字符流读数据的两种方式字符流复制Java文件 字符流 为什么出现字符流 汉字的存储如果是GBK编码占用2个字节&#xff0c;如果是UTF-8占用三个字节 用字节流复制文本文件时&#xff0c;文本文…

驱动开发错误汇编

本博文将会不定期更新。以便记录我的驱动开发生涯中的一些点点滴滴的技术细节和琐事。 生成时link找不到导出函数&#xff0c;比如"LNK2019 无法解析的外部符号 _FltCreateCommunicationPort32"。出现这种情况的原因是&#xff0c;驱动的编译环境忽略了所有的默认库&…

基于实例的学习方法

基于实例的学习方法 动机基本概念基于实例的学习基于实例的概念表示 1. 最近邻最近邻的例子理论结果最近邻&#xff08;1- NN&#xff09;:解释问题 K-近邻(KNN)KNN讨论1 &#xff1a;距离度量KNN 讨论2&#xff1a;属性KNN:属性归一化KNN:属性加权 KNN讨论3:连续取值目标函数K…

Python爬虫实战:自动化数据采集与分析

在大数据时代&#xff0c;数据采集与分析已经成为了许多行业的核心竞争力。Python作为一门广泛应用的编程语言&#xff0c;拥有丰富的爬虫库&#xff0c;使得我们能够轻松实现自动化数据采集与分析。本文将通过一个简单的示例&#xff0c;带您了解如何使用Python进行爬虫实战。…

C# 使用NPOI操作EXCEL

1.添加NOPI 引用->管理NuGet程序包->添加NOPI 2.相关程序集 3.添加命名空间 using NPOI.HSSF; using NPOI.XSSF; using System.IO; using NPOI.XSSF.UserModel; using NPOI.HSSF.UserModel; 4.从Excel导入的dgv样例 //NPOI读入dgv private void button1_Click(object s…

内存分析之GCViewer详细解读

文章目录 GCViewer详细解读一&#xff0c;Chart详解二&#xff0c;Event detail三&#xff0c;Summary四&#xff0c;Pause五&#xff0c;相关概念5.1 GC5.1.1 Full GC5.1.2 Minor GC 5.2 垃圾收集器5.2.1 串行收集器&#xff08;Serial&#xff09;5.2.2 **ParNew收集器**5.2.…

Python切换输入法的实战代码

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

经纬恒润荣获吉利汽车“最佳价值贡献”奖

8月18日&#xff0c;以“全面向新 共创共赢”为主题&#xff0c;吉利汽车在宁波成功举行2023年电子电器核心供应商恳谈会。经纬恒润凭借在项目合作上持续创新、高效协同等优异表现&#xff0c;获得“最佳价值贡献”奖项。 作为国产汽车代表性品牌之一&#xff0c;吉利汽车积极推…

多通道振弦数据记录仪在岩土工程隧洞中的完整解决方案

多通道振弦数据记录仪在岩土工程隧洞中的完整解决方案 隧洞工程是一种非常复杂的工程类型&#xff0c;需要高度的安全性和精确性。而在岩土工程中&#xff0c;振弦是一种非常常用的测试方法&#xff0c;用于测定岩土体的物理性质以及地震波传播特性等&#xff0c;以利于对隧洞…

【深入浅出设计模式--状态模式】

深入浅出设计模式--状态模式 一、背景二、问题三、解决方案四、 适用场景总结五、后记 一、背景 状态模式是一种行为设计模式&#xff0c;让你能在一个对象的内部状态变化时改变其行为&#xff0c;使其看上去就像改变了自身所属的类一样。其与有限状态机的概念紧密相关&#x…

解决Three.js辉光背景不透明

使用此pass canvas元素的background都能看到 不过相应的辉光颜色和背景颜色不相容的地方看起来颜色会怪一些 如图 不过如果是纯色就没什么问题了 //ts-nocheck /** Author: hongbin* Date: 2023-04-06 11:44:14* LastEditors: hongbin* LastEditTime: 2023-04-06 11:49:23* De…

电缆工厂 3D 可视化管控系统 | 智慧工厂

近年来&#xff0c;我国各类器材制造业已经开始向数字化生产转型&#xff0c;使得生产流程变得更加精准高效。通过应用智能设备、物联网和大数据分析等技术&#xff0c;企业可以更好地监控生产线上的运行和质量情况&#xff0c;及时发现和解决问题&#xff0c;从而提高生产效率…

libevent源码学习3---事件event

libevent源码学习3—事件event libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括: 文件描述符已经就绪, 可以读取或者写入文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)超时事件发生某信号用户触发事件 所有事件具有相似的生命周期。…

SpringBoot集成Redis、Redisson保姆教程【附源码】

1. SpringBoot集成Redis 关于Redis的安装,这里就不重复介绍了,需要的朋友可以看我之前的博文Redis多系统安装(Windows、Linux、Ubuntu) Redis原生命令大全,作者整理的很详细,大部分命令转化为java命令基本也是关键词 Redis 命令参考 接下来开始我们的正题,一起学习下…