<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to [email protected] so we can send you a copy immediately. * * @category Zend * @package Zend_Pdf * @subpackage Actions * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ /** Internally used classes */ require_once 'Zend/Pdf/Element.php'; require_once 'Zend/Pdf/Element/Array.php'; require_once 'Zend/Pdf/Element/Numeric.php'; require_once 'Zend/Pdf/Element/String.php'; /** Zend_Pdf_Outline */ require_once 'Zend/Pdf/Outline.php'; /** * Traceable PDF outline representation class * * Instances of this class trace object update uperations. That allows to avoid outlines PDF tree update * which should be performed at each document update otherwise. * * @package Zend_Pdf * @subpackage Outlines * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Pdf_Outline_Loaded extends Zend_Pdf_Outline { /** * Outline dictionary object * * @var Zend_Pdf_Element_Dictionary|Zend_Pdf_Element_Object|Zend_Pdf_Element_Reference */ protected $_outlineDictionary; /** * original array of child outlines * * @var array */ protected $_originalChildOutlines = array(); /** * Get outline title. * * @return string * @throws Zend_Pdf_Exception */ public function getTitle() { if ($this->_outlineDictionary->Title === null) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outline dictionary Title entry is required.'); } return $this->_outlineDictionary->Title->value; } /** * Set outline title * * @param string $title * @return Zend_Pdf_Outline */ public function setTitle($title) { $this->_outlineDictionary->Title->touch(); $this->_outlineDictionary->Title = new Zend_Pdf_Element_String($title); return $this; } /** * Sets 'isOpen' outline flag * * @param boolean $isOpen * @return Zend_Pdf_Outline */ public function setIsOpen($isOpen) { parent::setIsOpen($isOpen); if ($this->_outlineDictionary->Count === null) { // Do Nothing. return this; } $childrenCount = $this->_outlineDictionary->Count->value; $isOpenCurrentState = ($childrenCount > 0); if ($isOpen != $isOpenCurrentState) { $this->_outlineDictionary->Count->touch(); $this->_outlineDictionary->Count->value = ($isOpen? 1 : -1)*abs($childrenCount); } return $this; } /** * Returns true if outline item is displayed in italic * * @return boolean */ public function isItalic() { if ($this->_outlineDictionary->F === null) { return false; } return $this->_outlineDictionary->F->value & 1; } /** * Sets 'isItalic' outline flag * * @param boolean $isItalic * @return Zend_Pdf_Outline */ public function setIsItalic($isItalic) { if ($this->_outlineDictionary->F === null) { $this->_outlineDictionary->touch(); $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isItalic? 1 : 0); } else { $this->_outlineDictionary->F->touch(); if ($isItalic) { $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 1; } else { $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~1; } } return $this; } /** * Returns true if outline item is displayed in bold * * @return boolean */ public function isBold() { if ($this->_outlineDictionary->F === null) { return false; } return $this->_outlineDictionary->F->value & 2; } /** * Sets 'isBold' outline flag * * @param boolean $isBold * @return Zend_Pdf_Outline */ public function setIsBold($isBold) { if ($this->_outlineDictionary->F === null) { $this->_outlineDictionary->touch(); $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isBold? 2 : 0); } else { $this->_outlineDictionary->F->touch(); if ($isBold) { $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 2; } else { $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~2; } } return $this; } /** * Get outline text color. * * @return Zend_Pdf_Color_Rgb */ public function getColor() { if ($this->_outlineDictionary->C === null) { return null; } $components = $this->_outlineDictionary->C->items; require_once 'Zend/Pdf/Color/Rgb.php'; return new Zend_Pdf_Color_Rgb($components[0], $components[1], $components[2]); } /** * Set outline text color. * (null means default color which is black) * * @param Zend_Pdf_Color_Rgb $color * @return Zend_Pdf_Outline */ public function setColor(Zend_Pdf_Color_Rgb $color) { $this->_outlineDictionary->touch(); if ($color === null) { $this->_outlineDictionary->C = null; } else { $components = $color->getComponents(); $colorComponentElements = array(new Zend_Pdf_Element_Numeric($components[0]), new Zend_Pdf_Element_Numeric($components[1]), new Zend_Pdf_Element_Numeric($components[2])); $this->_outlineDictionary->C = new Zend_Pdf_Element_Array($colorComponentElements); } return $this; } /** * Get outline target. * * @return Zend_Pdf_Target * @throws Zend_Pdf_Exception */ public function getTarget() { if ($this->_outlineDictionary->Dest !== null) { if ($this->_outlineDictionary->A !== null) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outline dictionary may contain Dest or A entry, but not both.'); } require_once 'Zend/Pdf/Destination.php'; return Zend_Pdf_Destination::load($this->_outlineDictionary->Dest); } else if ($this->_outlineDictionary->A !== null) { require_once 'Zend/Pdf/Action.php'; return Zend_Pdf_Action::load($this->_outlineDictionary->A); } return null; } /** * Set outline target. * Null means no target * * @param Zend_Pdf_Target|string $target * @return Zend_Pdf_Outline * @throws Zend_Pdf_Exception */ public function setTarget($target = null) { $this->_outlineDictionary->touch(); if (is_string($target)) { require_once 'Zend/Pdf/Destination/Named.php'; $target = Zend_Pdf_Destination_Named::create($target); } if ($target === null) { $this->_outlineDictionary->Dest = null; $this->_outlineDictionary->A = null; } else if ($target instanceof Zend_Pdf_Destination) { $this->_outlineDictionary->Dest = $target->getResource(); $this->_outlineDictionary->A = null; } else if ($target instanceof Zend_Pdf_Action) { $this->_outlineDictionary->Dest = null; $this->_outlineDictionary->A = $target->getResource(); } else { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outline target has to be Zend_Pdf_Destination or Zend_Pdf_Action object or string'); } return $this; } /** * Set outline options * * @param array $options * @return Zend_Pdf_Actions_Traceable * @throws Zend_Pdf_Exception */ public function setOptions(array $options) { parent::setOptions($options); return $this; } /** * Create PDF outline object using specified dictionary * * @internal * @param Zend_Pdf_Element $dictionary (It's actually Dictionary or Dictionary Object or Reference to a Dictionary Object) * @param Zend_Pdf_Action $parentAction * @param SplObjectStorage $processedOutlines List of already processed Outline dictionaries, * used to avoid cyclic references * @return Zend_Pdf_Action * @throws Zend_Pdf_Exception */ public function __construct(Zend_Pdf_Element $dictionary, SplObjectStorage $processedDictionaries = null) { if ($dictionary->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('$dictionary mast be an indirect dictionary object.'); } if ($processedDictionaries === null) { $processedDictionaries = new SplObjectStorage(); } $processedDictionaries->attach($dictionary); $this->_outlineDictionary = $dictionary; if ($dictionary->Count !== null) { if ($dictionary->Count->getType() != Zend_Pdf_Element::TYPE_NUMERIC) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outline dictionary Count entry must be a numeric element.'); } $childOutlinesCount = $dictionary->Count->value; if ($childOutlinesCount > 0) { $this->_open = true; } $childOutlinesCount = abs($childOutlinesCount); $childDictionary = $dictionary->First; $children = new SplObjectStorage(); while ($childDictionary !== null) { // Check children structure for cyclic references if ($children->contains($childDictionary)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outline childs load error.'); } if (!$processedDictionaries->contains($childDictionary)) { $this->childOutlines[] = new Zend_Pdf_Outline_Loaded($childDictionary, $processedDictionaries); } $childDictionary = $childDictionary->Next; } $this->_originalChildOutlines = $this->childOutlines; } } /** * Dump Outline and its child outlines into PDF structures * * Returns dictionary indirect object or reference * * @internal * @param Zend_Pdf_ElementFactory $factory object factory for newly created indirect objects * @param boolean $updateNavigation Update navigation flag * @param Zend_Pdf_Element $parent Parent outline dictionary reference * @param Zend_Pdf_Element $prev Previous outline dictionary reference * @param SplObjectStorage $processedOutlines List of already processed outlines * @return Zend_Pdf_Element * @throws Zend_Pdf_Exception */ public function dumpOutline(Zend_Pdf_ElementFactory_Interface $factory, $updateNavigation, Zend_Pdf_Element $parent, Zend_Pdf_Element $prev = null, SplObjectStorage $processedOutlines = null) { if ($processedOutlines === null) { $processedOutlines = new SplObjectStorage(); } $processedOutlines->attach($this); if ($updateNavigation) { $this->_outlineDictionary->touch(); $this->_outlineDictionary->Parent = $parent; $this->_outlineDictionary->Prev = $prev; $this->_outlineDictionary->Next = null; } $updateChildNavigation = false; if (count($this->_originalChildOutlines) != count($this->childOutlines)) { // If original and current children arrays have different size then children list was updated $updateChildNavigation = true; } else if ( !(array_keys($this->_originalChildOutlines) === array_keys($this->childOutlines)) ) { // If original and current children arrays have different keys (with a glance to an order) then children list was updated $updateChildNavigation = true; } else { foreach ($this->childOutlines as $key => $childOutline) { if ($this->_originalChildOutlines[$key] !== $childOutline) { $updateChildNavigation = true; break; } } } $lastChild = null; if ($updateChildNavigation) { $this->_outlineDictionary->touch(); $this->_outlineDictionary->First = null; foreach ($this->childOutlines as $childOutline) { if ($processedOutlines->contains($childOutline)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.'); } if ($lastChild === null) { // First pass. Update Outlines dictionary First entry using corresponding value $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, null, $processedOutlines); $this->_outlineDictionary->First = $lastChild; } else { // Update previous outline dictionary Next entry (Prev is updated within dumpOutline() method) $childOutlineDictionary = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines); $lastChild->Next = $childOutlineDictionary; $lastChild = $childOutlineDictionary; } } $this->_outlineDictionary->Last = $lastChild; if (count($this->childOutlines) != 0) { $this->_outlineDictionary->Count = new Zend_Pdf_Element_Numeric(($this->isOpen()? 1 : -1)*count($this->childOutlines)); } else { $this->_outlineDictionary->Count = null; } } else { foreach ($this->childOutlines as $childOutline) { if ($processedOutlines->contains($childOutline)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.'); } $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines); } } return $this->_outlineDictionary; } public function dump($level = 0) { printf(":%3d:%s:%s:%s%s :\n", count($this->childOutlines),$this->isItalic()? 'i':' ', $this->isBold()? 'b':' ', str_pad('', 4*$level), $this->getTitle()); if ($this->isOpen() || true) { foreach ($this->childOutlines as $child) { $child->dump($level + 1); } } } }