PDFbox: случайное исключение при создании PDF-документов - PullRequest
0 голосов
/ 29 января 2019

Я использую 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-файла я обычно используюописание Apache PDFBox 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.В этом цикле я всегда создаю новые объекты 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 Ответ

0 голосов
/ 04 февраля 2019

1) Исключение COSStream has been closed and cannot be read при сохранении лучше всего проанализировать, посмотрев на конец частично сохраненного файла.Откройте его с помощью NOTEPAD ++, внизу вы увидите незавершенный поток.Опубликовать последние несколько строк, начиная с последней строки, которая имеет "номер 0 объекта".Это будет указывать на то, какой тип 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 («рабочий файл»).Итак, позже (сборка мусора) этот не имеющий ссылки PDDocument завершается и закрывает все связанные потоки, включая созданный вами поток изображения.

Таким образом, решение состоит в том, чтобы передать PDDocument вашего собственного документа, а не какой-то временный объект.

4) Обратите внимание, что это также относится к шрифтам, поэтому не передавайте new PDDocument() методу создания шрифта.(не относится к вам, но, возможно, к людям в будущем).

...