0

I have a digital sign pdf with 2 form field having the same name. when i try fill up the field using PDFBOX and open it in Acrobat the field content is empty. It only will show the content when i click on the field.

This my code

    File inFile= new File("pdfFile/Untitled_sign.pdf");
    OutputStream outFile = new FileOutputStream("pdfFile/Untitled_sign_2.pdf");
    PDDocument pdfDocument = PDDocument.load(inFile);

    PDAcroForm acroForm = pdfDocument.getDocumentCatalog().getAcroForm();

    Set<COSDictionary> cosDis=  new HashSet<>();
    if (acroForm != null) {
        List<PDField> fields = acroForm.getFields();
        //acroForm.setNeedAppearances(true);
        for (PDField field : fields) {
            if(field instanceof PDTextField) {
                System.out.println(field.getFullyQualifiedName());
                if(field.getFullyQualifiedName().equals("Text1")) {
                    try {
                        field.setValue("100");
                        System.out.println("New value: " + field.getValueAsString());
                        
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    field.getCOSObject().setNeedToBeUpdated(true);
                    cosDis.add(field.getCOSObject());
                }
            }
        }
        acroForm.getCOSObject().setNeedToBeUpdated(true);
        cosDis.add(acroForm.getCOSObject());
    }
      pdfDocument.saveIncremental(outFile,cosDis);

here the pdf https://drive.google.com/file/d/1SeGiPhoEqtkuKGHT3FG063d9lbtFrnt0/view?usp=sharing

and here the result https://drive.google.com/file/d/1LHxDuHIztRv-r27pYTmRYIwUEkdaX_75/view?usp=sharing

[EDIT]

after Tilman recommended using field.getWidgets().get(0).getAppearance().getCOSObject().setNeedToBeUpdated(true); it able to display correctly. But when i do digital sign again, the content will go back to original content. For example, I fill in the pdf with "test" and digital sign the pdf. (original pdf https://drive.google.com/file/d/1SoVgWMfRVbVsWVOSRRFU7bjyJARHFmZV/view?usp=sharing)

(first digital sign pdf https://drive.google.com/file/d/1sbklXp8s1R9OoH3xwJPZ54o0TUV_bl4m/view?usp=sharing)

after digital sign i used pdfbox to change the content from "test" to "100". it able to display "100" correctly in acrobat. (pdf with "100" https://drive.google.com/file/d/13ViNcGJ7g0d6Kcxn8MsGQKF9DMFCD-1a/view?usp=sharing)

but if i digital sign again it will change back to "test" (second digital sign https://drive.google.com/file/d/1uiv4UddZHnMBNzNE7LlIg7Gb1bzUVLlM/view?usp=sharing) This the new code i added but is this the same. when it digital sign the content will chnage back to "test"

PDDocument pdfDocument = PDDocument.load(inFile);

    PDAcroForm acroForm = pdfDocument.getDocumentCatalog().getAcroForm();

    Set<COSDictionary> cosDis=  new HashSet<>();
    if (acroForm != null) {
        List<PDField> fields = acroForm.getFields();
        for (PDField field : fields) {
            if(field instanceof PDTextField) {
                if(field.getFullyQualifiedName().equals("Text1")) {
                    try {
                        field.setValue("200");
                        System.out.println("New value: " + field.getValueAsString());
                        
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    field.getCOSObject().setNeedToBeUpdated(true);
                    for(PDAnnotationWidget wed: field.getWidgets()) {
                        wed.getAppearance().getCOSObject().setNeedToBeUpdated(true);
                        wed.getCOSObject().setNeedToBeUpdated(true);
                    }
                    COSDictionary fieldDictionary = field.getCOSObject();
                    COSDictionary dictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.AP);
                    if(dictionary!=null) {
                    dictionary.setNeedToBeUpdated(true);
                    COSStream stream = (COSStream) dictionary.getDictionaryObject(COSName.N);
                    stream.setNeedToBeUpdated(true);
                    }
                    while (fieldDictionary != null)
                    {
                        fieldDictionary.setNeedToBeUpdated(true);
                        fieldDictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.PARENT);
                    }
                }
            }
        }
        acroForm.setSignaturesExist(true);
        acroForm.setAppendOnly(true);
        acroForm.getCOSObject().setDirect(true);
        acroForm.getCOSObject().setNeedToBeUpdated(true);
        //acroForm.setNeedAppearances(true);
        COSObject pdfFields = acroForm.getCOSObject().getCOSObject(COSName.FIELDS);
        if (pdfFields != null) {
            pdfFields.setNeedToBeUpdated(true);
        }
        pdfDocument.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
        cosDis.add(pdfDocument.getDocumentCatalog().getCOSObject());
        cosDis.add(acroForm.getCOSObject());
    }
      pdfDocument.saveIncremental(outFile,cosDis);
cryingCat
  • 33
  • 3
  • i have try to add acroForm.setNeedAppearances(true); but this will cause the signature to become invalid – cryingCat Jan 12 '23 at 08:13
  • Try adding the apparance stuff as well with `field.getWidgets().get(0).getAppearance().getCOSObject()`, the question is somewhat related to https://stackoverflow.com/questions/74997537/fill-out-field-for-pdf-revision-when-signing-with-pdfbox/75003497 although that question has a different usage of `saveIncremental`. – Tilman Hausherr Jan 15 '23 at 16:26
  • thank you. with field.getWidgets().get(0).getAppearance().getCOSObject().setNeedToBeUpdated(true); i able to fix it. but i think this is not perfect because when i digital sign it agian it will change back to original content. for example, if fill in pdf form with "test" and digital sign. after that, i used pdfbox to change the content from "test" to "100". it able to display correctly. but when i digital sign again, the content will go back to "test" not "100". – cryingCat Jan 16 '23 at 03:27
  • You have TWO widgets. So you should include both and their appearance in the update list. (When there is only one widget, the field usually is the widget, i.e. they share a dictionary) – Tilman Hausherr Jan 17 '23 at 19:27
  • yes i updated both my widgets it still the same. for(PDAnnotationWidget wed: field.getWidgets()) { wed.getAppearance().getCOSObject().setNeedToBeUpdated(true); wed.getCOSObject().setNeedToBeUpdated(true); } – cryingCat Jan 18 '23 at 02:27
  • You need to add them to `cosDis` list, that is what counts when using this `saveIncremental` method, not `setNeedToBeUpdated()` IIRC. Also use `PDFDebugger` to check whether the appearances are there after running the code. (In the menu, choose "View", "Show internal structure" to look at the acroform stuff) – Tilman Hausherr Jan 18 '23 at 09:34

1 Answers1

0

Thank you Tilman Hausherr. the solution i found is by setNeedToBeUpdated() in the COSName.AP and COSName.N inside the COSName.KIDS.

PDF structure

here my final code.

    PDDocument pdfDocument = PDDocument.load(inFile);
    PDAcroForm acroForm = pdfDocument.getDocumentCatalog().getAcroForm();
    Set<COSDictionary> cosDis=  new HashSet<>();
    if (acroForm != null) {
        List<PDField> fields = acroForm.getFields();
        for (PDField field : fields) {
            if(field instanceof PDTextField) {
                if(field.getFullyQualifiedName().equals("Text1")) {
                    try {
                        field.setValue("200");
                        System.out.println("New value: " + field.getValueAsString());
                        
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    field.getCOSObject().setNeedToBeUpdated(true);
                    cosDis.add(field.getCOSObject());
                    for(PDAnnotationWidget wed: field.getWidgets()) {
                        wed.getAppearance().getCOSObject().setNeedToBeUpdated(true);
                        wed.getCOSObject().setNeedToBeUpdated(true);
                         cosDis.add(wed.getAppearance().getCOSObject());
                         cosDis.add(wed.getCOSObject());
                    }
                    COSDictionary fieldDictionary = field.getCOSObject();
                    COSDictionary dictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.AP);
                    if(dictionary!=null) {
                       dictionary.setNeedToBeUpdated(true);
                       cosDis.add(dictionary);
                       COSStream stream = (COSStream) dictionary.getDictionaryObject(COSName.N);
                       stream.setNeedToBeUpdated(true);
                       cosDis.add(stream);
                    }else {
                        COSArray dictionary2 = (COSArray) fieldDictionary.getDictionaryObject(COSName.KIDS);
                        for (int i = 0; i < dictionary2.size(); i++)
                        {
                            COSDictionary kidAp=(COSDictionary) ((COSDictionary)dictionary2.getObject(i)).getDictionaryObject(COSName.AP);
                            if(kidAp!=null) {
                                kidAp.setNeedToBeUpdated(true);
                                cosDis.add(kidAp);
                                COSStream kidApstream = (COSStream) kidAp.getDictionaryObject(COSName.N);
                                kidApstream.setNeedToBeUpdated(true);
                                cosDis.add(kidApstream);
                            }
                        }
                    }
                    while (fieldDictionary != null)
                    {
                        fieldDictionary.setNeedToBeUpdated(true);
                        cosDis.add(fieldDictionary);
                        fieldDictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.PARENT);
                    }
                }
            }
        }
        acroForm.setSignaturesExist(true);
        acroForm.setAppendOnly(true);
        acroForm.getCOSObject().setDirect(true);
        acroForm.getCOSObject().setNeedToBeUpdated(true);
        
        //acroForm.setNeedAppearances(true);
        COSObject pdfFields = acroForm.getCOSObject().getCOSObject(COSName.FIELDS);
        if (pdfFields != null) {
            pdfFields.setNeedToBeUpdated(true);
        }
        pdfDocument.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
        cosDis.add(pdfDocument.getDocumentCatalog().getCOSObject());
        cosDis.add(acroForm.getCOSObject());
    }
      pdfDocument.saveIncremental(outFile,cosDis);

Hope this help. ;)

cryingCat
  • 33
  • 3