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

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

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

相关文章

如何为您的企业选择合适的多因素认证?

在传统的网络安全架构中&#xff0c;重点在于防止非法入侵&#xff0c;例如防火墙、VPN 、堡垒机等安全设备的重心都在于防止用户违规访问企业资源&#xff0c;一旦合法用户的账号密码被入侵者拿到&#xff0c;就可以冒充合法用户访问企业资源&#xff0c;所有的安全设备形同虚…

解决:ImportError: cannot import name ‘Adam‘ from ‘keras.optimizers‘

解决&#xff1a;ImportError: cannot import name ‘Adam‘ from ‘keras.optimizers‘ 背景 在使用之前的代码时&#xff0c;报错&#xff1a; from keras.optimizers import Adam ImportError: cannot import name ‘Adam’ 报错问题 from keras.optimizers import Adam I…

微机原理_2

一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案&#xff0c;请将选定的答案填涂在答题纸的相应位置上。&#xff09; 下列数中最大的数为&#xff08;&#xff09; A. 10010101B B. (126)8 C. 96H D. 100 CPU 执行 OUT 60H,…

vue一个页面左边是el-table表格 当点击每条数据时可以在右边界面编辑表格参数,右边保存更新左边表格数据

实现思路&#xff1a; 1.点击当前行通过row拿到当前行数据。 2.将当前行数据传给子组件。 3.子组件监听父组件传过来的数据并映射在界面。 4.点击保存将修改的值传给父组件更新表格。 5.父组件收到修改过后的值&#xff0c;可以通过字段判断比如id&#xff0c;通过 findIn…

Co-DETR:DETRs与协同混分配训练论文学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2211.12860.pdf 代码地址&#xff1a; GitHub - Sense-X/Co-DETR: [ICCV 2023] DETRs with Collaborative Hybrid Assignments Training 摘要 作者提出了一种新的协同混合任务训练方案&#xff0c;即Co-DETR&#xff0c;以从多种标…

element中el-switch的v-model自定义值

一、问题 element中的el-switch的值默认都是true或false&#xff0c;但是有些时候后端接口该字段可能是0或者1&#xff0c;如果说再转换一次值&#xff0c;那就有点太费力了。如下所示&#xff1a; <template><el-switchinactive-text"否"active-text&quo…

System-V共享内存和基于管道通信实现的进程池

文章目录 一.进程间通信:进程间通信的本质: 二.Linux管道通信匿名管道:关于管道通信的要点:基于匿名管道构建进程池: 三.System-V共享内存共享内存和命名管道协同通信 参考Linux内核源码版本------linux-2.4.3 一.进程间通信: 操作系统中,为了保证安全性,进程之间具有严格的独…

Java设计模式系列:单例设计模式

Java设计模式系列&#xff1a;单例设计模式 介绍 所谓类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法&#xff08;静态方法&#xff09; 比如 Hiberna…

Doris 简介(一)

Apache Doris 由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018 年贡献到 Apache 社区后&#xff0c;更名为 Doris &#xff09;&#xff0c;在百度内部&#xff0c;有超过 200 个产品线在使用&#xff0c;部署机器超过 1000 台&#xff0c;单一业务最大可达到上…

某60区块链安全之未初始化的存储指针实战一学习记录

区块链安全 文章目录 区块链安全未初始化的存储指针实战一实验目的实验环境实验工具实验原理实验过程 未初始化的存储指针实战一 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约未初始化的存储指针漏洞 找到合约漏洞进行分析并形成利用 实验环境 Ubuntu18.04操…

栈和队列的OJ题--12.括号匹配

12.括号匹配 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a;该题比较简单&#xff0c;是对栈特性很好的应用&#xff0c;具体操作如下&#xff1a;循环遍历String中的字符&#xff0c;逐个取到每个括号&#xff0c;如果该括号是&#xff1a;1. …

微服务负载均衡器Ribbon

1.什么是Ribbon 目前主流的负载方案分为以下两种&#xff1a; 集中式负载均衡&#xff0c;在消费者和服务提供方中间使用独立的代理方式进行负载&#xff0c;有硬件的&#xff08;比如 F5&#xff09;&#xff0c;也有软件的&#xff08;比如 Nginx&#xff09;。 客户端根据…