Java实现html填充导出pdf

Java实现html填充导出pdf

1.依赖添加和pom修改

 <!-- Thymeleaf 模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- OpenPDF 库 -->
        <dependency>
            <groupId>com.github.librepdf</groupId>
            <artifactId>openpdf</artifactId>
            <version>1.3.29</version>
        </dependency>

        <!-- HTML转PDF工具(flying-saucer) -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-core</artifactId>
            <version>9.1.20</version>
        </dependency>

        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-openpdf</artifactId>
            <version>9.1.20</version>
        </dependency>

为了解决:java.io.IOException: /app/idmp-datam-job.jar!/BOOT-INF/classes!/fonts/SimSun.ttf not found as file or resource.

<resources>
            <resource>
                <directory>src/main/resources</directory>
                <!--开启过滤,用指定的参数替换directory下的文件中的参数-->
                <filtering>true</filtering>
                <excludes>
                    <exclude>fonts/</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>fonts/</include>
                </includes>
            </resource>
        </resources>

2.工具类

操作过程主要出现问题的地方在于字体的问题,字体放在resource/fonts目录下。

第一次运行的时候将文件复制出来然后每次执行就读取外边的。

import com.lowagie.text.pdf.BaseFont;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * @author lxz
 * pdf导出
 */
@Slf4j
public class PdfExportUtil {

    /**
     *
     * @param list     需要写入 PDF 的字符串集合
     * @param fileName 生成的 PDF 文件名
     * @throws IOException 如果发生 IO 异常
     */

    public static String exportPdf(List<Map<String,String>> list, String fileName) throws IOException {
        // 创建临时文件
        String path = System.getProperty("user.dir") + File.separator + fileName + ".pdf";
        log.info("【pdf生成】,路径:" + path);
        File tempFile = new File(path);
        if (!tempFile.exists()) {
            log.info("【pdf生成】,文件不存在创建!");
            tempFile.createNewFile();
        }
        // 使用 FileOutputStream 将 PDF 写入到临时文件
        log.info("【pdf生成】,完成html内容生成!");
        // 生成html内容
        String htmlContent = generateHtmlContent(list, fileName);
        // 创建ITextRenderer实例
        ITextRenderer renderer = new ITextRenderer();
        // 设置字体路径,从 resources/fonts 目录加载 SimSun 字体
        ITextFontResolver fontResolver = renderer.getFontResolver();
        Resource resource = new ClassPathResource("fonts/SimSun.ttf");
        File newFontDir = new File(System.getProperty("user.dir") + File.separator + "fonts");
        if (!newFontDir.exists()) {
            newFontDir.mkdirs();
        }
        File newFontFile = new File(newFontDir, "SimSun.ttf");
        if (!newFontFile.exists()) {
            newFontFile.createNewFile();
            // 将 resource 内容写入 newFontFile
            try (InputStream inputStream = resource.getInputStream();
                 OutputStream outputStream = new FileOutputStream(newFontFile)) {
                // 使用缓冲区进行复制
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
            log.info("【pdf下载】,字体文件已成功复制到" + newFontFile.getAbsolutePath());
        } else {
            log.info("【pdf下载】,字体已存在:" + newFontFile.getAbsolutePath());
        }
        fontResolver.addFont(newFontFile.getPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        log.info("【pdf生成】,加载字体成功!");
        // 设置HTML内容
        renderer.setDocumentFromString(htmlContent);
        log.info("【pdf生成】,生成内容!");
        renderer.layout();
        // 输出PDF到响应输出流
        try (OutputStream outputStream = new FileOutputStream(tempFile)) {
            renderer.createPDF(outputStream);
            outputStream.flush();
            log.info("【pdf生成】,生成完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return path;
    }


    /**
     * 生成HTML内容
     * @return 渲染后的HTML字符串
     */
    public static String generateHtmlContent(List<Map<String,String>> list, String fileName) {
        //thymeleaf构造模板引擎;给html文件赋值
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        //默认是找classpath路径下的资源
        resolver.setPrefix("templates/");
        //模板文件后缀
        resolver.setSuffix(".html");
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(resolver);
        log.info("【pdf生成】,填充html内容!");
        Context context = new Context();
        context.setVariable("title", fileName);
        context.setVariable("list", list);
        return templateEngine.process("pdf_template", context);
    }
}

字体文件: 

 

3.模板文件

resource/templates目录下。

主要靠thymeleaf来实现的。

 内容如下:

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title th:text="${title}"></title>
    <style>
        body {
            font-family: 'SimSun', sans-serif;
        }
    </style>
</head>
<body>
<div data-th-each="item:${list}">
    <div th:text="${item.content}" th:style="${item.style}"></div>
</div>
</body>
</html>

4.测试方法

@RestController
@RequestMapping("/api/pdf")
public class PdfController {

    @GetMapping("/test")
    @TinyResponse
    public void downloadPdf() throws Exception {
        List<Map<String,String>> list = new ArrayList<>();
        for (int i =0; i< 100; i++) {
            Map<String,String> map = new HashMap<>();
            map.put("content", "测试内容生成111111");
            map.put("style", "color:red;font-size: 12px;");
            list.add(map);
        }
        String fileName = "测试pdf名称";
        PdfExportUtil.exportPdf(list, fileName);

    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3基于Element-plus的Select组建进行二次封装-demo

效果 组件 <template><ElSelectclass"follow-records-pairs-select"v-model"selectVal":placeholder"placeholder"change"selectChange"><ElOptionv-for"item in options":key"item.value":labe…

点跟踪论文—RAFT: Recurrent All-Pairs Field Transforms for Optical Flow-递归的全对场光流变换

点目标跟踪论文—RAFT: Recurrent All-Pairs Field Transforms for Optical Flow-递归的全对场光流变换 读论文RAFT密集光流跟踪的笔记 RAFT是一种新的光流深度网络结构&#xff0c;由于需要基于点去做目标的跟踪&#xff0c;因此也是阅读了像素级别跟踪的一篇ECCV 2020的经典…

Golang 怎么高效处理ACM模式输入输出

文章目录 问题bufio.NewReader高效的原理 再次提交 问题 最近在练习牛客上单调栈题目时&#xff0c;要求自己处理出入输出&#xff0c;也就是读题库要求的输入&#xff0c;计算最终结果&#xff0c;并打印输出 当我用fmt.Scan处理输入&#xff0c;用fmt.Println处理输出时&am…

使用React和Redux构建可扩展的前端应用

&#x1f496; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4bb; Gitee主页&#xff1a;瑕疵的gitee主页 &#x1f680; 文章专栏&#xff1a;《热点资讯》 使用React和Redux构建可扩展的前端应用 1 引言 2 React入门 2.1 安装React 2.2 创建组件 3 Redux基础 3.1 安装Redu…

Jsoup在Java中:解析京东网站数据

对于电商网站如京东来说&#xff0c;其页面上的数据包含了丰富的商业洞察。对于开发者而言&#xff0c;能够从这些网站中提取有价值的信息&#xff0c;进行分析和应用&#xff0c;无疑是一项重要的技能。本文将介绍如何使用Java中的Jsoup库来解析京东网站的数据。 Jsoup简介 …

特殊类设计与设计模式

&#x1f30e;特殊类设计与设计模式 文章目录&#xff1a; 特殊类设计与设计模式 特殊类设计       设计一个只能在堆上创建对象的类       设计一个只能在栈上创建对象的类       请设计一个不能被拷贝的类       请设计一个不能被继承的类 设计模式…

【汇编语言】第一个程序(一)—— 一个源程序从写出到执行的过程

文章目录 前言1. 第一步&#xff1a;编写汇编源程序2. 第二步&#xff1a;对源程序进行编译连接3. 第三步&#xff1a;执行可执行文件中的程序结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程…

【GIT】.cr、.gitattributes 、 .gitignore和.git各文件夹讲解介绍

在 Git 项目中&#xff0c;.cr、.gitattributes 和 .gitignore 文件分别用于不同的配置和管理功能。下面分别解释这些文件的作用和用途&#xff1a; 1. .gitignore 文件 作用&#xff1a; .gitignore 文件用于指定哪些文件或目录应该被 Git 忽略&#xff0c;不会被追踪或提交…

大数据-185 Elasticsearch - ELK 家族 Logstash 安装配置 Input 插件-stdin stdout

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

「C/C++」C++ STL容器库 之 std::string 字符串类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

vue使用jquery的ajax,页面跳转

一、引入jquery依赖 打开终端更新npm npm install -g npm 更新完后引入输入npm install jquery 加载完后 在最外层的package.json文件中加入以下代码 配置好后导入jquery 设置变量用于接收服务器传输的数据 定义ajax申请数据 服务器的Controller层传输数据 &#xff08;…

linux介绍与基本指令

前言 本次博客将会讲解linux的来源历史、linux操作系统的理解以及它的一些基本指令。 1.linux的介绍 linux的来源 linux的来源最初还是要说到unix操作系统的。 1968年&#xff0c;一些来自通用电器公司、贝尔实验室和麻省理工学院的研究人员开发了一个名叫Multics的特殊操作…

C++ 基于自主实现的红黑树封装Map和Set (下)

C 基于自主实现的红黑树封装Map和Set &#xff08;上&#xff09;-CSDN博客 本文针对上文中没有完成的迭代器接口进行一个补充。 1. 箭头访问 在map的测试中使用箭头访问测试&#xff0c;我们可以复习到: 测试刚才重载的-> , 出现了经典双箭头问题 按理来说应该是像下图一样…

uniapp-components(封装组件)

<myitem></myitem> 在其他类里面这样调用。

Python数值计算(28)——理查森外推法

1. 基础知识 理查森外推法( Richardson extrapolation)是一种提高某些数值过程精度的简单方法&#xff0c;在数值方法中广泛应用。 理查森外推法的基本思想是通过对原函数进行多次求导&#xff0c;并在每一步求导的基础上进行线性组合&#xff0c;得到一个新的函数&#xff0c…

智能时代摩托车一键启动无钥匙进入感受科技前线

向智能化与高性能迈进,技术创新与绿色转型引领摩托车行业智能化出行。 摩托车一键启动无钥匙进入功能是一种先进的车辆控制系统&#xff0c;它允许驾驶员在不使用传统机械钥匙的情况下&#xff0c;通过智能感应技术自动解锁和启动摩托车。这种系统通常包括一个智能钥匙&#x…

从零开始学习 YOLOv8:目标检测与车牌识别实例

1. 引言 什么是目标检测&#xff1f; 目标检测就像是在寻找隐藏的宝藏。想象一下&#xff0c;你在一个巨大的图画里&#xff0c;里面藏着无数的物体&#xff0c;而你的任务是迅速找到其中的几样&#xff0c;比如说&#xff0c;一只流浪的小猫和一辆红色的小轿车。目标检测就是…

HTML作业

作业 复现下面的图片 复现结果 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><form action"#"method"get"enctype"text/plain"><…

【实验六】基于前馈神经网络的二类任务

1 数据集构建 2 模型构建 2.1 线性层算子 2.2 Logistic算子 2.3 层次串行组合 3 损失函数 4 模型优化 4.1 反向传播算法 4.2 损失函数 4.3 Logistic算子 4.4 线性层 4.5 整个网络 4.6 优化器 5 完善Runner类&#xff1a;RunnerV2_1 6 模型训练 7 性能评价 8 完…

Java应用程序的测试覆盖率之设计与实现(二)-- jacoco agent

说在前面的话 要想获得测试覆盖率报告&#xff0c;第一步要做的是&#xff0c;采集覆盖率数据&#xff0c;并输入到tcp。 而本文便是介绍一种java应用程序部署下的推荐方式。 作为一种通用方案&#xff0c;首先不想对应用程序有所侵入&#xff0c;其次运维和管理方便。 正好…