Search code examples
javapdfpdfboxe-signature

How to Attach an Image to a PDF Form Field Without Invalidating an Existing Signature using PDFBox?


Usecase: I'm working on a feature that requires attaching an image to a PDF without invalidating a pre-existing digital signature. The PDF should have a form field designated for image attachment, which can be populated later. I want to implememnt this using PDFBox.

Implementation: Since PDFs lack a dedicated image form field, I’m utilizing a PDPushButton as a workaround, following the method outlined in these resources;

Additionally, the PDF includes a signature form field. The process involves signing the signature field first and subsequently attaching the image to the PDPushButton field. However, this sequence is causing the signature to become invalid.

Here is the code for attaching an image to the PDPushButton.

@SneakyThrows
public static void fillInitialField(String inputFilePath, String outputFilePath) {
    // Load input file
    PDDocument document = PDDocument.load(new File(inputFilePath));

    // Find and link the relevant signature field
    PDPushButton initial = PdfService.findInitial(document, "132323423180965");

    PDImageXObject pdImageXObject = PDImageXObject.createFromFile("initial.png", document);
    float width = 10 * pdImageXObject.getWidth();
    float height = 10 * pdImageXObject.getHeight();

    PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(document);
    pdAppearanceStream.setResources(new PDResources());
    try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream)) {
        pdPageContentStream.drawImage(pdImageXObject, 200, 300, width, height);
    }
    pdAppearanceStream.setBBox(new PDRectangle(width, height));

    List<PDAnnotationWidget> widgets = initial.getWidgets();
    for (PDAnnotationWidget pdAnnotationWidget : widgets) {

        PDAppearanceDictionary pdAppearanceDictionary = pdAnnotationWidget.getAppearance();
        if (pdAppearanceDictionary == null) {
            pdAppearanceDictionary = new PDAppearanceDictionary();
            pdAnnotationWidget.setAppearance(pdAppearanceDictionary);
        }

        pdAppearanceDictionary.setNormalAppearance(pdAppearanceStream);
    }
    initial.setReadOnly(true);

    // Save and close the document
    FileOutputStream fos = new FileOutputStream(outputFilePath);
    document.save(fos);
    document.close();
}

I’ve created a repository that replicates this issue, which can be found here: https://github.com/ContractSPAN/ImageFormFieldIssue

How can I implement an image attachment to a PDF form field in such a way that it doesn’t invalidate an existing signature? I am open to alternative approaches to achieve this functionality.


Solution

  • You do a regular save at the end of fillInitialField. A regular save will re-arrange and change the signed byte range. Thus, you instead will need to do a saveIncremental like you do at the end of sign.

    Beware, though, a saveIncremental in pdfbox 2 requires you to mark (setNeedToBeUpdated(true)) all changed objects including a sequence of objects leading to them respectively from the trailer. In case of signing, a lot of that marking already is done by PDFBox itself. But in your fillInitialField method you'll have to do all the marking yourself.