如何通过PDFBox为签名部件添加打印属性控制印章打印可见性
解决Adobe Reader打印时隐藏PDF签名印章的问题
问题分析
你的代码尝试通过可选内容组(OCG)控制签名印章的打印状态,但存在几个关键问题导致无效:
- 创建的OCG对象未注册到文档的可选内容属性字典中,Adobe Reader无法识别该OCG的规则
- 签名的可视化图片未被关联到OCG,也没有实际绘制到页面
- OCG的
PrintState属性设置方式不符合PDF规范要求
修正后的代码实现
private InputStream createVisualSignatureTemplate(PDDocument srcDoc, PDRectangle rect, BufferedImage image, int pageNum, PDSignature signature) throws IOException { PDDocument doc = new PDDocument(); PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox()); doc.addPage(page); PDAcroForm acroForm = new PDAcroForm(doc); doc.getDocumentCatalog().setAcroForm(acroForm); // 1. 创建并注册可选内容组(OCG)用于控制打印状态 PDOCProperties ocProperties = doc.getDocumentCatalog().getOCProperties(); if (ocProperties == null) { ocProperties = new PDOCProperties(doc); doc.getDocumentCatalog().setOCProperties(ocProperties); } PDOptionalContentGroup ocg = new PDOptionalContentGroup(doc, "SignatureNoPrint"); // 设置OCG的打印使用规则:打印时隐藏 COSDictionary usage = new COSDictionary(); COSDictionary printDict = new COSDictionary(); printDict.setItem(COSName.getPDFName("PrintState"), COSName.OFF); usage.setItem(COSName.getPDFName("Print"), printDict); ocg.getCOSObject().setItem(COSName.getPDFName("Usage"), usage); // 将OCG添加到文档的OC属性中 ocProperties.getOCGs().add(ocg); // 2. 创建签名字段和Widget注释 PDSignatureField signatureField = new PDSignatureField(acroForm); PDAnnotationWidget widget = signatureField.getWidgets().get(0); // 将Widget注释关联到OCG,控制其打印可见性 widget.setOptionalContentGroup(ocg); signatureField.setValue(signature); acroForm.getFields().add(signatureField); acroForm.setSignaturesExist(true); acroForm.setAppendOnly(true); widget.setRectangle(rect); widget.setPage(page); page.getAnnotations().add(widget); // 3. 创建签名可视化内容并关联OCG PDFormXObject form = new PDFormXObject(doc); form.setBBox(rect); PDResources resources = new PDResources(); form.setResources(resources); // 将印章图片转为PDImageXObject ByteArrayOutputStream baosImage = new ByteArrayOutputStream(); ImageIO.write(image, "PNG", baosImage); PDImageXObject signatureImage = PDImageXObject.createFromByteArray(doc, baosImage.toByteArray(), "Signature"); // 在FormXObject中绘制图片 try (PDPageContentStream cs = new PDPageContentStream(doc, form)) { float scaleX = rect.getWidth() / signatureImage.getWidth(); float scaleY = rect.getHeight() / signatureImage.getHeight(); cs.drawImage(signatureImage, 0, 0, signatureImage.getWidth() * scaleX, signatureImage.getHeight() * scaleY); } // 将FormXObject设置为Widget的外观 PDAppearanceDictionary appearance = new PDAppearanceDictionary(); PDAppearanceStream appearanceStream = new PDAppearanceStream(form); appearance.setNormalAppearance(appearanceStream); widget.setAppearance(appearance); // 保存文档 ByteArrayOutputStream baos = new ByteArrayOutputStream(); doc.save(baos); doc.close(); return new ByteArrayInputStream(baos.toByteArray()); }
关键修正点说明
- 注册OCG:必须将创建的OCG添加到文档的
OCProperties字典中,否则阅读器无法识别该可选内容规则 - 关联Widget到OCG:通过
widget.setOptionalContentGroup(ocg)将注释和OCG绑定,确保打印时应用OCG的隐藏规则 - 正确设置PrintState:严格按照PDF规范构造
Usage下的Print字典,使用标准的COSName而非字符串键 - 绘制可视化内容:补充了图片绘制代码,并将其设置为签名Widget的外观,确保印章正确显示且受OCG控制
验证方法
- 生成修改后的PDF后,打开Adobe Reader,查看打印预览确认印章已隐藏
- 若需临时开启印章打印,可通过「视图」→「显示/隐藏」→「可选内容」调整对应OCG的状态
内容的提问来源于stack exchange,提问作者Serendipity
相关产品推荐
相关产品推荐

