Sunday, December 23, 2007

Getting started with iText - Part 18 (Concatenating PDF Forms)

In Part 18 we translate the ConcatenateForms.java example. It is an extremely simple example that concatenates multiple PDF forms. You can run this example with MX7 or CF8 and theoretically MX6 (if you have installed iText). Of course if you are running CF8 you should use the new cfpdf tag for merging PDF files and forms instead. A CF8 only example is included at the end for comparison purposes.

*Groan* Do I have to RTM?


Truth be told some of us have an unfortunate tendency to skim articles, and ignore the referenced links, so we can jump to the interesting part: the code. (Now when I say "us" .. I do not mean myself of course.. ;) This entry is a good example of how not reading the documentation can burn you.

Now the iText tutorial is a bit old, but it does mention two important things about the PdfCopyFields object used in this example. First it states the PdfCopyFields class keeps all documents in memory unlike PdfCopy. That is an important distinction. Second it notes that if you have fields with the same name they will be merged so, it's probably a good idea to rename them if that's the case.

As a test, I created two copies of the SimpleRegistrationForm.pdf file. Then used the sample code to concatenate the two files. Because fields with the same name are merged, typing a value into one of the fields, such as "name", effects all of the fields with that same name. Since the process takes the separate forms and merges them into one AcroForm, this behavior makes sense. But I think it is worth emphasizing so this behavior does not take you by surprise. Note, using CF8's cfpdf tag to merge forms produces the same results.

Now for the good part ..



What you will need for this example


Download the following (2) files from the iText site and place them in the same directory as your .cfm script


Code


Documentation: Manipulating existing PDF documents
Source: ConcatenateForms.java

iText Example

<h1>Concatenate PDF Forms example</h1>
Concatenates 2 PDF files with forms. The resulting PDF has 1 merged AcroForm.<br>
<cfscript>
savedErrorMessage = "";

// cfSearching: All file paths are relative to the current directory
fullPathToOutputFile = ExpandPath("./ConcatenatedForms.pdf");
// cfSearching: Using an array of paths here instead of separate variables for each file
arrayOfInputFiles = arrayNew(1);
arrayAppend(arrayOfInputFiles, ExpandPath("./SimpleRegistrationForm.pdf"));
arrayAppend(arrayOfInputFiles, ExpandPath("./TextFields.pdf"));

try {

outStream = createObject("java", "java.io.FileOutputStream").init( fullPathToOutputFile );
copy = createObject("java", "com.lowagie.text.pdf.PdfCopyFields").init(outStream);
PdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader");

for (fileIndex = 1; fileIndex LTE arrayLen(arrayOfInputFiles); fileIndex = fileIndex + 1) {
reader = PdfReader.init( arrayOfInputFiles[fileIndex] );
copy.addDocument(reader);
}

WriteOutput("Finished!");
}
catch (java.language.Exception de) {
savedErrorMessage = de;
}
// cfSearching: close iText and output stream objects
if (IsDefined("copy")) {
copy.close();
}
if (IsDefined("outputStream")) {
outputStream.close();
}
</cfscript>


<!--- show any errors --->
<cfif len(savedErrorMessage) gt 0>
Error. Unable to create file
<cfdump var="#savedErrorMessage#">
</cfif>


ColdFusion 8 example

<h1>Concatenate PDF Forms example (ColdFusion 8)</h1>

<cfset listOfInputFiles = ExpandPath("./SimpleRegistrationForm.pdf")>
<cfset listOfInputFiles = listAppend(listOfInputFiles, ExpandPath("./TextFields.pdf"))>
<cfset fullPathToOutputFile = ExpandPath("./ConcatenatedForms.pdf")>

<cfpdf action="merge"
source="#listOfInputFiles#"
destination="#fullPathToOutputFile#"
keepbookmark="true"
overwrite="true">

4 comments:

Anonymous,  September 8, 2008 at 1:36 PM  

Hello

Your tutorial is amazing

Have you got a chapter about tables ?

Turnbull

cfSearching September 8, 2008 at 7:07 PM  

@Turnbull,

It is not my tutorial ;-) It is just a translation of the java tutorials done by Bruno Lowagie and crew. While a bit dated, they are a good introduction to iText. The book "iText in Action" is also an excellent resource.
http://itextdocs.lowagie.com/tutorial/index.php

Anyway, I posted a few more translations from the iText tutorial on tables here:
iText - Table examples

HTH

Anonymous,  June 11, 2009 at 9:01 AM  

Thanks for all your work, your examples have been invaluable.

We switched from ActivePDF to ABCPDF which was a huge step forward in stability when calling third party COM Objects from within ColdFusion to handle PDF manipulation. But there are still issues when dealing with really large files so I'm looking at converting our existing code to use the iText library.

In ABCPDF there is an interesting function to remap a PDF using myDoc.RemapPages("1 2 3 1 2 3") This would take a three page doc and remap it to a six page doc barely increasing the size because the additional pages were merely references to the original pages.

If you were to do this in iText would the better approach be to use templates or can it be done with PdfCopy without storing multiple copies of the same page within the PDF, only multiple refrences. On small files it's no big deal but when you create pdf's with hundreds or a thousand pages everything starts to breakdown.

cfSearching June 13, 2009 at 4:46 AM  

@chrisutt,

Interesting question. It should work with PdfCopy (not PdfCopyFields). I believe PdfCopy only stores a reference to imported pages.

But the best approach may also depend on what you need to copy (bookmarks, annotations, forms, etcetera).

- Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep