I am using PDFBox to generate a bunch of invoices in a loop. This is working in general, but unfortunately I am getting the following exception from time to time in the loop. Starting the generation again once or twice for the failed invoices will create all of them sooner or later.
java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?
at org.apache.pdfbox.cos.COSStream.checkClosed(COSStream.java:83)
at org.apache.pdfbox.cos.COSStream.createRawInputStream(COSStream.java:133)
at org.apache.pdfbox.pdfwriter.COSWriter.visitFromStream(COSWriter.java:1202)
at org.apache.pdfbox.cos.COSStream.accept(COSStream.java:400)
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteObject(COSWriter.java:521)
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteObjects(COSWriter.java:459)
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteBody(COSWriter.java:443)
at org.apache.pdfbox.pdfwriter.COSWriter.visitFromDocument(COSWriter.java:1096)
at org.apache.pdfbox.cos.COSDocument.accept(COSDocument.java:417)
at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1369)
at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1256)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1279)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1250)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1238)
at de.xx.xxx.CreateLandscapePDF.createPdf(CreateLandscapePDF.java:37)
at de.xx.xxx.CreateInvoiceAsPDF.createPdf(CreateInvoiceAsPDF.java:172)
...
I have already looked into some similar questions like here PDFbox saying PDDocument closed when its not and I just think that it has something to do with freed objects by the garbage collector, but I do not see the fault in my code.
For the creation of the PDF itself I am using in general the description of Apache PDFBox Cookbook at https://pdfbox.apache.org/1.8/cookbook/documentcreation.html. I more or less only add more content, an image, some text blocks, a table and so on.
public class CreateLandscapePDF {
private ArrayList<ContentBlock> content;
private PDRectangle pageDIN;
private PDDocument doc;
public CreateLandscapePDF(ArrayList<ContentBlock> content, PDRectangle pageDIN) {
this.content = content;
this.pageDIN = pageDIN;
}
public void createPdf(String pdfFileName) throws IOException
{
doc = new PDDocument();
PDPage page = new PDPage(pageDIN);
doc.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.OVERWRITE, false);
for (ContentBlock contentBlock : content) {
contentBlock.getContentHelper().writeContentToPDF(contentStream);
contentStream.moveTo(0, 0);
}
contentStream.close();
doc.save( pdfFileName );
doc.close();
}
}
In my creation process I have the loop in the CreateInvoiceAsPDF.createPdf method. In this loop I create always new objects of CreateLandscapePDF.
CreateLandscapePDF pdf = new CreateLandscapePDF(contentList, PDRectangle.A4);
pdf.createPdf(TEMP_FILEPATH_NAME + pdfFileName);
The writeContentToPDF method only places the several content like text, images and lines at a defined pixel unit into the page. As an example I put the code from my TextContentHelper:
public void writeContentToPDF(PDPageContentStream contentStream) throws IOException {
float maxTextWidth = 1;
contentStream.beginText();
float fontSize = content.getFontSize();
PDFont font = content.getFont();
contentStream.setFont(font, fontSize);
contentStream.setLeading(content.getLineSpace() * fontSize);
float xPos =0;
for (Object text : content.getContent()) {
if (text instanceof String) {
float textWidth = UnitTranslator.getPixUnitFromTextLength(font, fontSize, (String) text);
switch (content.getAlignment()) {
case CENTER:
xPos = 0.5f*(content.getXEndPosition()+content.getXPosition()-textWidth);
contentStream.newLineAtOffset(xPos, content.getYPosition());
break;
case RIGHT:
xPos = content.getXEndPosition()-textWidth;
contentStream.newLineAtOffset(xPos, content.getYPosition());
break;
default:
xPos = content.getXPosition();
contentStream.newLineAtOffset(xPos, content.getYPosition());
break;
}
contentStream.showText((String) text);
contentStream.newLine();
contentStream.newLineAtOffset(-xPos, -content.getYPosition());
if (textWidth > maxTextWidth) {
maxTextWidth = textWidth;
}
}
}
contentStream.endText();
if (content.isBorder()) {
createTextBlockBorder(contentStream, maxTextWidth, fontSize);
}
}
I appreciate any hint to solve this annoying problem!
writeContentToPDF. Please do also make sure you're using the latest PDFBox version (2.0.13) and the latest java. That is 1.8.202 (or 201) or 11.0.2. - Tilman Hausherrcontent.getFont()come from? Was the font object generated for THAT PDDocument? Or is it global for all PDFs or for a group of PDFs? (Which won't work) - Tilman Hausherrnew PDDocumentto a font creation method, and that PDDocument is of course unreferenced, so it would be closed automatically at some later time. - Tilman Hausherr