You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Adobe Sign签名的PDF中提取签名图像?

从Adobe Sign签名PDF中提取签名图像的方法

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

签名示例图

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

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渲染成位图,但现有代码存在几个需要调整的问题。

代码修正要点

  1. 定位正确的XObject:当前代码获取的是Xi3,但实际签名对应的是Xf2,需确保目标对象正确。
  2. 避免错误缩放:不要随意设置缩放矩阵,Form XObject自身的矩阵已包含正确的位置和缩放信息,直接绘制即可。
  3. 处理坐标系统: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:00:07