web/lib/Zend/Pdf.php
changeset 1230 68c69c656a2c
parent 807 877f952ae2bd
equal deleted inserted replaced
1229:5a6b6e770365 1230:68c69c656a2c
    12  * obtain it through the world-wide-web, please send an email
    12  * obtain it through the world-wide-web, please send an email
    13  * to license@zend.com so we can send you a copy immediately.
    13  * to license@zend.com so we can send you a copy immediately.
    14  *
    14  *
    15  * @category   Zend
    15  * @category   Zend
    16  * @package    Zend_Pdf
    16  * @package    Zend_Pdf
    17  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    17  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    19  * @version    $Id: Pdf.php 24593 2012-01-05 20:35:02Z matthew $
    19  * @version    $Id$
    20  */
    20  */
    21 
    21 
    22 
    22 
    23 /** User land classes and interfaces turned on by Zend/Pdf.php file inclusion. */
    23 /** User land classes and interfaces turned on by Zend/Pdf.php file inclusion. */
    24 /** @todo Section should be removed with ZF 2.0 release as obsolete            */
    24 /** @todo Section should be removed with ZF 2.0 release as obsolete            */
    76  * Class agregates document level properties and entities (pages, bookmarks,
    76  * Class agregates document level properties and entities (pages, bookmarks,
    77  * document level actions, attachments, form object, etc)
    77  * document level actions, attachments, form object, etc)
    78  *
    78  *
    79  * @category   Zend
    79  * @category   Zend
    80  * @package    Zend_Pdf
    80  * @package    Zend_Pdf
    81  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    81  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
    82  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    82  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    83  */
    83  */
    84 class Zend_Pdf
    84 class Zend_Pdf
    85 {
    85 {
    86   /**** Class Constants ****/
    86   /**** Class Constants ****/
    93     /**
    93     /**
    94      * PDF file header.
    94      * PDF file header.
    95      */
    95      */
    96     const PDF_HEADER  = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
    96     const PDF_HEADER  = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
    97 
    97 
    98 
    98     /**
       
    99      * Form field options
       
   100      */
       
   101     const PDF_FORM_FIELD_READONLY = 1;
       
   102     const PDF_FORM_FIELD_REQUIRED = 2;
       
   103     const PDF_FORM_FIELD_NOEXPORT = 4;
    99 
   104 
   100     /**
   105     /**
   101      * Pages collection
   106      * Pages collection
   102      *
   107      *
   103      * @todo implement it as a class, which supports ArrayAccess and Iterator interfaces,
   108      * @todo implement it as a class, which supports ArrayAccess and Iterator interfaces,
   199      *
   204      *
   200      * @var Zend_Pdf_Parser
   205      * @var Zend_Pdf_Parser
   201      */
   206      */
   202     protected $_parser;
   207     protected $_parser;
   203 
   208 
   204 
       
   205     /**
   209     /**
   206      * List of inheritable attributesfor pages tree
   210      * List of inheritable attributesfor pages tree
   207      *
   211      *
   208      * @var array
   212      * @var array
   209      */
   213      */
   210     protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
   214     protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
       
   215 
       
   216     /**
       
   217      * List of form fields
       
   218      *
       
   219      * @var array - Associative array, key: name of form field, value: Zend_Pdf_Element
       
   220      */
       
   221     protected $_formFields = array();
   211 
   222 
   212     /**
   223     /**
   213      * True if the object is a newly created PDF document (affects save() method behavior)
   224      * True if the object is a newly created PDF document (affects save() method behavior)
   214      * False otherwise
   225      * False otherwise
   215      *
   226      *
   297      * If $source is a string and $load is false, then it loads document
   308      * If $source is a string and $load is false, then it loads document
   298      * from a binary string.
   309      * from a binary string.
   299      *
   310      *
   300      * If $source is a string and $load is true, then it loads document
   311      * If $source is a string and $load is true, then it loads document
   301      * from a file.
   312      * from a file.
   302 
       
   303      * $revision used to roll back document to specified version
   313      * $revision used to roll back document to specified version
   304      * (0 - current version, 1 - previous version, 2 - ...)
   314      * (0 - current version, 1 - previous version, 2 - ...)
   305      *
   315      *
   306      * @param string  $source - PDF file to load
   316      * @param string  $source - PDF file to load
   307      * @param integer $revision
   317      * @param integer $revision
       
   318      * @param bool    $load
   308      * @throws Zend_Pdf_Exception
   319      * @throws Zend_Pdf_Exception
   309      * @return Zend_Pdf
   320      * @return Zend_Pdf
   310      */
   321      */
   311     public function __construct($source = null, $revision = null, $load = false)
   322     public function __construct($source = null, $revision = null, $load = false)
   312     {
   323     {
   328                 $this->_loadPages($this->_trailer->Root->Pages);
   339                 $this->_loadPages($this->_trailer->Root->Pages);
   329             }
   340             }
   330 
   341 
   331             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
   342             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
   332             $this->_loadOutlines($this->_trailer->Root);
   343             $this->_loadOutlines($this->_trailer->Root);
       
   344             $this->_loadJavaScript($this->_trailer->Root);
       
   345             $this->_loadFormFields($this->_trailer->Root);
   333 
   346 
   334             if ($this->_trailer->Info !== null) {
   347             if ($this->_trailer->Info !== null) {
   335                 $this->properties = $this->_trailer->Info->toPhp();
   348                 $this->properties = $this->_trailer->Info->toPhp();
   336 
   349 
   337                 if (isset($this->properties['Trapped'])) {
   350                 if (isset($this->properties['Trapped'])) {
   440 
   453 
   441         $this->pages = array();
   454         $this->pages = array();
   442         $this->_loadPages($this->_trailer->Root->Pages);
   455         $this->_loadPages($this->_trailer->Root->Pages);
   443     }
   456     }
   444 
   457 
   445 
       
   446     /**
   458     /**
   447      * Load pages recursively
   459      * Load pages recursively
   448      *
   460      *
   449      * @param Zend_Pdf_Element_Reference $pages
   461      * @param Zend_Pdf_Element_Reference $pages
   450      * @param array|null $attributes
   462      * @param array|null                 $attributes
       
   463      * @throws Zend_Pdf_Exception
   451      */
   464      */
   452     protected function _loadPages(Zend_Pdf_Element_Reference $pages, $attributes = array())
   465     protected function _loadPages(Zend_Pdf_Element_Reference $pages, $attributes = array())
   453     {
   466     {
   454         if ($pages->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
   467         if ($pages->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
   455             require_once 'Zend/Pdf/Exception.php';
   468             require_once 'Zend/Pdf/Exception.php';
   534 
   547 
   535     /**
   548     /**
   536      * Load outlines recursively
   549      * Load outlines recursively
   537      *
   550      *
   538      * @param Zend_Pdf_Element_Reference $root Document catalog entry
   551      * @param Zend_Pdf_Element_Reference $root Document catalog entry
       
   552      * @throws Zend_Pdf_Exception
   539      */
   553      */
   540     protected function _loadOutlines(Zend_Pdf_Element_Reference $root)
   554     protected function _loadOutlines(Zend_Pdf_Element_Reference $root)
   541     {
   555     {
   542         if ($root->Outlines === null) {
   556         if ($root->Outlines === null) {
   543             return;
   557             return;
   571         $this->_originalOutlines = $this->outlines;
   585         $this->_originalOutlines = $this->outlines;
   572 
   586 
   573         if ($root->Outlines->Count !== null) {
   587         if ($root->Outlines->Count !== null) {
   574             $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
   588             $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
   575         }
   589         }
       
   590     }
       
   591 
       
   592     /**
       
   593      * Load JavaScript
       
   594      *
       
   595      * Populates the _javaScript string, for later use of getJavaScript method.
       
   596      *
       
   597      * @param Zend_Pdf_Element_Reference $root Document catalog entry
       
   598      */
       
   599     protected function _loadJavaScript(Zend_Pdf_Element_Reference $root)
       
   600     {
       
   601         if (null === $root->Names || null === $root->Names->JavaScript
       
   602             || null === $root->Names->JavaScript->Names
       
   603         ) {
       
   604             return;
       
   605         }
       
   606 
       
   607         foreach ($root->Names->JavaScript->Names->items as $item) {
       
   608             if ($item instanceof Zend_Pdf_Element_Reference
       
   609                 && $item->S->value === 'JavaScript'
       
   610             ) {
       
   611                 $this->_javaScript[] = $item->JS->value;
       
   612             }
       
   613         }
       
   614     }
       
   615   
       
   616     /**
       
   617      * Load form fields
       
   618      *
       
   619      * Populates the _formFields array, for later lookup of fields by name
       
   620      *
       
   621      * @param Zend_Pdf_Element_Reference $root Document catalog entry
       
   622      */
       
   623     protected function _loadFormFields(Zend_Pdf_Element_Reference $root)
       
   624     {
       
   625         if ($root->AcroForm === null || $root->AcroForm->Fields === null) {
       
   626             return;
       
   627         }
       
   628 
       
   629         foreach ($root->AcroForm->Fields->items as $field) {
       
   630             /* We only support fields that are textfields and have a name */
       
   631             if ($field->FT && $field->FT->value == 'Tx' && $field->T
       
   632                 && $field->T !== null
       
   633             ) {
       
   634                 $this->_formFields[$field->T->value] = $field;
       
   635             }
       
   636         }
       
   637 
       
   638         if (!$root->AcroForm->NeedAppearances
       
   639             || !$root->AcroForm->NeedAppearances->value
       
   640         ) {
       
   641             /* Ask the .pdf viewer to generate its own appearance data, so we do not have to */
       
   642             $root->AcroForm->add(
       
   643                 new Zend_Pdf_Element_Name('NeedAppearances'),
       
   644                 new Zend_Pdf_Element_Boolean(true)
       
   645             );
       
   646             $root->AcroForm->touch();
       
   647         }
       
   648     }
       
   649 
       
   650     /**
       
   651      * Retrieves a list with the names of the AcroForm textfields in the PDF
       
   652      *
       
   653      * @return array of strings
       
   654      */
       
   655     public function getTextFieldNames()
       
   656     {
       
   657         return array_keys($this->_formFields);
       
   658     }
       
   659 
       
   660     /**
       
   661      * Sets the value of an AcroForm text field
       
   662      *
       
   663      * @param string $name Name of textfield
       
   664      * @param string $value Value
       
   665      * @throws Zend_Pdf_Exception if the textfield does not exist in the pdf
       
   666      */
       
   667     public function setTextField($name, $value)
       
   668     {
       
   669         if (!isset($this->_formFields[$name])) {
       
   670             throw new Zend_Pdf_Exception(
       
   671                 "Field '$name' does not exist or is not a textfield"
       
   672             );
       
   673         }
       
   674 
       
   675         /** @var Zend_Pdf_Element $field */
       
   676         $field = $this->_formFields[$name];
       
   677         $field->add(
       
   678             new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value)
       
   679         );
       
   680         $field->touch();
       
   681     }
       
   682 
       
   683     /**
       
   684      * Sets the properties for an AcroForm text field
       
   685      *
       
   686      * @param string $name
       
   687      * @param mixed  $bitmask
       
   688      * @throws Zend_Pdf_Exception
       
   689      */
       
   690     public function setTextFieldProperties($name, $bitmask)
       
   691     {
       
   692         if (!isset($this->_formFields[$name])) {
       
   693             throw new Zend_Pdf_Exception(
       
   694                 "Field '$name' does not exist or is not a textfield"
       
   695             );
       
   696         }
       
   697 
       
   698         $field = $this->_formFields[$name];
       
   699         $field->add(
       
   700             new Zend_Pdf_Element_Name('Ff'),
       
   701             new Zend_Pdf_Element_Numeric($bitmask)
       
   702         );
       
   703         $field->touch();
       
   704     }
       
   705 
       
   706     /**
       
   707      * Marks an AcroForm text field as read only
       
   708      *
       
   709      * @param string $name
       
   710      */
       
   711     public function markTextFieldAsReadOnly($name)
       
   712     {
       
   713         $this->setTextFieldProperties($name, self::PDF_FORM_FIELD_READONLY);
   576     }
   714     }
   577 
   715 
   578     /**
   716     /**
   579      * Orginize pages to tha pages tree structure.
   717      * Orginize pages to tha pages tree structure.
   580      *
   718      *
   914     }
  1052     }
   915 
  1053 
   916     /**
  1054     /**
   917      * Set specified named destination
  1055      * Set specified named destination
   918      *
  1056      *
   919      * @param string $name
  1057      * @param string                                             $name
   920      * @param Zend_Pdf_Destination_Explicit|Zend_Pdf_Action_GoTo $target
  1058      * @param Zend_Pdf_Destination_Explicit|Zend_Pdf_Action_GoTo $destination
       
  1059      * @throws Zend_Pdf_Exception
   921      */
  1060      */
   922     public function setNamedDestination($name, $destination = null)
  1061     public function setNamedDestination($name, $destination = null)
   923     {
  1062     {
   924         if ($destination !== null  &&
  1063         if ($destination !== null  &&
   925             !$destination instanceof Zend_Pdf_Action_GoTo  &&
  1064             !$destination instanceof Zend_Pdf_Action_GoTo  &&
   973     /**
  1112     /**
   974      * Resolve destination.
  1113      * Resolve destination.
   975      *
  1114      *
   976      * Returns Zend_Pdf_Page page object or null if destination is not found within PDF document.
  1115      * Returns Zend_Pdf_Page page object or null if destination is not found within PDF document.
   977      *
  1116      *
   978      * @param Zend_Pdf_Destination $destination  Destination to resolve
  1117      * @param Zend_Pdf_Destination $destination Destination to resolve
   979      * @param boolean $refreshPagesHash  Refresh page collection hashes before processing
  1118      * @param bool $refreshPageCollectionHashes Refresh page collection hashes before processing
   980      * @return Zend_Pdf_Page|null
  1119      * @return Zend_Pdf_Page|null
   981      * @throws Zend_Pdf_Exception
  1120      * @throws Zend_Pdf_Exception
   982      */
  1121      */
   983     public function resolveDestination(Zend_Pdf_Destination $destination, $refreshPageCollectionHashes = true)
  1122     public function resolveDestination(Zend_Pdf_Destination $destination, $refreshPageCollectionHashes = true)
   984     {
  1123     {
  1032      * Returns null if root node is deleted or updated action overwise.
  1171      * Returns null if root node is deleted or updated action overwise.
  1033      *
  1172      *
  1034      * @todo Give appropriate name and make method public
  1173      * @todo Give appropriate name and make method public
  1035      *
  1174      *
  1036      * @param Zend_Pdf_Action $action
  1175      * @param Zend_Pdf_Action $action
  1037      * @param boolean $refreshPagesHash  Refresh page collection hashes before processing
  1176      * @param bool $refreshPageCollectionHashes Refresh page collection hashes before processing
  1038      * @return Zend_Pdf_Action|null
  1177      * @return Zend_Pdf_Action|null
  1039      */
  1178      */
  1040     protected function _cleanUpAction(Zend_Pdf_Action $action, $refreshPageCollectionHashes = true)
  1179     protected function _cleanUpAction(Zend_Pdf_Action $action, $refreshPageCollectionHashes = true)
  1041     {
  1180     {
  1042         if ($this->_pageReferences === null  ||  $refreshPageCollectionHashes) {
  1181         if ($this->_pageReferences === null  ||  $refreshPageCollectionHashes) {
  1127     /**
  1266     /**
  1128      * Extract font attached to the page by specific font name
  1267      * Extract font attached to the page by specific font name
  1129      *
  1268      *
  1130      * $fontName should be specified in UTF-8 encoding
  1269      * $fontName should be specified in UTF-8 encoding
  1131      *
  1270      *
       
  1271      * @param string $fontName
  1132      * @return Zend_Pdf_Resource_Font_Extracted|null
  1272      * @return Zend_Pdf_Resource_Font_Extracted|null
  1133      * @throws Zend_Pdf_Exception
  1273      * @throws Zend_Pdf_Exception
  1134      */
  1274      */
  1135     public function extractFont($fontName)
  1275     public function extractFont($fontName)
  1136     {
  1276     {
  1384 
  1524 
  1385             return '';
  1525             return '';
  1386         }
  1526         }
  1387     }
  1527     }
  1388 
  1528 
  1389 
  1529     /**
  1390     /**
  1530      * Sets the document-level JavaScript
  1391      * Set the document-level JavaScript
  1531      *
  1392      *
  1532      * Resets and appends
  1393      * @param string $javascript
  1533      *
  1394      */
  1534      * @param string|array $javaScript
  1395     public function setJavaScript($javascript)
  1535      */
  1396     {
  1536     public function setJavaScript($javaScript)
  1397         $this->_javaScript = $javascript;
  1537     {
  1398     }
  1538         $this->resetJavaScript();
  1399 
  1539 
       
  1540         $this->addJavaScript($javaScript);
       
  1541     }
       
  1542 
       
  1543     /**
       
  1544      * Resets the document-level JavaScript
       
  1545      */
       
  1546     public function resetJavaScript()
       
  1547     {
       
  1548         $this->_javaScript = null;
       
  1549 
       
  1550         $root = $this->_trailer->Root;
       
  1551         if (null === $root->Names || null === $root->Names->JavaScript) {
       
  1552             return;
       
  1553         }
       
  1554         $root->Names->JavaScript = null;
       
  1555     }
       
  1556 
       
  1557     /**
       
  1558      * Appends JavaScript to the document-level JavaScript
       
  1559      *
       
  1560      * @param string|array $javaScript
       
  1561      * @throws Zend_Pdf_Exception
       
  1562      */
       
  1563     public function addJavaScript($javaScript)
       
  1564     {
       
  1565         if (empty($javaScript)) {
       
  1566             throw new Zend_Pdf_Exception(
       
  1567                 'JavaScript must be a non empty string or array of strings'
       
  1568             );
       
  1569         }
       
  1570 
       
  1571         if (!is_array($javaScript)) {
       
  1572             $javaScript = array($javaScript);
       
  1573         }
       
  1574 
       
  1575         if (null === $this->_javaScript) {
       
  1576             $this->_javaScript = $javaScript;
       
  1577         } else {
       
  1578             $this->_javaScript = array_merge($this->_javaScript, $javaScript);
       
  1579         }
       
  1580 
       
  1581         if (!empty($this->_javaScript)) {
       
  1582             $items = array();
       
  1583 
       
  1584             foreach ($this->_javaScript as $javaScript) {
       
  1585                 $jsCode = array(
       
  1586                     'S'  => new Zend_Pdf_Element_Name('JavaScript'),
       
  1587                     'JS' => new Zend_Pdf_Element_String($javaScript)
       
  1588                 );
       
  1589                 $items[] = new Zend_Pdf_Element_String('EmbeddedJS');
       
  1590                 $items[] = $this->_objFactory->newObject(
       
  1591                     new Zend_Pdf_Element_Dictionary($jsCode)
       
  1592                 );
       
  1593             }
       
  1594 
       
  1595             $jsRef = $this->_objFactory->newObject(
       
  1596                 new Zend_Pdf_Element_Dictionary(
       
  1597                     array('Names' => new Zend_Pdf_Element_Array($items))
       
  1598                 )
       
  1599             );
       
  1600 
       
  1601             if (null === $this->_trailer->Root->Names) {
       
  1602                 $this->_trailer->Root->Names = new Zend_Pdf_Element_Dictionary();
       
  1603             }
       
  1604             $this->_trailer->Root->Names->JavaScript = $jsRef;
       
  1605         }
       
  1606     }
  1400 
  1607 
  1401     /**
  1608     /**
  1402      * Convert date to PDF format (it's close to ASN.1 (Abstract Syntax Notation
  1609      * Convert date to PDF format (it's close to ASN.1 (Abstract Syntax Notation
  1403      * One) defined in ISO/IEC 8824).
  1610      * One) defined in ISO/IEC 8824).
  1404      *
  1611      *