首页 > 解决方案 > 无法从正在编写 Java 的文档中复制间接对象

问题描述

我创建了一个这样的方法:

  public PdfDocument addBlankPage(final MediaModel pdfDocument) throws IOException {

    final InputStream inputStream = mediaService.getStreamFromMedia(pdfDocument);
    byte[] bytes = IOUtils.toByteArray(inputStream);
    final PdfReader reader = new PdfReader(new ByteArrayInputStream(bytes));
    final PdfWriter writer = new PdfWriter(pdfDocument.getRealFileName());
    final PdfDocument document = new PdfDocument(reader, writer);
    int index = document.getNumberOfPages();
    final PageSize ps = new PageSize(document.getFirstPage().getPageSize());
    document.addNewPage(index + 1, ps);
    reader.close();
    writer.close();
    return document;

}

为了向 PdfDocument 添加一个新的空白页,它看起来很好并且它的“似乎”可以工作。但是,当我尝试将带有空白页(由我的方法添加)的 PdfDocument 与此方法中的其他现有 pdf 文档合并时:

 public .... {

    ByteArrayOutputStream mergedPdfStream = new ByteArrayOutputStream();
    PdfDocument mergedPdf = new PdfDocument(new PdfWriter(mergedPdfStream));

    for (PdfDocument doc : pdfDocuments) {
        int n = doc.getNumberOfPages();

        for (int i = 1; i <= n; i++) {

            PdfPage page = doc.getPage(i).copyTo(mergedPdf);
            mergedPdf.addPage(page);

        }
    }
    ....

}

它抛出:

 com.itextpdf.kernel.PdfException: Cannot copy indirect object from the document that is being written.
at com.itextpdf.kernel.pdf.PdfObject.copyTo(PdfObject.java:318) ~[kernel-7.1.1.jar:?]
at com.itextpdf.kernel.pdf.PdfDictionary.copyTo(PdfDictionary.java:443) ~[kernel-7.1.1.jar:?]
at com.itextpdf.kernel.pdf.PdfPage.copyTo(PdfPage.java:379) ~[kernel-7.1.1.jar:?]
at com.itextpdf.kernel.pdf.PdfPage.copyTo(PdfPage.java:364) ~[kernel-7.1.1.jar:?]

我用谷歌搜索了它,但没有找到任何相关信息。有什么提示吗?

PD:我 100% 确定我的方法是有罪的,因为当我在不使用空白页方法的情况下合并其他 PDF 时,它总是有效的。

标签: javapdfitextpdf-generationitext7

解决方案


您在这个问题和上一个问题中观察到的情况由于 iTextPdfDocument类的特殊性:虽然它确实代表了一个 PDF 文档,但它并没有将其全部保存在内存或某些可访问的存储中。特别是如果您向其中添加内容,默认情况下,此新内容会PdfWriter尽快从内存中刷新到PdfDocument.

这使您能够在使用 iText 创建大型 PDF 时保持相当低的内存占用,这在高吞吐量应用程序中可能非常相关。

PdfDocument缺点是对实例的使用有限制;特别是,您无法从已写入的实例中自由复制,因为要复制的数据的当前状态可能不再可检索。

为防止您复制不一致的数据,iText 不允许从PdfDocument写入的实例(即具有.PdfWriter

因此,

  • 如果要从文档中复制PdfDocument,则需要在没有PdfWriter;的情况下进行初始化
  • 如果您想(不平凡)更改文档,则PdfDocument需要使用a进行初始化PdfWriter
  • 因此,如果您想从文档中更改复制,则不能PdfDocument对两个操作使用相同的实例!

因此,对于您的用例,您必须

  • PdfDocument在应用更改后获取with的输出PdfWriter并将其用作 a PdfDocumentwithout的输入PdfWriter以进行复制;
  • 或从源文件中打开两个单独PdfDocument的实例,一个带有 ,一个不带有PdfWriter,然后将更改应用到第一个并从第二个复制。

如果您要复制的数据应包含您应用的更改,则前一个选项是必需的。如果它们不包含它们,则后者是必要的。如果您不关心任何一种方式,或者如果您知道复制的数据不受更改的影响,则任何一个选项都可以。


在您的情况下,您将所有文档中的所有页面复制pdfDocuments到目标文档中,因此您特别希望将应用的更改也复制到目标中。因此,前一个选项适用,您必须在应用更改后获取with的输出,并将其用作 a without的输入以进行复制。PdfDocumentPdfWriterPdfDocumentPdfWriter

您可以通过更改您的方式来做到addBlankPage这一点:

public PdfDocument addBlankPage(final MediaModel pdfDocument) throws IOException {
    try (   InputStream inputStream = mediaService.getStreamFromMedia(pdfDocument);
            PdfReader reader = new PdfReader(inputStream);
            PdfWriter writer = new PdfWriter(pdfDocument.getRealFileName());
            PdfDocument document = new PdfDocument(reader, writer)) {
        document.addNewPage(document.getFirstPage().getPageSize());
    }
    return new PdfDocument(new PdfReader(pdfDocument.getRealFileName()));
}

或者如果您实际上不想将 PDF 写入文件系统:

public PdfDocument addBlankPage(final MediaModel pdfDocument) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (   InputStream inputStream = mediaService.getStreamFromMedia(pdfDocument);
            PdfReader reader = new PdfReader(inputStream);
            PdfWriter writer = new PdfWriter(baos);
            PdfDocument document = new PdfDocument(reader, writer)) {
        document.addNewPage(document.getFirstPage().getPageSize());
    }
    return new PdfDocument(new PdfReader(new ByteArrayInputStream(baos.toByteArray())));
}

推荐阅读