如何从Adobe Sign签名的PDF中提取签名图像?
从Adobe Sign签名PDF中提取签名图像的方法
我正尝试从Adobe Sign签名的PDF中提取签名图像,但不清楚Adobe是如何添加该图像的。

我已经用Java的iText和PDFBox做过测试,平时用iText RUPS这类工具遍历PDF结构时,能找到可见签名图像并直接下载,但这次没找到,不过发现了疑似存储签名图像的XObject。

相关页面流内容
q q 0.07843 0.45098 0.90196 RG 0.07843 0.45098 0.90196 rg 1 w q BT 1 0 0 1 0 1.78 Tm /F1 6 Tf 0.07843 0.45098 0.90196 rg ({0009002000270027002a00010018002a002d0027001f00010497000200300022000104400443047600010441043f044104420001044004410477043f044300010008000e001506d404410498}) Tj 0 g ET Q 0 8.28 m 103.55 8.28 l S Q 0 0 0 RG 0 0 0 rg q 1 0 0 1 0 0 cm /Xf2 Do Q Q
Xf2引用的流内容
1 J 1 j 1.000 w 30.315 10.580 m 30.315 10.580 30.315 10.580 30.315 10.587 c S 1.000 w 30.315 10.587 m 30.315 10.594 30.315 10.608 30.315 10.627 c S 1.000 w 30.315 10.627 m 30.315 10.646 30.315 10.670 30.315 10.683 c S ...
问题
请问能否将其提取为图像?
我使用的PDFBox验证代码
try (PDDocument document = PDDocument.load(pdf)) { PDAnnotation annotation = document.getPage(0).getAnnotations().get(0); PDResources resources = annotation.getPage().getResources(); Iterator<COSName> names = resources.getXObjectNames().iterator(); PDXObject xi3Object = resources.getXObject(names.next()); PDStream xi3Stream = xi3Object.getStream(); // OR COSDictionary page = (COSDictionary) document.getDocument().getObjectsByType(COSName.ANNOT).get(0) .getDictionaryObject(COSName.P); COSDictionary resources = (COSDictionary) page.getItem(COSName.RESOURCES); COSDictionary xObject = (COSDictionary) resources.getItem(COSName.XOBJECT); COSObject xi3Object = (COSObject) xObject.getItem("Xi3"); COSStream xi3Stream = (COSStream) xi3Object.getObject(); PDFormXObject pdFormXObject = new PDFormXObject(xi3Stream); PDPage page = new PDPage(new PDRectangle(pdFormXObject.getBBox().getWidth(), pdFormXObject.getBBox().getHeight())); try (PDDocument tempDocument = new PDDocument()) { PDPageContentStream contents = new PDPageContentStream(tempDocument, page); AffineTransform affineTransform = new AffineTransform(); affineTransform.setToTranslation(0, 0); affineTransform.setToScale(0.75d, 0.75d); pdFormXObject.setMatrix(affineTransform); contents.drawForm(pdFormXObject); contents.restoreGraphicsState(); contents.close(); tempDocument.addPage(page); PDFRenderer renderer = new PDFRenderer(tempDocument); BufferedImage image = renderer.renderImageWithDPI(0, 600); try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ImageIO.write(image, "png", baos); return baos.toByteArray(); } } }
解决方案
你发现的Xf2是一个Form XObject,它并没有存储传统位图图像,而是用PDF路径绘制指令(如m移动起点、c绘制贝塞尔曲线、S描边)构建签名图形——这是Adobe Sign常用的签名存储方式:将签名转换为矢量路径,而非嵌入位图。
要提取这种签名为图像,你的核心思路是对的:将Form XObject渲染成位图,但现有代码存在几个需要调整的问题。
代码修正要点
- 定位正确的XObject:当前代码获取的是
Xi3,但实际签名对应的是Xf2,需确保目标对象正确。 - 避免错误缩放:不要随意设置缩放矩阵,Form XObject自身的矩阵已包含正确的位置和缩放信息,直接绘制即可。
- 处理坐标系统:PDF坐标原点在左下角,渲染时无需额外翻转,保持默认逻辑即可。
修正后的PDFBox代码
try (PDDocument document = PDDocument.load(new File("signed.pdf"))) { // 获取页面第一个签名注释 PDAnnotation signatureAnnot = document.getPage(0).getAnnotations().get(0); // 获取注释关联的资源(签名Form XObject通常存于此) PDResources annotResources = signatureAnnot.getResources(); if (annotResources == null) { // 若注释无资源, fallback到页面资源 annotResources = document.getPage(0).getResources(); } // 获取目标Xf2 Form XObject PDFormXObject signatureForm = (PDFormXObject) annotResources.getXObject(COSName.getPDFName("Xf2")); if (signatureForm == null) { throw new IllegalArgumentException("未找到Xf2 Form XObject"); } // 创建临时文档渲染签名 try (PDDocument tempDoc = new PDDocument()) { PDRectangle bbox = signatureForm.getBBox(); PDPage tempPage = new PDPage(bbox); tempDoc.addPage(tempPage); try (PDPageContentStream contentStream = new PDPageContentStream(tempDoc, tempPage)) { // 直接绘制Form XObject,使用其自带矩阵 contentStream.drawForm(signatureForm); } // 以600DPI渲染为位图 PDFRenderer renderer = new PDFRenderer(tempDoc); BufferedImage signatureImage = renderer.renderImageWithDPI(0, 600); // 保存为PNG文件 ImageIO.write(signatureImage, "PNG", new File("signature.png")); } } catch (IOException e) { e.printStackTrace(); }
关键说明
- 矢量转位图的清晰度由渲染DPI决定,600DPI足以保证签名细节完整。
- 若注释资源中找不到
Xf2,可遍历页面所有XObject,通过查看流中的路径指令定位目标对象。 - 这种方式提取的是渲染后的位图,无法还原原始矢量,但完全满足图像提取的需求。
内容的提问来源于stack exchange,提问作者krillov
相关产品推荐
相关产品推荐

