IText merge documents with acrofields

Depending on what you want exactly, different scenarios are possible, but in any case: you are doing it wrong. You should use either PdfCopy or PdfSmartCopy to merge documents.

The different scenarios are explained in the following video tutorial.

You can find most of the examples in the iText sandbox.

Merging different forms (having different fields)

If you want to merge different forms without flattening them, you should use PdfCopy as is done in the MergeForms example:

public void createPdf(String filename, PdfReader[] readers) throws IOException, DocumentException {
    Document document = new Document();
    PdfCopy copy = new PdfCopy(document, new FileOutputStream(filename));
    copy.setMergeFields();
    document.open();
    for (PdfReader reader : readers) {
        copy.addDocument(reader);
    }
    document.close();
    for (PdfReader reader : readers) {
        reader.close();
    }
}

In this case, readers is an array of PdfReader instances containing different forms (with different field names), hence we use PdfCopy and we make sure that we don’t forget to use the setMergeFields() method, or the fields won’t be copied.

Merging identical forms (having identical fields)

In this case, we need to rename the fields, because we probably want different values on different pages. In PDF a field can only have a single value. If you merge identical forms, you have multiple visualizations of the same field, but each visualization will show the same value (because in reality, there is only one field).

Let’s take a look at the MergeForms2 example:

public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
    Document document = new Document();
    PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
    copy.setMergeFields();
    document.open();
    List<PdfReader> readers = new ArrayList<PdfReader>();
    for (int i = 0; i < 3; ) {
        PdfReader reader = new PdfReader(renameFields(src, ++i));
        readers.add(reader);
        copy.addDocument(reader);
    }
    document.close();
    for (PdfReader reader : readers) {
        reader.close();
    }
}

public byte[] renameFields(String src, int i) throws IOException, DocumentException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PdfReader reader = new PdfReader(src);
    PdfStamper stamper = new PdfStamper(reader, baos);
    AcroFields form = stamper.getAcroFields();
    Set<String> keys = new HashSet<String>(form.getFields().keySet());
    for (String key : keys) {
        form.renameField(key, String.format("%s_%d", key, i));
    }
    stamper.close();
    reader.close();
    return baos.toByteArray();
}

As you can see, the renameFields() method creates a new document in memory. That document is merged with other documents using PdfSmartCopy. If you’d use PdfCopy here, your document would be bloated (as we’ll soon find out).

Merging flattened forms

In the FillFlattenMerge1, we fill out the forms using PdfStamper. The result is a PDF file that is kept in memory and that is merged using PdfCopy. While this example is fine if you’d merge different forms, this is actually an example on how not to do it (as explained in the video tutorial).

The FillFlattenMerge2 shows how to merge identical forms that are filled out and flattened correctly:

public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
    Document document = new Document();
    PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
    document.open();
    ByteArrayOutputStream baos;
    PdfReader reader;
    PdfStamper stamper;
    AcroFields fields;
    StringTokenizer tokenizer;
    BufferedReader br = new BufferedReader(new FileReader(DATA));
    String line = br.readLine();
    while ((line = br.readLine()) != null) {
        // create a PDF in memory
        baos = new ByteArrayOutputStream();
        reader = new PdfReader(SRC);
        stamper = new PdfStamper(reader, baos);
        fields = stamper.getAcroFields();
        tokenizer = new StringTokenizer(line, ";");
        fields.setField("name", tokenizer.nextToken());
        fields.setField("abbr", tokenizer.nextToken());
        fields.setField("capital", tokenizer.nextToken());
        fields.setField("city", tokenizer.nextToken());
        fields.setField("population", tokenizer.nextToken());
        fields.setField("surface", tokenizer.nextToken());
        fields.setField("timezone1", tokenizer.nextToken());
        fields.setField("timezone2", tokenizer.nextToken());
        fields.setField("dst", tokenizer.nextToken());
        stamper.setFormFlattening(true);
        stamper.close();
        reader.close();
        // add the PDF to PdfCopy
        reader = new PdfReader(baos.toByteArray());
        copy.addDocument(reader);
        reader.close();
    }
    br.close();
    document.close();
}

These are three scenarios. Your question is too unclear for anyone but you to decide which scenario is the best fit for your needs. I suggest that you take the time to learn before you code. Watch the video, try the examples, and if you still have doubts, you’ll be able to post a smarter question.

Leave a Comment