Itext Merge Documents with Acrofields

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.

itext sharp merge pdfs with acrofields - fields go missing when merging

You are merging the documents the wrong way. See MergeForms to find out how to do it correctly. The key line that is missing in your code, is:

copy.setMergeFields();

Without it, the fields disappear (as you have noticed).

There's also a MergeForms2 example that explains how to merge two identical forms. In this case, you need to rename the fields, because each field needs to have a unique name. I'm adding a reference to this second example, because I see that you also try renaming the fields. There is, however, a serious flaw in your code: you create a stamper object, but you never do stamper.close(). Your use of the reader object is also problematic. All in all, it would be best to throw away your code, and to start anew using the two examples from the official iText web site.

Update: I've added the tags itext and itextsharp to your question. Only then I noticed that you're using iTextSharp instead of iText. Porting the Java code to C# should be easy for a C# developer, but I've never written a C# program, so please use the JAVA examples as if you were using pseudo-code. The code in C# won't be that different.

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

Itext Sharp Merge Pdfs with acrofields

I found a solution to this problem thanks to this answer by rhens

All I had to do is modify my GeneratePdf function by adding one line:

form.GenerateAppearances = true;

Here is the end result:

private static byte[] GeneratePdf(Dictionary<String, String> formKeys, String pdfPath)
{
var templatePath = System.Web.HttpContext.Current.Server.MapPath(pdfPath);
var reader = new PdfReader(templatePath);
var outStream = new MemoryStream();
var stamper = new PdfStamper(reader, outStream);

var form = stamper.AcroFields;
form.GenerateAppearances = true; //Added this line, fixed my problem
var fieldKeys = form.Fields.Keys;

foreach (KeyValuePair<String, String> pair in formKeys)
{
if (fieldKeys.Any(f => f == pair.Key))
{
form.SetField(pair.Key, pair.Value);
}
}
stamper.Close();
reader.Close();

return flattenPdf(outStream.ToArray());
}

and the flattenPdf stays the same as in my question.

Concatenate pdf without flattening but preserve fields

Bruno's answer originally assumed the stamper.setFormFlattening(true) call from the OP's original code indicated that the form should be flattened. As it turned out this was not the case, the fields were meant to remain.

Thus, Bruno removed the form flattening line and pointed out that the result now was editable, i.e. the form fields were present. But the OP still insisted that they were gone.

As it turns out, both were right, each in his or her own way. The difference: The form fields were present in the output as widget annotations on the page but the AcroForm form definition was gone.

To make an iText 5.5.x PdfCopy instance create an AcroForm form definition in the target document which contains the merged form fields of all the copied source documents, one has to activate its mergeFields mode!

If you wonder why this mode isn't active by default: It has a drawback, all source PdfReader objects must remain open until the target PdfCopy instance is closed which can result in a substantially larger memory footprint of the code.

To work in mergeFields mode, the OP's concatenatePdfs method

void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
Document document = new Document();
FileOutputStream outputStream = new FileOutputStream(outputFile);
PdfCopy copy = new PdfSmartCopy(document, outputStream);
document.open();
for (byte[] inFile : listOfPdfFiles) {
PdfReader reader = new PdfReader(inFile);
copy.addDocument(reader);
reader.close();
}
document.close();
}

has to be rewritten like this:

void concatenatePdfs(List<byte[]> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
Document document = new Document();
FileOutputStream outputStream = new FileOutputStream(outputFile);
PdfCopy copy = new PdfSmartCopy(document, outputStream);
copy.setMergeFields();
document.open();
List<PdfReader> pdfReaders = new ArrayList<>();
for (byte[] inFile : listOfPdfFiles) {
PdfReader reader = new PdfReader(inFile);
copy.addDocument(reader);
pdfReaders.add(reader);
}
document.close();
pdfReaders.forEach(r -> r.close());
}

(CopyWithField method concatenatePdfs)

As you see, the mergeFields mode is activated by copy.setMergeFields() and the source PdfReader instances are not closed immediately after adding to copy anymore but instead collected in pdfReaders and only closed after copy is closed (which implicitly is closed during document.close()).

How to read marge fillable pdf data using itextsharp

You are unable to marge fallible PDF files because you are using an old version of iText. Please upgrade to iText 7 for .NET and read the iText 7 jump-start tutorial, more specifically chapter 6 where it says:

Merging forms

This is how it's done:

PdfDocument destPdfDocument = new PdfDocument(new PdfWriter(dest));
PdfDocument[] sources = new PdfDocument[] {
new PdfDocument(new PdfReader(SRC1)),
new PdfDocument(new PdfReader(SRC2)) };
PdfPageFormCopier formCopier = new PdfPageFormCopier();
foreach (PdfDocument sourcePdfDocument in sources) {
sourcePdfDocument.CopyPagesTo(1,
sourcePdfDocument.GetNumberOfPages(), destPdfDocument, formCopier);
sourcePdfDocument.Close();
}
destPdfDocument.Close();


Related Topics



Leave a reply



Submit