我想要一个pdf由多个用户顺序签名。他们还可以在对文档进行数字签名时添加文本或姓名首字母的图像。我正在使用 pdfbox 版本 2.0.25.
我在问题的编辑 3 中使用了 here 中的代码来添加文本,同时还签署了文档。 下面是将签名字段添加到 pdf 的代码。
try (FileOutputStream fos = new FileOutputStream(signedFile)) {
PDSignature signature = new PDSignature();
PDDocument doc = PDDocument.load(inputFile);
for (DocumentSigningObject signingObject : documentSigningObjectList) {
SignatureOptions signatureOptions = new SignatureOptions();
SignerOptionEntity signerOption = signingObject.getSignerOptionEntity();
PDRectangle rect = createSignatureRectangle(doc, signingObject.getPlaceholderEntity());
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);
signatureOptions.setPage(signingObject.getSignatureComponents().getPage() - 1);
signatureOptions
.setVisualSignature(createVisualSignatureTemplate(doc, rect,
signerOption, signingObject.getSignatureComponents().getPage(),
signingObject.getSignatureComponents().getText()));
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName(name);
if (Objects.nonNull(signerOption) && doLocationExists(signerOption)) {
signature.setLocation(getFormattedLocationValue(signerOption));
}
signature.setSignDate(Calendar.getInstance());
signature.setReason(Objects.nonNull(signerOption)
&& StringUtils.isNotBlank(signerOption.getSignatureNote())
? signerOption.getSignatureNote() : "");
doc.addSignature(signature, signatureOptions);
ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(IOUtils.toByteArray(externalSigning.getContent()));
base64Hash = Base64.getEncoder().encodeToString(hashBytes);
externalSigning.setSignature(new byte[0]);
offset = signature.getByteRange()[1] + 1;
IOUtils.closeQuietly(signatureOptions);
}
方法 createSignatureVisualTemplate()
try (PDDocument doc = new PDDocument()) {
int pageNum = pageNo - 1;
PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
doc.addPage(page);
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
acroForm = new PDAcroForm(doc);
}
doc.getDocumentCatalog().setAcroForm(acroForm);
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
PDSignatureField signatureField = new PDSignatureField(acroForm);
List<PDField> acroFormFields = acroForm.getFields();
acroFormFields.add(signatureField);
PDAnnotationWidget widget = signatureField.getWidgets().get(0);
widget.setRectangle(rect);
PDStream stream = new PDStream(doc);
PDFormXObject form = new PDFormXObject(stream);
PDResources res = new PDResources();
form.setResources(res);
form.setFormType(1);
PDRectangle pdRectangle = new PDRectangle(rect.getWidth(), rect.getHeight());
Matrix initialScale = null;
switch (srcDoc.getPage(pageNum).getRotation()) {
case 90:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
initialScale = Matrix.getScaleInstance(pdRectangle.getWidth() / pdRectangle.getHeight(),
pdRectangle.getHeight() / pdRectangle.getWidth());
break;
case 180:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
break;
case 270:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
initialScale = Matrix.getScaleInstance(pdRectangle.getWidth() / pdRectangle.getHeight(),
pdRectangle.getHeight() / pdRectangle.getWidth());
break;
case 0:
default:
break;
}
form.setBBox(pdRectangle);
// from PDVisualSigBuilder.createAppearanceDictionary()
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.getCOSObject().setDirect(true);
PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
appearance.setNormalAppearance(appearanceStream);
widget.setAppearance(appearance);
try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream)) {
if (initialScale != null) {
cs.transform(initialScale);
}
cs.saveGraphicsState();
//Use the signature image as received, without any modification
if (Objects.nonNull(signatureOption) && Objects.nonNull(signatureOption.getSignatureImage())) {
PDImageXObject img = PDImageXObject.createFromByteArray(doc,
getBase64ImageByteData(signatureOption), null);
cs.drawImage(img, 0, 0, rect.getWidth(), rect.getHeight());
}
cs.restoreGraphicsState();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
return new ByteArrayInputStream(baos.toByteArray());
第一个用户签署文档后,在 AdobeAcrobatReader 中打开 pdf 时会显示以下消息。
现在第二个用户签署了已经签署的 pdf 并添加了他的姓名首字母。现在阅读器上显示的消息是:
文本是在签名字段添加到文档之前添加的,但它说文档是在签名后更新的。虽然两个签名仍然有效,但有人可以解释消息和方式,以便仅显示签名的有效性。