使用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); }
补充说明
- Acrobat的签名面板仅识别注册在AcroForm中的签名字段,单纯的签名注解(Widget)无法被纳入面板列表;
- 后端代码能识别签名是因为IText/OpenPDF的API会遍历所有注解和AcroForm字段,而Acrobat有更严格的显示规则;
PdfFormField.createSignature已默认设置字段类型为Sig,无需额外配置。
内容的提问来源于stack exchange,提问作者gWombat
相关产品推荐
相关产品推荐

