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:
<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.
