使用itextpdf填充表单域并生成pdf

文章目录

  • 前言
  • 一、准备工作
    • 1.1 安装软件
    • 1.2 准备pdf
    • 1.3 设置表单域
  • 二、创建项目
  • 三、编写代码
    • 3.1 编写工具类
    • 3.2 测试
  • 四、测试结果

前言

最近手上有个任务,就是需要做一个pdf导出的功能。

可选择的技术点比较多,我这边综合考虑之后,使用的是 itext。
大致有两种实现思路:
1️⃣:使用软件【Adobe Acrobat DC】去做一个pdf模版,将表单域指定好,随后使用代码去填充参数,最终得到一个pdf或字节数组。

2️⃣:使用【Freemarker】渲染html页面,最终使用代码将该页面转换为pdf。

我这边当前的需求比较适合第一种方式。

一、准备工作

1.1 安装软件

首先是安装软件(失效的话麻烦评论区留言)
链接:https://pan.baidu.com/s/1O8JtVuK87VYbzx0DGQyJ1g
提取码:a0hy

1.2 准备pdf

新建一个word文档,插入一个表格,效果如下:
在这里插入图片描述

然后将word导出为pdf文件。
再使用我们刚刚安装好的软件打开。

1.3 设置表单域

在工具栏中找到“准备表单”功能,点击打开。
在这里插入图片描述

然后修改自己想要的文本域字段名、字体大小等配置:
在这里插入图片描述

也可以右键新增文本域(我这里的title就是新增的):
在这里插入图片描述
随后我们保存pdf即可。最终我们会得到这样一个pdf:
在这里插入图片描述

二、创建项目

创建一个普通的 springboot项目,引入依赖如下:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.2.5</version><type>pom</type></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

resources 目录中创建 templates 文件夹,并将我们前边准备好的pdf放进去。效果如下:
在这里插入图片描述
我这里准备的文件是 personal_info.pdf

三、编写代码

3.1 编写工具类

package org.feng.pdf;import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;import java.io.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;/*** 填充pdf表单域** @author fengjinsong* @date 2023-06-28 16:55:38* @version: 1.0*/
@Slf4j
@Data
public class FillFormFieldsToPdfTemplateUtil {/*** 默认模板路径*/private static final String DEFAULT_TEMPLATE_DIRECTORY;/*** 默认字体(pdf显示中文)*/private static final PdfFont DEFAULT_FONT;/*** 模版路径*/private String templateDirectory;/*** 字体*/private PdfFont pdfFont;public FillFormFieldsToPdfTemplateUtil() {}static {try {DEFAULT_TEMPLATE_DIRECTORY = ResourceUtils.getURL("classpath:").getPath() + "/templates/";DEFAULT_FONT = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);} catch (IOException e) {throw new RuntimeException(e);}}/*** 依据pdf模板,填充表单域,生成pdf文件** @param templateFileName 模版文件名,不能为空* @param formFieldsParams 表单域需要的参数,不能为空* @param destPdfPath      目标位置* @throws IOException 涉及到文件操作*/public void generatePdfFile(String templateFileName, String destPdfPath, Map<String, String> formFieldsParams) throws IOException {// 构造pdf阅读器,指定输入流PdfReader pdfReader = new PdfReader(new FileInputStream(getTemplatePath(templateFileName)));// 构造pdf写出器,指定输出流OutputStream fos = new FileOutputStream(destPdfPath);// 构造pdfDocument实例PdfDocument pdf = new PdfDocument(pdfReader, new PdfWriter(fos));// 设置为a4纸张大小pdf.setDefaultPageSize(PageSize.A4);// 替换参数(如果有参数的话)PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);fillFormFields(form, formFieldsParams);// 锁定表单,不让修改form.flattenFields();pdf.close();}/*** 根据pdf模版和表单域参数获取pdf对应的字节数组** @param templateFileName 模版文件名,不能为空* @param formFieldsParams 表单域需要的参数,不能为空* @return 填充了表单域参数之后的pdf字节数组* @throws IOException 涉及到文件操作*/public byte[] generatePdfByteArray(String templateFileName, Map<String, String> formFieldsParams) throws IOException, RuntimeException {// 构造pdf阅读器,指定输入流PdfReader pdfReader = new PdfReader(new FileInputStream(getTemplatePath(templateFileName)));// 构造pdf写出器,指定输出流ByteArrayOutputStream outputStream = new ByteArrayOutputStream();PdfWriter pdfWriter = new PdfWriter(outputStream);// 构造pdfDocument实例PdfDocument pdf = new PdfDocument(pdfReader, pdfWriter);// 设置为a4纸张大小pdf.setDefaultPageSize(PageSize.A4);// 替换参数(如果有参数的话)PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);fillFormFields(form, formFieldsParams);// 锁定表单,不让修改form.flattenFields();// 关闭资源pdf.close();// 返回最终结果return outputStream.toByteArray();}/*** 获取模版文件路径** @param templateFileName 模版文件名,在指定的模版目录下* @return 模版文件路径*/private String getTemplatePath(String templateFileName) {// 拼接完整模版路径return Objects.nonNull(templateDirectory) ?templateDirectory + templateFileName : DEFAULT_TEMPLATE_DIRECTORY + templateFileName;}/*** 填充表单域** @param form             表单* @param formFieldsParams 表单需要的动态参数*/private void fillFormFields(PdfAcroForm form, Map<String, String> formFieldsParams) {// 获取所有的表单域Map<String, PdfFormField> fields = form.getFormFields();// 获取当前字体PdfFont currentFont = pdfFont == null ? DEFAULT_FONT : pdfFont;formFieldsParams.forEach((key, value) -> {// 获取某个表单域Optional<PdfFormField> formFieldOptional = Optional.ofNullable(fields.get(key));formFieldOptional.ifPresent(formField -> {// 设置字体、替换值formField.setFont(currentFont).setValue(value);});});}
}

3.2 测试

准备一个 Controller 类,如下:

package org.feng.controller;import lombok.extern.slf4j.Slf4j;
import org.feng.pdf.FillFormFieldsToPdfTemplateUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** itextpdf7控制器** @author fengjinsong* @date 2023-06-29 09:00:04* @version: 1.0*/
@Controller
@Slf4j
public class ItextPdf7Controller {/*** 下载文件** @param response 响应对象;用于设置响应参数*/@GetMapping("/download")public ResponseEntity<byte[]> download(HttpServletResponse response) throws IOException {HttpHeaders headers = new HttpHeaders();// application/octet-stream二进制流数据(文本下载)。headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);// 下载显示的文件名,解决中文名称乱码问题String downloadFileName = System.currentTimeMillis() + ".pdf";// 弹出保存框即资源选择器response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);// 组装参数Map<String, String> data = new HashMap<>();data.put("title", "个人简介");data.put("name", "我的某个朋友");data.put("age", "30");data.put("likes", "女");data.put("birthday", "2022-12-22");// 使用数据填充pdf模版并转换为字节数组FillFormFieldsToPdfTemplateUtil pdfTemplateUtil = new FillFormFieldsToPdfTemplateUtil();byte[] bytes = pdfTemplateUtil.generatePdfByteArray("personal_info.pdf", data);// 返回结果return new ResponseEntity<>(bytes, headers, HttpStatus.CREATED);}@ResponseBody@GetMapping("/generate")public String generatePdfFile() throws IOException {// 指定输出的目录String path = ResourceUtils.getURL("classpath:").getPath() + "/templates/";// 组装参数Map<String, String> data = new HashMap<>();data.put("title", "个人简介");data.put("name", "我的某个朋友");data.put("age", "30");data.put("birthday", "2022-12-22");data.put("likes", "女");// 在项目中生成pdfFillFormFieldsToPdfTemplateUtil pdfTemplateUtil = new FillFormFieldsToPdfTemplateUtil();pdfTemplateUtil.generatePdfFile("personal_info.pdf", path + "dsadsad.pdf", data);// 返回结果return "success";}
}

四、测试结果

在项目中生成pdf文件,发送请求 GET http://localhost:8080/generate
下载生成的pdf文件,发送请求 GET http://localhost:8080/download

生成的pdf效果如下:

在这里插入图片描述

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

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

相关文章

【单例模式】—— 每天一点小知识

&#x1f4a7; 单例模式 \color{#FF1493}{单例模式} 单例模式&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f995;生动形…

基于卷积变分自动编码器的3D数据处理与重建【CVAE】

在这个项目中&#xff0c;我们将学习如何使用卷积变分自动编码器 (CVAE) 来处理和重建 3D 湍流数据。 我们使用计算流体动力学 (CFD) 方法生成 3D 湍流立方体&#xff0c;每个 3D 立方体沿着三个速度分量携带物理信息&#xff08;与图像数据类似&#xff0c;被视为单独的通道&…

Http host 标头攻击

一、什么是http host 标头攻击 HTTP Host 标头攻击是一种网络安全攻击技术&#xff0c;利用了 HTTP 协议中的 Host 标头字段的漏洞。Host 标头字段用于指定客户端请求的目标主机名或域名。 攻击者可以通过构造恶意的 HTTP 请求&#xff0c;伪造或篡改 Host 标头字段的值&#x…

Linux系统安全:安全技术和防火墙

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、Linux系统安全&#xff1a;安全技术和防火墙1.安全技术&#xff1a;2.入侵防御系统 (Intrusion Prevention System) :3.防火墙 ( Erewall ) :4.防水墙&#xff…

GPT模型训练实践(3)-参数训练和代码实践

一、参数训练 GPT模型参数的训练过程宏观上有两个大环节&#xff0c;先从上往下进行推理&#xff0c;再从下往上进行训练&#xff0c;具体过程为&#xff1a; 1、模型初始化参数随机取得&#xff1b; 2、计算模型输出与真实数据的差距&#xff08;损失值和梯度&#xff09; …

Linux 6.5增加对高通开源GPU Adreno 690的支持

导读即将推出的Linux 6.5内核将把对高通Adreno 690 GPU的支持添加到开源的MSM内核图形/显示驱动程序中。A690主要用于骁龙8cx第三代&#xff08;SC8280XP&#xff09;平台&#xff0c;而联想ThinkPad X13s笔记本电脑和其他硬件也采用了该平台。 新的支持将包含近200行代码&…

JS数据分组引发的思考

我们经常需要对数据进行分组(分类)&#xff0c;得到需要的格式。 1.简单示例 例如有一份对象数组的数据 const data [{ name: Liam, age: 18, sex: male },{ name: Bob, age: 20, sex: male },{ name: Diana, age: 21, sex: female },{ name: Grace, age: 20, sex: female }…

【云原生丶Kubernetes】Kubernetes初体验

人生若只如初见&#xff0c;何事秋风悲画扇。 前言 Kubernetes 是目前最流行的容器编排工具之一&#xff0c;由Google开发并维护。它提供了完整的容器编排解决方案&#xff0c;包括自动化部署、资源管理和调度、服务发现和负载均衡等功能。 然而&#xff0c;对于初学者来说&a…

【Linux】文件描述符(下篇)

文章目录 &#x1f4d6; 前言1. 文件描述符fd的分配规则2. 重定向的本质3. 缓冲区的理解3.1 感受缓冲区的存在&#xff1a;3.2 正式认识缓冲区&#xff1a;综合例题&#xff1a; 4. 模拟实现C语言的文件操作5. 完善之前实现的shell5.1 程序替换&#xff0c;会影响曾经子进程打开…

决策树 ID3 手工推导

掌握决策树ID3算法的原理&#xff0c;通过增益熵实现手工推导的过程。 参考案例&#xff1a;https://cuijiahua.com/blog/2017/11/ml_2_decision_tree_1.html 机器学习实战教程(二)&#xff1a;决策树基础篇之让我们从相亲说起 决策树 ID3 手工推导 决策树 ID3 简介 ID3作为一种…

多个微信号如何管理?

很多公司都在发愁这几个问题&#xff1a; 1、拥有多个微信号&#xff0c;不想管理多台手机&#xff0c;想将所有微信号进行统一管理 2、想用软件来代替传统的营销体系&#xff0c;安全性上也要有保障 3、用人成本太大与公司的效益不成正比 4、多个账号发圈不方便&#xff0…

Neo4j docker 部署

想要运行简单测试一下neo4j&#xff0c;就直接使用docker创建了一个容器&#xff0c;并用cypher-shell本地连接neo4j&#xff0c;创建图进行测试。 1 开启docker sudo systemctl start docker2 拉取镜像源 sudo docker pull neo4j # 默认latest版本3 查看本地镜像&#xff0…