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

itext - PDF模板套打

目录

环境配置

快速使用

代码实现

添加图片

封装


项目需求:获取列表数据之后直接将数据生成一个pdf。因此需要使用到 itext 对pdf进行直接操作。

环境配置

需要为pdf添加文字域,因此需要安装Adobe Acrobat

准备一个空的PDF文件,如果有现成的模板更好

依赖配置,我们使用itext的7版本

        <dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.2.3</version><type>pom</type></dependency>

快速使用

使用Adobe Acrobat Pro DC打开空PDF,使用 文字域 工具为PDF添加文字域,要注意为每个文字域命名。

如果你有现成的模板PDF,直接使用识别域可以识别空白区域然后自动生成文字域,但是一般都不太准确

如果你的单个数据很多的话,可以在属性中设置多行 

设置完文字域之后记得保存。

代码实现

@SpringBootTest
class StickerApplicationTests {private static final String TEMP_PATH = "C:\\Users\\An1ong\\Desktop\\Stickers.pdf";//生成PDF的位置private static final String DEST_PATH = "C:\\Users\\An1ong\\Desktop\\StickersOut.pdf";//本地上字体的路径private static final String FONT_PATH = "";@Autowiredprivate StickerService stickerService;@Testvoid contextLoads() throws IOException {//创建一个新的PDF文件,并写入数据PdfReader reader = new PdfReader(TEMP_PATH);// 创建一个 PdfWriter 对象以写入新的PDFPdfWriter writer = new PdfWriter(DEST_PATH);// 创建一个 PdfDocument 对象PdfDocument pdfDoc = new PdfDocument(reader, writer);// 获取 PDF 表单PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false);//获得数据,准备填充List<Sticker> stickerList = stickerService.list(10);//文本填充for(int i = 0; i < stickerList.size();i++){Sticker sticker = stickerList.get(i);// 生成自定义序号,格式为 "001"、"002"、"003"String customId = String.format("%03d", i + 1);String idFieldName = "id" + (i + 1);String nameFieldName = "name" + (i + 1);PdfFormField idField = form.getField(idFieldName);if (idField != null) {idField.setValue(customId);}PdfFormField nameField = form.getField(nameFieldName);if (nameField != null) {nameField.setValue(sticker.getStickerName());}}//消除掉表单域form.flattenFields();//关闭流pdfDoc.close();}
}

行数也不算少,但里面的逻辑其实很简单。这是一个Springboot的单元测试,我调用service中的方法获取了一个装着对象的列表。

用PdfReader读取你要套写的模板,用PdfWriter将数据写入模板。创建出一个PdfDocument对象并将这两个参数传入就可以开始对PDF操作了。

注意,这个过程不会直接在原PDF上操作,而是生成一个新的PDF进行操作,程序结束后原PDF模版还是空白的。

 PdfAcroFrom获取PDF表单,然后PdfFormField获取其中的文字域,最后使用for循环动态的将数据套打在模板上就完成了。

最终会生成一个新的文件

最终效果:

之所以要在最后调用form.flattenFields消除掉表单域是因为如果不消除表单域的话就会变成这样。


更新......

添加图片

现在又有新的需求,除了自定义序号和文本之外,还要每个的后面添加图片。

添加文本域

代码实现

PdfFormField imgField = form.getField(imgFieldName);if (imgField != null){List<PdfWidgetAnnotation> widgets = imgField.getWidgets();PdfWidgetAnnotation widget = widgets.get(0);float x1 = widget.getRectangle().getAsNumber(0).floatValue();float y1 = widget.getRectangle().getAsNumber(1).floatValue();float x2 = widget.getRectangle().getAsNumber(2).floatValue();float y2 = widget.getRectangle().getAsNumber(3).floatValue();float fieldWidth = x2 - x1;float fieldHeight = y2 - y1;Image image = img.scaleToFit(fieldWidth,fieldHeight);image.setFixedPosition(x1,y1);Document document = new Document(pdfDoc);document.add(image);}

由于文本域是放置文本的,不能直接放置一个图片上去。所以我们实现的思路是,在放置图片的位置一个文本域,然后根据文本域的坐标将图片移过去,这就是这段代码的思路。

可以得到文本域左下角坐标和右上角坐标 ,通过简单的数学计算得到这块矩形的长和宽,然后使用scaleToFit就可以让图片的长和宽与文字域矩形的长宽一样了。

封装

我们可以把这个在单元测试中的程序封装成工具类重复使用

package com.wal.sticker.util;import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Image;
import com.wal.sticker.pojo.Sticker;import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;public class PdfPrintUtil {//    private static final String TEMP_PATH = "C:\\Users\\An1ong\\Desktop\\Stickers.pdf";
//
//    private static final String DEST_PATH = "C:\\Users\\An1ong\\Desktop\\StickersOut.pdf";public static void printPDF(String DEST_PATH,List<Sticker> stickerList) throws IOException, URISyntaxException {String TEMP_PATH = getResourcePath("templates/background.pdf");String IMG_PATH = getResourcePath("static/img/buttonImg.png");//创建一个新的PDF文件,并写入数据PdfReader reader = new PdfReader(TEMP_PATH);// 创建一个 PdfWriter 对象以写入新的PDFPdfWriter writer = new PdfWriter(DEST_PATH);// 创建一个 PdfDocument 对象PdfDocument pdfDoc = new PdfDocument(reader, writer);// 获取 PDF 表单PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false);// 加载图片Image img = new Image(ImageDataFactory.create(IMG_PATH));//文本和图片填充for(int i = 0; i < stickerList.size();i++){Sticker sticker = stickerList.get(i);// 生成自定义序号,格式为 "001"、"002"、"003"String customId = String.format("%03d", i + 1);String idFieldName = "id" + (i + 1);String nameFieldName = "text" + (i + 1);String imgFieldName = "img" + (i + 1);PdfFormField idField = form.getField(idFieldName);if (idField != null) {idField.setValue(customId);}PdfFormField nameField = form.getField(nameFieldName);if (nameField != null) {nameField.setValue(sticker.getStickerName());}// 添加图像PdfFormField imgField = form.getField(imgFieldName);if (imgField != null){List<PdfWidgetAnnotation> widgets = imgField.getWidgets();PdfWidgetAnnotation widget = widgets.get(0);float x1 = widget.getRectangle().getAsNumber(0).floatValue();float y1 = widget.getRectangle().getAsNumber(1).floatValue();float x2 = widget.getRectangle().getAsNumber(2).floatValue();float y2 = widget.getRectangle().getAsNumber(3).floatValue();float fieldWidth = x2 - x1;float fieldHeight = y2 - y1;Image image = img.scaleToFit(fieldWidth,fieldHeight);
//
//                float scaledWidth = image.getImageScaledWidth();
//                float scaledHeight = image.getImageScaledHeight();
//
//                float centerX = x1 + (fieldWidth / 2) - (scaledWidth / 2);
//                float centerY = x2 + (fieldHeight / 2) - (scaledHeight / 2);image.setFixedPosition(x1,y1);Document document = new Document(pdfDoc);document.add(image);}}//消除掉表单域
//        form.flattenFields();//关闭流pdfDoc.close();}private static String getResourcePath(String fileName) throws URISyntaxException {ClassLoader classLoader = PdfPrintUtil.class.getClassLoader();Path path = Paths.get(classLoader.getResource(fileName).toURI());return path.toString();}}

相关文章:

itext - PDF模板套打

目录 环境配置 快速使用 代码实现 添加图片 封装 项目需求&#xff1a;获取列表数据之后直接将数据生成一个pdf。因此需要使用到 itext 对pdf进行直接操作。 环境配置 需要为pdf添加文字域&#xff0c;因此需要安装Adobe Acrobat 准备一个空的PDF文件&#xff0c;如果有现…...

指针的使用和传址调用

1.引入 学习指针的⽬的是使⽤指针解决问题&#xff0c;那什么问题&#xff0c;⾮指针不可呢&#xff1f; 例如&#xff1a;写⼀个函数&#xff0c;交换两个整型变量的值。 ⼀番思考后&#xff0c;我们可能写出这样的代码&#xff1a; #include <stdio.h> void Swap1(int…...

【Leetcode】【实现循环队列】【数据结构】

代码实现&#xff1a; typedef struct {int front;int back;int k;int* a;} MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->frontobj->back; }bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->back1)%(obj->…...

电力感知边缘计算网关产品设计方案-软件架构(业务流程)

软件架构(业务流程) 基于前端系统提供的硬件通信平台,后端系统以控制执行单元为核心,协同控制通信管理、驱动适配、存储单元等职能单元完成与前端系统的通信数据交互业务,在经历以下业务流程后,完成设备自适应通信业务功能。 1.外部设备通信前端系统 前端系统连接新的…...

【labelimg打不开】

labelimg打不开 一、 报错1.1 排除错误 **解决方法&#xff1a;** 一、 报错 当运行labelimg程序报错此条时 AssertionError: Missing string id : useDefaultLabel 1.1 排除错误 第一&#xff0c;进入创建的虚拟环境&#xff0c;输入pip list 查看是否安装了labelimg 第二&…...

JAVA之异常详解

1. 异常的概念与体系结构 1.1 异常的概念 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常 1. 算术异常 public class Test {public static void main(String[] args) {System.out.println(10/0);} } 因为 0 不能当被除数&#xff0c;所以报出了异常&#…...

模型优化【2】-剪枝[局部剪枝]

模型剪枝是一种常见的模型压缩技术&#xff0c;它可以通过去除模型中不必要的参数和结构来减小模型的大小和计算量&#xff0c;从而提高模型的效率和速度。在 PyTorch 中&#xff0c;我们可以使用一些库和工具来实现模型剪枝。 pytorch实现剪枝的思路是生成一个掩码&#xff0…...

VMware 系列:ESXI6.7升级7.0

ESXI6.7升级7.0 一、下载补丁二、上传文件三 启用Shell四、登录Shell后台五、删除不兼容驱动六、正常升级最近,将一台使用ESXI6.7的虚拟机升级到了7.0版本,下面记录一下自己的升级过程。 升级条件 首先确保硬件是否能升级到7.0版本,物理网卡驱动为e1000e不能升级,如果是ig…...

4-20mA高精度采集方案

下载链接&#xff01;https://mp.weixin.qq.com/s?__bizMzU2OTc4ODA4OA&mid2247557466&idx1&snb5a323285c2629a41d2a896764db27eb&chksmfcfaf28dcb8d7b9bb6211030d9bda53db63ab51f765b4165d9fa630e54301f0406efdabff0fb&token976581939&langzh_CN#rd …...

案例022:基于微信小程序的行政复议在线预约系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…...

Go 工具链详解(七):模块缓存清理工具

go mod 缓存 在 Golang 中&#xff0c;模块是对一组版本化的包的集合的描述。Go 1.11 版本引入了模块支持&#xff0c;通过 go mod 命令提供了对模块的管理。Go 模块的一个重要特性是依赖管理&#xff0c;可以清晰地定义项目所依赖的模块及对应的版本&#xff0c;并确保代码使…...

1.7 C语言之函数概述

1.7 C语言之函数概述 一、概述二、练习 一、概述 函数就是把一组计算操作封装起来&#xff0c;供程序员调用&#xff0c;我们只需知道其提供了什么功能&#xff0c;而无需关注具体实现细节(前提是其久经考验&#xff0c;设计没有问题&#xff0c;后续我们自己写的函数大概率还…...

CTA-GAN:基于生成对抗性网络的主动脉和颈动脉非集中CT血管造影 CT到增强CT的合成技术

Generative Adversarial Network–based Noncontrast CT Angiography for Aorta and Carotid Arteries 基于生成对抗性网络的主动脉和颈动脉非集中CT血管造影背景贡献实验方法损失函数Thinking 基于生成对抗性网络的主动脉和颈动脉非集中CT血管造影 https://github.com/ying-f…...

电源控制系统架构(PCSA)之电源管理基础设施组件

目录 6.5 电源管理基础设施组件 6.5.1 电源策略单元 6.5.2 时钟控制器 6.5.3 低功耗Distributor 6.5.4 低功耗Combiner 6.5.5 P-Channel到Q-Channel转换器 6.5 电源管理基础设施组件 6.5.1 电源策略单元 本节介绍电源策略单元(Power Policy Unit, PPU)。PPU的完整细节见…...

影刀RPA_boss直聘翻页(避坑)

boss直聘翻页这里有个坑 问题&#xff1a; 无限循环中&#xff0c;点击下一页按钮&#xff0c;直到不可点击为止。 发现&#xff0c;在点到第5页的时候&#xff0c;再次点击下一页&#xff0c;直接就点击了页码10&#xff0c;导致流程直接就结束了。 在第5页进行校验&#xff0…...

第十四章 控制值的转换 - 在DISPLAYLIST中投影值

文章目录 第十四章 控制值的转换 - 在DISPLAYLIST中投影值在DISPLAYLIST中投影值 第十四章 控制值的转换 - 在DISPLAYLIST中投影值 在DISPLAYLIST中投影值 对于 %String 类型&#xff08;或任何子类&#xff09;的属性&#xff0c;XML 投影可以使用 DISPLAYLIST 参数。 简单…...

C++类与对象(5)—流运算符重载、const、取地址

目录 一、流输出 1、实现单个输出 2、实现连续输出 二、流输入 总结&#xff1a; 三、const修饰 四、取地址 .取地址及const取地址操作符重载 五、[ ]运算符重载 一、流输出 1、实现单个输出 创建一个日期类。 class Date { public:Date(int year 1, int month 1,…...

Vue框架学习笔记——事件修饰符

文章目录 前文提要事件修饰符prevent&#xff08;常用&#xff09;stop&#xff08;不常用&#xff09;事件冒泡stop使用方法三层嵌套下的stop三层嵌套看出的stop&#xff1a; once&#xff08;常用&#xff09;capture&#xff08;不常用&#xff09;self&#xff08;不常用&a…...

嵌入式虚拟机原理

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…...

AMESim|Make failed:Unable to create an excutable for the system

最近在AMESIM与MATLAB进行联合仿真的时候遇到如下问题&#xff1a; Make failed:Unable to create an excutable for the system. 看了网上的解决办法如下 配置环境变量重装AMESIM&#xff0c;有顺序要求&#xff0c;首先是VS&#xff0c;然后是AMESIM与MATLAB。在AMESIM安装…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...