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

使用Flying Saucer生成PDF后添加签名域不显示于Acrobat签名面板问题

问题描述

我是Java操作PDF的新手,想实现基于HTML输入、通过Flying Saucer生成PDF后添加签名域的流程:先通过HTML生成PDF内容,再添加签名域供用户签名。

我的代码如下:

try (var os = Files.newOutputStream(destinationFile)) {
    var renderer = new ITextRenderer();
    renderer.setDocumentFromString(generateHtml(report));
    renderer.layout();
    renderer.createPDF(os, false);
    
    var field = PdfFormField.createSignature(renderer.getWriter());
    field.setWidget(new Rectangle(50, 100, 400, 200), PdfAnnotation.HIGHLIGHT_OUTLINE);
    field.setFieldName("Signature1");
    field.setFieldFlags(PdfAnnotation.FLAGS_PRINT);
    renderer.getWriter().addAnnotation(field);
    renderer.finishPDF();
} catch (IOException ex) {
    throw new RuntimeException(ex);
}

目前在Acrobat中可以完成签名,但签名并未显示在Acrobat的签名面板中:
签名面板未显示签名

我是否遗漏了什么?


编辑补充:
后续通过后端代码检查签名时,发现签名已被正确填充:

public void verifySignatures(MultipartFile file){
    try (var fis = file.getInputStream()) {
        try(var resultIS = new ByteArrayInputStream(fis.readAllBytes())) {
            PdfReader resultReader = new PdfReader(resultIS);
            extractVerifiedCryptoSignature(resultReader);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

private static void extractVerifiedCryptoSignature(PdfReader reader) {
    log.info("签名提取中...");
    PdfPKCS7.loadCacertsKeyStore();
    try {
        var fields = reader.getAcroFields();
        var signatures = fields.getSignedFieldNames();
        for (String signature : signatures) {
            log.info("");
            printSignatureDetails(fields, signature);

            var pk = fields.verifySignature(signature);
            var certificate = pk.getSigningCertificate();
            var subjectFields = PdfPKCS7.getSubjectFields(certificate);
            log.info("证书主体字段: ");
            subjectFields.getAllFields().forEach((key, value) -> log.info("\t键: {}, 值: {}", key, String.join(", ", value)));
            log.info("证书验证结果: " + pk.verify());

            var sdf = new SimpleDateFormat("yyyy-MM-dd");
            log.info("签名日期: " + sdf.format(pk.getSignDate().getTime()));
            log.info("时间戳验证结果: " + pk.verifyTimestampImprint());
        }
    } catch (SignatureException | NoSuchAlgorithmException e) {
        log.error(e.getMessage(), e);
    }
}

private static void printSignatureDetails(AcroFields fields, String signature) {
    log.info("签名字段名: " + signature);
    log.info("签名覆盖整个文档: " + fields.signatureCoversWholeDocument(signature));
    log.info("修订版本: " + fields.getRevision(signature));
}

得到的日志如下:

2025-05-07T13:17:03.336+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 签名提取中...
2025-05-07T13:17:03.340+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 
2025-05-07T13:17:03.340+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 签名字段名: Signature1
2025-05-07T13:17:03.340+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 签名覆盖整个文档: true
2025-05-07T13:17:03.340+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 修订版本: 1
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 证书主体字段: 
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     :  键: C, 值: FR
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     :  键: E, 值: ***@gmail.com
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     :  键: OU, 值: 
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     :  键: CN, 值: ***
2025-05-07T13:17:03.460+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     :  键: O, 值: 
2025-05-07T13:17:03.461+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 证书验证结果: true
2025-05-07T13:17:03.461+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 签名日期: 2025-05-07
2025-05-07T13:17:03.461+02:00  INFO 43369 --- [fra-lbd-lifts] [nio-8080-exec-8] o.j.f.l.s.s.l.signpdf.SignPdfService     : 时间戳验证结果: false

问题解决

核心原因

签名能被后端代码识别但不显示在Acrobat签名面板,是因为签名域未被正确注册到PDF的AcroForm表单结构中。Flying Saucer生成PDF时默认不会初始化AcroForm,仅通过addAnnotation添加签名注解,Acrobat无法将其识别为正式的签名字段并展示在面板中。

修复代码

修改代码,确保创建并初始化AcroForm,同时将签名字段注册到AcroForm中:

try (var os = Files.newOutputStream(destinationFile)) {
    var renderer = new ITextRenderer();
    renderer.setDocumentFromString(generateHtml(report));
    renderer.layout();
    renderer.createPDF(os, false);
    
    var writer = renderer.getWriter();
    // 初始化AcroForm(如果不存在)
    var acroForm = writer.getAcroForm();
    if (acroForm == null) {
        acroForm = new AcroForm(writer);
        writer.setAcroForm(acroForm);
    }
    
    var field = PdfFormField.createSignature(writer);
    field.setWidget(new Rectangle(50, 100, 400, 200), PdfAnnotation.HIGHLIGHT_OUTLINE);
    field.setFieldName("Signature1");
    field.setFieldFlags(PdfAnnotation.FLAGS_PRINT);
    // 将字段添加到AcroForm,注解会自动随字段关联,无需单独调用addAnnotation
    acroForm.addField(field);
    
    renderer.finishPDF();
} catch (IOException ex) {
    throw new RuntimeException(ex);
}

补充说明

  1. Acrobat的签名面板仅识别注册在AcroForm中的签名字段,单纯的签名注解(Widget)无法被纳入面板列表;
  2. 后端代码能识别签名是因为IText/OpenPDF的API会遍历所有注解和AcroForm字段,而Acrobat有更严格的显示规则;
  3. PdfFormField.createSignature已默认设置字段类型为Sig,无需额外配置。

内容的提问来源于stack exchange,提问作者gWombat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 05:19:59