Hack 78 Permanently Merge a PDF Form and its Data

figs/expert.gif figs/hack78.gif

Provide online users with a copy of their completed form to save.

Adobe Reader enables a user to add, change, view, and print form data, but it does not enable a user to save the filled-in PDF form to disk. Saving the file produces a lovely copy of an empty form. How annoying!

Correct this problem server-side by merging the PDF form and its data. Then, offer this filled-in form as a download for the user's records. After merging, the form fields remain interactive, even though they display the user's data. Go a step further and flatten this form so that field data becomes a permanent part of the PDF pages. After flattening, filled-in fields are no longer interactive. You can merge and flatten forms using the iText library or our command-line pdftk. Both are free software.

6.6.1 Merge or Flatten a Form and Its Data in Java

The iText library (http://www.lowagie.com/iText/ or http://itextpdf.sf.net) is a remarkable tool for manipulating PDF documents. The following Java program, merge_pdf_fdf, demonstrates how to merge or flatten a PDF and its form data FDF [Hack #77] using iText. Run this code from your command line, or integrate it into your web application.

/*

  merge_pdf_fdf, version 1.0

  merge an input PDF file with an input FDF file

  to create a filled-in PDF; optionally flatten the

  FDF data so that it becomes part of the page

  http://www.pdfhacks.com/merge_pdf_fdf/



  invoke from the command line like this:



    java -classpath ./itext-paulo.jar:. \

    merge_pdf_fdf input.pdf input.fdf output.pdf



  or:



    java -classpath ./itext-paulo.jar:. \

    merge_pdf_fdf input.pdf input.fdf output.pdf flatten



  adjust the classpath to the location of your iText jar

 */



import java.io.*;

import com.lowagie.text.pdf.*;



public class merge_pdf_fdf extends java.lang.Object {

    

  public static void main(String args[]) {

    if ( args.length == 3 || args.length == 4 ) {

      try {

        // the input PDF

        PdfReader reader = 

          new PdfReader( args[0] );

        reader.consolidateNamedDestinations( );

        reader.removeUnusedObjects( );



        // the input FDF

        FdfReader fdf_reader= 

          new FdfReader( args[1] );



        // PdfStamper acts like a PdfWriter

        PdfStamper pdf_stamper= 

          new PdfStamper( reader,

                          new FileOutputStream( args[2] ) );



        if( args.length == 4 ) { // "flatten"

          // filled-in data becomes a permanent part of the page

          pdf_stamper.setFormFlattening( true );

        }

        else {

          // filled-in data will 'stick' to the form fields,

          // but it will remain interactive

          pdf_stamper.setFormFlattening( false );

        }



        // sets the form fields from the input FDF

        AcroFields fields=

          pdf_stamper.getAcroFields( );

        fields.setFields( fdf_reader );



        // closing the stamper closes the underlying

        // PdfWriter; the PDF document is written

        pdf_stamper.close( );

      }

      catch( Exception ee ) {

        ee.printStackTrace( );

      }

    }

    else { // input error

      System.err.println("arguments: file1.pdf file2.fdf destfile [flatten]");

    }

  }

}

To create a command-line Java program, copy the preceding code into a file named merge_pdf_fdf.java. Then, compile merge_pdf_fdf.java using javac, setting the classpath to the name and location of your iText jar:

javac -classpath  ./itext-paulo.jar  merge_pdf_fdf.java

Finally, invoke merge_pdf_fdf like so:

java -classpath  ./itext-paulo.jar :. \

merge_pdf_fdf 

input.pdf input.fdf output.pdf

6.6.2 Merge or Flatten a Form and Its Data with pdftk

Use pdftk [Hack #79] to merge a form with an FDF datafile [Hack #77] and create a new PDF. The fields will display the given data, but they also remain interactive. pdftk's fill_form operation takes the filename of an FDF file as its argument. For example:

pdftk  form.pdf  fill_form  data.fdf  output  filled_form.pdf

You can't combine the fill_form operation with any other operation (e.g., cat), but you can supply additional output options for encryption [Hack #52] .

Flatten form data permanently into the page by adding the flatten_form output option. The resulting PDF data will no longer be interactive.

pdftk  form.pdf  fill_form  data.fdf  output  filled_form.pdf  flatten_form

Or, if your PDF form already has field data, just flatten it:

pdftk  filled_form.pdf  output  flattened_form.pdf  flatten_form

6.6.3 Merge or Flatten with pdftk in PHP

After installing pdftk on your web server, you can invoke it from your PHP scripts to merge PDF forms with FDF data. Use our PHP script forge_fdf [Hack #77] to cast your data into FDF. Then, save this FDF data into a temporary file. Finally, call pdftk to create a new PDF from your PDF form and FDF data.

The following PHP code could be used for this purpose:

<?php

// session_fdf is your function for converting

// the user's session state into an FDF string

$fdf_ss= session_fdf( $_GET['id'] );



$temp_fn= tempnam( '/tmp', 'tempfdf' );

$temp_fp= fopen( $temp_fn, 'wb' );

if( $temp_fp ) {

  fwrite( $temp_fp, $fdf_ss );

  fclose( $temp_fp );



  header( 'Content-type: application/pdf' );

  passthru( '/usr/local/bin/pdftk form.pdf fill_form '.$temp_fn.

            ' output - flatten' ); // output to stdout (-)



  unlink( $temp_fn );

}

?>