PDFBox2添加签名间批注被Acrobat判定修改的原因及解决方法
问题根源
- 页面批注列表被整体替换:代码中通过
page.setAnnotations(annotations)替换整个页面批注数组,而非追加新批注。增量保存时,这会导致PDF的Annots字典被完全重写,Acrobat会将此视为对原有批注集合的修改。 - 不必要的对象更新标记:代码中错误标记页面资源为需更新(
page.getResources().getCOSObject().setNeedToBeUpdated(true)),但实际未修改页面资源,这会引发Acrobat对文档变更的误判。 - 批注锁定属性缺失:仅设置
ReadOnly不足以让Acrobat认定批注不可修改,需同时设置Locked属性。
解决步骤
1. 直接追加批注,避免替换列表
移除page.setAnnotations(annotations),改为直接向页面批注列表添加新对象:
page.getAnnotations().add(t);
2. 移除不必要的更新标记
删除page.getResources().getCOSObject().setNeedToBeUpdated(true),仅保留与批注相关对象的更新标记。
3. 补充批注锁定属性
在创建图章批注时添加:
t.setLocked(true);
4. 优化外观流资源处理
简化字体资源添加逻辑,确保资源关联正确(虽不影响显示,但能避免潜在的PDF结构异常)。
修改后的完整代码
import org.apache.pdfbox.pdmodel.PDAppearanceContentStream; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationRubberStamp; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; import org.apache.pdfbox.util.Matrix; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class PDFAnnotStampText { public static void main(String[] args) { PDDocument document = null; String filename = "20221217-VisibleSign-BeforeAnnot-02"; try { document = PDDocument.load(new File("testfiles/" + filename + ".pdf")); if (!document.isEncrypted()) { System.out.println("not encrypted"); OutputStream resultFile = new FileOutputStream("testfiles/" + filename + "_stamptxt.pdf"); PDPage page = document.getPage(0); addAnnotation("annotation name", document, page, 50, 500, "Annot powered"); document.saveIncremental(resultFile); document.close(); } } catch (IOException e) { throw new RuntimeException(e); } } private static void addAnnotation(String name, PDDocument doc, PDPage page, float x, float y, String text) throws IOException { PDAnnotationRubberStamp t = new PDAnnotationRubberStamp(); t.setAnnotationName(name); t.setPrinted(true); t.setReadOnly(true); t.setLocked(true); // 新增锁定属性 t.setContents(text); float realWidth = 100, realHeight = 15; PDRectangle rect = new PDRectangle(x, y, realWidth, realHeight); t.setRectangle(rect); PDAppearanceDictionary ap = new PDAppearanceDictionary(); ap.setNormalAppearance(createAppearanceStream(doc, t)); t.setAppearance(ap); // 直接追加批注,不替换整个列表 page.getAnnotations().add(t); // 仅保留必要的更新标记 ap.getCOSObject().setNeedToBeUpdated(true); t.getCOSObject().setNeedToBeUpdated(true); page.getCOSObject().setNeedToBeUpdated(true); doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true); doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true); } private static void modifyAppearanceStream(PDAppearanceStream aps, PDAnnotationRubberStamp ann) throws IOException { try (PDAppearanceContentStream apsContent = new PDAppearanceContentStream(aps)) { PDRectangle rect = ann.getRectangle(); // 设置外观流相对坐标系 aps.setBBox(new PDRectangle(0, 0, rect.getWidth(), rect.getHeight())); PDResources res = new PDResources(); PDFont font = PDType1Font.HELVETICA_BOLD; res.add(font); // 简化字体添加逻辑 aps.setResources(res); apsContent.beginText(); apsContent.setFont(font, 12); // 调整文本位置至批注区域内(可选,优化显示) apsContent.setTextMatrix(Matrix.getTranslateInstance(5, 10)); apsContent.showText(ann.getContents()); apsContent.endText(); } aps.getResources().getCOSObject().setNeedToBeUpdated(true); aps.getCOSObject().setNeedToBeUpdated(true); } private static PDAppearanceStream createAppearanceStream(final PDDocument document, PDAnnotationRubberStamp ann) throws IOException { PDAppearanceStream aps = new PDAppearanceStream(document); modifyAppearanceStream(aps, ann); return aps; } }
内容的提问来源于stack exchange,提问作者Pulse Mind
相关产品推荐
相关产品推荐

