1

I have some questions wrt pdfbox.

  1. I want to sucessively sign a document that is subject to changes, e.g. a) originalPdf (signed by X), b) an image is added to the pdf (then signed by person Y), etc., such that the signatures are all valid. How can I reach that with pdfbox, if possible? I tried several things (e.g. with saveIncremental) but they did not give the intended result.
  2. Or do I need to define empty fields beforehand and allow them to be updated with images such that signatures are valid? Is this performed with Annotations, if yes, how could we realize that?

Any helpful tips or code references in the public domain would be very helpful. Thanks.

.... 
contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true); 
PDImageXObject pdImage = PDImageXObject.createFromFile("C:/logo.png", doc); 
....  
page.getResources().getCOSObject().getCOSDictionary(COSName.XOBJECT).setNeedToBeUpdated(true); 
page.getCOSObject().setNeedToBeUpdated(true); 
page.getResources().getCOSObject().setNeedToBeUpdated(true);    
doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true); 
doc.saveIncremental(fos);
mkl
  • 90,588
  • 15
  • 125
  • 265
JDee
  • 13
  • 2
  • Did you try the examples and what didn't work? – Tilman Hausherr Dec 27 '20 at 20:41
  • Please first be aware that only a small set of changes is allowed to signed documents, see [here](https://stackoverflow.com/a/16711745/1729265) for some details. So indeed, you cannot change page contents after signing, and whether or not you can fill form fields or even use arbitrary annotations depends on the signature type of the original signature. – mkl Dec 27 '20 at 21:48
  • I tried the following – JDee Dec 28 '20 at 16:18
  • `.... contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true); PDImageXObject pdImage = PDImageXObject.createFromFile("C:/logo.png", doc); .... page.getResources().getCOSObject().getCOSDictionary(COSName.XOBJECT).setNeedToBeUpdated(true); page.getCOSObject().setNeedToBeUpdated(true); page.getResources().getCOSObject().setNeedToBeUpdated(true); doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true); doc.saveIncremental(fos);` – JDee Dec 28 '20 at 16:28
  • The signature was then invalid. Based on your comment @mkl, how can I insert images e.g. as an AcroForm or Annotation such that inserting images does not invalidate the signatures? Does there exist any examples? Thx. – JDee Dec 28 '20 at 16:34
  • Is the existing signature a certification signature or an approval signature? And in the former case, which MDP value is used? In general a new arbitrary annotation is a good idea, merely in case of a certification signature with "only form fill-ins allowed" you should use an existing button field and change its appearance. – mkl Dec 28 '20 at 20:41

1 Answers1

1

Only a small set of changes is allowed to signed documents, see here for some details. So indeed, you cannot change page contents after signing, and whether or not you can fill form fields or even use arbitrary annotations depends on the signature type of the original signature.

If the signature(s) do allow adding annotations, though, i.e. if there only are approval (non-certification) signatures or at most a certification signature with annotations, form fill-in, and digital signatures allowed, you can add images in annotations.

With PDFBox you can add an annotation showing an image in an incremental update like this:

PDDocument document = ...;
PDImageXObject image = ...;
OutputStream result = ...;

PDAppearanceStream appearanceStream = new PDAppearanceStream(document);
appearanceStream.setBBox(new PDRectangle(1, 1));
appearanceStream.setResources(new PDResources());
try (   PDPageContentStream contentStream = new PDPageContentStream(document, appearanceStream) ) {
    contentStream.drawImage(image, new Matrix());
}

PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.setNormalAppearance(appearanceStream);

PDAnnotationRubberStamp stamp = new PDAnnotationRubberStamp();
stamp.setLocked(true);
stamp.setLockedContents(true);
stamp.setPrinted(true);
stamp.setReadOnly(true);
stamp.setAppearance(appearance);
stamp.setIntent("StampImage");
stamp.setRectangle(new PDRectangle(200, 500, 100, 100));

PDPage page = document.getPage(0);
page.getAnnotations().add(stamp);

Set<COSDictionary> objectsToWrite = new HashSet<>();
objectsToWrite.add(page.getCOSObject());

document.saveIncremental(result, objectsToWrite);

(AddToSignedFile test testAddImageAnnotation)

I here used a feature available in the development head towards 3.0.0, the saveIncremental overload with a second argument that accepts a collection of dictionaries to save. If you are working with an earlier version, you may instead have to mark a path of objects from the document catalog to the page object for inclusion in the incremental update using setNeedToBeUpdated.


If the signature(s) don't allow adding annotations, though, but do allow form editing, i.e. if there is a certification signature with form fill-in and digital signatures allowed, you can at least fill in form fields. This in particular may include setting the appearance of a pushbutton to an image of your choice as Adobe regularly mis-uses pushbuttons as pseudo image-fields. This of course means that you need to have a field prepared for each desired later addition.

If the signatures don't even allow that, i.e. if there is a certification signature with no changes allowed, or if there are no open form fields available for your additions, you're out of luck.

As an aside, since PDF 2.0 the certification level can also be made stricter by an entry in the signature locking dictionary of an approval signature. You may have to consider this in the cases above...

mkl
  • 90,588
  • 15
  • 125
  • 265
  • `saveIncremental(OutputStream output, Set objectsToWrite)` is available in the 2.0 branch since 2.0.22, as part of issue https://issues.apache.org/jira/browse/PDFBOX-45 which took 15 years to be resolved – Tilman Hausherr Jan 05 '21 at 10:06
  • Thanks a lot for the answers and code @mkl. That solves the problem. Prio your answer I tried it in different ways, but it did not work. But your code finally does the job. – JDee Jan 05 '21 at 12:55
  • @TilmanHausherr Ah, ok. I'm usually testing against 3.0.0-SNAPSHOT only, so I wasn't aware that that feature already is included in a 2.x. – mkl Jan 05 '21 at 16:32