在 for 循环中使用 PdfStamper 的最佳方法
Best approach to use PdfStamper in for loop
我有下面的 iText
代码来读取文件并将其添加到主 PDF 文件中,所以它基本上是在现有 PDF 中的绝对位置添加 PDF 页面。主 PDF 中的绝对位置和页码将动态确定。有时它可能位于 100,100(x,y)
的第 1 页或 250,250(x,y)
的第 2 页。我正在遍历 PDF 对象,其中每个对象代表 PDF 文件,然后我将应用业务逻辑将 PDF 对象转换为 PDF 文件,即 srcPdf
。现在我需要在主 PDF 的绝对位置添加这个 srcPdf
(这里是 pdfStamper
):
for(ListOfPdfObject pdfObj: ListOfPdfObjects) {
// code to create srcPdf so there will be new srcPdf for each iteration. srcPdf is flattened pdf of acro form field pdf.
PdfReader reader2 = new PdfReader(srcPdf.getAbsolutePath());
PdfImportedPage page = pdfStamper.getImportedPage(reader2, 1);
pdfStamper.insertPage(1, reader2.getPageSize(1));
pdfStamper.getUnderContent(1).addTemplate(page, 100, 100);
pdfStamper.close(); // problem is here
reader2.close();
}
此处 pdfStamper
是在 for-loop
之外创建的,如下所示:
PdfReader pdfReader = new PdfReader(new FileInputStream(tempPdf));
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(destPdf));
问题是,如果我在 for-loop
之后关闭 pdfStamper
,它会抛出 RandomAccessSource not opened
异常。如果我在 for 循环内关闭,我将不得不在 for-loop
内再次创建。你能给我指出正确的方向吗?
正如我在对 的回答中所解释的,使用 PdfStamper
只是满足您要求的一种方式。如果您需要操作 单个 PDF 文档 并且可以从 [= 添加 单个页面 ,PdfStamper
可能是您的最佳选择50=]另一个 PDF 正如我之前的回答所展示的那样。
但是,您现在表示必须连接多个 PDF 文件。在这种情况下,使用 PdfStamper
并不是最佳选择。您应该考虑切换到 PdfCopy
:
假设您有以下文件。
String[] paths = new String[]{
"resources/to_be_inserted_1.pdf",
"resources/to_be_inserted_2.pdf",
"resources/to_be_inserted_3.pdf"
};
您需要在路径为 "resources/main_document.pdf"
的现有 PDF 的开头插入每个文档的第一页(且仅插入第一页),然后您可以这样做:
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(dest));
document.open();
PdfReader reader;
for (String path : paths) {
reader = new PdfReader(path);
copy.addPage(copy.getImportedPage(reader, 1));
reader.close();
}
reader = new PdfReader("resources/main_document.pdf");
copy.addDocument(reader);
reader.close();
document.close();
如您所见,addPage()
方法添加单个页面,而 addDocument()
方法添加文档的所有页面。
更新
看来你不想插入新页面,而是想叠加页面:你想添加页数在之上或在现有内容之下。
在那种情况下,您确实需要 PdfStamper
,但您犯了两个严重错误。
- 您关闭循环内的
stamper
。一旦 stamper
关闭,它就关闭了:你不能再向它添加任何内容。您需要将 stamper.close()
移到循环之外。
- 您关闭了循环内的
reader
,但 stamper
尚未 释放 reader
。您应该先释放 reader。
这在 SuperImpose 示例中显示:
public static final String SRC = "resources/pdfs/primes.pdf";
public static final String[] EXTRA =
{"resources/pdfs/hello.pdf", "resources/pdfs/base_url.pdf", "resources/pdfs/state.pdf"};
public static final String DEST = "results/stamper/primes_superimpose.pdf";
PdfReader reader = new PdfReader(SRC);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(DEST));
PdfContentByte canvas = stamper.getUnderContent(1);
PdfReader r;
PdfImportedPage page;
for (String path : EXTRA) {
r = new PdfReader(path);
page = stamper.getImportedPage(r, 1);
canvas.addTemplate(page, 0, 0);
stamper.getWriter().freeReader(r);
r.close();
}
stamper.close();
在这种情况下,我总是将导入的页面添加到主文档的第 1 页。如果要将导入的页面添加到不同的页面,则需要在循环内创建canvas
对象。
我有下面的 iText
代码来读取文件并将其添加到主 PDF 文件中,所以它基本上是在现有 PDF 中的绝对位置添加 PDF 页面。主 PDF 中的绝对位置和页码将动态确定。有时它可能位于 100,100(x,y)
的第 1 页或 250,250(x,y)
的第 2 页。我正在遍历 PDF 对象,其中每个对象代表 PDF 文件,然后我将应用业务逻辑将 PDF 对象转换为 PDF 文件,即 srcPdf
。现在我需要在主 PDF 的绝对位置添加这个 srcPdf
(这里是 pdfStamper
):
for(ListOfPdfObject pdfObj: ListOfPdfObjects) {
// code to create srcPdf so there will be new srcPdf for each iteration. srcPdf is flattened pdf of acro form field pdf.
PdfReader reader2 = new PdfReader(srcPdf.getAbsolutePath());
PdfImportedPage page = pdfStamper.getImportedPage(reader2, 1);
pdfStamper.insertPage(1, reader2.getPageSize(1));
pdfStamper.getUnderContent(1).addTemplate(page, 100, 100);
pdfStamper.close(); // problem is here
reader2.close();
}
此处 pdfStamper
是在 for-loop
之外创建的,如下所示:
PdfReader pdfReader = new PdfReader(new FileInputStream(tempPdf));
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(destPdf));
问题是,如果我在 for-loop
之后关闭 pdfStamper
,它会抛出 RandomAccessSource not opened
异常。如果我在 for 循环内关闭,我将不得不在 for-loop
内再次创建。你能给我指出正确的方向吗?
正如我在对 PdfStamper
只是满足您要求的一种方式。如果您需要操作 单个 PDF 文档 并且可以从 [= 添加 单个页面 ,PdfStamper
可能是您的最佳选择50=]另一个 PDF 正如我之前的回答所展示的那样。
但是,您现在表示必须连接多个 PDF 文件。在这种情况下,使用 PdfStamper
并不是最佳选择。您应该考虑切换到 PdfCopy
:
假设您有以下文件。
String[] paths = new String[]{
"resources/to_be_inserted_1.pdf",
"resources/to_be_inserted_2.pdf",
"resources/to_be_inserted_3.pdf"
};
您需要在路径为 "resources/main_document.pdf"
的现有 PDF 的开头插入每个文档的第一页(且仅插入第一页),然后您可以这样做:
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(dest));
document.open();
PdfReader reader;
for (String path : paths) {
reader = new PdfReader(path);
copy.addPage(copy.getImportedPage(reader, 1));
reader.close();
}
reader = new PdfReader("resources/main_document.pdf");
copy.addDocument(reader);
reader.close();
document.close();
如您所见,addPage()
方法添加单个页面,而 addDocument()
方法添加文档的所有页面。
更新
看来你不想插入新页面,而是想叠加页面:你想添加页数在之上或在现有内容之下。
在那种情况下,您确实需要 PdfStamper
,但您犯了两个严重错误。
- 您关闭循环内的
stamper
。一旦stamper
关闭,它就关闭了:你不能再向它添加任何内容。您需要将stamper.close()
移到循环之外。 - 您关闭了循环内的
reader
,但stamper
尚未 释放reader
。您应该先释放 reader。
这在 SuperImpose 示例中显示:
public static final String SRC = "resources/pdfs/primes.pdf";
public static final String[] EXTRA =
{"resources/pdfs/hello.pdf", "resources/pdfs/base_url.pdf", "resources/pdfs/state.pdf"};
public static final String DEST = "results/stamper/primes_superimpose.pdf";
PdfReader reader = new PdfReader(SRC);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(DEST));
PdfContentByte canvas = stamper.getUnderContent(1);
PdfReader r;
PdfImportedPage page;
for (String path : EXTRA) {
r = new PdfReader(path);
page = stamper.getImportedPage(r, 1);
canvas.addTemplate(page, 0, 0);
stamper.getWriter().freeReader(r);
r.close();
}
stamper.close();
在这种情况下,我总是将导入的页面添加到主文档的第 1 页。如果要将导入的页面添加到不同的页面,则需要在循环内创建canvas
对象。