Java 魔法:精准掌控 PDF 合同模板,指定页码与关键字替换签章日期
朋友们!在实际业务场景中,经常会碰到处理 PDF 合同模板的需求,要在几十页的合同里对指定页面替换公章、签名和日期,还涉及多人签名以及多个公司盖公章。下面就给大家分享两种用 Java 处理这类问题的方法,一种是通过指定页码和坐标,另一种是通过指定页面关键字来进行替换。
准备工作
咱们使用 iText 库来完成这些操作,如果你用 Maven 管理项目,在 pom.xml 里添加以下依赖:
<dependencies><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency>
</dependencies>
方法一:指定页码坐标进行替换
实现思路
先读取 PDF 模板文件,利用 PdfStamper 在原模板基础上进行修改。通过指定页码和坐标,把公章、签名图片插入到对应位置,同时在指定坐标处添加日期文本。
示例代码
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;public class PdfContractProcessorByCoordinate {public static void main(String[] args) {String templatePath = "contract_template.pdf";String outputPath = "contract_output_by_coordinate.pdf";String sealImagePath1 = "seal1.png";String sealImagePath2 = "seal2.png";String signatureImagePath1 = "signature1.png";String signatureImagePath2 = "signature2.png";try {// 读取 PDF 模板PdfReader reader = new PdfReader(templatePath);PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPath));// 获取首页、中间页和最后一页int totalPages = reader.getNumberOfPages();int middlePage = totalPages / 2;// 插入公章和签名insertImage(stamper, sealImagePath1, 1, 200, 200); // 首页插入公章 1insertImage(stamper, sealImagePath2, middlePage, 300, 300); // 中间页插入公章 2insertImage(stamper, signatureImagePath1, totalPages, 400, 400); // 最后一页插入签名 1insertImage(stamper, signatureImagePath2, totalPages, 500, 400); // 最后一页插入签名 2// 插入日期insertDate(stamper, 1, 200, 100); // 首页插入日期// 关闭 stamper 和 readerstamper.close();reader.close();System.out.println("PDF 合同处理完成(指定页码坐标)!");} catch (IOException | DocumentException e) {e.printStackTrace();}}/*** 在指定页面插入图片* @param stamper PdfStamper 对象* @param imagePath 图片路径* @param pageNumber 页面编号* @param x 图片插入的 x 坐标* @param y 图片插入的 y 坐标* @throws IOException* @throws DocumentException*/private static void insertImage(PdfStamper stamper, String imagePath, int pageNumber, float x, float y) throws IOException, DocumentException {Image image = Image.getInstance(imagePath);image.scaleToFit(100, 100); // 调整图片大小image.setAbsolutePosition(x, y);PdfContentByte contentByte = stamper.getOverContent(pageNumber);contentByte.addImage(image);}/*** 在指定页面插入日期* @param stamper PdfStamper 对象* @param pageNumber 页面编号* @param x 日期插入的 x 坐标* @param y 日期插入的 y 坐标* @throws DocumentException* @throws IOException*/private static void insertDate(PdfStamper stamper, int pageNumber, float x, float y) throws DocumentException, IOException {PdfContentByte contentByte = stamper.getOverContent(pageNumber);PdfTemplate template = contentByte.createTemplate(100, 30);ColumnText columnText = new ColumnText(template);// 设置字体BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);com.itextpdf.text.Font font = new com.itextpdf.text.Font(baseFont, 12);// 获取当前日期SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");String currentDate = dateFormat.format(new Date());columnText.addElement(new com.itextpdf.text.Paragraph(currentDate, font));columnText.setSimpleColumn(0, 0, 100, 30);columnText.go();contentByte.addTemplate(template, x, y);}
}
代码解释
-
main方法:读取 PDF 模板文件,创建PdfStamper对象,调用insertImage方法插入公章和签名图片,调用insertDate方法插入日期,最后关闭PdfStamper和PdfReader。 -
insertImage方法:将指定路径的图片插入到指定页面的指定位置。 -
insertDate方法:在指定页面的指定位置插入当前日期。
方法二:指定页面关键字进行替换
实现思路
先读取 PDF 模板文件,逐页查找包含关键字的页面。找到页面后,在该页面根据关键字的位置来插入公章、签名图片和日期文本。
示例代码
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;public class PdfContractProcessorByKeyword {public static void main(String[] args) {String templatePath = "contract_template.pdf";String outputPath = "contract_output_by_keyword.pdf";String sealImagePath1 = "seal1.png";String sealImagePath2 = "seal2.png";String signatureImagePath1 = "signature1.png";String signatureImagePath2 = "signature2.png";String keyword1 = "甲方签字";String keyword2 = "乙方盖章";try {// 读取 PDF 模板PdfReader reader = new PdfReader(templatePath);PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPath));// 查找关键字并插入内容findAndReplace(stamper, reader, keyword1, sealImagePath1, signatureImagePath1);findAndReplace(stamper, reader, keyword2, sealImagePath2, signatureImagePath2);// 插入日期insertDateByKeyword(stamper, reader, "合同日期", 50, 20);// 关闭 stamper 和 readerstamper.close();reader.close();System.out.println("PDF 合同处理完成(指定页面关键字)!");} catch (IOException | DocumentException e) {e.printStackTrace();}}/*** 查找关键字并插入公章和签名* @param stamper PdfStamper 对象* @param reader PdfReader 对象* @param keyword 关键字* @param sealImagePath 公章图片路径* @param signatureImagePath 签名图片路径* @throws IOException* @throws DocumentException*/private static void findAndReplace(PdfStamper stamper, PdfReader reader, String keyword, String sealImagePath, String signatureImagePath) throws IOException, DocumentException {int totalPages = reader.getNumberOfPages();for (int page = 1; page <= totalPages; page++) {String pageText = PdfTextExtractor.getTextFromPage(reader, page);if (pageText.contains(keyword)) {// 假设在关键字下方一定位置插入公章和签名float x = 200;float y = getYPosition(pageText, keyword) - 150;insertImage(stamper, sealImagePath, page, x, y);insertImage(stamper, signatureImagePath, page, x + 150, y);}}}/*** 根据关键字插入日期* @param stamper PdfStamper 对象* @param reader PdfReader 对象* @param keyword 关键字* @param offsetX x 偏移量* @param offsetY y 偏移量* @throws DocumentException* @throws IOException*/private static void insertDateByKeyword(PdfStamper stamper, PdfReader reader, String keyword, float offsetX, float offsetY) throws DocumentException, IOException {int totalPages = reader.getNumberOfPages();for (int page = 1; page <= totalPages; page++) {String pageText = PdfTextExtractor.getTextFromPage(reader, page);if (pageText.contains(keyword)) {float x = 200;float y = getYPosition(pageText, keyword) + offsetY;insertDate(stamper, page, x + offsetX, y);}}}/*** 获取关键字的 y 坐标(简单示例,实际可能需要更复杂的计算)* @param pageText 页面文本* @param keyword 关键字* @return y 坐标*/private static float getYPosition(String pageText, String keyword) {// 这里简单返回一个固定值,实际应用中需要根据文本布局计算return 500;}/*** 在指定页面插入图片* @param stamper PdfStamper 对象* @param imagePath 图片路径* @param pageNumber 页面编号* @param x 图片插入的 x 坐标* @param y 图片插入的 y 坐标* @throws IOException* @throws DocumentException*/private static void insertImage(PdfStamper stamper, String imagePath, int pageNumber, float x, float y) throws IOException, DocumentException {Image image = Image.getInstance(imagePath);image.scaleToFit(100, 100); // 调整图片大小image.setAbsolutePosition(x, y);PdfContentByte contentByte = stamper.getOverContent(pageNumber);contentByte.addImage(image);}/*** 在指定页面插入日期* @param stamper PdfStamper 对象* @param pageNumber 页面编号* @param x 日期插入的 x 坐标* @param y 日期插入的 y 坐标* @throws DocumentException* @throws IOException*/private static void insertDate(PdfStamper stamper, int pageNumber, float x, float y) throws DocumentException, IOException {PdfContentByte contentByte = stamper.getOverContent(pageNumber);PdfTemplate template = contentByte.createTemplate(100, 30);ColumnText columnText = new ColumnText(template);// 设置字体BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);com.itextpdf.text.Font font = new com.itextpdf.text.Font(baseFont, 12);// 获取当前日期SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");String currentDate = dateFormat.format(new Date());columnText.addElement(new com.itextpdf.text.Paragraph(currentDate, font));columnText.setSimpleColumn(0, 0, 100, 30);columnText.go();contentByte.addTemplate(template, x, y);}
}
代码解释
-
main方法:读取 PDF 模板文件,创建PdfStamper对象,调用findAndReplace方法根据关键字插入公章和签名,调用insertDateByKeyword方法根据关键字插入日期,最后关闭PdfStamper和PdfReader。 -
findAndReplace方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入公章和签名图片。 -
insertDateByKeyword方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入日期。 -
getYPosition方法:获取关键字的 y 坐标,这里只是简单返回一个固定值,实际应用中需要根据文本布局进行更复杂的计算。
朋友们!通过这两种方法,你就可以使用 Java 灵活地处理 PDF 合同模板,替换指定页面的公章、签名和日期啦。赶紧动手试试吧!
相关文章:
Java 魔法:精准掌控 PDF 合同模板,指定页码与关键字替换签章日期
朋友们!在实际业务场景中,经常会碰到处理 PDF 合同模板的需求,要在几十页的合同里对指定页面替换公章、签名和日期,还涉及多人签名以及多个公司盖公章。下面就给大家分享两种用 Java 处理这类问题的方法,一种是通过指定…...
Ollama 部署本地大语言模型
一、下载安装ollama 1.百度 ollama Ollama 2.点击下载 可以复制下载链接,使用下载器下载。 3.双击安装 默认安装目录:C:\Users\用户名\AppData\Local\Programs\Ollama 二、更改模型下载目录 0.默认下载目录 (跳过) 之前没下载过模型,不…...
Jackson扁平化处理对象
POJO对象 Data public class People {private PeopleInfo peopleInfo;private List<String> peopleIds;private Map<String, String> peopleMap;Datapublic static class PeopleInfo {private String name;private String address;} }JSON序列化处理 直接将对象进…...
在 Ubuntu 上安装 MySQL 的详细指南
在Ubuntu环境中安装 mysql-server 以及 MySQL 开发包(包括头文件和动态库文件),并处理最新版本MySQL初始自动生成的用户名和密码,可以通过官方的APT包管理器轻松完成。以下是详细的步骤指南,包括从官方仓库和MySQL官方…...
pytest-xdist 进行多进程并发测试!
在软件开发过程中,测试是确保代码质量和可靠性的关键步骤。随着项目规模的扩大和复杂性的增加,测试用例的执行效率变得尤为重要。为了加速测试过程,特别是对于一些可以并行执行的测试用 例,pytest-xdist 提供了一种强大的工具&…...
24.ppt:小李-图书策划方案【1】
目录 NO1234 NO5678 NO1234 新建PPT两种方式👇docx中视图→导航窗格→标题1/2/3ppt新建幻灯片→从大纲→重置开始→版式设计→主题插入→表格 NO5678 SmartArt演示方案:幻灯片放映→自定义幻灯片放映→新建→选中添加...
模型 替身决策
系列文章分享模型,了解更多👉 模型_思维模型目录。替身决策,换位思考,多角度决策。 1 替身决策模型的应用 1.1 替身决策模型在面试中的应用-小李的求职面试 小李是一名应届毕业生,正在积极寻找工作机会。在面试过程中…...
ESP32S3读取数字麦克风INMP441的音频数据
ESP32S3 与 INMP441 麦克风模块的集成通常涉及使用 I2S 接口进行数字音频数据的传输。INMP441 是一款高性能的数字麦克风,它通过 I2S 接口输出音频数据。在 Arduino 环境中,ESP32S3 的开发通常使用 ESP-IDF(Espressif IoT Development Framew…...
docker环境下部署face-search开源人脸识别模型
由于我们是直接将face-search部署在docker容器中的,所以,在部署之前一定要检查一下自己的docker环境,要不然部署过程中会出现各种各样的问题 我这里的docker环境是 一、安装docker环境 如果docker版本比较低或者docker-compose的版本比较低的情况下,部署的时候docker的yml…...
301.华为交换机堆叠技术基础
华为交换机堆叠技术基础 一、概念及原理部分1.堆叠简介1.1 什么是堆叠1.2 可靠性网络架构1.3 华为堆叠设备1.4 其他厂商的堆叠2.堆叠的示意图3.堆叠的应用3.1 中小企业3.2 园区网4.堆叠的原理4.1基本的概念4.2 堆叠建立4.3 角色选举4.4 版本同步4.5 配置同步4.6 堆叠系统的登录…...
【数据库创建】用ij工具部署Derby数据库并验证
Java有一个内置的Derby数据库,是一个完全用Java语言编写的、功能强大的微型数据库,其基础引擎和内嵌的JDBC驱动总共大约2MB大小。Derby为用户提供了轻量的标准数据库引擎,它可以紧密地嵌入到任何基于Java的解决方案中。 Derby的特性令人惊奇&…...
飞牛fnOS安装了Airplay没有声音找不到声卡的问题
主要问题描述:我在飞牛的Docker里安装了 Airplay, 这样把NAS接一个外接音箱,就可以当成无线音箱来用,直接把手机的音乐播放投到上面来播放。 (文章底部有写我是怎么安装Airplay的) 我的报错如下࿱…...
netcore openTelemetry+prometheus+grafana
一、netcore项目 二、openTelemetry 三、prometheus 四、grafana添加Dashborad aspire/src/Grafana/dashboards at main dotnet/aspire GitHub 导入:aspnetcore.json和aspnetcore-endpoint.json 效果:...
全程Kali linux---CTFshow misc入门(38-50)
第三十八题: ctfshow{48b722b570c603ef58cc0b83bbf7680d} 第三十九题: 37换成1,36换成0,就得到长度为287的二进制字符串,因为不能被8整除所以,考虑每7位转换一个字符,得到flag。 ctfshow{5281…...
DeepSeek与人工智能的结合:探索搜索技术的未来
云边有个稻草人-CSDN博客 目录 引言 一、DeepSeek的技术背景 1.1 传统搜索引擎的局限性 1.2 深度学习在搜索中的优势 二、DeepSeek与人工智能的结合 2.1 自然语言处理(NLP) 示例代码:基于BERT的语义搜索 2.2 多模态搜索 示例代码&…...
用 DeepSeek + Kimi 自动做 PPT,效率起飞
以下是使用 DeepSeek Kimi 自动做 PPT 的详细操作步骤: 利用 DeepSeek 生成 PPT 内容: 访问 DeepSeek 官网,完成注册/登录后进入对话界面。输入指令,例如“请用 Markdown 格式生成一份关于[具体主题]的 PPT 大纲,需包…...
LeetCode 每日一题 2025/2/3-2025/2/9
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 2/3 680. 验证回文串 II2/4 922. 按奇偶排序数组 II2/5 90. 子集 II2/6 47. 全排列 II2/7 59. 螺旋矩阵 II2/8 63. 不同路径 II2/9 80. 删除有序数组中的重复项 II 2/3 680…...
自动驾驶数据集三剑客:nuScenes、nuImages 与 nuPlan 的技术矩阵与生态协同
目录 1、引言 2、主要内容 2.1、定位对比:感知与规划的全维覆盖 2.2、数据与技术特性对比 2.3、技术协同:构建全栈研发生态 2.4、应用场景与评估体系 2.5、总结与展望 3、参考文献 1、引言 随着自动驾驶技术向全栈化迈进,Motional 团…...
设计模式 ->模板方法模式(Template Method Pattern)
模板方法模式 模板方法模式是一种行为设计模式,它在一个方法中定义一个操作的算法骨架,而将一些步骤延迟到子类中实现。它允许子类在不改变算法结构的情况下重新定义算法中的某些步骤 特点 算法骨架: 在基类中定义算法的框架延迟实现&…...
DeepSeekMoE 论文解读:混合专家架构的效能革新者
论文链接:DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models 目录 一、引言二、背景知识(一)MoE架构概述(二)现有MoE架构的问题 三、DeepSeekMoE架构详解(一&a…...
机器学习之心的创作纪念日
机缘 今天,是我成为创作者的第1460天。 在这段时间里,获得了很大的成长。 虽然日常忙碌但还在坚持创作、初心还在。 日常 创作已经成为我生活的一部分,尤其是在我的工作中,创作是不可或缺的,创作都是核心能力之一。…...
【python】简单的flask做页面。一组字母组成的所有单词。这里的输入是一组字母,而输出是所有可能得字母组成的单词列表
目录结构如下: https://github.com/kaede316/Pythons_pj.git 效果: 后续可扩展为工具网站: 更新 2025.02.09 1、增加等间距制作人 时间信息 2、增加判断润年的功能...
[权限提升] Linux 提权 维持 — 系统错误配置提权 - Sudo 滥用提权
关注这个专栏的其他相关笔记:[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01:Sudo 滥用提权原理 Sudo 是一个 Linux 系统管理命令,它允许系统管理员授予普通用户以指定身份执行指定命令的权限。该命令不仅减少了 Root 用户的登录时间和管理时…...
【算法】快速排序算法的实现:C 和 C++ 版本
1. 算法简介 快速排序(Quick Sort)是由英国计算机科学家霍尔(C.A.R. Hoare)在1960年提出的一种高效的排序算法。它采用了分治法(Divide and Conquer)策略,通常具有很好的性能。在平均情况下,快速排序的时间复杂度为 O(n log n),但在最坏情况下可能退化为 O(n^2),不过…...
如何修改IDEA的maven远程仓库地址
IDEA自动的maven的远程仓库地址为国外地址,导致下载依赖时很慢,通过如下方法可以将其修改为国内地址 选中模块,右击,创建setting.xml文件 添加阿里仓库地址 <mirrors><mirror><id>nexus-aliyun</id><…...
LLMs之DeepSeek r1:TinyZero(复现 DeepSeek R1 Zero 的核心功能)的简介、安装和使用方法、案例应用之详细攻略
LLMs之DeepSeek r1:TinyZero(复现 DeepSeek R1 Zero 的核心功能)的简介、安装和使用方法、案例应用之详细攻略 目录 TinyZero的简介 1、TinyZero的特点 TinyZero的安装和使用方法 1、安装 创建 conda 环境 数据准备 (倒计时任务) 训练执行 单GPU (适用于模型…...
亚博microros小车-原生ubuntu支持系列:23 人脸识别追踪
背景知识: 本节跟上一篇的物体识别追踪类似,换了opencv的函数来做人脸识别 函数定义如下: detectMultiScale(image, scaleFactor, minNeighbors, flags, minSize, maxSize)scaleFactor参数控制每个图像序列的缩放比例。该参数决定了在每个…...
[7] 游戏机项目说明
[7] 游戏机项目说明 在这节课中,我们将学习如何基于FreeRTOS开发一个简单的游戏项目。我们会使用一个开源项目nwatch,它是一个基于STM32的开源手表,包含了三个游戏。我们的目标是将这个游戏移植到我们的开发板上,并逐步使用FreeR…...
Kubernetes之kube-proxy运行机制分析
一、基础知识 1.Kubernetes再创建服务时会为服务分配一个虚拟IP地址,客户端通过这个虚拟Ip地址来访问服务,而服务则负责将请求转发到后端pod上。 2.上述阐述的过程为一个反向代理的过程,但是这个反向代理和普通的反向代理的区别是它的IP地址是…...
微信小程序调用企业微信客户服务插件联通企业微信客服
需求背景:用户在小程序页面点击按钮添加企业微信的客服 相关技术:基于uniapp开发的微信小程序 插件名称:企业微信客户服务插件「联系我」插件 - 文档 - 企业微信开发者中心 仔细阅读文档「联系我」插件 - 文档 - 企业微信开发者中心 以下是我的实例代码 1.首先先小程序管…...
