java操作pdf添加骑缝章
依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
代码
public static void stamperCheckMarkPdf(String infilePath, String outFilePath, String picPath, String keyWord) {
PdfReader reader = null;
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();
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) {}
}
}
public static Image[] subImages(String imgPath, int n) throws IOException, BadElementException {
Image[] nImage = new Image[n];
ByteArrayOutputStream out = new ByteArrayOutputStream();
Resource resource = new ClassPathResource(imgPath);
InputStream inputStream = resource.getInputStream();
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 {
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;
}
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 {
reader = new PdfReader(pdfData);
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);
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;
}
@Override
public void beginTextBlock() {
}
@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.getBaseline().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);
}
}
@Override
public void endTextBlock() {
}
@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, "");
}

章图片

【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤