当前位置: 首页 > 知识库问答 >
问题:

PDFbox:创建PDF文档时偶尔出现异常

左丘成仁
2023-03-14

我正在使用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)
...

我已经研究了一些类似的问题,比如这里的PDFbox说PDDocument在未关闭时关闭,我只是认为它与垃圾收集器释放的对象有关,但我没有看到我的代码中的错误。

对于PDF本身的创建,我通常使用ApachePDFBox Cookbook的描述https://pdfbox.apache.org/1.8/cookbook/documentcreation.html.我或多或少只添加了更多的内容、一个图像、一些文本块、一个表格等等。

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方法中有一个循环。在这个循环中,我总是创建CreateLandscape ePDF的新对象。

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个答案

傅英喆
2023-03-14

1)COSStream已关闭,无法读取异常,此时最好通过查看部分保存文件的末尾来分析保存。使用NOTEpad打开它,您会在底部看到一个不完整的流。从最后一行“数字0 obj”开始发布最后几行。这将表明哪种COSStream有问题。

2) 您的文件显示了一个图像XObject(“/Type/XObject/Subtype/image”)。

3)进一步的研究表明,你用

PDImageXObject pdImage = PDImageXObject.createFromByteArray(new PDDocument(), ...);

你偶尔也会收到警告:你没有关闭PDF文档。

这是因为您的new PDDocument()对象被传递到createFromByteArray方法,但没有保留,PDFBox只需要它来获取该PDDocument的内存管理内容(“临时文件”)。所以稍后(垃圾收集)这个未引用的PDDocument将被最终确定,并关闭所有相关的流,其中包括您创建的图像流。

因此,解决方案是传递自己文档的PDDocument,而不是一些临时对象。

4)请注意,这也适用于字体,因此不要将new PDDocument()传递给字体创建方法。(不适用于您,但可能适用于未来的人)。

 类似资料:
  • 主要内容:创建一个空的PDF文档,实例现在让我们了解如何使用PDFBox库创建PDF文档。 创建一个空的PDF文档 可以通过实例化类来创建一个空的PDF文档。使用这个类的方法将文档保存在所需的位置。 以下是创建一个空的PDF文档的步骤。 第1步: 创建空白文档 包中的类是PDF文档的内存中表示形式。 因此,通过实例化这个类,可以创建一个空的,如下面的代码块所示。 第2步: 保存文档 创建文档后,需要将此文档保存在所需的路径中,可以使用

  • 问题内容: 我需要创建一个PDF,其中将包含执行状态报告,其中状态将以表格结构显示。是否可以使用PDFBOX API生成pdf表格式? 以下是一些用于创建新PDF文档的示例代码: 问题答案: 试试这个: 只需在函数中调用此方法

  • 主要内容:分割PDF文档中的页面,示例在前一章中,我们已经看到了如何将JavaScript添加到PDF文档。 现在来学习如何将给定的PDF文档分成多个文档。 分割PDF文档中的页面 可以使用类将给定的PDF文档分割为多个PDF文档。 该类用于将给定的PDF文档分成几个其他文档。 以下是拆分现有PDF文档的步骤 第1步:加载现有的PDF文档 使用类的静态方法加载现有的PDF文档。 此方法接受一个文件对象作为参数,因为这是一个静态方法,可

  • 主要内容:加密PDF文档,示例在前一章中,我们已经看到了如何在PDF文档中插入图像。 在本章中,我们将学习如何加密PDF文档。 加密PDF文档 使用和类提供的方法加密PDF文档。 类用于通过为其分配访问权限来保护PDF文档。 使用此教程,您可以限制用户执行以下操作。 打印文档 修改文档的内容 复制或提取文档的内容 添加或修改注释 填写交互式表单域 提取文字和图形以便视障人士使用 汇编文件 打印质量下降 类用于向文档添加基于密码

  • 现在让我们了解如何使用PDFBox库创建PDF文档。 创建空PDF文档 您可以通过实例化PDDocument类来创建空PDF文档。 您可以使用Save()方法将文档保存在所需的位置。 以下是创建空PDF文档的步骤。 第1步:创建一个空文档 属于包org.apache.pdfbox.pdmodel的PDDocument类是PDFDocument的内存中表示形式。 因此,通过实例化此类,您可以创建一个

  • 主要内容:将JavaScript添加到PDF文档,示例在前一章中,我们学习了如何将图像插入到PDF文档中。 在本章中,将学习如何将JavaScript添加到PDF文档。 将JavaScript添加到PDF文档 可以使用类将JavaScript操作添加到PDF文档。 它代表了JavaScript操作。 以下是将JavaScript操作添加到现有PDF文档的步骤。 第1步:加载现有的PDF文档 使用类的静态方法加载现有的PDF文档。 此方法接受一个文件对