Best approach to use PdfStamper in for loop

2019-02-28 17:10发布

问题:

I have below iText code to read files and adding it into master PDF file, so it is basically adding PDF page in the existing PDF at absolute position. Absolute position and page number in the master PDF will be decided dynamically. Sometimes it could be on page 1 with 100,100(x,y) or page 2 with 250,250(x,y). I am looping through the PDF objects where each object represent PDF file, then I will apply business logic to convert PDF object into PDF file and that is srcPdf. Now I need to add this srcPdf at absolute position in master PDF(which is pdfStamper here):

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

Here pdfStamper is created outside for-loop like below:

PdfReader pdfReader = new PdfReader(new FileInputStream(tempPdf));
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(destPdf));

The problem is if I close pdfStamper after for-loop it throws RandomAccessSource not opened exception. If I close inside for loop I will have to create again inside for-loop. Could you please point me at right direction.

回答1:

As explained in my answer to Extract pdf page and insert into existing pdf, using PdfStamper is only one way to meet your requirement. PdfStamper is probably your best choice if you need to manipulate a single PDF document and it's possible to add a single page from another PDF as my previous answer demonstrates.

However, you now indicate that you have to concatenate multiple PDF files. In that case, using PdfStamper isn't the best choice. You should consider switching to PdfCopy:

Suppose that you have the following files.

String[] paths = new String[]{
    "resources/to_be_inserted_1.pdf",
    "resources/to_be_inserted_2.pdf",
    "resources/to_be_inserted_3.pdf"
};

You need to insert the first page (and only the first page) of each of these documents at the start of an existing PDF with path "resources/main_document.pdf", then you could do something like this:

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

As you can see, the addPage() method adds a single page, whereas the addDocument() method adds all the pages of a document.

Update

It seems that you don't want to insert new pages, but that you want to superimpose pages: you want to add pages on top of or under existing content.

In that case, you indeed need PdfStamper, but you're making two crucial errors.

  1. You close the stamper inside the loop. Once the stamper is closed, it is closed: you can't add any more content to it. You need to move stamper.close() outside the loop.
  2. You close the reader inside the loop, but stamper hasn't released the reader yet. You should free the reader first.

This is shown in the SuperImpose example:

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

In this case, I always add the imported pages to page 1 of the main document. If you want to add the imported pages to different pages, you need to create the canvas object inside the loop.



标签: java itext