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!