Hi all,
Since a few people expressed interest, I thought I'd write up a quick tutorial and
example. Please send me any feedback you may have. Hope people find it useful.
------------------
Populating PDF Forms using ColdFusion and the Adobe FDF file format.
The objective is to deliver an PDF form on the fly with values pre-filled on the
server side with dynamic data. I'll assume that to get the dynamic data, you'd
execute a query, say qryGetData as an example. The data can really be from any
source. Example CFML and FDF code is included below, while the sample PDF file can be
downloaded from <http://www.worlddesign.com/testing/myForm.pdf>. You can also run the
example from <http://www.worlddesign.com/testing/returnPDF.cfm>
1) First you will need your PDF form. Create a regular PDF document, and using the
full Acrobat (not the reader) "draw" your form fields in it. Give these form fields
some meaningful names (I like to give them the same names as my CF variables to keep
things clear).
2) For the form default values, put in CF variables! So for example you have a field
for First Name-- for it's value, in Acrobat, you could put in #qryGetData.FirstName#
(the default value for a given field is found in it's Properties/Options dialog).
Don't forget the # signs. You can actually use ANY CF command/function within the
field values.
3) From Acrobat, export your form definitions as an FDF file (File/Export/Form
Data...). This creates a plain text file containing the field definitions and the
default field values (ie, all your CF variables). Save the PDF too. The PDF will
need to be accessible via a URL from your site, and the FDF will need to be accessible
to CF Server (as a CFINCLUDE). For the example lets call them myForm.fdf and
myForm.pdf.
4) Open up the FDF file in CF Studio (or your favorite plain text editor). You'll
need to make a few minor adjustments here. The FDF format is very terse and is pretty
easy to work with. You don't have to understand it's structure to do any of the
following.
a) Scroll down to the end and find the line that has '/F (myForm.pdf)>>' on
it. Replace the part inside the parenthesis with a full URL which will point to the
PDF template on your server. For example, the line might read like this (w/out the
quotes): '/F (http://www.myServer.com/PDF/myForm.pdf)>>'
b) Wrap CFOUTPUT tags around the entire contents of the FDF file.
c) If you used any CF functions within your form field values (such as
DollarFormat()) you will need to do a search/replace. Acrobat escapes all '(' and ')'
by preceding them with a backslash (\). Replace '\(' with '(' and '\)' with ')'
globally (this is very quick).
d) Occasionally Acrobat will put a line break in the middle of your CF
variable. You can find these later during debugging, and/or quickly eyeball the FDF
file and look for line breaks in the middle of a variable. The line break will also
be escaped with a '\' preceding the break. Remove the line break and the \. You can
also double-check that there are no stray # signs while you're at it. If you have a
large FDF, it's easier to fix this during debug since CF will tell you the exact line
number of the error.
5) After you save the modified FDF file, you're ready for the CF part. It's pretty
simple and there are probably several ways to do it.
Here's what I've done. You have your action template (or whatever), say
act_returnPDF.cfm. First you want to make sure that the action which runs this
template won't generate any other content besides the PDF file (so no other
headers/footers/etc). Avoid white space too. A leading blank line or any other
content in the results will screw things up.
6) In your action template, perform your query that returns the variable names you've
used in the PDF/FDF templates, or otherwise assign values to the variables.
7) FDF uses the parenthesis characters as delimiters (see step 4c, above). If any of
your content may have '(' or ')' in it, you need to escape these using '\' in front of
them. See the sample code for an example.
8) Then, you basically just CFINCLUDE the FDF file. Any CFML and all CF variables are
interpreted by CF and replaced with data. Since you want to send the FDF file to the
browser as an FDF (not text/html), you can use CFCONTENT to return the output. Here's
an actual example (also see the sample code below for a more complete example):
<CFHEADER NAME="Content-Disposition" VALUE="inline; filename=myForm.fdf">
<CFCONTENT TYPE="application/vnd.fdf">
<CFINCLUDE TEMPLATE="myForm.fdf">
The CFHEADER line sets the file name that will be sent (so IE knows it's an FDF file),
while the CFCONTENT makes sure Netscape knows what to do (since it uses the MIME type
like a good little browser should).
Note: To debug, comment out, or otherwise exclude, the CFHEADER and CFCONTENT tags.
This will allow you to see the raw FDF file being returned, and will show any CF
errors that might be thrown. Make sure you get a "clean" FDF file being returned
before continuing. A clean FDF file will start with '%FDF' and end with '%%EOF'.
9) Then it gets a touch complicated to follow the flow. When the browser sees the FDF
file type, it runs Acrobat (full or Reader) which opens the FDF file and sees the /F
switch and the URL to the PDF template. Acrobat then requests the PDF file via that
URL. Once Acrobat retrieves the template PDF, it performs the field substitutions,
replacing the defined form fields in the PDF with the data that is specified in the
FDF. The result is a PDF file with the data dynamically populated.
The full range of options for PDF documents applies, such as protecting from editing,
selecting, printing, etc., etc.
Another cool feature of this is that you can have one FDF file which works for any
number of PDF templates. As long as the fields are named the same across the PDFs,
you can dynamically drive the URL in the /F switch in the FDF file to point to any
PDF. For example you have the same basic form that is laid out differently in each
State. With just one FDF file you can deliver any State's custom form using different
PDF templates.
Active-X Method:
BTW, please note that there is a slightly different method for doing this using
Adobe's ActiveX FDF component. Stephen Aylor recently posted a good description of
how to do it that way to CF-Talk [Subject: Re: follow-up question to PDF [sample
ActiveX], Date: Mon, 13 Nov 2000 20:39:58 -0800]. As Stephen points out in a
follow-up email, this method might be preferable to the above if your form field
definitions change often, since you can avoid mucking around in the FDF file
altogether. There are probably other methods for accomplishing the same thing, using
Adobe's FDF tools <http://partners.adobe.com/asn/developer/acrosdk/forms.html>
-------------------------
Code examples:
-------------------------
This is myForm.fdf:
(Note that it points to a PDF template that's located on my server. You can
download/save the PDF to examine it, if you wish, via the URL specified.)
------ start code ------
<CFOUTPUT>%FDF-1.2
%����
1 0 obj
<<
/FDF << /Fields [ << /V (#variables.Body#)/T (Body)>> << /V (#variables.To#)/T (To)>>
]
/F (http://www.worlddesign.com/testing/myForm.pdf)>>
>>
endobj
trailer
<<
/Root 1 0 R
>>
%%EOF
</CFOUTPUT>
----- end code -----
This is returnPDF.cfm:
----- start code ------
<CFSETTING ENABLECFOUTPUTONLY="YES">
<!--- returnPDF.cfm, example/test code.
Returns an Adobe FDF file to the browser. The FDF file contains form value
data and a pointer to a template PDF file. The FDF file being used is called
myForm.fdf. The end result is that the PDF template is displayed to the user using
the form data dynamically populated in the FDF file.
See <http://partners.adobe.com/asn/developer/acrosdk/forms.html> for more
information of Adobe's FDF format.
--->
<!--- Set a debug flag. A value of 1 will help debug the FDF file if necessary. --->
<CFPARAM NAME="URL.Debug" DEFAULT="0">
<!--- Set values for the variables used in myForm.fdf.
This could also be a query or whatever. --->
<CFSET variables.To = "[EMAIL PROTECTED]">
<CFSET variables.Body = "This is some text (for) the body of the message.">
<!--- Need to escape any parenthesis using the backslash
(the parenthesis are used as control characters in FDF).
This step is only necessary if your values might contain '(' or ')'
characters. --->
<CFSET variables.Body = ReplaceList( variables.Body, "(,)", "\(,\)" )>
<!--- If debug is not on, specify the file name and content type for the browser --->
<CFIF NOT URL.Debug>
<CFHEADER NAME="Content-Disposition" VALUE="inline; filename=myForm.fdf">
<CFCONTENT TYPE="application/vnd.fdf">
</CFIF>
<!--- Avoid any white space before the actual content is sent.
A leading blank line will cause an Acrobat error. --->
<CFSETTING ENABLECFOUTPUTONLY="NO"><CFINCLUDE TEMPLATE="myForm.fdf">
------ end code --------
Kind regards,
-Max Paperno
11/14/00, all reproduction rights reserved.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Structure your ColdFusion code with Fusebox. Get the official book at
http://www.fusionauthority.com/bkinfo.cfm
Archives: http://www.mail-archive.com/[email protected]/
Unsubscribe: http://www.houseoffusion.com/index.cfm?sidebar=lists