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 ****/ |
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'; |
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) { |
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 * |