ColdFusion has had the ability to work with PDF forms for a while, but I never got a chance to actually play with it till last night. A client needed me to build a simple POC that used an HTML form to accept user input. This user needed the input to be passed to the PDF and saved there. For the most part the process was painless, but there were a few gotchas I wanted to share with folks.
The first thing I ran into was that the initial PDF was not compliant with ColdFusion's PDF form handling. The PDF certainly looked normal. I could open it in Acrobat. I could see and modify form fields. But in ColdFusion something odd happened when I tried to get data out of it.
<cfpdfform action="read" source="#expandPath('./cms116.pdf')#" xmldata="x" result="r">
</cfpdfform>
<cfdump var="#x#" label="XMLData">
<cfdump var="#r#" label="Result">
The code above should have retrieved the form data as both a struct and XML. When I ran it I got: Either datafile XML or data XML contained in the PDF document is invalid.
On a whim I got rid of the result attribute. Then it worked. But if I tried to parse the XML variable (X), I got an error stating the XML was invalid. At this point I had no idea what to do so I asked around. All around smart guy Dave Watts suggested that the PDF I had was probably using an older style of form called "AcroForm". I needed a PDF using the newer style called a LiveCycle form. He went on to say:
It has to be a LiveCycle form. LiveCycle forms are created using LiveCycle Designer, which ships with Acrobat Pro (and is available separately). LC forms are basically a PDF container for an XFA payload - the form itself is represented as an XML document conforming to the XFA schema. Forms created directly in Acrobat are not LC forms - they're generally called "AcroForms". Third-party PDF form creation tools generally create AcroForms rather than LC forms, but there may be third-party tools available that create LC forms that I don't know about.
http://en.wikipedia.org/wiki/XFA
Luckily the client had a newer form of the PDF and as soon as I changed to that, it worked. So cool. I still think ColdFusion should be able to sniff that form type and throw an exception (one that is helpful), but, now you know, and knowing is you know what.
Now that my script worked, I could use the struct as a way of seeing the names of the form fields. This is important because you probably can't guess them just by looking. Some fields did have obvious names but many did not. I ended up using this script as a guide for building my code that would then set the fields.
Here is where things got weird though. For text fields, you could easily set them by just supplying a value:
<cfpdfformparam name="FACILITy NAME" value="#form.facilityname#">
That just worked. But checkboxes were a bit weird. I tried setting them by using true, 1, and "checked" as values, but nothing worked. So then I went back to my original PDF form and did the obvious - I checked the checkbox. I then saved it, reran my "get the fields and dump them" script, and I saw that when I checked a checkbox, the value was On. Setting it to Off would turn it off.
To make it even more interesting, not all checkboxes act the same. Another field on the form had something like this: "Is Ray Cool [ ]No [ ]Yes". I assumed there would be two fields for this representing each checkbox. Instead there was only one. And get this. The no value was only true when the value was No and yes was only true when the value was yes. Both of these were case sensitive. Again - I had to use my original form quite a bit here to see what values were being persisted when I entered data.
Hope this helps! Here is the complete POC I built. I can't share the original PDF, but hopefully it gives you an idea of how such an app could be built. Note that normally I'd use a dynamic name for the PDF as the code right now would not work with multiple users.
<cfif structKeyExists(form, "submit")>
<cfpdfform action="populate" source="#expandPath('./cms116_2.pdf')#" destination="#expandPath('./test.pdf')#" overwrite=true>
<!--- text field --->
<cfpdfformparam name="FACILITy NAME" value="#form.facilityname#">
<!--- checkbox it MUST be "On", not "on" --->
<cfif structKeyExists(form, "initialapp")>
<cfpdfformparam name="Initial Application" value="On">
<cfelse>
<cfpdfformparam name="Initial Application" value="Off">
</cfif>
<!--- testing yes no --->
<cfpdfformparam name="Is this a shared lab" value="#form.sharedlab#">
</cfpdfform>
<p>
PDF saved: <a href="test.pdf">test.pdf</a>
</p>
</cfif>
<form method="post">
facility name: <input type="text" name="facilityname"><br/>
initial application? <input type="checkbox" name="initialapp"><br/>
shared lab? <select name="sharedlab">
<option>yes</option>
<option>No</option>
</select><br/>
<input type="submit" name="submit">
</form>
Archived Comments
Just to clarify, Adobe Acrobat creates PDF forms that are stored as bit data. LiveCycle Designer creates PDF forms that are stored as XML. If you want to use ColdFusion to work with the form and data, you must create the form in LiveCycle Designer, or open the Acrobat form in LiveCycle Designer and modify it to suit your needs as XML.
ColdFusion could not access the Acrobat form bit data, which I think is why no error or exception was thrown.
When you Acrobat versions 7-9 or X, you get LiveCycle Designer installed in the same installation. Users of Acrobat XI do not get LIveCycle Designer installed with the Acrobat installation. It is now a separate license but does have a trial version available like all the other Adobe products.
Livecycle Designer offers much more flexibility when working with PDF forms, and is a program that many do not even know exists. As I start off saying in my PDF classes, a PDF form is not a PDF form, there are different types of PDF forms. You need to be sure your PDF form is the correct type of form for your workflow.
Thank you for posting on this. I just this morning put this same information into my blog at cmairscreate.com.
Thanks for the info Candyce. One small note:
"ColdFusion could not access the Acrobat form bit data, which I think is why no error or exception was thrown."
To be clear, CF *did* throw an error when the result attribute was used, and it threw an error if you tried to parse the XML. I really think CF should be able to detect AcroForm vs LiveCycle and throw a *better* exception.
Ray, this is a very helpful post. I found that Google Chrome's preview of a PDF does not display the input data even though it is there. Firefox is terrible with the document and cannot even parse where the fields start and end. Internet Explorer uses Acrobat's in browser viewer. Of course opening the document in Acrobat itself works perfectly.
It would be really awesome if CF could just "print" the PDF to a flattened version that didn't have the fields anymore. Just the data in the right places. I know that this is a LiveCycle thing, but I run into formatting issues with Acrobat a lot when working with forms and non-Acrobat readers.
I'm on my mobile device so can't check the docs now but I'm pretty sure cfpdf has a flatten option.
It does have a flatten option and that fixes a lot of problems. Be sure to flatten after any merges also.
<cfpdf action="write" source="#output_space#" destination="#view_file#" overwrite="yes" flatten="yes" saveOption = "linear" />
Thanks Daniel and Ray. Unfortunately I use LiveCycle Designer, so I cannot use the cfpdf tag on those forms.
I thought we agreed that *those* were the ones that worked?
I've done a bunch of pdf forms that are submitted to CF for processing. One thing that has moved me away from them is that Chrome now defaults to their PDF viewer which does not support submitting pdfs. So you need to educate your users to go into Chrome and make sure they are using Adobe Reader.
Looking forward to CF11 and the added PDF functionality.
Craig - check out CF11 PDF functionality. they are embedding many livecycle features. I believe LC output will basically be there for you.
Thank you so much for this. Was trying to read a client's PDF form to see about filling it in using CF10, and getting that same error. After reading this, opening the form in LiveCycleDesigner, and saving it from there...it works! *whew*