web/lib/Zend/Pdf.php
changeset 1230 68c69c656a2c
parent 807 877f952ae2bd
--- a/web/lib/Zend/Pdf.php	Thu May 07 15:10:09 2015 +0200
+++ b/web/lib/Zend/Pdf.php	Thu May 07 15:16:02 2015 +0200
@@ -14,9 +14,9 @@
  *
  * @category   Zend
  * @package    Zend_Pdf
- * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id: Pdf.php 24593 2012-01-05 20:35:02Z matthew $
+ * @version    $Id$
  */
 
 
@@ -78,7 +78,7 @@
  *
  * @category   Zend
  * @package    Zend_Pdf
- * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  */
 class Zend_Pdf
@@ -95,7 +95,12 @@
      */
     const PDF_HEADER  = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
 
-
+    /**
+     * Form field options
+     */
+    const PDF_FORM_FIELD_READONLY = 1;
+    const PDF_FORM_FIELD_REQUIRED = 2;
+    const PDF_FORM_FIELD_NOEXPORT = 4;
 
     /**
      * Pages collection
@@ -201,7 +206,6 @@
      */
     protected $_parser;
 
-
     /**
      * List of inheritable attributesfor pages tree
      *
@@ -210,6 +214,13 @@
     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();
+
+    /**
      * True if the object is a newly created PDF document (affects save() method behavior)
      * False otherwise
      *
@@ -299,12 +310,12 @@
      *
      * If $source is a string and $load is true, then it loads document
      * from a file.
-
      * $revision used to roll back document to specified version
      * (0 - current version, 1 - previous version, 2 - ...)
      *
      * @param string  $source - PDF file to load
      * @param integer $revision
+     * @param bool    $load
      * @throws Zend_Pdf_Exception
      * @return Zend_Pdf
      */
@@ -330,6 +341,8 @@
 
             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
             $this->_loadOutlines($this->_trailer->Root);
+            $this->_loadJavaScript($this->_trailer->Root);
+            $this->_loadFormFields($this->_trailer->Root);
 
             if ($this->_trailer->Info !== null) {
                 $this->properties = $this->_trailer->Info->toPhp();
@@ -442,12 +455,12 @@
         $this->_loadPages($this->_trailer->Root->Pages);
     }
 
-
     /**
      * Load pages recursively
      *
      * @param Zend_Pdf_Element_Reference $pages
-     * @param array|null $attributes
+     * @param array|null                 $attributes
+     * @throws Zend_Pdf_Exception
      */
     protected function _loadPages(Zend_Pdf_Element_Reference $pages, $attributes = array())
     {
@@ -536,6 +549,7 @@
      * Load outlines recursively
      *
      * @param Zend_Pdf_Element_Reference $root Document catalog entry
+     * @throws Zend_Pdf_Exception
      */
     protected function _loadOutlines(Zend_Pdf_Element_Reference $root)
     {
@@ -576,6 +590,130 @@
     }
 
     /**
+     * Load JavaScript
+     *
+     * Populates the _javaScript string, for later use of getJavaScript method.
+     *
+     * @param Zend_Pdf_Element_Reference $root Document catalog entry
+     */
+    protected function _loadJavaScript(Zend_Pdf_Element_Reference $root)
+    {
+        if (null === $root->Names || null === $root->Names->JavaScript
+            || null === $root->Names->JavaScript->Names
+        ) {
+            return;
+        }
+
+        foreach ($root->Names->JavaScript->Names->items as $item) {
+            if ($item instanceof Zend_Pdf_Element_Reference
+                && $item->S->value === 'JavaScript'
+            ) {
+                $this->_javaScript[] = $item->JS->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) {
+            /* We only support fields that are textfields and have a name */
+            if ($field->FT && $field->FT->value == 'Tx' && $field->T
+                && $field->T !== null
+            ) {
+                $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"
+            );
+        }
+
+        /** @var Zend_Pdf_Element $field */
+        $field = $this->_formFields[$name];
+        $field->add(
+            new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value)
+        );
+        $field->touch();
+    }
+
+    /**
+     * Sets the properties for an AcroForm text field
+     *
+     * @param string $name
+     * @param mixed  $bitmask
+     * @throws Zend_Pdf_Exception
+     */
+    public function setTextFieldProperties($name, $bitmask)
+    {
+        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('Ff'),
+            new Zend_Pdf_Element_Numeric($bitmask)
+        );
+        $field->touch();
+    }
+
+    /**
+     * Marks an AcroForm text field as read only
+     *
+     * @param string $name
+     */
+    public function markTextFieldAsReadOnly($name)
+    {
+        $this->setTextFieldProperties($name, self::PDF_FORM_FIELD_READONLY);
+    }
+
+    /**
      * Orginize pages to tha pages tree structure.
      *
      * @todo atomatically attach page to the document, if it's not done yet.
@@ -916,8 +1054,9 @@
     /**
      * Set specified named destination
      *
-     * @param string $name
-     * @param Zend_Pdf_Destination_Explicit|Zend_Pdf_Action_GoTo $target
+     * @param string                                             $name
+     * @param Zend_Pdf_Destination_Explicit|Zend_Pdf_Action_GoTo $destination
+     * @throws Zend_Pdf_Exception
      */
     public function setNamedDestination($name, $destination = null)
     {
@@ -975,8 +1114,8 @@
      *
      * Returns Zend_Pdf_Page page object or null if destination is not found within PDF document.
      *
-     * @param Zend_Pdf_Destination $destination  Destination to resolve
-     * @param boolean $refreshPagesHash  Refresh page collection hashes before processing
+     * @param Zend_Pdf_Destination $destination Destination to resolve
+     * @param bool $refreshPageCollectionHashes Refresh page collection hashes before processing
      * @return Zend_Pdf_Page|null
      * @throws Zend_Pdf_Exception
      */
@@ -1034,7 +1173,7 @@
      * @todo Give appropriate name and make method public
      *
      * @param Zend_Pdf_Action $action
-     * @param boolean $refreshPagesHash  Refresh page collection hashes before processing
+     * @param bool $refreshPageCollectionHashes Refresh page collection hashes before processing
      * @return Zend_Pdf_Action|null
      */
     protected function _cleanUpAction(Zend_Pdf_Action $action, $refreshPageCollectionHashes = true)
@@ -1129,6 +1268,7 @@
      *
      * $fontName should be specified in UTF-8 encoding
      *
+     * @param string $fontName
      * @return Zend_Pdf_Resource_Font_Extracted|null
      * @throws Zend_Pdf_Exception
      */
@@ -1386,18 +1526,85 @@
         }
     }
 
+    /**
+     * Sets the document-level JavaScript
+     *
+     * Resets and appends
+     *
+     * @param string|array $javaScript
+     */
+    public function setJavaScript($javaScript)
+    {
+        $this->resetJavaScript();
+
+        $this->addJavaScript($javaScript);
+    }
+
+    /**
+     * Resets the document-level JavaScript
+     */
+    public function resetJavaScript()
+    {
+        $this->_javaScript = null;
+
+        $root = $this->_trailer->Root;
+        if (null === $root->Names || null === $root->Names->JavaScript) {
+            return;
+        }
+        $root->Names->JavaScript = null;
+    }
 
     /**
-     * Set the document-level JavaScript
+     * Appends JavaScript to the document-level JavaScript
      *
-     * @param string $javascript
+     * @param string|array $javaScript
+     * @throws Zend_Pdf_Exception
      */
-    public function setJavaScript($javascript)
+    public function addJavaScript($javaScript)
     {
-        $this->_javaScript = $javascript;
+        if (empty($javaScript)) {
+            throw new Zend_Pdf_Exception(
+                'JavaScript must be a non empty string or array of strings'
+            );
+        }
+
+        if (!is_array($javaScript)) {
+            $javaScript = array($javaScript);
+        }
+
+        if (null === $this->_javaScript) {
+            $this->_javaScript = $javaScript;
+        } else {
+            $this->_javaScript = array_merge($this->_javaScript, $javaScript);
+        }
+
+        if (!empty($this->_javaScript)) {
+            $items = array();
+
+            foreach ($this->_javaScript as $javaScript) {
+                $jsCode = array(
+                    'S'  => new Zend_Pdf_Element_Name('JavaScript'),
+                    'JS' => new Zend_Pdf_Element_String($javaScript)
+                );
+                $items[] = new Zend_Pdf_Element_String('EmbeddedJS');
+                $items[] = $this->_objFactory->newObject(
+                    new Zend_Pdf_Element_Dictionary($jsCode)
+                );
+            }
+
+            $jsRef = $this->_objFactory->newObject(
+                new Zend_Pdf_Element_Dictionary(
+                    array('Names' => new Zend_Pdf_Element_Array($items))
+                )
+            );
+
+            if (null === $this->_trailer->Root->Names) {
+                $this->_trailer->Root->Names = new Zend_Pdf_Element_Dictionary();
+            }
+            $this->_trailer->Root->Names->JavaScript = $jsRef;
+        }
     }
 
-
     /**
      * Convert date to PDF format (it's close to ASN.1 (Abstract Syntax Notation
      * One) defined in ISO/IEC 8824).