PDFbox:创建 PDF 文档时偶尔出现异常
PDFbox: sporadic exception while creating PDF Documents
我正在使用 PDFBox 循环生成一堆发票。
这通常是有效的,但不幸的是我在循环中不时遇到以下异常。为失败的发票重新生成一次或两次,迟早会创建所有这些发票。
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)
...
我已经研究过一些类似的问题,例如此处 ,我只是认为它与垃圾收集器释放的对象有关,但我没有在我的代码中发现错误。
为了创建 PDF 本身,我一般使用 https://pdfbox.apache.org/1.8/cookbook/documentcreation.html 上的 Apache PDFBox Cookbook 的描述。
我或多或少只是添加了更多内容,一张图片,一些文本块,一个table等等。
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();
}
}
在我的创建过程中,我在 CreateInvoiceAsPDF.createPdf 方法中有一个循环。在这个循环中,我总是创建 CreateLandscapePDF 的新对象。
CreateLandscapePDF pdf = new CreateLandscapePDF(contentList, PDRectangle.A4);
pdf.createPdf(TEMP_FILEPATH_NAME + pdfFileName);
writeContentToPDF 方法仅将定义的像素单元的文本、图像和线条等几种内容放入页面中。作为示例,我将 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);
}
}
感谢任何解决这个恼人问题的提示!
1) 保存时的COSStream has been closed and cannot be read
异常最好通过查看部分保存文件的末尾来分析。用NOTEPAD++打开它,你会在底部看到一个不完整的流。 Post 最后几行,从最后一行 "number 0 obj" 开始。这将表明哪种 COSStream 有问题。
2) 您的文件显示图像 XObject ("/Type /XObject /Subtype /Image")。
3) 进一步的研究表明您使用
创建了图像
PDImageXObject pdImage = PDImageXObject.createFromByteArray(new PDDocument(), ...);
你偶尔也会收到警告 Warning: You did not close a PDF Document
。
这是因为您的 new PDDocument()
对象已传递给 createFromByteArray
方法但未保留,PDFBox 只需要它来获取 PDDocument 的内存管理内容("scratch file" ).所以稍后(垃圾收集)这个未引用的 PDDocument 被最终确定,并关闭所有相关流,其中包括您创建的图像流。
所以解决方案是传递您自己文档的 PDDocument,而不是一些临时对象。
4) 请注意,这也适用于字体,所以不要将 new PDDocument()
传递给字体创建方法。 (不适用于你,但可能适用于未来的人)
我正在使用 PDFBox 循环生成一堆发票。 这通常是有效的,但不幸的是我在循环中不时遇到以下异常。为失败的发票重新生成一次或两次,迟早会创建所有这些发票。
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)
...
我已经研究过一些类似的问题,例如此处
为了创建 PDF 本身,我一般使用 https://pdfbox.apache.org/1.8/cookbook/documentcreation.html 上的 Apache PDFBox Cookbook 的描述。 我或多或少只是添加了更多内容,一张图片,一些文本块,一个table等等。
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();
}
}
在我的创建过程中,我在 CreateInvoiceAsPDF.createPdf 方法中有一个循环。在这个循环中,我总是创建 CreateLandscapePDF 的新对象。
CreateLandscapePDF pdf = new CreateLandscapePDF(contentList, PDRectangle.A4);
pdf.createPdf(TEMP_FILEPATH_NAME + pdfFileName);
writeContentToPDF 方法仅将定义的像素单元的文本、图像和线条等几种内容放入页面中。作为示例,我将 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);
}
}
感谢任何解决这个恼人问题的提示!
1) 保存时的COSStream has been closed and cannot be read
异常最好通过查看部分保存文件的末尾来分析。用NOTEPAD++打开它,你会在底部看到一个不完整的流。 Post 最后几行,从最后一行 "number 0 obj" 开始。这将表明哪种 COSStream 有问题。
2) 您的文件显示图像 XObject ("/Type /XObject /Subtype /Image")。
3) 进一步的研究表明您使用
创建了图像PDImageXObject pdImage = PDImageXObject.createFromByteArray(new PDDocument(), ...);
你偶尔也会收到警告 Warning: You did not close a PDF Document
。
这是因为您的 new PDDocument()
对象已传递给 createFromByteArray
方法但未保留,PDFBox 只需要它来获取 PDDocument 的内存管理内容("scratch file" ).所以稍后(垃圾收集)这个未引用的 PDDocument 被最终确定,并关闭所有相关流,其中包括您创建的图像流。
所以解决方案是传递您自己文档的 PDDocument,而不是一些临时对象。
4) 请注意,这也适用于字体,所以不要将 new PDDocument()
传递给字体创建方法。 (不适用于你,但可能适用于未来的人)