朋友们!在实际业务场景中,经常会碰到处理 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
方法插入日期,最后关闭PdfStamper
和PdfReader
。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
方法根据关键字插入日期,最后关闭PdfStamper
和PdfReader
。findAndReplace
方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入公章和签名图片。insertDateByKeyword
方法:逐页查找包含关键字的页面,在该页面根据关键字的位置插入日期。getYPosition
方法:获取关键字的 y 坐标,这里只是简单返回一个固定值,实际应用中需要根据文本布局进行更复杂的计算。
朋友们!通过这两种方法,你就可以使用 Java 灵活地处理 PDF 合同模板,替换指定页面的公章、签名和日期啦。赶紧动手试试吧!