5

I have read here about how saveIncremental is working, and my final result needs to be something like here , in the way that I have managed to create visible signature with multiple visualization based on the signature field itself (not like in the response, but the response helped me so much). To elaborate the title, my base task for now is to create an empty signature field on an already signed document without braking the existing signature. However, the example here is not working with saveIncremental. I have added the following snippet (adapted) to the end of the main function, but without result:

    acroForm.setSignaturesExist(true);
    acroForm.setAppendOnly(true);
    acroForm.getCOSObject().setDirect(true);
// ...


COSDictionary pageTreeObject = pdPage.getCOSObject(); 
while (pageTreeObject != null) {
    pageTreeObject.setNeedToBeUpdated(true);
    pageTreeObject = (COSDictionary) pageTreeObject.getDictionaryObject(COSName.PARENT);
}

The resulting file does not contain any signature field. I have tried to update COSObject.needToBeUpdated(true) to acroform, signatureField, pddocument, page, widget, without result. The signature field only appears when I normally Save.

EDIT: I have managed to add an empty signature field (code edited on the COSObject.needToBeUpdated chain), but it breaks the existing signature.

enter image description here

What do I miss? Thanks!

My actual code:

public class CreateEmptySignatureForm {

public static void main(String[] args) throws IOException
{
    InputStream resource = new FileInputStream("test-semnat.pdf");
    PDDocument document = PDDocument.load(resource);
            
    PDPage page = document.getPage(0);

    // Add a new AcroForm and add that to the document
    PDAcroForm acroForm = new PDAcroForm(document);
    document.getDocumentCatalog().setAcroForm(acroForm);

    acroForm.setSignaturesExist(true);
    acroForm.setAppendOnly(true);
    acroForm.getCOSObject().setDirect(true);

    // Create empty signature field, it will get the name "Signature1"
    PDSignatureField signatureField = new PDSignatureField(acroForm);
    PDAnnotationWidget widget = signatureField.getWidgets().get(0);
    PDRectangle rect = new PDRectangle(50, 250, 200, 50);
    widget.setRectangle(rect);
    widget.getCOSObject().setNeedToBeUpdated(true);
    widget.setPage(page);
    page.getAnnotations().add(widget);
    page.getCOSObject().setNeedToBeUpdated(true);
    acroForm.getFields().add(signatureField);

    // general updates
    document.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);

    OutputStream os = new FileOutputStream("fooo.pdf");
    document.saveIncremental(os);
    System.out.println("done");
}
}

test-semnat.pdf

Botea Florin
  • 543
  • 6
  • 24
  • 1
    I'd prefer that you put the code that contains the "setNeedToBeUpdated(true)" because these are important. There needs to be a "chain" of updated objects. Please attach also the start file. (If it still doesn't work, read PDFBOX-45 and build that branch) – Tilman Hausherr Jun 27 '20 at 08:49
  • I will update my code when I will arive at home tonight. Can you please point me to PDFBOX-45 ? Are you reffering on this - https://issues.apache.org/jira/plugins/servlet/mobile#issue/PDFBOX-45 ? – Botea Florin Jun 27 '20 at 10:39
  • Yes, it is that one. – Tilman Hausherr Jun 27 '20 at 11:31
  • Hi @TilmanHausherr . I read PDFBOX-45. Is a great feature and is saving a lot of code. As I understand, saveIncremental is auto-detecting changes made between document versions. I hope you success in any plan you have. Now, back to my code, I have managed to add that empty signature field, but it brakes the existing signature... Is there a way to solve this? Many thanks! – Botea Florin Jun 27 '20 at 19:26
  • I see the answer was accepted, so I'll assume that comment is obsolete. – Tilman Hausherr Jun 28 '20 at 09:30

1 Answers1

6

The remaining problem after your question update is that you replace the existing AcroForm definition:

// Add a new AcroForm and add that to the document
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);

That changes too much, more than allowed and more than you actually want.

What you want is to retrieve the existing AcroForm definition and only mark it and its Fields entry for saving:

PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
acroForm.getCOSObject().setNeedToBeUpdated(true);
COSObject fields = acroForm.getCOSObject().getCOSObject(COSName.FIELDS);
if (fields != null)
    fields.setNeedToBeUpdated(true);

With that code I get for your example document:

Signature panel

(Of course, for documents without an existing AcroForm definition you will probably want to add a new one, so

PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
    acroForm = new PDAcroForm(document);
    document.getDocumentCatalog().setAcroForm(acroForm);
}
acroForm.getCOSObject().setNeedToBeUpdated(true);
COSObject fields = acroForm.getCOSObject().getCOSObject(COSName.FIELDS);
if (fields != null)
    fields.setNeedToBeUpdated(true);

is probably the complete variant.)

mkl
  • 90,588
  • 15
  • 125
  • 265
  • 1
    Thank you for taking the time to help me! Thank you for your help and sorry for my bad question (it was just a matter of extra tries by my side, but I was just discouraged by COS update chain when I have asked the question). You have my gratitude (@mki & @TilmanHausherr) – Botea Florin Jun 28 '20 at 07:26