Question

I currently have a project there are a number of forms that are processed and stored in the DB. Upon successful completion an admin is notified by email with the contents of that form submission.

The problem is for one of these forms i need it to look exactly like the mail order version which I have in PDF format.

So I have two basic options:

  1. Figure out all the coordinates of the "field" i need to write to, and then overlay my drawn text at those coordinates
  2. Turn the pdf into a pdf form using Acrobat Pro's form wizard and then set the field values programmatically

Option 1 i know is doable. I've done similar things before. The problem is the form is pretty complex and there are A LOT of coordinates to figure out... Moreover, there is a lot of trial and error to this process.

Option 2 seems like it would be easier so long as i can access the fields through iteration or name/id and just set the values.

So my question is, does Zend_Pdf support the manipulation of PDF form fields? I dont see anything in the API other than Submit and Reset form actions that would denote it supports this.

Additionally, if there are other OO F/OSS PDF libraries that would support option 2 i would be interested in hearing about them as well as any alternative approaches.

Was it helpful?

Solution

Sorry this is a bit late but thought this may be useful...

If you have access to add extra components to your server then you can use PDF Labs PDF Tooklit (pdftk) library - it is a command line utility but can obviously be accessed by system/exec/passthru commands in PHP. You can see pdftk info here: http://www.pdflabs.com/docs/pdftk-man-page/ PDFTK will allow you to merge PDFs, add background PDFs and fill form fields within a PDF (plus loads more) - see the fill_form switch.

If you can add pdftk to your server then you can also use Andrew Heiss's pdftk-php class to make it easier to update the form fields in your pdf from the info pulled from your DB - you can see more info at: https://github.com/andrewheiss/pdftk-php/

One last comment - if you are ever wanting to create PDFs on the fly directly from HTML then by far the best solution is WKHTML2PDF - http://code.google.com/p/wkhtmltopdf/ - it basically works like a PDF screenshot of any HTML screen (a little more complex than that but you get the idea).

As you can probably tell I have just been working on a very similar problem and have gone through soooo many headaches to get a working solution.

OTHER TIPS

prodigitalson, I wanted to post this solution for you, just in case you were still curious about wanting to find an answer. It only works on PDF's optimized for version 1.5 (Acrobat 6.0), but it does work beautifully. It is an unofficial patch for Zend Framework 1.12.3 to fill PDF Form Fields. Site with the discussion and patch

NO INSTALLATION, NO OUTSIDE PROGRAMS, NO COORDINATES

First update your php.ini file with something like the following (note: i will have to change my .ini file on my actual web server when I upload these changes):

include_path = ".;C:\wamp\www\includes"

Just a note: I moved all of the library contents out from 'ZendFramework-1.12.3\library' folder into a folder called Zend: C:\wamp\www\includes\Zend just for the ease of referencing the library (which is all you need anyways).

Then in your php file (I used 'DIRECTORY_SEPARATOR' so that you can use it on a Win or Unix server and that I won't have to make any code changes depending on where my .php file is, I'll only have to make server configuration changes):

require_once('Zend'.DIRECTORY_SEPARATOR.'Loader'.DIRECTORY_SEPARATOR.'Autoloader.php');
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('Zend_');

And then for the actual code use:

$pdf = Zend_Pdf::load('input-file-containing-form.pdf');
$pdf->setTextField('name', 'Someone');
$pdf->setTextField('address', '1 Main Street');
$pdf->setTextField('city', 'Cyberspace');
$pdf->save('outputfile.pdf');

Or as I did it for my purposes (I also included the code that I used to email my finished employment application and then delete the .pdf file so that it doesn't clog up my server: attach_mailer_class.php available here Copyright (c) 2006, Olaf Lederer):

// Write $_POST form data to associative array
foreach ($_POST as $key => $value) { 
    $NameArray[$key] = $value;
}

// Path to PDF application fillable file
$pdf_path = dirname(__FILE__) . "\\docs";
$pdf_filename = 'employment_applicationJBzend.pdf';
$pdf_file_path = $pdf_path . "\\" . $pdf_filename;

// Path to PDF application file save location
$result_path = dirname(__FILE__) . "\\results";
$result_filename = ucfirst($_POST['first_name']) . ucfirst($_POST['last_name']) . $filedatetime . '.pdf';
$result_file_path = $result_path . "\\" . $result_filename;

//Filling PDF fields | Example: $pdf->setTextField('position_applied_for', 'IT Manager');
$pdf = Zend_Pdf::load($pdf_file_path);

foreach ($NameArray as $key1 => $value) {
    foreach($ExceptionArray as $key2 => $value)
    {
        if($key1 == $ExceptionArray[$key2]){
            $boolSetText = false;
            break;
        }else{
            $boolSetText = true;
        }
    }
    if($boolSetText){
        $pdf->setTextField($key1, $NameArray[$key1]); 
    }
}
$pdf->save($result_file_path);

//Create and send message using 'attach_mailer_class.php
$email = new attach_mailer($from_name, $from_mail, $mail_to, $cc = "", $bcc = "", $subject);
$email->text_body = $message;
$email->add_attach_file($result_file_path);
// $email->add_attach_file("ip2nation.zip"); 
$email->process_mail();
unlink($result_file_path);

If page no longer exists here is the patch for PDF.php (which if you don't know how to run the actual patch, basically you go through your PDF.php file and replace all of the lines that below have a '+' in front of them. You can find where they are by the location tag '@@ -202,6 +202,13 @@' which is right around line 200, then just copy and paste to replace the old code with the new):

--- Pdf.php.orig    2009-11-15 17:52:57.000000000 +0100
+++ Pdf.php 2010-01-07 04:05:23.000000000 +0100
@@ -202,6 +202,13 @@
      * @var array
      */
     protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
+    
+    /**
+     * List of form fields
+     *
+     * @var array - Associative array, key: name of form field, value: Zend_Pdf_Element
+     */
+    protected $_formFields = array();

     /**
      * Request used memory manager
@@ -315,6 +322,7 @@

             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
             $this->_loadOutlines($this->_trailer->Root);
+            $this->_loadFormfields($this->_trailer->Root);

             if ($this->_trailer->Info !== null) {
                 $this->properties = $this->_trailer->Info->toPhp();
@@ -557,6 +565,61 @@
             $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
         }
     }
+    
+    /**
+     * Load form fields
+     * Populates the _formFields array, for later lookup of fields by name
+     *
+     * @param Zend_Pdf_Element_Reference $root Document catalog entry
+     */
+    protected function _loadFormFields(Zend_Pdf_Element_Reference $root)
+    {
+      if ($root->AcroForm === null || $root->AcroForm->Fields === null) {
+        return;
+      }
+      
+      foreach ($root->AcroForm->Fields->items as $field)
+      {
+          if ( $field->FT->value == 'Tx' && $field->T !== null ) /* We only support fields that are textfields and have a name */
+          {
+              $this->_formFields[$field->T->value] = $field;
+          }
+      }
+      
+      if ( !$root->AcroForm->NeedAppearances || !$root->AcroForm->NeedAppearances->value )
+      {
+        /* Ask the .pdf viewer to generate its own appearance data, so we do not have to */
+        $root->AcroForm->add(new Zend_Pdf_Element_Name('NeedAppearances'), new Zend_Pdf_Element_Boolean(true) );
+        $root->AcroForm->touch();
+      }
+    }
+    
+    /**
+     * Retrieves a list with the names of the AcroForm textfields in the PDF
+     *
+     * @return array of strings
+     */
+    public function getTextFieldNames()
+    {
+      return array_keys($this->_formFields);
+    }
+    
+    /**
+     * Sets the value of an AcroForm text field
+     *
+     * @param string $name Name of textfield
+     * @param string $value Value
+     * @throws Zend_Pdf_Exception if the textfield does not exist in the pdf
+     */
+    public function setTextField($name, $value)
+    {
+      if ( !isset($this->_formFields[$name]))
+        throw new Zend_Pdf_Exception("Field '$name' does not exist or is not a textfield");
+      
+      $field = $this->_formFields[$name];
+      $field->add(new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value) );
+      $field->touch();      
+    }

     /**
      * Orginize pages to tha pages tree structure.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top