I want to ask a question. I added digital paging seal to a multi-page PDF, each page has the same seal, add the digital signature once on the first page, and then the other pages only need to quote the appearance of the first seal. But using adobe Acrobat DC to open, there will be an extra "123" signature in the generated document. What causes it?
I wrote the following code based on this answer and it helped me a lot.
addAp(doc, doc.getPage(0), rect, signature, xz[0]);
for (int i = 0; i < doc.getNumberOfPages() - 1; i++) {
addAnnots(doc.getPage(i));
}
addAp(doc, doc.getPage(1), lerect, signature, xz[1]);
for (int i = 1; i < doc.getNumberOfPages(); i++) {
addAnnots(doc.getPage(i));
}
void addAp(PDDocument pdDocument, PDPage pdPage, PDRectangle rectangle, PDSignature signature, BufferedImage signatureImage) throws IOException {
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
List<PDField> acroFormFields = acroForm.getFields();
PDSignatureField signatureField = new PDSignatureField(acroForm);
signatureField.setValue(signature);
PDAnnotationWidget widget = signatureField.getWidgets().get(0);
acroFormFields.clear();
acroFormFields.add(signatureField);
widget.setRectangle(rectangle);
widget.setPage(pdPage);
// from PDVisualSigBuilder.createHolderForm()
PDStream stream = new PDStream(pdDocument);
PDFormXObject form = new PDFormXObject(stream);
PDResources res = new PDResources();
form.setResources(res);
form.setFormType(1);
PDRectangle bbox = new PDRectangle(rectangle.getWidth(), rectangle.getHeight());
form.setBBox(bbox);
// from PDVisualSigBuilder.createAppearanceDictionary()
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.getCOSObject().setDirect(true);
PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
appearance.setNormalAppearance(appearanceStream);
widget.setAppearance(appearance);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ImageIO.write(signatureImage, "png", bao);
bao.flush();
byte[] imageByte = bao.toByteArray();
bao.close();
PDImageXObject pdImage = PDImageXObject.createFromByteArray(pdDocument, imageByte, null);
try (PDPageContentStream cs = new PDPageContentStream(pdDocument, appearanceStream)) {
PDExtendedGraphicsState r0 = new PDExtendedGraphicsState();
r0.setBlendMode(BlendMode.DARKEN);
cs.setGraphicsStateParameters(r0);
cs.addComment("This is a comment");
cs.drawImage(pdImage, 0, 0, rectangle.getWidth(), rectangle.getHeight());
}
setPdAnnotationWidget(widget);
}
void addAnnots(PDPage pdPage) throws IOException {
pdPage.getAnnotations().add(getPdAnnotationWidget());
COSDictionary pageTreeObject = pdPage.getCOSObject();
while (pageTreeObject != null) {
pageTreeObject.setNeedToBeUpdated(true);
pageTreeObject = (COSDictionary) pageTreeObject.getDictionaryObject(COSName.PARENT);
}
}
PDFBOX version is 2.0.20.
After modification:
ArrayList<PDAnnotationWidget> listWidget = addAp1(doc, signature);
addAp2(doc, doc.getPage(0), rect, xz[0], listWidget.get(0));
for (int i = 0; i < doc.getNumberOfPages() - 1; i++) {
addAnnots(doc.getPage(i));
}
addAp2(doc, doc.getPage(1), lerect, xz[1], listWidget.get(1));
for (int i = 1; i < doc.getNumberOfPages(); i++) {
addAnnots(doc.getPage(i));
}
ArrayList<PDAnnotationWidget> addAp1(PDDocument pdDocument, PDSignature signature) throws IOException {
ArrayList<PDAnnotationWidget> widgetList = new ArrayList<>();
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
List<PDField> acroFormFields = acroForm.getFields();
PDAnnotationWidget widget1 = new PDAnnotationWidget();
PDAnnotationWidget widget2 = new PDAnnotationWidget();
widgetList.add(widget1);
widgetList.add(widget2);
PDSignatureField signatureField = new PDSignatureField(acroForm);
signatureField.setValue(signature);
signatureField.setWidgets(widgetList);
acroFormFields.clear();
acroFormFields.add(signatureField);
return widgetList;
}
void addAp2(PDDocument pdDocument, PDPage pdPage, PDRectangle rectangle, BufferedImage signatureImage, PDAnnotationWidget widget) throws IOException {
widget.setPage(pdPage);
widget.setRectangle(rectangle);
....
}