Java 实现给pdf文件指定位置盖章功能

Java 实现给pdf文件指定位置盖章功能

开发中遇到一个需求, 需要给用户上传的的pdf文件, 指定位置上盖公章的功能, 经过调研和对比, 最终确定实现思路.

这里是使用pdf文件中的关键字进行章子的定位, 之所以这样考虑是因为如果直接写死坐标的话, 可能会出现因pdf大小, 缩放, 盖章位置变更的原因, 导致公章位置错位. 所以选择了根据关键字定位的方式.

下面列出具体的实现方式:

  1. 使用的是Java语言, 使用Maven管理依赖, 下面是操作pdf所依赖的库的坐标

    <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.12</version>
    </dependency>
    <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version>
    </dependency>
    
  2. 下面是具体的代码逻辑

    • CustomRenderListener, 自定义渲染监听器, 实现查找关键字自有逻辑

      import com.itextpdf.awt.geom.Rectangle2D.Float;
      import com.itextpdf.text.pdf.parser.ImageRenderInfo;
      import com.itextpdf.text.pdf.parser.RenderListener;
      import com.itextpdf.text.pdf.parser.TextRenderInfo;
      import lombok.Data;/*** pdf关键词帮助类** @author wdhcr*/
      @Data
      public class CustomRenderListener implements RenderListener {// 坐标private float[] coordinate = null;// 关键字private String keyWord;// pdf当前页private int page;@Overridepublic void beginTextBlock() {}@Overridepublic void endTextBlock() {}@Overridepublic void renderImage(ImageRenderInfo arg0) {}@Overridepublic void renderText(TextRenderInfo textRenderInfo) {String text = textRenderInfo.getText();if (null != text && text.contains(keyWord)) {Float boundingRange = textRenderInfo.getBaseline().getBoundingRectange();coordinate = new float[3];coordinate[0] = boundingRange.x;coordinate[1] = boundingRange.y;coordinate[2] = page;}}
      }
      
    • 获取关键字坐标的工具类

      import com.itextpdf.text.Image;
      import com.itextpdf.text.pdf.PdfContentByte;
      import com.itextpdf.text.pdf.PdfGState;
      import com.itextpdf.text.pdf.PdfReader;
      import com.itextpdf.text.pdf.PdfStamper;
      import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
      import com.jkwl.common.exception.BaseException;
      import lombok.SneakyThrows;
      import lombok.extern.slf4j.Slf4j;import java.io.*;/*** Pdf定位工具类* @author wdhcr*/
      @Slf4j
      public class PdfUtils {/*** 获取关键字所在PDF坐标** @param pdfReader 流* @param keyWords  关键词* @return float[] 坐标*/public static float[] getKeyWords(PdfReader pdfReader, String keyWords) {float[] coordinate = null;int page = 0;try {int pageNum = pdfReader.getNumberOfPages();PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);CustomRenderListener renderListener = new CustomRenderListener();renderListener.setKeyWord(keyWords);for (page = 1; page <= pageNum; page++) {renderListener.setPage(page);pdfReaderContentParser.processContent(page, renderListener);coordinate = renderListener.getCoordinate();if (coordinate != null) {break;}}} catch (IOException e) {e.printStackTrace();}return coordinate;}@SneakyThrowspublic static void stampToPdf(InputStream pdfInputStream, String keyWords, InputStream sealInputStream, OutputStream outputStream) {byte[] sealBytes = sealInputStream.readAllBytes();PdfReader reader = new PdfReader(pdfInputStream);float[] xyz = PdfUtils.getKeyWords(reader, keyWords);if (xyz == null) {throw new BaseException("未找到盖章位置");}PdfStamper stamper = new PdfStamper(reader, outputStream);// 将印章图片放在pdf文件的第?页PdfContentByte over = stamper.getOverContent((int) xyz[2]);// 需要插入的图片Image contractSealImg = Image.getInstance(sealBytes);// 保存状态over.saveState();// 图片处理PdfGState pdfGState = new PdfGState();// 给图片设置透明度,一般不需要pdfGState.setFillOpacity(0.8F);over.setGState(pdfGState);contractSealImg.setAbsolutePosition(xyz[0] + 50, xyz[1] - 40);// 设置图片大小contractSealImg.scaleAbsolute(100, 100);// 将图片添加到pdf文件over.addImage(contractSealImg);over.restoreState();stamper.setFormFlattening(true);// 关闭流stamper.close();reader.close();}}
      
    • 测试demo

          @SneakyThrowspublic static void main(String[] args) {FileInputStream pdfInputStream = new FileInputStream("/Users/Desktop/报告模版.pdf");FileInputStream sealInputStream = new FileInputStream("/Users/Desktop/dbd.png");FileOutputStream fileOutputStream = new FileOutputStream("/Users/Desktop/报告模版盖章.pdf");// 关键字为: 签发日期PdfUtils.stampToPdf(pdfInputStream, "签发日期", sealInputStream, fileOutputStream);fileOutputStream.close();pdfInputStream.close();sealInputStream.close();System.out.println("盖章完成");}
      
    • 公章如图所示

      seal

    • 报告模版如下:

      image-20241211145537180

    • 盖完章后

      image-20241211145618814

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

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

相关文章

ASP.NET Core API + MySql

环境 数据库&#xff1a; mysql8.0 后端&#xff1a; vs2022 ASP.NET Core API .net 8 前端&#xff1a; Hbuilderx bootstrap 5.3.0 jquery v3.7.1 bootstrap-table 1.23.5 创建项目 添加资源包 AutoMapper Microsoft.EntityFrameworkCore.Tools 8.0.0 Pomelo.EntityFramew…

Bananna Pi开源社区联合矽昌通信打造开源的低成本Wifi5路由器

香蕉派 BPI-Wifi5 路由器采用矽昌SF19A2890S2芯片方案设计。它是一款高性能无线路由器&#xff0c;适用于小微企业、家庭和其他网络环境。Banana Pi开源社区提供整体解决方案。所有代码开源&#xff0c;用户可以在上面自由开发自己的应用。 Banana Pi wifi5 路由器github代码: …

item2 for macos

安装Item2 brew install iterm2 查看终端类型 cat /etc/shells Mac OS X 10.15 已经将默认的shell从Bash换成了zsh&#xff0c;所以不用安装&#xff0c;10.15以前的可以使用下面的命令进行安装 brew install zsh 安装Oh My ZSH # curl sh -c "$(curl -fsSL https://ra…

Pytest-Bdd-Playwright 系列教程(14):Docstring 参数

Pytest-Bdd-Playwright 系列教程&#xff08;14&#xff09;&#xff1a;Docstring 参数 前言一、什么是docstring?二、基本语法三、主要特点四、实际例子五、注意事项六、使用建议总结 前言 在自动化测试的过程中&#xff0c;我们经常需要处理复杂的测试数据或需要输入多行文…

手机租赁系统开发指南一站式服务流程解析

内容概要 手机租赁系统的开发是一个复杂但有趣的过程&#xff0c;像搭建乐高一样&#xff0c;只要找到合适的模块&#xff0c;就能打造出一个宾至如归的租赁平台。在这部分&#xff0c;我们将对开发流程的整体结构进行简要概述&#xff0c;并指出每个环节的重要性。 首先&…

OpenAI 正式赋予 ChatGPT 通过视频实时与用户互动的能力

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

EasyExcel设置表头上面的那种大标题(前端传递来的大标题)

1、首先得先引用easyExcel的版本依赖&#xff0c;我那 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency> 2、然后得弄直接的实体类&#xff0c;&…

纯血鸿蒙崛起,原生Android挑战?两大操作系统巅峰对决,智能设备未来谁主沉浮?

鸿蒙HarmonyOS和原生Android系统虽然在一些方面相似&#xff0c;但在架构、设计理念、API、开发工具等方面存在一些差异。鸿蒙系统的目标是跨设备、分布式的操作系统&#xff0c;强调多设备协同和资源共享&#xff0c;而Android则主要集中在智能手机和移动设备领域。 下面将从…

计算机网络:传输层、应用层、网络安全、视频/音频/无线网络、下一代因特网

目录 &#xff08;五&#xff09;传输层 1&#xff0e;传输层寻址与端口 2&#xff0e;无连接服务与面向连接服务 3. 传输连接的建立与释放 4. UDP 的优点 5. UDP 和 TCP 报文段报头格式 6. TCP 的流量控制 7&#xff0e;TCP 的拥塞控制 8. TCP 传送连接的管理 &#…

【前端开发】HTML+CSS网页,可以拿来当作业(免费开源)

HTML代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content_lizhongyu"widthdevice-width, initial-scale1.0"><title>小兔鲜儿-新鲜、惠民、快捷<…

CV(4)--边缘提取和相机模型

前言 仅记录学习过程&#xff0c;有问题欢迎讨论 边缘提取&#xff08;涉及语义分割&#xff09;&#xff1a; 图象的边缘是指图象局部区域亮度变化显著的部分,也有正负之分&#xff0c;暗到亮为正 求边缘的幅度&#xff1a;sobel&#xff0c;Canny算子 图像分高频分量和低…

智能技术引领未来:自动图像标注的创新应用与发展

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;传知代码 欢迎大家点赞收藏评论&#x1f60a; 目录 概述算法原理核心逻辑效果演示使用方式参考文献 参考文献&#xff1a;需要本文的详细复现过程的项目源码、数据和预训练好的模型可从该地址处获…

C语言-排序

常见的排序算法分为以下四种&#xff0c;插入排序&#xff0c;选择排序&#xff0c;交换排序&#xff0c;归并排序。 一、插入排序 (一)直接插入排序 直接插入排序&#xff0c;将一段数组看做被分成已排序序列和未排序序列&#xff0c;排序过程是从未排序序列的元素开始&…

【Java笔记】LinkedList 底层结构

一、LinkedList 的全面说明 LinkedList底层实现了双向链表和双端队列特点可以添加任意元素(元素可以重复)&#xff0c;包括null线程不安全&#xff0c;没有实现同步 二、LinkedList 的底层操作机制 三、LinkedList的增删改查案例 public class LinkedListCRUD { public stati…

网管平台(基础篇):路由器的介绍与管理

路由器简介 路由器&#xff08;Router&#xff09;是一种计算机网络设备&#xff0c;它的主要作用是将数据通过打包&#xff0c;并按照一定的路径选择算法&#xff0c;将网络传送至目的地。路由器能够连接两个或更多个网络&#xff0c;并根据信道的情况自动选择和设定路由&…

排序算法(2):选择排序

问题 排序 [30, 24, 5, 58, 18, 36, 12, 42, 39] 选择排序 选择排序每次从待排序序列中选出最小&#xff08;或最大&#xff09;的元素&#xff0c;将其放到序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;或最大&#xff09;元素…

Tongweb8命令行使用收集(by lqw)

文章目录 声明对应版本修改thanos用户密码部署应用到默认实例节点相关操作新增节点(一般一个服务器ip只能装一个节点)启动节点(需确认节点没有运行)停止节点删除节点节点新增应用节点查看应用节点启动应用节点停止应用节点卸载应用(谨慎操作,卸载后应用就没有了,建议备份后…

Artec Leo3D扫描仪在重型机械设备定制中的应用【沪敖3D】

挑战&#xff1a;一家加拿大制造商需要有效的方法&#xff0c;为富于变化且难度较高的逆向工程&#xff0c;快速、安全、准确地完成重型机械几何采集。 解决方案&#xff1a;Artec Leo, Artec Studio, Geomagic for SOLIDWORKS 效果&#xff1a;Artec Leo三维扫描代替过去的手动…

题海拾贝:力扣 141.环形链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…

Vite快速构建Vue教程

步骤 1: 初始化项目目录 创建一个名为 projects 的文件夹&#xff0c;作为存放所有 Vite 项目的根目录。这个文件夹将容纳多个独立的 Vite 项目。 步骤 2: 创建 Vite 项目 右键点击 projects 文件夹并选择“在此处打开终端”或使用您偏好的代码编辑器&#xff08;如 VSCode&…