java操作pdf添加骑缝章

发布时间 2023-04-04 14:36:57作者: 橙香五花肉

java操作pdf添加骑缝章

依赖

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>

代码

/**
 * 盖骑缝章
 *
 * @param infilePath  原PDF路径
 * @param outFilePath 输出PDF路径
 * @param picPath     章图片路径
 * @param keyWord     盖章关键字位置
 */
public static void stamperCheckMarkPdf(String infilePath, String outFilePath, String picPath, String keyWord) {
    // 选择需要印章的pdf
    PdfReader reader = null;
    // 加完印章后的pdf
    PdfStamper stamp = null;
    try {
        reader = new PdfReader(infilePath);
        stamp = new PdfStamper(reader, Files.newOutputStream(Paths.get(outFilePath)));

        // 获得第一页
        Rectangle pageSize = reader.getPageSize(1);
        float height = pageSize.getHeight();
        float width  = pageSize.getWidth();

        // 获取pdf文档中的页数
        int nums = reader.getNumberOfPages();
        // 生成骑缝章切割图片
        Image[] nImage = subImages(picPath, nums);

        for(int n = 1; n <= nums; n++){
            // 设置在第几页打印印章
            PdfContentByte over = stamp.getOverContent(n);
            // 选择图片
            Image img = nImage[n-1];
            // 控制图片位置
            img.setAbsolutePosition(width-img.getWidth(),height/2-img.getHeight()/2);
            over.addImage(img);

            // 最后一页,查询盖章处,盖章
            // 存在的问题:这边我控制的是当最后一页才去根据关键字查找盖章,如果关键字出现在前两页自然找不到
            if (n == nums) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                InputStream is = null;
                try {
                    is = Files.newInputStream(Paths.get(infilePath));

                    byte[] buffer = new byte[is.available()];
                    int m;
                    while ((m = is.read(buffer)) != -1) {
                        bos.write(buffer, 0, m);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        bos.close();
                        if (is != null) {
                            is.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                // 获取输出流
                byte[] bytes = bos.toByteArray();
                // 获取关键字位置
                List<Map<String, Object>> wordsCoordinates = getWordsCoordinate(bytes, keyWord);

                if (!CollectionUtils.isEmpty(wordsCoordinates)) {
                    Map<String, Object> map = wordsCoordinates.get(wordsCoordinates.size() - 1);
                    float x = (float) map.get("x");
                    float y = (float) map.get("y");
                    float w = (float) map.get("fontWidth") * 6;
                    float h = (float) map.get("fontHeight");

                    Image[] nImage2 = subImages(picPath, 1);
                    Image img2 = nImage2[0];
                    float xx = x + w - img2.getWidth() / 2;
                    float yy = y - img2.getHeight() / 2;

                    // 控制图片位置
                    img2.setAbsolutePosition(xx, yy);
                    over.addImage(img2);
                }
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if (null != stamp) {
                stamp.close();
            }
            if (null != reader) {
                reader.close();
            }
        } catch (Exception ignored) {}
    }
}

/**
 * 切割图片
 *
 * @param imgPath 原始图片路径
 * @param n       切割份数
 * @return itextPdf的Image[]
 */
public static Image[] subImages(String imgPath, int n) throws IOException, BadElementException {

    Image[] nImage = new Image[n];

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    // 服务器测试和resource下测试
    Resource resource = new ClassPathResource(imgPath);
    InputStream inputStream = resource.getInputStream();
    // 使用本地图片测试方法
    // File resource = new File(imgPath);
    // InputStream inputStream = Files.newInputStream(resource.toPath());
    BufferedImage img = ImageIO.read(inputStream);

    // 图片高
    int h = img.getHeight();
    // 图片宽
    int w = img.getWidth();

    int sw = w / n;
    for(int i = 0; i < n; i++){
        BufferedImage subImg;
        if( i == n - 1){
            // 最后剩余部分
            subImg = img.getSubimage(i * sw, 0, w-i*sw, h);
        }else {
            // 前n-1块均匀切
            subImg = img.getSubimage(i * sw, 0, sw, h);
        }
        ImageIO.write(subImg, imgPath.substring(imgPath.lastIndexOf('.')+1),out);
        nImage[i] = Image.getInstance(out.toByteArray());
        out.flush();
        out.reset();
    }
    return nImage;
}


/**
 * 获取关键词位置
 * 
 * @param pdfData pdf二进制字节流
 * @param keyWord 关键字
 * @return List<Map<String, Object>>
 */
public static List<Map<String, Object>> getWordsCoordinate(byte[] pdfData, String keyWord) {

    if (StringUtils.isBlank(keyWord)) {
        return null;
    }

    List<Map<String, Object>> result = new ArrayList<>();
    PdfReader reader = null;

    try {
        // pdfData :可以是二进制,也可以是文件路径,两种方式选择一种
        reader = new PdfReader(pdfData);
        // 获取pdf页数
        int pages = reader.getNumberOfPages();
        for (int pageNum = 1; pageNum <= pages; pageNum++) {
            // 每页的宽度
            float width = reader.getPageSize(pageNum).getWidth();
            // 每页的高度
            float height = reader.getPageSize(pageNum).getHeight();

            RenderListenerHelper renderListenerHelper = new RenderListenerHelper(pageNum, width, height);

            // 解析pdf,定位位置
            PdfContentStreamProcessor processor = new PdfContentStreamProcessor(renderListenerHelper);
            PdfDictionary pageDic = reader.getPageN(pageNum);
            PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
            processor.processContent(ContentByteUtils.getContentBytesForPage(reader, pageNum), resourcesDic);
            // 文本内容
            String content = renderListenerHelper.getContent();
            // 文本每个字对应的坐标
            List<Map<String, Object>> charPositions = renderListenerHelper.getCharPositions();
            for (int i = 0; i < content.length(); i++) {
                // 获取关键字所在位置
                int keyIndex = content.indexOf(keyWord, i);
                if (keyIndex == -1) {
                    break;
                }
                result.add(charPositions.get(keyIndex));
                i = keyIndex + 1;
            }
        }
    } catch (Exception e) {
        log.error("获取pdf关键字坐标失败:", e);
    } finally {
        reader.close();
    }
    return result;
}

static class RenderListenerHelper implements RenderListener {

    private final int pageNum;

    private final float pageWidth;

    private final float pageHeight;

    private final StringBuilder contentBuilder = new StringBuilder();

    private final List<Map<String, Object>> charPositions = new ArrayList<>();

    public RenderListenerHelper(int pageNum, float pageWidth, float pageHeight) {
        this.pageNum = pageNum;
        this.pageWidth = pageWidth;
        this.pageHeight = pageHeight;
    }

    public String getContent() {
        return contentBuilder.toString();
    }

    public List<Map<String, Object>> getCharPositions() {
        return charPositions;
    }

    // step 2 遇到"BT"执行
    @Override
    public void beginTextBlock() {

    }

    // step 3 文字主要处理方法
    @Override
    public void renderText(TextRenderInfo renderInfo) {

        //获取文本内容每个字信息集合
        List<TextRenderInfo> characterRenderInfos = renderInfo.getCharacterRenderInfos();

        for (TextRenderInfo textRenderInfo : characterRenderInfos) {
            String word = textRenderInfo.getText();
            if (word.length() > 1) {
                word = word.substring(word.length() - 1);
            }

            // 关键字上边缘坐标
            // Rectangle2D.Float boundingRectange = textRenderInfo.getAscentLine().getBoundingRectange();
            // 关键字标准坐标(中间)
            Rectangle2D.Float boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
            // 关键字下边缘坐标
            // Rectangle2D.Float boundingRectange = textRenderInfo.getDescentLine().getBoundingRectange();
            // 正常坐标
            Float x = boundingRectange.x;
            Float y = boundingRectange.y;

            Map<String, Object> coordinate = new HashMap<>(8);

            coordinate.put("x", x);
            coordinate.put("y", y);
            // 页数
            coordinate.put("pageNum", pageNum);
            // 字体长度
            coordinate.put("fontWidth", boundingRectange.width);
            // 字段高度
            coordinate.put("fontHeight", boundingRectange.height);
            charPositions.add(coordinate);
            contentBuilder.append(word);
        }
    }

    // step 4(最后执行的,只执行一次),遇到“ET”执行
    @Override
    public void endTextBlock() {

    }

    // step 1(图片处理方法)
    @Override
    public void renderImage(ImageRenderInfo renderInfo) {

    }
}

测试

public static void main(String[] args) throws DocumentException, IOException {
    // 输出文件
    String outfilePath = "C:/" + UUID.randomUUID() +".pdf";
    // 图片路径
    String picPath = "C:/章.png";
    stamperCheckMarkPdf("C:/合同.pdf", outfilePath, picPath, "");
}

章图片