我创建了这样的方法:

  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方法时,它始终有效。

最佳答案

您在此问题和previous问题中看到的内容归因于iText PdfDocument类的特殊性:尽管它确实表示PDF文档,但它并未将其全部保存在内存或某些可访问的存储中。特别是如果您向其中添加内容,则默认情况下,此新内容将尽快从内存中清除到PdfWriter,从而使PdfDocument无法访问它。

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

缺点是PdfDocument实例的使用受到限制;尤其是您不能从已写入的实例中自由复制,因为复制的数据的当前状态可能不再可检索。

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

从而,


如果要从文档中复制,则PdfDocument需要初始化而无需PdfWriter
如果要(非常简单地)更改文档,则需要使用PdfDocument初始化PdfWriter
因此,如果您要更改和复制文档,则不能对两个操作使用相同的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())));
}

10-07 23:58