Fields in PDFs have an Unusual Property: All fields with the same name are the same field. They share a value. This is handy when the form refers to the same person and you have a nice naming scheme across forms. It's Not Handy when you want to put 20 instances of a single form into a single PDF.
This makes merging multiple forms challenging, to say the least. The most common option (thanks to iText), is to flatten the forms prior to merging them, at which point you're no long merging forms, and the problem Goes Away.
The other option is to rename your fields prior to merging them. This can make data extraction difficult later, can break scripts, and is generally a PITA. That's why flattening is so much more popular.
There's a class in iText called PdfCopyFields
, and it will correctly copy fields from one document to another... it will also merge fields with the same name correctly, such that they really share a single value and Acrobat/Reader doesn't have to do a bunch of extra work on the file to get it that way before displaying it to a user.
However, PdfCopyFields
will not rename fields for you. To do that, you need to get the AcroFields
object from the PdfReader
in question, and call renameField(String, String)
on Each And Every Field prior to merging the documents with PdfCopyFields
.
All this is for "AcroForm"-based PDF forms. If you're dealing with XFA forms (forms from LiveCycle Designer), all bets are off. You have to muck with the XML, A Lot.
And heaven help you if you have to combine forms from both.
So ass-u-me-ing that you're working with AcroForm fields, the code might look something like this (forgive my Java):
public void mergeForms(String outpath, String inPaths[]) {
PdfCopyFields copier = new PdfCopyFields(new FileOutputStream(outpath) );
for (String curInPath : inPaths) {
PdfReader reader = new PdfReader(curInPath);
renameFields(reader.getAcroFields());
copier.addDocument(reader);
}
copier.close();
}
private static int counter = 0;
private void renameFields(AcroFields fields) {
Set<String> fieldNames = fields.getFields().keySet();
String prepend = String.format("_%d.", counter++);
for(String fieldName : fieldNames) {
fields.rename(fieldName, prepend + fieldName);
}
}
Ideally, renameFields
would also create a generic field object named prepend's-value and make all the other fields in the document it's children. This would make Acrobat/Reader's life easier and avoid an apparently unnecessary "save changes?" request when closing the resulting PDF from Acrobat.
Yes, that's why Acrobat will sometimes ask you to save changes when You Didn't Do Anything! Acrobat did something behind the scenes.