FIX: Empty PDF Form Field Values Using iText

For the past several days I've been troubleshooting a problem with blank fields on our dynamically ColdFusion generated PDF forms. Finally, I found the solution.

Sometimes data used to populate a form field on any of our PDF's would randomly show up blank when viewing the PDF in Acrobat Reader. It was very random and difficult to track because sometimes the data was there and then on the next PDF build, it was empty. However, it always happened with the same field. Here is a snapshot of the affected fields.

As I modified the other fields on this snapshot, the value of the App_PrimaryName_1 field would appear and disappear. Even moving one of the fields caused the value of App_PrimaryName_1 to disappear. Very frustrating.

Oddly enough, other PDF forms not related to this one had the same problem. The commonality between all of the fields that were missing data on any of the PDF's was that the fields were always one of the first fields on the first page of the PDF.

The following attempts to remedy the problem didn't work:
1) Starting with a fresh PDF and add the form fields one-by-one.
2) Changing the field to multi-line, various font sizes including auto, no spell check, read-only, required, etc. Any possible combination.
3) Removing all fields and placing the App_PrimaryName_1 field all by itself on the PDF.
4) Placing the App_PrimaryName_1 field on a different page.
5) Removng all pages except the page containing App_PrimaryName_1.
6) Changing PDF font colors using iText setFieldProperty() method.
7) Changing PDF field margins using iText setExtraMargin() method.
8) Regenerating the fields using iText regenerateField() method.
9) Various other techniques...

Frustrated and worried that our production date would be pushed back another week, I decided to look at my ColdFusion code that rendered the PDF data. More specifically the query that returns the data from the database. It basically contains two columns: first is the field name and second is the field value.

I noticed that it was returning field names that did not exist in the PDF. However, I still attempted to set the fields even though they were not on the PDF. So, instead of setting all fields sent from the database, I modified the ColdFusion code to verify that the field exists. This should be easy enough using the getFieldType() method in the AcroFields class. However, it seems that Adobe has modified the iText code slightly resulting in several methods being unavailable, including this one. (Adobe uses iText to render HTML pages into PDF using the <cfdocument> tag.)

Thus, I had to improvise:

// Retrieve all of the fields in the form. Luckily, this method was available. It returns a ColdFusion struct.
<cfset pdfFields = pdfForm.getFields()>

// Now begin looping through the struct.
<cfloop collection="#pdfFields#" item="t">

// Then begin looping through the array of values from the database.
<cfloop from="1" to="#ArrayLen(aryValues)#" index="k">

// If the field name exists, set the field value and then delete that entry from the array for faster processing on the next iteration.
<cfif Compare(aryValues[k].name,t) EQ 0>
   <cfset pdfForm.setField(Trim(aryValues[k].name), aryValues[k].value)>
   <cfset ArrayDeleteAt(aryValues,k)>
   <cfbreak>
</cfif>

</cfloop>

</cfloop>

After several attempts, the code change worked! It now correctly rendered all appropriate fields on the PDF form.

Thus, there seems to be either a bug in iText that causes the missing data when you attempt to set the value of a field that doesn't exist. This fix is a bit bulky compared to the original code, but cleaner in that I'm setting only fields that exist. Even with the extra processing overhead, it is actually faster at rendering the PDF's than the previous code. Probably because its only setting data on existing fields.

I think I'll have a Kit Kat.

Populating PDF Forms with ColdFusion and iText

For several years our products have populated PDF forms with data from a database. This has worked very well with our implementation of a modified cfpdfform custom tag similar to Ben Forta's.

However, one problem that we could not solve was the need to flatten the PDF forms after they've been populated. Essentially, we needed to remove the fields but leave the values in the same place. This was not possible with the cfpdfform. This is possible with CF8, but I'm not going to tell my customers to run out and purchase CF8. Especially when some of them have Enterprise licenses.

Anyway, I found iText. An open source Java library specifically designed to manipulate PDF's. It's fantastic. It should be noted that the iText library is found in the CF7 lib directory! Which makes it easy to incorporate into your CF project. How interesting of Adobe (or was it Macromedia at the time?) to use the iText library instead of one of their own?

Well, I replaced the 500 lines of code in our cfpdfform tags with about 30 lines of code using iText. Its much more efficient and, for the most part, is very reliable. Here is a sample of the code we used:

//define path and file name of the PDF template with form
pdfFile=arguments.PDFTemplate;

//define the name of my output file
newFile=arguments.PDFOutput;

//create the output file
fileIO=createObject("java","java.io.FileOutputStream").init(newFile);

//load the template PDF with the iText PDF reader
reader = createObject("java","com.lowagie.text.pdf.PdfReader").init(pdfFile);

//load the template into the iText PDF stamper and specify the output file
pdfStamper = createObject("java","com.lowagie.text.pdf.PdfStamper").init(reader, fileIO);

//create a form object to reference
pdfForm = pdfStamper.getAcroFields();

//set the form fields
for (i = 1; i LT ArrayLen(aryValues); i = i + 1){
   pdfForm.setField(Trim(aryValues[i].name),aryValues[i].value);
}

// Flatten the form to remove text fields and replace with existing data.
pdfStamper.setFormFlattening(true);

// Close stamper.
pdfStamper.close();

I'm a big fan of iText. So much so that I purchase the book "iText In Action." It has been an invaluable resource for our dynamic PDF generation.

Contact Chris SchofieldBlogCFC was created by Raymond Camden. This blog is running version 5.9.001.