Search code examples
javaspring-bootpdfpdfbox

Merging two pdf documents with PDFMergerUtility throws: IOException("Page tree root must be a dictionary")


I have a spring boot application and I am trying to merge two pdf files. The one I am getting as a byte array from another service and the one I have it locally in my resources file: /static/documents/my-file.pdf. This is the code of how I am getting byte array from my file from resources:

public static byte[] getMyPdfContentForLocale(final Locale locale) {
    byte[] result = new byte[0];
    try {
        final File myFile = new ClassPathResource(TEMPLATES.get(locale)).getFile();
        final Path filePath = Paths.get(myFile.getPath());
        result = Files.readAllBytes(filePath);
    } catch (IOException e) {
        LOGGER.error(format("Failed to get document for local %s", locale), e);
    }
    return result;
}

I am getting the file and getting the byte array. Later I am trying to merge this two files with the following code:

PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
pdfMergerUtility.addSource(new ByteArrayInputStream(offerDocument));
pdfMergerUtility.addSource(new ByteArrayInputStream(merkblattDocument));
ByteArrayOutputStream os = new ByteArrayOutputStream();
pdfMergerUtility.setDestinationStream(os);
pdfMergerUtility.mergeDocuments(null);
os.toByteArray();

But unfortunately it throws an error:

throw new IOException("Page tree root must be a dictionary");

I have checked and it makes this validation before it throws it:

if (!(root.getDictionaryObject(COSName.PAGES) instanceof COSDictionary))
    {
        throw new IOException("Page tree root must be a dictionary");
    }

And I really have no idea what does this mean and how to fix it. The strangest thing is that I have created totally new project and tried the same code to merge two documents (the same documents) and it works!

Additionally what I have tried is:

  1. Change the spring boot version if it is ok
  2. Set the mergeDocuments method like this: pdfMergerUtility.mergeDocuments(setupMainMemoryOnly())
  3. Set the mergeDocuments method like this: pdfMergerUtility.mergeDocuments(setupTempFileOnly())
  4. Get the bytes with a different method not using the Files from java.nio:
  5. And also executed this in a different thread
  6. Merging files only locally stored (in resources)
  7. Merging the file that I am getting from another service - this works btw and that is why I am sure he is ok

Can anyone help with this?


Solution

  • The issue as Tilman Hausherr said is in that resource filtering that you can find in your pom file. If you have a case where you are not allowed to modify this then this approach will help you:

    final String path = new 
    ClassPathResource(TEMPLATES.get(locale)).getFile().getAbsolutePath();
    final File file = new File(path);
    final Path filePath = Paths.get(file.getPath());
    result = Files.readAllBytes(filePath);
    

    and then just pass the bytes to the pdfMergerUtility object (or even the whole file instead of the list of bytes).