From 0922edb1cb0e4173429813b0500117b6c2857bc5 Mon Sep 17 00:00:00 2001 From: Jesper Ek Date: Wed, 27 Aug 2014 16:35:50 +0200 Subject: [PATCH] Release 1.12.8 --- library/Zend/Pdf.php | 218 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 6 deletions(-) diff --git a/library/Zend/Pdf.php b/library/Zend/Pdf.php index 792bc21..615b3ca 100644 --- a/library/Zend/Pdf.php +++ b/library/Zend/Pdf.php @@ -96,6 +96,13 @@ class Zend_Pdf 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 * * @todo implement it as a class, which supports ArrayAccess and Iterator interfaces, @@ -199,7 +206,6 @@ class Zend_Pdf */ protected $_parser; - /** * List of inheritable attributesfor pages tree * @@ -208,6 +214,13 @@ class Zend_Pdf 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 * @@ -328,6 +341,8 @@ class Zend_Pdf $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(); @@ -575,6 +590,130 @@ class Zend_Pdf } /** + * 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. @@ -1387,17 +1526,84 @@ class Zend_Pdf } } - /** - * Set the document-level JavaScript + * Sets the document-level JavaScript + * + * Resets and appends * - * @param string $javascript + * @param string|array $javaScript + */ + public function setJavaScript($javaScript) + { + $this->resetJavaScript(); + + $this->addJavaScript($javaScript); + } + + /** + * Resets the document-level JavaScript */ - public function setJavaScript($javascript) + public function resetJavaScript() { - $this->_javaScript = $javascript; + $this->_javaScript = null; + + $root = $this->_trailer->Root; + if (null === $root->Names || null === $root->Names->JavaScript) { + return; + } + $root->Names->JavaScript = null; } + /** + * Appends JavaScript to the document-level JavaScript + * + * @param string|array $javaScript + * @throws Zend_Pdf_Exception + */ + public function addJavaScript($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