Java 魔法:精准掌控 PDF 合同模板,指定页码与关键字替换签章日期

发布于:2025-02-10 ⋅ 阅读:(48) ⋅ 点赞:(0)

朋友们!在实际业务场景中,经常会碰到处理 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); // 首页插入公章 1
            insertImage(stamper, sealImagePath2, middlePage, 300, 300); // 中间页插入公章 2
            insertImage(stamper, signatureImagePath1, totalPages, 400, 400); // 最后一页插入签名 1
            insertImage(stamper, signatureImagePath2, totalPages, 500, 400); // 最后一页插入签名 2

            // 插入日期
            insertDate(stamper, 1, 200, 100); // 首页插入日期

            // 关闭 stamper 和 reader
            stamper.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 方法插入日期,最后关闭 PdfStamperPdfReader

  • 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 和 reader
            stamper.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 方法根据关键字插入日期,最后关闭 PdfStamperPdfReader

  • findAndReplace 方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入公章和签名图片。

  • insertDateByKeyword 方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入日期。

  • getYPosition 方法:获取关键字的 y 坐标,这里只是简单返回一个固定值,实际应用中需要根据文本布局进行更复杂的计算。

朋友们!通过这两种方法,你就可以使用 Java 灵活地处理 PDF 合同模板,替换指定页面的公章、签名和日期啦。赶紧动手试试吧!


网站公告

今日签到

点亮在社区的每一天
去签到