首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >iText 2.1.7 PdfCopy.addPage(页面)找不到页面引用吗?

iText 2.1.7 PdfCopy.addPage(页面)找不到页面引用吗?
EN

Stack Overflow用户
提问于 2016-01-15 19:22:00
回答 2查看 3.8K关注 0票数 2

我正在维护一个使用iText 2.1.7创建PDF的web应用程序。我想拿一个现有PDF的内容,并把它放在pdf文档,代码是在创建过程中。我有以下内容(编辑:更完整的代码):

代码语言:javascript
运行
复制
package itexttest;

import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;

public class ITextTest 
{
    public static void main(String[] args) 
    {
        try
        {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
            PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
            bigDoc.open();

            Paragraph par = new Paragraph("one");
            bigDoc.add(par);
            bigDoc.add(new Paragraph("three"));

            addPdfPage(bigDoc, os, "c:/insertable.pdf");

            bigDoc.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private static void addPdfPage(Document document, OutputStream outputStream, String location) {
        try {

            PdfReader pdfReader = new PdfReader(location);
            int pages = pdfReader.getNumberOfPages();

            PdfCopy pdfCopy = new PdfCopy(document, outputStream);
            PdfImportedPage page = pdfCopy.getImportedPage(pdfReader, 1);
            pdfCopy.addPage(page);
        }
        catch (Exception e) {
            System.out.println("Cannot add PDF from PSC: <" + location + ">: " + e.getMessage());
            e.printStackTrace();
        }
    }

}

这会引发一个错误,PdfWriter.getPageReference()为null。

我怎么会不正确地使用这个?如何从现有文档中获取页面并将其放入当前文档中?请注意,我所处的地方根本不方便将文件写入临时存储或其他任何东西。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-01-18 14:31:36

我不再积极地使用旧的iText版本了,但是从那以后有些事情没有改变。因此,在这里,代码和指针中的一些问题有助于解决这些问题:

当前代码中的主要问题是

  • 重用Document实例(您已经为PdfWriter使用并已经打开)用于PdfCopy;虽然Document可以支持多个侦听器,但它们都需要在调用open之前注册;此构造的用例是以两种不同格式并行创建同一个文档;
  • PdfWriterPdfCopy都使用相同的输出流;结果不是一个有效的PDF,而是字节范围从两个不同的PDF广泛混合在一起,即肯定不会是一个有效的PDF。

正确使用PdfCopy

您可以通过首先在ByteArrayOutputStream中创建包含新段落的新PDF (关闭所涉及的Document )来重构代码,然后将此PDF和要添加到新PDF中的其他页面复制。

例如:

代码语言:javascript
运行
复制
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
bigDoc.open();
Paragraph par = new Paragraph("one");
bigDoc.add(par);
bigDoc.add(new Paragraph("three"));
bigDoc.close();

ByteArrayOutputStream os2 = new ByteArrayOutputStream();
Document finalDoc = new Document();
PdfCopy copy = new PdfCopy(finalDoc, new FileOutputStream(RESULT2));
finalDoc.open();
PdfReader reader = new PdfReader(os.toByteArray());
for (int i = 0; i < reader.getNumberOfPages();) {
    copy.addPage(copy.getImportedPage(reader, ++i));
}
PdfReader pdfReader = new PdfReader("c:/insertable.pdf");
copy.addPage(copy.getImportedPage(pdfReader, 1));
finalDoc.close();
reader.close();
pdfReader.close();

// result PDF
byte[] result = os2.toByteArray();           

只使用PdfWriter

您也可以通过直接将页面导入PdfWriter来更改代码,例如:

代码语言:javascript
运行
复制
ByteArrayOutputStream os = new ByteArrayOutputStream();
Document bigDoc = new Document(PageSize.LETTER, 50, 50, 110, 60);
PdfWriter writer = PdfWriter.getInstance(bigDoc, os);
bigDoc.open();
Paragraph par = new Paragraph("one");
bigDoc.add(par);
bigDoc.add(new Paragraph("three"));

PdfReader pdfReader = new PdfReader("c:/insertable.pdf");
PdfImportedPage page = writer.getImportedPage(pdfReader, 1);
bigDoc.newPage();
PdfContentByte canvas = writer.getDirectContent();
canvas.addTemplate(page, 1, 0, 0, 1, 0, 0);

bigDoc.close();
pdfReader.close();

// result PDF
byte[] result = os.toByteArray();           

这种方法看起来更好,因为不需要中间PDF。不幸的是,这种外表是骗人的,这种做法是有一些缺点的。

这里,并不是像文档一样复制和添加整个原始页面,而是只将其内容流用作模板的内容,然后从实际的新文档页中引用该模板。这尤其意味着:

  • 如果导入的页面具有与新目标文档不同的维度,则它的某些部分可能会被剪切,而新页的某些部分仍然是空的。正因为如此,您经常会发现代码的变体,通过缩放和旋转,尝试使导入的页面和目标页面适合。
  • 原始页面内容现在在一个模板中,该模板从新页面中引用。如果使用相同的机制将这个新页面导入到另一个文档中,则会得到一个引用模板的页面,该模板再次引用具有原始内容的模板。如果将此页面导入另一个文档,则会获得另一个间接级别。等。等等。 不幸的是,符合PDF的查看者只需要在有限的程度上支持这种间接性。如果您继续此过程,您的页面内容可能突然不再可见。如果原始页面已经带来了它自己的引用模板层次结构,那么这种情况可能会更早而不是更晚发生。
  • 如果只复制内容,则将丢失不在内容流中的原始页的属性。这尤其涉及到注释,如表单字段或某些类型的突出显示标记,甚至某些类型的自由文本。

(顺便说一句,这些模板在通用的PDF规范术语中称为表单XObjects。)

This answer在合并PDF的上下文中显式地处理PdfCopyPdfWriter的使用。

票数 3
EN

Stack Overflow用户

发布于 2016-01-18 17:48:42

下面是另一个版本,包含了mkl的更正,希望这些名字能够回答其他问题:

代码语言:javascript
运行
复制
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;

import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;

public class PdfPlay
{
      public static void main(String[] args) 
      {
          try
          {
              ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();

              Document document1 = new Document(PageSize.LETTER, 50, 50, 110, 60);
              PdfWriter writer1 = PdfWriter.getInstance(document1, outputStream1);

              document1.open();
              document1.add(new Paragraph("one"));
              document1.add(new Paragraph("two"));
              document1.add(new Paragraph("three"));
              document1.close();

              byte[] withInsert = addPdfPage(outputStream1, "insertable.pdf");

          }
          catch (Exception e)
          {
              e.printStackTrace();
          }
      }

      private static byte[] addPdfPage(ByteArrayOutputStream outputStream1, String insertFilename) 
      {
          try 
          {
            ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
            Document document2 = new Document();
            PdfCopy copy = new PdfCopy(document2, new FileOutputStream("inserted.pdf"));
            document2.open();
            PdfReader outputStream1Reader = new PdfReader(outputStream1.toByteArray());
            for (int i=1; i<=outputStream1Reader.getNumberOfPages(); i++)
            {
              copy.addPage(copy.getImportedPage(outputStream1Reader, i));
            }
            PdfReader insertReader = new PdfReader(insertFilename);
            copy.addPage(copy.getImportedPage(insertReader, 1));

            document2.close();
            outputStream1Reader.close();
            insertReader.close();

            byte[] result = outputStream2.toByteArray();
            return result;
          }
          catch (Exception e) 
          {
              System.out.println("Cannot add PDF from PSC: <" + insertFilename + ">: " + e.getMessage());
              e.printStackTrace();
              return null;
          }
      }

}

如果在程序的默认目录中与文件'insertable.pdf‘一起运行,该程序将在同一个目录中生成文件'inserted.pdf’,其中第一页上有文本“一”、“二”和“三”,第二页上有'insertable.pdf‘的第一页。

因此,mkl的修正是有效的;要在我想使用它的环境中使用它,有几个问题:

我有一个程序,我想要使用这个功能,这是一个web应用程序,所以没有现成的访问一个地方写一个文件。我假设我可以使用ByteArrayOutputStream代替输出文件,就像这里所做的那样。

为了插入内容,绝对有必要创建一个新的输出流吗?我希望找到一种方法来告诉某些iText组件:“这是一个文件;阅读它的第一页并将其插入我已经打开的文档/outputStream/writer中。在内存中的Pdf文档可能会变得相当大;我宁愿不必复制所有现有的PDF结构,这样我就可以添加另一个结构。如果我最终从多个文档中插入页面,我想我可能需要多次这样做,我想……

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34818288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档