00001 <?php
00027 require_once 'Zend/Pdf/Page.php';
00028
00030 require_once 'Zend/Pdf/Style.php';
00031
00033 require_once 'Zend/Pdf/Color/GrayScale.php';
00034
00036 require_once 'Zend/Pdf/Color/Rgb.php';
00037
00039 require_once 'Zend/Pdf/Color/Cmyk.php';
00040
00042 require_once 'Zend/Pdf/Color/Html.php';
00043
00045 require_once 'Zend/Pdf/Image.php';
00046
00048 require_once 'Zend/Pdf/Font.php';
00049
00050
00052 require_once 'Zend/Pdf/Element.php';
00053 require_once 'Zend/Pdf/Element/Array.php';
00054 require_once 'Zend/Pdf/Element/String/Binary.php';
00055 require_once 'Zend/Pdf/Element/Boolean.php';
00056 require_once 'Zend/Pdf/Element/Dictionary.php';
00057 require_once 'Zend/Pdf/Element/Name.php';
00058 require_once 'Zend/Pdf/Element/Null.php';
00059 require_once 'Zend/Pdf/Element/Numeric.php';
00060 require_once 'Zend/Pdf/Element/String.php';
00061
00062
00078 class Zend_Pdf
00079 {
00080
00081
00085 const PDF_VERSION = '1.4';
00086
00090 const PDF_HEADER = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
00091
00092
00093
00103 public $pages = array();
00104
00120 public $properties = array();
00121
00129 protected $_originalProperties = array();
00130
00136 protected $_javaScript = null;
00137
00144 protected $_namedTargets = array();
00145
00151 public $outlines = array();
00152
00159 protected $_originalOutlines = array();
00160
00167 protected $_originalOpenOutlinesCount = 0;
00168
00174 protected $_trailer = null;
00175
00181 protected $_objFactory = null;
00182
00188 protected static $_memoryManager = null;
00189
00196 protected $_parser;
00197
00198
00204 protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
00205
00211 static public function getMemoryManager()
00212 {
00213 if (self::$_memoryManager === null) {
00214 require_once 'Zend/Memory.php';
00215 self::$_memoryManager = Zend_Memory::factory('none');
00216 }
00217
00218 return self::$_memoryManager;
00219 }
00220
00226 static public function setMemoryManager(Zend_Memory_Manager $memoryManager)
00227 {
00228 self::$_memoryManager = $memoryManager;
00229 }
00230
00231
00239 public static function parse(&$source = null, $revision = null)
00240 {
00241 return new Zend_Pdf($source, $revision);
00242 }
00243
00251 public static function load($source = null, $revision = null)
00252 {
00253 return new Zend_Pdf($source, $revision, true);
00254 }
00255
00265 public function save($filename, $updateOnly = false)
00266 {
00267 if (($file = @fopen($filename, $updateOnly ? 'ab':'wb')) === false ) {
00268 require_once 'Zend/Pdf/Exception.php';
00269 throw new Zend_Pdf_Exception( "Can not open '$filename' file for writing." );
00270 }
00271
00272 $this->render($updateOnly, $file);
00273
00274 fclose($file);
00275 }
00276
00296 public function __construct($source = null, $revision = null, $load = false)
00297 {
00298 require_once 'Zend/Pdf/ElementFactory.php';
00299 $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1);
00300
00301 if ($source !== null) {
00302 require_once 'Zend/Pdf/Parser.php';
00303 $this->_parser = new Zend_Pdf_Parser($source, $this->_objFactory, $load);
00304 $this->_pdfHeaderVersion = $this->_parser->getPDFVersion();
00305 $this->_trailer = $this->_parser->getTrailer();
00306 if ($this->_trailer->Encrypt !== null) {
00307 require_once 'Zend/Pdf/Exception.php';
00308 throw new Zend_Pdf_Exception('Encrypted document modification is not supported');
00309 }
00310 if ($revision !== null) {
00311 $this->rollback($revision);
00312 } else {
00313 $this->_loadPages($this->_trailer->Root->Pages);
00314 }
00315
00316 $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
00317 $this->_loadOutlines($this->_trailer->Root);
00318
00319 if ($this->_trailer->Info !== null) {
00320 $this->properties = $this->_trailer->Info->toPhp();
00321
00322 if (isset($this->properties['Trapped'])) {
00323 switch ($this->properties['Trapped']) {
00324 case 'True':
00325 $this->properties['Trapped'] = true;
00326 break;
00327
00328 case 'False':
00329 $this->properties['Trapped'] = false;
00330 break;
00331
00332 case 'Unknown':
00333 $this->properties['Trapped'] = null;
00334 break;
00335
00336 default:
00337
00338
00339 break;
00340 }
00341 }
00342
00343 $this->_originalProperties = $this->properties;
00344 }
00345 } else {
00346 $this->_pdfHeaderVersion = Zend_Pdf::PDF_VERSION;
00347
00348 $trailerDictionary = new Zend_Pdf_Element_Dictionary();
00349
00353 $docId = md5(uniqid(rand(), true));
00354 $docIdLow = substr($docId, 0, 16);
00355 $docIdHigh = substr($docId, 16, 16);
00356
00357 $trailerDictionary->ID = new Zend_Pdf_Element_Array();
00358 $trailerDictionary->ID->items[] = new Zend_Pdf_Element_String_Binary($docIdLow);
00359 $trailerDictionary->ID->items[] = new Zend_Pdf_Element_String_Binary($docIdHigh);
00360
00361 $trailerDictionary->Size = new Zend_Pdf_Element_Numeric(0);
00362
00363 require_once 'Zend/Pdf/Trailer/Generator.php';
00364 $this->_trailer = new Zend_Pdf_Trailer_Generator($trailerDictionary);
00365
00369 $docCatalog = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
00370 $docCatalog->Type = new Zend_Pdf_Element_Name('Catalog');
00371 $docCatalog->Version = new Zend_Pdf_Element_Name(Zend_Pdf::PDF_VERSION);
00372 $this->_trailer->Root = $docCatalog;
00373
00377 $docPages = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
00378 $docPages->Type = new Zend_Pdf_Element_Name('Pages');
00379 $docPages->Kids = new Zend_Pdf_Element_Array();
00380 $docPages->Count = new Zend_Pdf_Element_Numeric(0);
00381 $docCatalog->Pages = $docPages;
00382 }
00383 }
00384
00390 public function revisions()
00391 {
00392 $revisions = 1;
00393 $currentTrailer = $this->_trailer;
00394
00395 while ($currentTrailer->getPrev() !== null && $currentTrailer->getPrev()->Root !== null ) {
00396 $revisions++;
00397 $currentTrailer = $currentTrailer->getPrev();
00398 }
00399
00400 return $revisions++;
00401 }
00402
00410 public function rollback($steps)
00411 {
00412 for ($count = 0; $count < $steps; $count++) {
00413 if ($this->_trailer->getPrev() !== null && $this->_trailer->getPrev()->Root !== null) {
00414 $this->_trailer = $this->_trailer->getPrev();
00415 } else {
00416 break;
00417 }
00418 }
00419 $this->_objFactory->setObjectCount($this->_trailer->Size->value);
00420
00421
00422 $this->_trailer->Root->touch();
00423
00424 $this->pages = array();
00425 $this->_loadPages($this->_trailer->Root->Pages);
00426 }
00427
00428
00435 protected function _loadPages(Zend_Pdf_Element_Reference $pages, $attributes = array())
00436 {
00437 if ($pages->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
00438 require_once 'Zend/Pdf/Exception.php';
00439 throw new Zend_Pdf_Exception('Wrong argument');
00440 }
00441
00442 foreach ($pages->getKeys() as $property) {
00443 if (in_array($property, self::$_inheritableAttributes)) {
00444 $attributes[$property] = $pages->$property;
00445 $pages->$property = null;
00446 }
00447 }
00448
00449
00450 foreach ($pages->Kids->items as $child) {
00451 if ($child->Type->value == 'Pages') {
00452 $this->_loadPages($child, $attributes);
00453 } else if ($child->Type->value == 'Page') {
00454 foreach (self::$_inheritableAttributes as $property) {
00455 if ($child->$property === null && array_key_exists($property, $attributes)) {
00461 if ($attributes[$property] instanceof Zend_Pdf_Element_Object ||
00462 $attributes[$property] instanceof Zend_Pdf_Element_Reference) {
00463 $child->$property = $attributes[$property];
00464 } else {
00465 $child->$property = $this->_objFactory->newObject($attributes[$property]);
00466 }
00467 }
00468 }
00469
00470 require_once 'Zend/Pdf/Page.php';
00471 $this->pages[] = new Zend_Pdf_Page($child, $this->_objFactory);
00472 }
00473 }
00474 }
00475
00483 protected function _loadNamedDestinations(Zend_Pdf_Element_Reference $root, $pdfHeaderVersion)
00484 {
00485 if ($root->Version !== null && version_compare($root->Version->value, $pdfHeaderVersion, '>')) {
00486 $versionIs_1_2_plus = version_compare($root->Version->value, '1.1', '>');
00487 } else {
00488 $versionIs_1_2_plus = version_compare($pdfHeaderVersion, '1.1', '>');
00489 }
00490
00491 if ($versionIs_1_2_plus) {
00492
00493
00494 if ($root->Names !== null && $root->Names->Dests !== null) {
00495 require_once 'Zend/Pdf/NameTree.php';
00496 require_once 'Zend/Pdf/Target.php';
00497 foreach (new Zend_Pdf_NameTree($root->Names->Dests) as $name => $destination) {
00498 $this->_namedTargets[$name] = Zend_Pdf_Target::load($destination);
00499 }
00500 }
00501 } else {
00502
00503
00504 if ($root->Dests !== null) {
00505 if ($root->Dests->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
00506 require_once 'Zend/Pdf/Exception.php';
00507 throw new Zend_Pdf_Exception('Document catalog Dests entry must be a dictionary.');
00508 }
00509
00510 require_once 'Zend/Pdf/Target.php';
00511 foreach ($root->Dests->getKeys() as $destKey) {
00512 $this->_namedTargets[$destKey] = Zend_Pdf_Target::load($root->Dests->$destKey);
00513 }
00514 }
00515 }
00516 }
00517
00523 protected function _loadOutlines(Zend_Pdf_Element_Reference $root)
00524 {
00525 if ($root->Outlines === null) {
00526 return;
00527 }
00528
00529 if ($root->Outlines->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
00530 require_once 'Zend/Pdf/Exception.php';
00531 throw new Zend_Pdf_Exception('Document catalog Outlines entry must be a dictionary.');
00532 }
00533
00534 if ($root->Outlines->Type !== null && $root->Outlines->Type->value != 'Outlines') {
00535 require_once 'Zend/Pdf/Exception.php';
00536 throw new Zend_Pdf_Exception('Outlines Type entry must be an \'Outlines\' string.');
00537 }
00538
00539 if ($root->Outlines->First === null) {
00540 return;
00541 }
00542
00543 $outlineDictionary = $root->Outlines->First;
00544 $processedDictionaries = new SplObjectStorage();
00545 while ($outlineDictionary !== null && !$processedDictionaries->contains($outlineDictionary)) {
00546 $processedDictionaries->attach($outlineDictionary);
00547
00548 require_once 'Zend/Pdf/Outline/Loaded.php';
00549 $this->outlines[] = new Zend_Pdf_Outline_Loaded($outlineDictionary);
00550
00551 $outlineDictionary = $outlineDictionary->Next;
00552 }
00553
00554 $this->_originalOutlines = $this->outlines;
00555
00556 if ($root->Outlines->Count !== null) {
00557 $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
00558 }
00559 }
00560
00569 protected function _dumpPages()
00570 {
00571 $root = $this->_trailer->Root;
00572 $pagesContainer = $root->Pages;
00573
00574 $pagesContainer->touch();
00575 $pagesContainer->Kids->items = array();
00576
00577 foreach ($this->pages as $page ) {
00578 $page->render($this->_objFactory);
00579
00580 $pageDictionary = $page->getPageDictionary();
00581 $pageDictionary->touch();
00582 $pageDictionary->Parent = $pagesContainer;
00583
00584 $pagesContainer->Kids->items[] = $pageDictionary;
00585 }
00586
00587 $this->_refreshPagesHash();
00588
00589 $pagesContainer->Count->touch();
00590 $pagesContainer->Count->value = count($this->pages);
00591
00592
00593
00594 foreach ($this->_namedTargets as $name => $namedTarget) {
00595 if ($namedTarget instanceof Zend_Pdf_Destination_Explicit) {
00596
00597 if ($this->resolveDestination($namedTarget, false) === null) {
00598 unset($this->_namedTargets[$name]);
00599 }
00600 } else if ($namedTarget instanceof Zend_Pdf_Action) {
00601
00602 if ($this->_cleanUpAction($namedTarget, false) === null) {
00603
00604 unset($this->_namedTargets[$name]);
00605 }
00606 } else {
00607 require_once 'Zend/Pdf/Exception.php';
00608 throw new Zend_Pdf_Exception('Wrong type of named targed (\'' . get_class($namedTarget) . '\').');
00609 }
00610 }
00611
00612 // Refresh outlines
00613 require_once 'Zend/Pdf/RecursivelyIteratableObjectsContainer.php';
00614 $iterator = new RecursiveIteratorIterator(new Zend_Pdf_RecursivelyIteratableObjectsContainer($this->outlines), RecursiveIteratorIterator::SELF_FIRST);
00615 foreach ($iterator as $outline) {
00616 $target = $outline->getTarget();
00617
00618 if ($target !== null) {
00619 if ($target instanceof Zend_Pdf_Destination) {
00620 // Outline target is a destination
00621 if ($this->resolveDestination($target, false) === null) {
00622 $outline->setTarget(null);
00623 }
00624 } else if ($target instanceof Zend_Pdf_Action) {
00625 // Outline target is an action
00626 if ($this->_cleanUpAction($target, false) === null) {
00627 // Action is a GoTo action with an unresolved destination
00628 $outline->setTarget(null);
00629 }
00630 } else {
00631 require_once 'Zend/Pdf/Exception.php';
00632 throw new Zend_Pdf_Exception('Wrong outline target.');
00633 }
00634 }
00635 }
00636
00637 $openAction = $this->getOpenAction();
00638 if ($openAction !== null) {
00639 if ($openAction instanceof Zend_Pdf_Action) {
00640 // OpenAction is an action
00641 if ($this->_cleanUpAction($openAction, false) === null) {
00642 // Action is a GoTo action with an unresolved destination
00643 $this->setOpenAction(null);
00644 }
00645 } else if ($openAction instanceof Zend_Pdf_Destination) {
00646 // OpenAction target is a destination
00647 if ($this->resolveDestination($openAction, false) === null) {
00648 $this->setOpenAction(null);
00649 }
00650 } else {
00651 require_once 'Zend/Pdf/Exception.php';
00652 throw new Zend_Pdf_Exception('OpenAction has to be either PDF Action or Destination.');
00653 }
00654 }
00655 }
00656
00662 protected function _dumpNamedDestinations()
00663 {
00664 ksort($this->_namedTargets, SORT_STRING);
00665
00666 $destArrayItems = array();
00667 foreach ($this->_namedTargets as $name => $destination) {
00668 $destArrayItems[] = new Zend_Pdf_Element_String($name);
00669
00670 if ($destination instanceof Zend_Pdf_Target) {
00671 $destArrayItems[] = $destination->getResource();
00672 } else {
00673 require_once 'Zend/Pdf/Exception.php';
00674 throw new Zend_Pdf_Exception('PDF named destinations must be a Zend_Pdf_Target object.');
00675 }
00676 }
00677 $destArray = $this->_objFactory->newObject(new Zend_Pdf_Element_Array($destArrayItems));
00678
00679 $DestTree = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
00680 $DestTree->Names = $destArray;
00681
00682 $root = $this->_trailer->Root;
00683
00684 if ($root->Names === null) {
00685 $root->touch();
00686 $root->Names = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
00687 } else {
00688 $root->Names->touch();
00689 }
00690 $root->Names->Dests = $DestTree;
00691 }
00692
00696 protected function _dumpOutlines()
00697 {
00698 $root = $this->_trailer->Root;
00699
00700 if ($root->Outlines === null) {
00701 if (count($this->outlines) == 0) {
00702 return;
00703 } else {
00704 $root->Outlines = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
00705 $root->Outlines->Type = new Zend_Pdf_Element_Name('Outlines');
00706 $updateOutlinesNavigation = true;
00707 }
00708 } else {
00709 $updateOutlinesNavigation = false;
00710 if (count($this->_originalOutlines) != count($this->outlines)) {
00711 // If original and current outlines arrays have different size then outlines list was updated
00712 $updateOutlinesNavigation = true;
00713 } else if ( !(array_keys($this->_originalOutlines) === array_keys($this->outlines)) ) {
00714 // If original and current outlines arrays have different keys (with a glance to an order) then outlines list was updated
00715 $updateOutlinesNavigation = true;
00716 } else {
00717 foreach ($this->outlines as $key => $outline) {
00718 if ($this->_originalOutlines[$key] !== $outline) {
00719 $updateOutlinesNavigation = true;
00720 }
00721 }
00722 }
00723 }
00724
00725 $lastOutline = null;
00726 $openOutlinesCount = 0;
00727 if ($updateOutlinesNavigation) {
00728 $root->Outlines->touch();
00729 $root->Outlines->First = null;
00730
00731 foreach ($this->outlines as $outline) {
00732 if ($lastOutline === null) {
00733 // First pass. Update Outlines dictionary First entry using corresponding value
00734 $lastOutline = $outline->dumpOutline($this->_objFactory, $updateOutlinesNavigation, $root->Outlines);
00735 $root->Outlines->First = $lastOutline;
00736 } else {
00737 // Update previous outline dictionary Next entry (Prev is updated within dumpOutline() method)
00738 $currentOutlineDictionary = $outline->dumpOutline($this->_objFactory, $updateOutlinesNavigation, $root->Outlines, $lastOutline);
00739 $lastOutline->Next = $currentOutlineDictionary;
00740 $lastOutline = $currentOutlineDictionary;
00741 }
00742 $openOutlinesCount += $outline->openOutlinesCount();
00743 }
00744
00745 $root->Outlines->Last = $lastOutline;
00746 } else {
00747 foreach ($this->outlines as $outline) {
00748 $lastOutline = $outline->dumpOutline($this->_objFactory, $updateOutlinesNavigation, $root->Outlines, $lastOutline);
00749 $openOutlinesCount += $outline->openOutlinesCount();
00750 }
00751 }
00752
00753 if ($openOutlinesCount != $this->_originalOpenOutlinesCount) {
00754 $root->Outlines->touch;
00755 $root->Outlines->Count = new Zend_Pdf_Element_Numeric($openOutlinesCount);
00756 }
00757 }
00758
00781 public function newPage($param1, $param2 = null)
00782 {
00783 require_once 'Zend/Pdf/Page.php';
00784 if ($param2 === null) {
00785 return new Zend_Pdf_Page($param1, $this->_objFactory);
00786 } else {
00787 return new Zend_Pdf_Page($param1, $param2, $this->_objFactory);
00788 }
00789 }
00790
00797 public function getMetadata()
00798 {
00799 if ($this->_trailer->Root->Metadata !== null) {
00800 return $this->_trailer->Root->Metadata->value;
00801 } else {
00802 return null;
00803 }
00804 }
00805
00811 public function setMetadata($metadata)
00812 {
00813 $metadataObject = $this->_objFactory->newStreamObject($metadata);
00814 $metadataObject->dictionary->Type = new Zend_Pdf_Element_Name('Metadata');
00815 $metadataObject->dictionary->Subtype = new Zend_Pdf_Element_Name('XML');
00816
00817 $this->_trailer->Root->Metadata = $metadataObject;
00818 $this->_trailer->Root->touch();
00819 }
00820
00827 public function getJavaScript()
00828 {
00829 return $this->_javaScript;
00830 }
00831
00838 public function getOpenAction()
00839 {
00840 if ($this->_trailer->Root->OpenAction !== null) {
00841 require_once 'Zend/Pdf/Target.php';
00842 return Zend_Pdf_Target::load($this->_trailer->Root->OpenAction);
00843 } else {
00844 return null;
00845 }
00846 }
00847
00854 public function setOpenAction(Zend_Pdf_Target $openAction = null)
00855 {
00856 $root = $this->_trailer->Root;
00857 $root->touch();
00858
00859 if ($openAction === null) {
00860 $root->OpenAction = null;
00861 } else {
00862 $root->OpenAction = $openAction->getResource();
00863
00864 if ($openAction instanceof Zend_Pdf_Action) {
00865 $openAction->dumpAction($this->_objFactory);
00866 }
00867 }
00868
00869 return $this;
00870 }
00871
00879 public function getNamedDestinations()
00880 {
00881 return $this->_namedTargets;
00882 }
00883
00890 public function getNamedDestination($name)
00891 {
00892 if (isset($this->_namedTargets[$name])) {
00893 return $this->_namedTargets[$name];
00894 } else {
00895 return null;
00896 }
00897 }
00898
00905 public function setNamedDestination($name, $destination = null)
00906 {
00907 if ($destination !== null &&
00908 !$destination instanceof Zend_Pdf_Action_GoTo &&
00909 !$destination instanceof Zend_Pdf_Destination_Explicit) {
00910 require_once 'Zend/Pdf/Exception.php';
00911 throw new Zend_Pdf_Exception('PDF named destination must refer an explicit destination or a GoTo PDF action.');
00912 }
00913
00914 if ($destination !== null) {
00915 $this->_namedTargets[$name] = $destination;
00916 } else {
00917 unset($this->_namedTargets[$name]);
00918 }
00919 }
00920
00927 protected $_pageReferences = null;
00928
00935 protected $_pageNumbers = null;
00936
00942 protected function _refreshPagesHash()
00943 {
00944 $this->_pageReferences = array();
00945 $this->_pageNumbers = array();
00946 $count = 1;
00947 foreach ($this->pages as $page) {
00948 $pageDictionaryHashId = spl_object_hash($page->getPageDictionary()->getObject());
00949 $this->_pageReferences[$pageDictionaryHashId] = $page;
00950 $this->_pageNumbers[$count++] = $page;
00951 }
00952
00953 return $this;
00954 }
00955
00966 public function resolveDestination(Zend_Pdf_Destination $destination, $refreshPageCollectionHashes = true)
00967 {
00968 if ($this->_pageReferences === null || $refreshPageCollectionHashes) {
00969 $this->_refreshPagesHash();
00970 }
00971
00972 if ($destination instanceof Zend_Pdf_Destination_Named) {
00973 if (!isset($this->_namedTargets[$destination->getName()])) {
00974 return null;
00975 }
00976 $destination = $this->getNamedDestination($destination->getName());
00977
00978 if ($destination instanceof Zend_Pdf_Action) {
00979 if (!$destination instanceof Zend_Pdf_Action_GoTo) {
00980 return null;
00981 }
00982 $destination = $destination->getDestination();
00983 }
00984
00985 if (!$destination instanceof Zend_Pdf_Destination_Explicit) {
00986 require_once 'Zend/Pdf/Exception.php';
00987 throw new Zend_Pdf_Exception('Named destination target has to be an explicit destination.');
00988 }
00989 }
00990
00991 // Named target is an explicit destination
00992 $pageElement = $destination->getResource()->items[0];
00993
00994 if ($pageElement->getType() == Zend_Pdf_Element::TYPE_NUMERIC) {
00995 // Page reference is a PDF number
00996 if (!isset($this->_pageNumbers[$pageElement->value])) {
00997 return null;
00998 }
00999
01000 return $this->_pageNumbers[$pageElement->value];
01001 }
01002
01003 // Page reference is a PDF page dictionary reference
01004 $pageDictionaryHashId = spl_object_hash($pageElement->getObject());
01005 if (!isset($this->_pageReferences[$pageDictionaryHashId])) {
01006 return null;
01007 }
01008 return $this->_pageReferences[$pageDictionaryHashId];
01009 }
01010
01023 protected function _cleanUpAction(Zend_Pdf_Action $action, $refreshPageCollectionHashes = true)
01024 {
01025 if ($this->_pageReferences === null || $refreshPageCollectionHashes) {
01026 $this->_refreshPagesHash();
01027 }
01028
01029 // Named target is an action
01030 if ($action instanceof Zend_Pdf_Action_GoTo &&
01031 $this->resolveDestination($action->getDestination(), false) === null) {
01032 // Action itself is a GoTo action with an unresolved destination
01033 return null;
01034 }
01035
01036 // Walk through child actions
01037 $iterator = new RecursiveIteratorIterator($action, RecursiveIteratorIterator::SELF_FIRST);
01038
01039 $actionsToClean = array();
01040 $deletionCandidateKeys = array();
01041 foreach ($iterator as $chainedAction) {
01042 if ($chainedAction instanceof Zend_Pdf_Action_GoTo &&
01043 $this->resolveDestination($chainedAction->getDestination(), false) === null) {
01044 // Some child action is a GoTo action with an unresolved destination
01045 // Mark it as a candidate for deletion
01046 $actionsToClean[] = $iterator->getSubIterator();
01047 $deletionCandidateKeys[] = $iterator->getSubIterator()->key();
01048 }
01049 }
01050 foreach ($actionsToClean as $id => $action) {
01051 unset($action->next[$deletionCandidateKeys[$id]]);
01052 }
01053
01054 return $action;
01055 }
01056
01065 public function extractFonts()
01066 {
01067 $fontResourcesUnique = array();
01068 foreach ($this->pages as $page) {
01069 $pageResources = $page->extractResources();
01070
01071 if ($pageResources->Font === null) {
01072 // Page doesn't contain have any font reference
01073 continue;
01074 }
01075
01076 $fontResources = $pageResources->Font;
01077
01078 foreach ($fontResources->getKeys() as $fontResourceName) {
01079 $fontDictionary = $fontResources->$fontResourceName;
01080
01081 if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
01082 $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
01083 require_once 'Zend/Pdf/Exception.php';
01084 throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
01085 }
01086
01087 $fontResourcesUnique[spl_object_hash($fontDictionary->getObject())] = $fontDictionary;
01088 }
01089 }
01090
01091 $fonts = array();
01092 require_once 'Zend/Pdf/Exception.php';
01093 foreach ($fontResourcesUnique as $resourceId => $fontDictionary) {
01094 try {
01095
01096 require_once 'Zend/Pdf/Resource/Font/Extracted.php';
01097 $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
01098
01099 $fonts[$resourceId] = $extractedFont;
01100 } catch (Zend_Pdf_Exception $e) {
01101 if ($e->getMessage() != 'Unsupported font type.') {
01102 throw $e;
01103 }
01104 }
01105 }
01106
01107 return $fonts;
01108 }
01109
01118 public function extractFont($fontName)
01119 {
01120 $fontResourcesUnique = array();
01121 require_once 'Zend/Pdf/Exception.php';
01122 foreach ($this->pages as $page) {
01123 $pageResources = $page->extractResources();
01124
01125 if ($pageResources->Font === null) {
01126
01127 continue;
01128 }
01129
01130 $fontResources = $pageResources->Font;
01131
01132 foreach ($fontResources->getKeys() as $fontResourceName) {
01133 $fontDictionary = $fontResources->$fontResourceName;
01134
01135 if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference ||
01136 $fontDictionary instanceof Zend_Pdf_Element_Object) ) {
01137 require_once 'Zend/Pdf/Exception.php';
01138 throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.');
01139 }
01140
01141 $resourceId = spl_object_hash($fontDictionary->getObject());
01142 if (isset($fontResourcesUnique[$resourceId])) {
01143 continue;
01144 } else {
01145
01146 $fontResourcesUnique[$resourceId] = 1;
01147 }
01148
01149 if ($fontDictionary->BaseFont->value != $fontName) {
01150 continue;
01151 }
01152
01153 try {
01154
01155 require_once 'Zend/Pdf/Resource/Font/Extracted.php';
01156 return new Zend_Pdf_Resource_Font_Extracted($fontDictionary);
01157 } catch (Zend_Pdf_Exception $e) {
01158 if ($e->getMessage() != 'Unsupported font type.') {
01159 throw $e;
01160 }
01161
01162 }
01163 }
01164 }
01165
01166 return null;
01167 }
01168
01178 public function render($newSegmentOnly = false, $outputStream = null)
01179 {
01180
01181 if ($this->properties != $this->_originalProperties) {
01182 $docInfo = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary());
01183
01184 foreach ($this->properties as $key => $value) {
01185 switch ($key) {
01186 case 'Trapped':
01187 switch ($value) {
01188 case true:
01189 $docInfo->$key = new Zend_Pdf_Element_Name('True');
01190 break;
01191
01192 case false:
01193 $docInfo->$key = new Zend_Pdf_Element_Name('False');
01194 break;
01195
01196 case null:
01197 $docInfo->$key = new Zend_Pdf_Element_Name('Unknown');
01198 break;
01199
01200 default:
01201 require_once 'Zend/Pdf/Exception.php';
01202 throw new Zend_Pdf_Exception('Wrong Trapped document property vale: \'' . $value . '\'. Only true, false and null values are allowed.');
01203 break;
01204 }
01205
01206 case 'CreationDate':
01207 // break intentionally omitted
01208 case 'ModDate':
01209 $docInfo->$key = new Zend_Pdf_Element_String((string)$value);
01210 break;
01211
01212 case 'Title':
01213 // break intentionally omitted
01214 case 'Author':
01215 // break intentionally omitted
01216 case 'Subject':
01217 // break intentionally omitted
01218 case 'Keywords':
01219 // break intentionally omitted
01220 case 'Creator':
01221 // break intentionally omitted
01222 case 'Producer':
01223 if (extension_loaded('mbstring') === true) {
01224 $detected = mb_detect_encoding($value);
01225 if ($detected !== 'ASCII') {
01226 $value = chr(254) . chr(255) . mb_convert_encoding($value, 'UTF-16', $detected);
01227 }
01228 }
01229 $docInfo->$key = new Zend_Pdf_Element_String((string)$value);
01230 break;
01231
01232 default:
01233 // Set property using PDF type based on PHP type
01234 $docInfo->$key = Zend_Pdf_Element::phpToPdf($value);
01235 break;
01236 }
01237 }
01238
01239 $this->_trailer->Info = $docInfo;
01240 }
01241
01242 $this->_dumpPages();
01243 $this->_dumpNamedDestinations();
01244 $this->_dumpOutlines();
01245
01246 // Check, that PDF file was modified
01247 // File is always modified by _dumpPages() now, but future implementations may eliminate this.
01248 if (!$this->_objFactory->isModified()) {
01249 if ($newSegmentOnly) {
01250 // Do nothing, return
01251 return '';
01252 }
01253
01254 if ($outputStream === null) {
01255 return $this->_trailer->getPDFString();
01256 } else {
01257 $pdfData = $this->_trailer->getPDFString();
01258 while ( strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false ) {
01259 $pdfData = substr($pdfData, $byteCount);
01260 }
01261
01262 return '';
01263 }
01264 }
01265
01266 // offset (from a start of PDF file) of new PDF file segment
01267 $offset = $this->_trailer->getPDFLength();
01268 // Last Object number in a list of free objects
01269 $lastFreeObject = $this->_trailer->getLastFreeObject();
01270
01271 // Array of cross-reference table subsections
01272 $xrefTable = array();
01273 // Object numbers of first objects in each subsection
01274 $xrefSectionStartNums = array();
01275
01276 // Last cross-reference table subsection
01277 $xrefSection = array();
01278 // Dummy initialization of the first element (specail case - header of linked list of free objects).
01279 $xrefSection[] = 0;
01280 $xrefSectionStartNums[] = 0;
01281 // Object number of last processed PDF object.
01282 // Used to manage cross-reference subsections.
01283 // Initialized by zero (specail case - header of linked list of free objects).
01284 $lastObjNum = 0;
01285
01286 if ($outputStream !== null) {
01287 if (!$newSegmentOnly) {
01288 $pdfData = $this->_trailer->getPDFString();
01289 while ( strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false ) {
01290 $pdfData = substr($pdfData, $byteCount);
01291 }
01292 }
01293 } else {
01294 $pdfSegmentBlocks = ($newSegmentOnly) ? array() : array($this->_trailer->getPDFString());
01295 }
01296
01297 // Iterate objects to create new reference table
01298 foreach ($this->_objFactory->listModifiedObjects() as $updateInfo) {
01299 $objNum = $updateInfo->getObjNum();
01300
01301 if ($objNum - $lastObjNum != 1) {
01302 // Save cross-reference table subsection and start new one
01303 $xrefTable[] = $xrefSection;
01304 $xrefSection = array();
01305 $xrefSectionStartNums[] = $objNum;
01306 }
01307
01308 if ($updateInfo->isFree()) {
01309 // Free object cross-reference table entry
01310 $xrefSection[] = sprintf("%010d %05d f \n", $lastFreeObject, $updateInfo->getGenNum());
01311 $lastFreeObject = $objNum;
01312 } else {
01313 // In-use object cross-reference table entry
01314 $xrefSection[] = sprintf("%010d %05d n \n", $offset, $updateInfo->getGenNum());
01315
01316 $pdfBlock = $updateInfo->getObjectDump();
01317 $offset += strlen($pdfBlock);
01318
01319 if ($outputStream === null) {
01320 $pdfSegmentBlocks[] = $pdfBlock;
01321 } else {
01322 while ( strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false ) {
01323 $pdfBlock = substr($pdfBlock, $byteCount);
01324 }
01325 }
01326 }
01327 $lastObjNum = $objNum;
01328 }
01329 // Save last cross-reference table subsection
01330 $xrefTable[] = $xrefSection;
01331
01332 // Modify first entry (specail case - header of linked list of free objects).
01333 $xrefTable[0][0] = sprintf("%010d 65535 f \n", $lastFreeObject);
01334
01335 $xrefTableStr = "xref\n";
01336 foreach ($xrefTable as $sectId => $xrefSection) {
01337 $xrefTableStr .= sprintf("%d %d \n", $xrefSectionStartNums[$sectId], count($xrefSection));
01338 foreach ($xrefSection as $xrefTableEntry) {
01339 $xrefTableStr .= $xrefTableEntry;
01340 }
01341 }
01342
01343 $this->_trailer->Size->value = $this->_objFactory->getObjectCount();
01344
01345 $pdfBlock = $xrefTableStr
01346 . $this->_trailer->toString()
01347 . "startxref\n" . $offset . "\n"
01348 . "%%EOF\n";
01349
01350 $this->_objFactory->cleanEnumerationShiftCache();
01351
01352 if ($outputStream === null) {
01353 $pdfSegmentBlocks[] = $pdfBlock;
01354
01355 return implode('', $pdfSegmentBlocks);
01356 } else {
01357 while ( strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false ) {
01358 $pdfBlock = substr($pdfBlock, $byteCount);
01359 }
01360
01361 return '';
01362 }
01363 }
01364
01365
01371 public function setJavaScript($javascript)
01372 {
01373 $this->_javaScript = $javascript;
01374 }
01375
01376
01394 public static function pdfDate($timestamp = null)
01395 {
01396 if ($timestamp === null) {
01397 $date = date('\D\:YmdHisO');
01398 } else {
01399 $date = date('\D\:YmdHisO', $timestamp);
01400 }
01401 return substr_replace($date, '\'', -2, 0) . '\'';
01402 }
01403
01404 }