Convert your data into FDF so that Acrobat or Reader can merge it with a PDF form.
As discussed in [Hack #75], you can deliver filled-out PDF forms on the Web by serving FDF data. FDF data contains the URL for your PDF form and the data with which to fill the form. Upon receiving FDF data, Acrobat (or Reader) will open the referenced PDF form and then populate it with the given information. The next problem is how to easily create FDF data on your web server.
The PDF Reference [Hack #98] describes FDF in dizzying detail, and Adobe offers a free-of-charge FDF Toolkit with a dizzying license (http://partners.adobe.com/asn/acrobat/forms.jsp). But what you need is usually easy to create from the comfort of your favorite web programming language. We provide such a script written in PHP. It converts form data into an FDF file suitable for filling our basic PDF form [Hack #74] .
|
Elaborate forms might require the high-caliber Adobe FDF Toolkit to create suitable FDF. Most forms merely require their field data cast into the FDF syntax. We offer the forge_fdf script for this purpose. The FDF example from [Hack #75] shows the pattern evident in simple FDF files.
Instead of using a general-purpose FDF function or library, you can also consider exporting an FDF file from your form and then converting it into a template. Replace the form values with variables or other placeholders. Serve this template back to your form after filling the variables with user data. forge_fdf includes functions for encoding PDF strings and names [Hack #80], which you might find useful when filling in your template.
Pass form data and a PDF's URL into forge_fdf, and it returns the corresponding FDF as a string. Create an FDF file with this string or serve it directly to the client browser with Content-type: application/vnd.fdf. We offer an example a little later.
You must remember some FDF peculiarities when passing arguments to forge_fdf.
Provide the PDF form's URL (or filename) unless you plan to pass this FDF data as part of a larger URL that already references the PDF form. For example, if the FDF data will be served to the user like so:
http://localhost/fine_form.pdf#FDF=http://localhost/fine_data.fdf
pass an empty string as $pdf_form_url.
To exit a PDF form and replace it with an HTML page in the user's browser, serve the PDF an FDF with $pdf_form_url set to your HTML page's URL.
Load text, combo box, and listbox data into this array. It should be an array of string field names mapped to string field values. If you want a form field to be hidden or read-only, you must also add its name to $fields_hidden or $fields_readonly.
Load checkbox and radio button data into this array. It should be an array of string field names mapped to string field values. Often, true and false correspond to the case-sensitive strings Yes and Off. If you want a form field to be hidden or read-only, you must also add its name to $fields_hidden or $fields_readonly.
If you want a field to disappear from view, add its name to this array. Any field listed here also must be in $fdf_data_strings or $fdf_data_names.
If you don't want the user tinkering with a field's data, add its name to this array. Any field listed here also must be in $fdf_data_strings or $fdf_data_names.
For example, the following script uses forge_fdf to serve FDF data that should cause the user's browser to open http://localhost/form.pdf and set its fields to match our values:
<?php require_once('forge_fdf.php'); $pdf_form_url= "http://localhost/form.pdf"; $fdf_data_strings= array( 'text1' => $_GET['t'], 'text2' => 'Egads!' ); $fdf_data_names= array( 'check1' => 'Off', 'check2' => 'Yes' ); $fields_hidden= array( 'text2', 'check1' ); $fields_readonly= array( 'text1' ); header( 'content-type: application/vnd.fdf' ); echo forge_fdf( $pdf_form_url, $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly ); ?>
To see a more elaborate example of forge_fdf in action, visit http://www.pdfhacks.com/form_session/. Tinker with the online example or download PHP source code from this web page.
|
Copy this code into a file named forge_fdf.php and include it in your PHP scripts. Or, adapt this algorithm to your favorite language. Visit http://www.pdfhacks.com/forge_fdf/ to download the latest version.
<?php /* forge_fdf, by Sid Steward version 1.0 visit: http://www.pdfhacks.com/forge_fdf/ For text fields, combo boxes, and list boxes, add field values as a name => value pair to $fdf_data_strings. For checkboxes and radio buttons, add field values as a name => value pair to $fdf_data_names. Typically, true and false correspond to the (case-sensitive) names "Yes" and "Off". Any field added to the $fields_hidden or $fields_readonly array also must be a key in $fdf_data_strings or $fdf_data_names; this might be changed in the future Any field listed in $fdf_data_strings or $fdf_data_names that you want hidden or read-only must have its field name added to $fields_hidden or $fields_readonly; do this even if your form has these bits set already PDF can be particular about CR and LF characters, so I spelled them out in hex: CR == \x0d : LF == \x0a */ function escape_pdf_string( $ss ) { $ss_esc= ''; $ss_len= strlen( $ss ); for( $ii= 0; $ii< $ss_len; ++$ii ) { if( ord($ss{$ii})== 0x28 || // open paren ord($ss{$ii})== 0x29 || // close paren ord($ss{$ii})== 0x5c ) // backslash { $ss_esc.= chr(0x5c).$ss{$ii}; // escape the character w/ backslash } else if( ord($ss{$ii}) < 32 || 126 < ord($ss{$ii}) ) { $ss_esc.= sprintf( "\\%03o", ord($ss{$ii}) ); // use an octal code } else { $ss_esc.= $ss{$ii}; } } return $ss_esc; } function escape_pdf_name( $ss ) { $ss_esc= ''; $ss_len= strlen( $ss ); for( $ii= 0; $ii< $ss_len; ++$ii ) { if( ord($ss{$ii}) < 33 || 126 < ord($ss{$ii}) || ord($ss{$ii})== 0x23 ) // hash mark { $ss_esc.= sprintf( "#%02x", ord($ss{$ii}) ); // use a hex code } else { $ss_esc.= $ss{$ii}; } } return $ss_esc; } function forge_fdf( $pdf_form_url, $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly ) { $fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; // header $fdf.= "1 0 obj\x0d<< "; // open the Root dictionary $fdf.= "\x0d/FDF << "; // open the FDF dictionary $fdf.= "/Fields [ "; // open the form Fields array // string data, used for text fields, combo boxes, and list boxes foreach( $fdf_data_strings as $key => $value ) { $fdf.= "<< /V (".escape_pdf_string($value).")". "/T (".escape_pdf_string($key).") "; if( in_array( $key, $fields_hidden ) ) $fdf.= "/SetF 2 "; else $fdf.= "/ClrF 2 "; if( in_array( $key, $fields_readonly ) ) $fdf.= "/SetFf 1 "; else $fdf.= "/ClrFf 1 "; $fdf.= ">> \x0d"; } // name data, used for checkboxes and radio buttons // (e.g., /Yes and /Off for true and false) foreach( $fdf_data_names as $key => $value ) { $fdf.= "<< /V /".escape_pdf_name($value). " /T (".escape_pdf_string($key).") "; if( in_array( $key, $fields_hidden ) ) $fdf.= "/SetF 2 "; else $fdf.= "/ClrF 2 "; if( in_array( $key, $fields_readonly ) ) $fdf.= "/SetFf 1 "; else $fdf.= "/ClrFf 1 "; $fdf.= ">> \x0d"; } $fdf.= "] \x0d"; // close the Fields array // the PDF form filename or URL, if given if( $pdf_form_url ) { $fdf.= "/F (".escape_pdf_string($pdf_form_url).") \x0d"; } $fdf.= ">> \x0d"; // close the FDF dictionary $fdf.= ">> \x0dendobj\x0d"; // close the Root dictionary // trailer; note the "1 0 R" reference to "1 0 obj" above $fdf.= "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d"; $fdf.= "%%EOF\x0d\x0a"; return $fdf; } ?>