无法从正在编写的Java文档中复制间接对象 [英] Cannot copy indirect object from the document that is being written Java

查看:114
本文介绍了无法从正在编写的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而不使用空白的Page方法时,它总是有效的..

解决方案

您在此内容和上一个中观察到的内容>问题是由于iText PdfDocument类的特殊性:尽管它确实表示PDF文档,但它并不将其全部保存在内存或某些可访问的存储中.特别是如果您向其中添加内容,则默认情况下,此新内容将尽快从内存中冲出到PdfWriter,从而使PdfDocument无法访问它.

这使您能够在使用iText创建大型PDF时保持较低的内存占用,这在高吞吐量应用程序中可能是非常重要的一个因素.

缺点是PdfDocument实例的使用受到限制;特别是您不能从已写入的实例中自由复制,因为复制的数据的当前状态可能无法再获取.

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

因此

  • 如果您要文档进行复制,则PdfDocument需要初始化,一个PdfWriter
  • 如果要(非常简单地)更改文档,则需要使用> 和
  • 来初始化PdfDocument
  • 因此,如果您要从文档中更改副本,则不能对两个操作使用相同的PdfDocument实例!

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

  • 在应用更改后将PdfDocument的输出与PdfWriter一起使用,并用作PdfDocument的输入,而无需PdfWriter进行复制;
  • 或从源文件中打开两个单独的PdfDocument实例,一个实例带有和不带有PdfWriter,并将更改应用于第一个实例,然后从第二个实例复制.

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


在您的情况下,您将所有页面从pdfDocuments中的所有文档复制到目标文档,因此特别要将应用的更改也复制到目标.因此,采用前一个选项,您必须在应用更改后PdfDocument的输出与PdfWriter一起使用,并将其用作PdfDocument的输入,而无需PdfWriter进行复制.

您可以通过这样更改您的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())));
}

I've created a method like this:

  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;

}

In order to add a new blank page to a PdfDocument and it looks fine and its "seems" to work. However , when I try to merge a PdfDocument with a blank page (added by my method) with other existing pdf documents in this method:

 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);

        }
    }
    ....

}

It throws :

 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:?]

I googled it and I didn't found any relevant information . Any hint ?

PD: I'm 100% sure that my method is the guilty , because when I merge other PDFs without using the blank Page method , it always works..

解决方案

What you have observed in this and in your previous question is due to a peculiarity of the iText PdfDocument class: While it does represent a PDF document, it does not hold all of it in memory or in some accessible storage. In particular if you add content to it, this new content is by default flushed out of memory to the PdfWriter as soon as possible, making it inaccessible to the PdfDocument.

This enables you to keep the memory footprint fairly low while creating large PDFs with iText, a factor which can be very relevant in high-throughput applications.

The downside is that that there are restrictions to the use of PdfDocument instances; in particular you cannot freely copy from instances that have been written to as the current state of the data to copy might not be retrievable anymore.

To prevent you from copying inconsistent data, iText disallows copying from PdfDocument instances which can be written to, i.e. which have a PdfWriter.

Thus,

  • if you want to copy from a document, the PdfDocument needs to be initialized without a PdfWriter;
  • if you want to (non-trivially) change a document, the PdfDocument needs to be initialized with a PdfWriter;
  • so if you want to change and copy from a document, you cannot use the same PdfDocument instance for both actions!

For your use case, therefore, you have to

  • either take the output of the PdfDocument with PdfWriter after applying the changes and use it as input of a PdfDocument without PdfWriter to copy from;
  • or open two separate PdfDocument instances from the source file, one with and one without a PdfWriter, and apply the changes to the first and copy from the second.

The former option is necessary if the data you want to copy shall contain the changes you apply. The latter is necessary if they shall not contain them. If you don't care either way or if you know the copied data is not influenced by the changes, either option is ok.


In your case you copy all pages from all documents in pdfDocuments to a target document, so in particular you want the changes you applied also be copied to the target. Thus, the former option applies, you have to take the output of the PdfDocument with PdfWriter after applying the changes and use it as input of a PdfDocument without PdfWriter to copy from.

You can do so by changing your addBlankPage like this:

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()));
}

or if you don't actually want to write the PDF into the file system:

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())));
}

这篇关于无法从正在编写的Java文档中复制间接对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆