O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
PDF Hacks
By Sid Steward
August 2004
More Info

HACK
#76
Drive PDF Forms with Your Data
Convert your data into FDF so that Acrobat or Reader can merge it with a PDF form
The Code
[Discuss (0) | Link to this hack]

The Code

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;
}
?>


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.