the following code demonstrates a very strange bug. Once the "source" file is closed the "destination" file can not be saved and closed, it will throw "java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?"
If we comment out saving the source file, then the destination will save and close properly. This seems to clearly indicate that the source file contained a COSStream object that also existed in the destination file. The source file COSStream seems to get closed when we close the source file and then the destination can't be saved.
If we comment out flattening the source files AcroForm then the destination will save and close properly.
This simplistic example is trying to merge one copy of the form with itself, the bug will reproduce if you substitute certain other PDF files (all government forms that used to be XFA documents). Most PDFs will work in this scenario. We down converted the XFA documents to normal PDF to eliminate that as a variable and the bug still persisted.
Issue exists in PDFBox version 2.0.8 and older
@Test
public void testMergeGovernmentForms() throws Exception {
File file = new File("GeneralForbearance.pdf");
PDDocument destination = PDDocument.load(file);
PDDocument source = PDDocument.load(file);
source.getDocumentCatalog().getAcroForm().flatten(); //comment out just this line and the destination.save will pass
PDFMergerUtility appender = new PDFMergerUtility();
appender.appendDocument(destination, source);
source.close(); //comment out just this line and the destination.save will pass
destination.save(File.createTempFile("PrintMergeIssue", ".pdf"));
destination.close();
}
Download the GeneralForbearance.pdf from HERE
Additionally, if you "pre-flatten" the government form and save, you get the same behavior with even simpler code.
@Test
public void testMerge() throws Exception {
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
PDDocument src = PDDocument.load(new File("C:/temp/GovFormPreFlattened.pdf"));
PDDocument dest = PDDocument.load(new File("C:/temp/GovFormPreFlattened.pdf"));
pdfMergerUtility.appendDocument(dest, src);
src.close(); //if we don't close the src then we don't have an error
dest.save(File.createTempFile("MergeIssue",".PDF"));
dest.close();
}
The pre-flattened government form can be found HERE
document.getDocumentCatalog().setStructureTreeRoot(null);
– Tilman Hausherr