0

Saving document with attachment using saveIncremental() method saves document without attachment. Although it does work with save() method i.e attachment are embedded in the document

PDFBox Version - 2.0.26

Code:

 @Test
public static void testAttachment() throws IOException
{
    try (
            InputStream sourceStream = new FileInputStream("src/main/resources/sample_doc_usb_signed_2.pdf");
            final PDDocument document = PDDocument.load(sourceStream);
    )
    {


        List<String> att_list=List.of("src/main/resources/sample_doc.pdf",
                "src/main/resources/sample.docx",
                "src/main/resources/text.jpg"
        );

        final Map<String, PDComplexFileSpecification> embeddedFileMap = new HashMap<>();
        for (String attachPath : att_list) {

            Path path=Path.of(attachPath);
            String mimeType = Files.probeContentType(path);
            long size = Files.size(path);

            File file = new File(attachPath);

            String name = file.getName();

            FileInputStream  attachStream = new FileInputStream(file);

            final PDEmbeddedFile embeddedFile = new PDEmbeddedFile(document, attachStream);
            embeddedFile.setSubtype(mimeType);
            embeddedFile.setSize((int) size);
            embeddedFile.setCreationDate(Calendar.getInstance());
            embeddedFile.setModDate(Calendar.getInstance());
            //TODO - Encoding

            final PDComplexFileSpecification fileSpecification = new PDComplexFileSpecification();
            fileSpecification.setFile(name);
            fileSpecification.setFileDescription("This is attachment for field and supporting");
            fileSpecification.setEmbeddedFile(embeddedFile);


            embeddedFileMap.put(name, fileSpecification);
        }

        final PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode();
        efTree.setNames(embeddedFileMap);

        final PDDocumentNameDictionary names = new PDDocumentNameDictionary(document.getDocumentCatalog());
        names.setEmbeddedFiles(efTree);
        document.getDocumentCatalog().setNames(names);

        //This works
        document.save(new FileOutputStream("src/main/resources/sample_wi_attachment.pdf"));

        //Does not work  - File saved without attachments
//            document.saveIncremental(new FileOutputStream("src/main/resources/sample_wi_attachment.pdf"));
        }
    }

Note: Documents used in above test code are simple PDF

Adding attachments to Signed file (File is not certified) invalidates the signature

 private  void addAttachment() throws IOException {

    //File Signed - not Certified
    try(PDDocument document = PDDocument.load(new FileInputStream("src/main/resources/sample-signed_intermediate.pdf"))){
        PDDocumentCatalog catalog = document.getDocumentCatalog();
        PDDocumentNameDictionary names = catalog.getNames();
        PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles();
        Map<String, PDComplexFileSpecification> embeddedFileMap = efTree.getNames();


        Set<COSDictionary> objectsToWrite = new HashSet<>();

        embeddedFileMap= new HashMap<>();

        List<String> att_list=List.of("src/main/resources/sample_doc.pdf",
                "src/main/resources/AD23884.docx",
                "src/main/resources/text.jpg"

        );

        for (String attachPath : att_list) {

            Path path=Path.of(attachPath);
            String mimeType = Files.probeContentType(path);
            long size = Files.size(path);

            File file = new File(attachPath);

            String name = file.getName();

            FileInputStream  attachStream = new FileInputStream(file);


            final PDEmbeddedFile embeddedFile = new PDEmbeddedFile(document, attachStream);
            embeddedFile.setSubtype(mimeType);
            embeddedFile.setSize((int) size);
            embeddedFile.setCreationDate(Calendar.getInstance());
            embeddedFile.setModDate(Calendar.getInstance());
            //TODO - Encoding

            final PDComplexFileSpecification fileSpecification = new PDComplexFileSpecification();
            fileSpecification.setFile(name);
            fileSpecification.setFileDescription("This is attachment for field and supporting");
            fileSpecification.setEmbeddedFile(embeddedFile);

            embeddedFileMap.put(name, fileSpecification);
        }


        efTree.setNames(embeddedFileMap);

        names.setEmbeddedFiles(efTree);
        document.getDocumentCatalog().setNames(names);

        COSDictionary fieldDictionaryEf =efTree.getCOSObject();
        while (fieldDictionaryEf != null) {
            fieldDictionaryEf.setNeedToBeUpdated(true);
            fieldDictionaryEf = (COSDictionary) fieldDictionaryEf.getDictionaryObject(COSName.PARENT);
        }
        objectsToWrite.add(efTree.getCOSObject());

        COSDictionary fieldDictionaryNam =names.getCOSObject();
        while (fieldDictionaryNam != null) {
            fieldDictionaryNam.setNeedToBeUpdated(true);
            fieldDictionaryNam = (COSDictionary) fieldDictionaryNam.getDictionaryObject(COSName.PARENT);
        }
        objectsToWrite.add(names.getCOSObject());

        COSDictionary fieldDictionary =document.getDocumentCatalog().getCOSObject();
        while (fieldDictionary != null) {
            fieldDictionary.setNeedToBeUpdated(true);
            fieldDictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.PARENT);
        }
        objectsToWrite.add(document.getDocumentCatalog().getCOSObject());

        document.saveIncremental(new FileOutputStream("src/main/resources/attachment.pdf"),objectsToWrite);

    }
    catch (Exception e){
        throw e;
    }
}
Vas K
  • 93
  • 9
  • 2
    Try the other saveIncremental method, read the javadoc and pass the modified dictionaries and use the latest version. – Tilman Hausherr Aug 12 '22 at 09:12
  • 1
    Yes, **read and follow the hints in the JavaDocs** whichever `saveIncremental` overload you use. And using the other overload indeed is likely to be easier to use. – mkl Aug 12 '22 at 09:17
  • Worked with adding Catalog COSObject with setNeedToBeUpdated flag true to COSDictionary set and passing same in overloaded saveIncremental. But this breaks existing Signature , is there any way around to keep existing Signature valid? – Vas K Aug 16 '22 at 07:56
  • Maybe this is a certification signature which means you should not do certain changes? – Tilman Hausherr Aug 16 '22 at 09:44
  • PDF is not certified i.e. MDPPermission is not set to 1. I have added sample code which invalidates signature on attachment addition – Vas K Aug 16 '22 at 13:11
  • 2
    *"Adding attachments to Signed file (File is not certified) invalidates the signature"* - It invalidates the signature as Adobe treats normal signatures similar to certification signatures with form-fill-in and annotation-changes allowed. Addition of attachments is not allowed, though. See [this answer](https://stackoverflow.com/a/16711745/1729265) for the description of the permissions according to Adobe Acrobat around 9.x. The changes since then are small. – mkl Aug 16 '22 at 13:26
  • Thank you. Appreciate your help – Vas K Aug 16 '22 at 14:53

0 Answers0