今天接到一个需求,需要在pdf中的签名处,插入签名照片,但签名位置不固定,话不多说上代码:
1、首先引入itextpdf依赖包:
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency>
2、具体实现逻辑如下:
/*** description PDF插入图片(根据关键字位置动态插入)** @author yanzy* @version 1.0* @date 2025/3/27 17:26*/public static void imgToPdf2(String pdfPath, String imagePath, String outputPath, String keyword) {try {// 读取原始PDFPdfReader reader = new PdfReader(pdfPath);FileOutputStream fos = new FileOutputStream(outputPath);PdfStamper stamper = new PdfStamper(reader, fos);// 遍历PDF每一页,查找关键字并插入签名图片Boolean keyBool = true;for (int i = 1; i <= reader.getNumberOfPages(); i++) {// 获取当前页面的文本和坐标信息List<Position> positions = getPositions(reader, i);// 合并连续文本块StringBuilder combinedText = new StringBuilder();List<Position> textChunks = new ArrayList<>();for (Position pos : positions) {combinedText.append(pos.getText());textChunks.add(pos);}// 查找关键字在整个文本中的位置String fullText = combinedText.toString();int index = fullText.indexOf(keyword);if (index != -1) {// 获取关键字起始位置Position endPos = findPositionByIndex(textChunks, index + keyword.length() - 1);// 计算插入坐标(取结束位置右侧)float imgX = endPos.getX() + 20; // 横纵标,右侧偏移20单位float imgY = endPos.getY() - 20; // 纵坐标,向下移动20单位log.info("关键字位置 -> X:{} Y: {} | 图片位置 -> X: {} Y:{}", endPos.getX(), endPos.getY(), imgX, imgY);// 添加图片Image img = Image.getInstance(imagePath);// 图片尺寸img.scaleToFit(100, 50);// 设置图片的插入位置(X,Y坐标)img.setAbsolutePosition(imgX, imgY);// PdfStamper:直接修改原始PDF,避免图层顺序问题PdfContentByte canvas = stamper.getOverContent(i);canvas.addImage(img);keyBool = false;}}if (keyBool) {throw new Exception("未找到关键字位置!");}stamper.close();reader.close();log.info("图片插入成功!");} catch (Exception e) {e.printStackTrace();log.error("pdf插入图片失败! errMsg: {}", e.getMessage());}}/*** description 根据文本索引找到对应位置** @author yanzy* @version 1.0* @date 2025/3/27 17:26*/private static Position findPositionByIndex(List<Position> chunks, int targetIndex) {int currentIndex = 0;for (Position chunk : chunks) {int chunkLength = chunk.getText().length();if (currentIndex + chunkLength > targetIndex) {return chunk;}currentIndex += chunkLength;}return chunks.get(chunks.size() - 1);}/*** description 获取PDF页面的所有文本位置** @author yanzy* @version 1.0* @date 2025/3/27 17:26*/private static List<Position> getPositions(PdfReader reader, int pageNumber) throws Exception {List<Position> positions = new ArrayList<>();// 创建PdfContentByte和RenderListener来提取文本PdfReaderContentParser parser = new PdfReaderContentParser(reader);parser.processContent(pageNumber, new RenderListener() {@Overridepublic void beginTextBlock() {}@Overridepublic void endTextBlock() {}@Overridepublic void renderText(TextRenderInfo renderInfo) {String text = renderInfo.getText();float x = renderInfo.getBaseline().getStartPoint().get(0);float y = renderInfo.getBaseline().getStartPoint().get(1);positions.add(new Position(x, y, text));}@Overridepublic void renderImage(ImageRenderInfo renderInfo) {}});return positions;}public static void main(String[] args) {// 输入PDF路径String srcPdf = "E://test.pdf";// 输出PDF路径String destPdf = "E://output_image.pdf";// 图片路径String imagePath = "E://test.jpg";// 关键字String keyword = "本人签名:";//imgToPdf(srcPdf, imagePath, destPdf);imgToPdf2(srcPdf, imagePath, destPdf, keyword);}
/*** description pdf中的文本坐标** @author yanzy* @date 2025/3/28 15:36*/
public class Position {private final float x;private final float y;private final String text;public Position(float x, float y, String text) {this.x = x;this.y = y;this.text = text;}public float getX() {return x;}public float getY() {return y;}public String getText() {return text;}}
3、下面是pdf的内容:
4、运行程序后的结果:
5、可以看到签名照片已经成功加上了