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("盖章完成");
      
          }
    • 公章如图所示

    • 报告模版如下:

    • 盖完章后

相关推荐
程序员岳焱3 分钟前
Java 高级泛型实战:8 个场景化编程技巧
java·后端·编程语言
钢铁男儿13 分钟前
C# 类和继承(扩展方法)
java·servlet·c#
饮长安千年月19 分钟前
JavaSec-SpringBoot框架
java·spring boot·后端·计算机网络·安全·web安全·网络安全
移动开发者1号20 分钟前
Android 大文件分块上传实战:突破表单数据限制的完整方案
android·java·kotlin
代码匠心21 分钟前
从零开始学Flink:揭开实时计算的神秘面纱
java·大数据·后端·flink
jie1889457586636 分钟前
C++ 中的 const 知识点详解,c++和c语言区别
java·c语言·c++
网安INF41 分钟前
RSA加密算法:非对称密码学的基石
java·开发语言·密码学
蔡蓝1 小时前
设计模式-观察着模式
java·开发语言·设计模式
异常君1 小时前
@Bean 在@Configuration 中和普通类中的本质区别
java·spring·面试
jackson凌1 小时前
【Java学习笔记】Math方法
java·笔记·学习