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

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

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

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

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

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

    xml 复制代码
    <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, 自定义渲染监听器, 实现查找关键字自有逻辑

      java 复制代码
      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;
      
          @Override
          public void beginTextBlock() {
          }
      
          @Override
          public void endTextBlock() {
          }
      
          @Override
          public void renderImage(ImageRenderInfo arg0) {
          }
      
          @Override
          public 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;
              }
          }
      }
    • 获取关键字坐标的工具类

      java 复制代码
      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;
          }
      
      
          @SneakyThrows
          public 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

      java 复制代码
          @SneakyThrows
          public 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("盖章完成");
      
          }
    • 公章如图所示

    • 报告模版如下:

    • 盖完章后

相关推荐
MrZhangBaby4 分钟前
SQL-leetcode—1158. 市场分析 I
java·sql·leetcode
一只淡水鱼6619 分钟前
【spring原理】Bean的作用域与生命周期
java·spring boot·spring原理
五味香25 分钟前
Java学习,查找List最大最小值
android·java·开发语言·python·学习·golang·kotlin
jerry-8939 分钟前
Centos类型服务器等保测评整/etc/pam.d/system-auth
java·前端·github
Jerry Lau39 分钟前
大模型-本地化部署调用--基于ollama+openWebUI+springBoot
java·spring boot·后端·llama
小白的一叶扁舟43 分钟前
Kafka 入门与应用实战:吞吐量优化与与 RabbitMQ、RocketMQ 的对比
java·spring boot·kafka·rabbitmq·rocketmq
幼儿园老大*44 分钟前
【系统架构】如何设计一个秒杀系统?
java·经验分享·后端·微服务·系统架构
言之。1 小时前
【Java】面试中遇到的两个排序
java·面试·排序算法
计算机-秋大田1 小时前
基于SSM的家庭记账本小程序设计与实现(LW+源码+讲解)
java·前端·后端·微信小程序·小程序·课程设计
南宫生1 小时前
力扣动态规划-7【算法学习day.101】
java·数据结构·算法·leetcode·动态规划