web/lib/Zend/Pdf/Outline/Loaded.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    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.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Pdf
       
    17  * @subpackage Actions
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Loaded.php 23195 2010-10-21 10:12:12Z alexander $
       
    21  */
       
    22 
       
    23 
       
    24 /** Internally used classes */
       
    25 require_once 'Zend/Pdf/Element.php';
       
    26 require_once 'Zend/Pdf/Element/Array.php';
       
    27 require_once 'Zend/Pdf/Element/Numeric.php';
       
    28 require_once 'Zend/Pdf/Element/String.php';
       
    29 
       
    30 
       
    31 /** Zend_Pdf_Outline */
       
    32 require_once 'Zend/Pdf/Outline.php';
       
    33 
       
    34 /**
       
    35  * Traceable PDF outline representation class
       
    36  *
       
    37  * Instances of this class trace object update uperations. That allows to avoid outlines PDF tree update
       
    38  * which should be performed at each document update otherwise.
       
    39  *
       
    40  * @package    Zend_Pdf
       
    41  * @subpackage Outlines
       
    42  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    43  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    44  */
       
    45 class Zend_Pdf_Outline_Loaded extends Zend_Pdf_Outline
       
    46 {
       
    47     /**
       
    48      * Outline dictionary object
       
    49      *
       
    50      * @var Zend_Pdf_Element_Dictionary|Zend_Pdf_Element_Object|Zend_Pdf_Element_Reference
       
    51      */
       
    52     protected $_outlineDictionary;
       
    53 
       
    54     /**
       
    55      * original array of child outlines
       
    56      *
       
    57      * @var array
       
    58      */
       
    59     protected $_originalChildOutlines = array();
       
    60 
       
    61     /**
       
    62      * Get outline title.
       
    63      *
       
    64      * @return string
       
    65      * @throws Zend_Pdf_Exception
       
    66      */
       
    67     public function getTitle()
       
    68     {
       
    69         if ($this->_outlineDictionary->Title === null) {
       
    70             require_once 'Zend/Pdf/Exception.php';
       
    71             throw new Zend_Pdf_Exception('Outline dictionary Title entry is required.');
       
    72         }
       
    73         return $this->_outlineDictionary->Title->value;
       
    74     }
       
    75 
       
    76     /**
       
    77      * Set outline title
       
    78      *
       
    79      * @param string $title
       
    80      * @return Zend_Pdf_Outline
       
    81      */
       
    82     public function setTitle($title)
       
    83     {
       
    84         $this->_outlineDictionary->Title->touch();
       
    85         $this->_outlineDictionary->Title = new Zend_Pdf_Element_String($title);
       
    86         return $this;
       
    87     }
       
    88 
       
    89     /**
       
    90      * Sets 'isOpen' outline flag
       
    91      *
       
    92      * @param boolean $isOpen
       
    93      * @return Zend_Pdf_Outline
       
    94      */
       
    95     public function setIsOpen($isOpen)
       
    96     {
       
    97         parent::setIsOpen($isOpen);
       
    98 
       
    99         if ($this->_outlineDictionary->Count === null) {
       
   100             // Do Nothing.
       
   101             return this;
       
   102         }
       
   103 
       
   104         $childrenCount = $this->_outlineDictionary->Count->value;
       
   105         $isOpenCurrentState = ($childrenCount > 0);
       
   106         if ($isOpen != $isOpenCurrentState) {
       
   107             $this->_outlineDictionary->Count->touch();
       
   108             $this->_outlineDictionary->Count->value = ($isOpen? 1 : -1)*abs($childrenCount);
       
   109         }
       
   110 
       
   111         return $this;
       
   112     }
       
   113 
       
   114     /**
       
   115      * Returns true if outline item is displayed in italic
       
   116      *
       
   117      * @return boolean
       
   118      */
       
   119     public function isItalic()
       
   120     {
       
   121         if ($this->_outlineDictionary->F === null) {
       
   122             return false;
       
   123         }
       
   124         return $this->_outlineDictionary->F->value & 1;
       
   125     }
       
   126 
       
   127     /**
       
   128      * Sets 'isItalic' outline flag
       
   129      *
       
   130      * @param boolean $isItalic
       
   131      * @return Zend_Pdf_Outline
       
   132      */
       
   133     public function setIsItalic($isItalic)
       
   134     {
       
   135         if ($this->_outlineDictionary->F === null) {
       
   136             $this->_outlineDictionary->touch();
       
   137             $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isItalic? 1 : 0);
       
   138         } else {
       
   139             $this->_outlineDictionary->F->touch();
       
   140             if ($isItalic) {
       
   141                 $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 1;
       
   142             } else {
       
   143                 $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~1;
       
   144             }
       
   145         }
       
   146         return $this;
       
   147     }
       
   148 
       
   149     /**
       
   150      * Returns true if outline item is displayed in bold
       
   151      *
       
   152      * @return boolean
       
   153      */
       
   154     public function isBold()
       
   155     {
       
   156         if ($this->_outlineDictionary->F === null) {
       
   157             return false;
       
   158         }
       
   159         return $this->_outlineDictionary->F->value & 2;
       
   160     }
       
   161 
       
   162     /**
       
   163      * Sets 'isBold' outline flag
       
   164      *
       
   165      * @param boolean $isBold
       
   166      * @return Zend_Pdf_Outline
       
   167      */
       
   168     public function setIsBold($isBold)
       
   169     {
       
   170         if ($this->_outlineDictionary->F === null) {
       
   171             $this->_outlineDictionary->touch();
       
   172             $this->_outlineDictionary->F = new Zend_Pdf_Element_Numeric($isBold? 2 : 0);
       
   173         } else {
       
   174             $this->_outlineDictionary->F->touch();
       
   175             if ($isBold) {
       
   176                 $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | 2;
       
   177             } else {
       
   178                 $this->_outlineDictionary->F->value = $this->_outlineDictionary->F->value | ~2;
       
   179             }
       
   180         }
       
   181         return $this;
       
   182     }
       
   183 
       
   184 
       
   185     /**
       
   186      * Get outline text color.
       
   187      *
       
   188      * @return Zend_Pdf_Color_Rgb
       
   189      */
       
   190     public function getColor()
       
   191     {
       
   192         if ($this->_outlineDictionary->C === null) {
       
   193             return null;
       
   194         }
       
   195 
       
   196         $components = $this->_outlineDictionary->C->items;
       
   197 
       
   198         require_once 'Zend/Pdf/Color/Rgb.php';
       
   199         return new Zend_Pdf_Color_Rgb($components[0], $components[1], $components[2]);
       
   200     }
       
   201 
       
   202     /**
       
   203      * Set outline text color.
       
   204      * (null means default color which is black)
       
   205      *
       
   206      * @param Zend_Pdf_Color_Rgb $color
       
   207      * @return Zend_Pdf_Outline
       
   208      */
       
   209     public function setColor(Zend_Pdf_Color_Rgb $color)
       
   210     {
       
   211         $this->_outlineDictionary->touch();
       
   212 
       
   213         if ($color === null) {
       
   214             $this->_outlineDictionary->C = null;
       
   215         } else {
       
   216             $components = $color->getComponents();
       
   217             $colorComponentElements = array(new Zend_Pdf_Element_Numeric($components[0]),
       
   218                                             new Zend_Pdf_Element_Numeric($components[1]),
       
   219                                             new Zend_Pdf_Element_Numeric($components[2]));
       
   220             $this->_outlineDictionary->C = new Zend_Pdf_Element_Array($colorComponentElements);
       
   221         }
       
   222 
       
   223         return $this;
       
   224     }
       
   225 
       
   226     /**
       
   227      * Get outline target.
       
   228      *
       
   229      * @return Zend_Pdf_Target
       
   230      * @throws Zend_Pdf_Exception
       
   231      */
       
   232     public function getTarget()
       
   233     {
       
   234         if ($this->_outlineDictionary->Dest !== null) {
       
   235             if ($this->_outlineDictionary->A !== null) {
       
   236                 require_once 'Zend/Pdf/Exception.php';
       
   237                 throw new Zend_Pdf_Exception('Outline dictionary may contain Dest or A entry, but not both.');
       
   238             }
       
   239 
       
   240             require_once 'Zend/Pdf/Destination.php';
       
   241             return Zend_Pdf_Destination::load($this->_outlineDictionary->Dest);
       
   242         } else if ($this->_outlineDictionary->A !== null) {
       
   243             require_once 'Zend/Pdf/Action.php';
       
   244             return Zend_Pdf_Action::load($this->_outlineDictionary->A);
       
   245         }
       
   246 
       
   247         return null;
       
   248     }
       
   249 
       
   250     /**
       
   251      * Set outline target.
       
   252      * Null means no target
       
   253      *
       
   254      * @param Zend_Pdf_Target|string $target
       
   255      * @return Zend_Pdf_Outline
       
   256      * @throws Zend_Pdf_Exception
       
   257      */
       
   258     public function setTarget($target = null)
       
   259     {
       
   260         $this->_outlineDictionary->touch();
       
   261 
       
   262         if (is_string($target)) {
       
   263             require_once 'Zend/Pdf/Destination/Named.php';
       
   264             $target = Zend_Pdf_Destination_Named::create($target);
       
   265         }
       
   266 
       
   267         if ($target === null) {
       
   268             $this->_outlineDictionary->Dest = null;
       
   269             $this->_outlineDictionary->A    = null;
       
   270         } else if ($target instanceof Zend_Pdf_Destination) {
       
   271             $this->_outlineDictionary->Dest = $target->getResource();
       
   272             $this->_outlineDictionary->A    = null;
       
   273         } else if ($target instanceof Zend_Pdf_Action) {
       
   274             $this->_outlineDictionary->Dest = null;
       
   275             $this->_outlineDictionary->A    = $target->getResource();
       
   276         } else {
       
   277             require_once 'Zend/Pdf/Exception.php';
       
   278             throw new Zend_Pdf_Exception('Outline target has to be Zend_Pdf_Destination or Zend_Pdf_Action object or string');
       
   279         }
       
   280 
       
   281         return $this;
       
   282     }
       
   283 
       
   284     /**
       
   285      * Set outline options
       
   286      *
       
   287      * @param array $options
       
   288      * @return Zend_Pdf_Actions_Traceable
       
   289      * @throws Zend_Pdf_Exception
       
   290      */
       
   291     public function setOptions(array $options)
       
   292     {
       
   293         parent::setOptions($options);
       
   294 
       
   295         return $this;
       
   296     }
       
   297 
       
   298 
       
   299 
       
   300     /**
       
   301      * Create PDF outline object using specified dictionary
       
   302      *
       
   303      * @internal
       
   304      * @param Zend_Pdf_Element $dictionary (It's actually Dictionary or Dictionary Object or Reference to a Dictionary Object)
       
   305      * @param Zend_Pdf_Action  $parentAction
       
   306      * @param SplObjectStorage $processedOutlines  List of already processed Outline dictionaries,
       
   307      *                                             used to avoid cyclic references
       
   308      * @return Zend_Pdf_Action
       
   309      * @throws Zend_Pdf_Exception
       
   310      */
       
   311     public function __construct(Zend_Pdf_Element $dictionary, SplObjectStorage $processedDictionaries = null)
       
   312     {
       
   313         if ($dictionary->getType() != Zend_Pdf_Element::TYPE_DICTIONARY) {
       
   314             require_once 'Zend/Pdf/Exception.php';
       
   315             throw new Zend_Pdf_Exception('$dictionary mast be an indirect dictionary object.');
       
   316         }
       
   317 
       
   318         if ($processedDictionaries === null) {
       
   319             $processedDictionaries = new SplObjectStorage();
       
   320         }
       
   321         $processedDictionaries->attach($dictionary);
       
   322 
       
   323         $this->_outlineDictionary = $dictionary;
       
   324 
       
   325         if ($dictionary->Count !== null) {
       
   326             if ($dictionary->Count->getType() != Zend_Pdf_Element::TYPE_NUMERIC) {
       
   327                 require_once 'Zend/Pdf/Exception.php';
       
   328                 throw new Zend_Pdf_Exception('Outline dictionary Count entry must be a numeric element.');
       
   329             }
       
   330 
       
   331             $childOutlinesCount = $dictionary->Count->value;
       
   332             if ($childOutlinesCount > 0) {
       
   333                 $this->_open = true;
       
   334             }
       
   335             $childOutlinesCount = abs($childOutlinesCount);
       
   336 
       
   337             $childDictionary = $dictionary->First;
       
   338 
       
   339             $children = new SplObjectStorage();
       
   340             while ($childDictionary !== null) {
       
   341                 // Check children structure for cyclic references
       
   342                 if ($children->contains($childDictionary)) {
       
   343                     require_once 'Zend/Pdf/Exception.php';
       
   344                     throw new Zend_Pdf_Exception('Outline childs load error.');
       
   345                 }
       
   346 
       
   347                 if (!$processedDictionaries->contains($childDictionary)) {
       
   348                     $this->childOutlines[] = new Zend_Pdf_Outline_Loaded($childDictionary, $processedDictionaries);
       
   349                 }
       
   350 
       
   351                 $childDictionary = $childDictionary->Next;
       
   352             }
       
   353 
       
   354             $this->_originalChildOutlines = $this->childOutlines;
       
   355         }
       
   356     }
       
   357 
       
   358     /**
       
   359      * Dump Outline and its child outlines into PDF structures
       
   360      *
       
   361      * Returns dictionary indirect object or reference
       
   362      *
       
   363      * @internal
       
   364      * @param Zend_Pdf_ElementFactory    $factory object factory for newly created indirect objects
       
   365      * @param boolean $updateNavigation  Update navigation flag
       
   366      * @param Zend_Pdf_Element $parent   Parent outline dictionary reference
       
   367      * @param Zend_Pdf_Element $prev     Previous outline dictionary reference
       
   368      * @param SplObjectStorage $processedOutlines  List of already processed outlines
       
   369      * @return Zend_Pdf_Element
       
   370      * @throws Zend_Pdf_Exception
       
   371      */
       
   372     public function dumpOutline(Zend_Pdf_ElementFactory_Interface $factory,
       
   373                                                                   $updateNavigation,
       
   374                                                  Zend_Pdf_Element $parent,
       
   375                                                  Zend_Pdf_Element $prev = null,
       
   376                                                  SplObjectStorage $processedOutlines = null)
       
   377     {
       
   378         if ($processedOutlines === null) {
       
   379             $processedOutlines = new SplObjectStorage();
       
   380         }
       
   381         $processedOutlines->attach($this);
       
   382 
       
   383         if ($updateNavigation) {
       
   384             $this->_outlineDictionary->touch();
       
   385 
       
   386             $this->_outlineDictionary->Parent = $parent;
       
   387             $this->_outlineDictionary->Prev   = $prev;
       
   388             $this->_outlineDictionary->Next   = null;
       
   389         }
       
   390 
       
   391         $updateChildNavigation = false;
       
   392         if (count($this->_originalChildOutlines) != count($this->childOutlines)) {
       
   393             // If original and current children arrays have different size then children list was updated
       
   394             $updateChildNavigation = true;
       
   395         } else if ( !(array_keys($this->_originalChildOutlines) === array_keys($this->childOutlines)) ) {
       
   396             // If original and current children arrays have different keys (with a glance to an order) then children list was updated
       
   397             $updateChildNavigation = true;
       
   398         } else {
       
   399             foreach ($this->childOutlines as $key => $childOutline) {
       
   400                 if ($this->_originalChildOutlines[$key] !== $childOutline) {
       
   401                     $updateChildNavigation = true;
       
   402                     break;
       
   403                 }
       
   404             }
       
   405         }
       
   406 
       
   407         $lastChild = null;
       
   408         if ($updateChildNavigation) {
       
   409             $this->_outlineDictionary->touch();
       
   410             $this->_outlineDictionary->First = null;
       
   411 
       
   412             foreach ($this->childOutlines as $childOutline) {
       
   413                 if ($processedOutlines->contains($childOutline)) {
       
   414                     require_once 'Zend/Pdf/Exception.php';
       
   415                     throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.');
       
   416                 }
       
   417 
       
   418                 if ($lastChild === null) {
       
   419                     // First pass. Update Outlines dictionary First entry using corresponding value
       
   420                     $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, null, $processedOutlines);
       
   421                     $this->_outlineDictionary->First = $lastChild;
       
   422                 } else {
       
   423                     // Update previous outline dictionary Next entry (Prev is updated within dumpOutline() method)
       
   424                     $childOutlineDictionary = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines);
       
   425                     $lastChild->Next = $childOutlineDictionary;
       
   426                     $lastChild       = $childOutlineDictionary;
       
   427                 }
       
   428             }
       
   429 
       
   430             $this->_outlineDictionary->Last  = $lastChild;
       
   431 
       
   432             if (count($this->childOutlines) != 0) {
       
   433                 $this->_outlineDictionary->Count = new Zend_Pdf_Element_Numeric(($this->isOpen()? 1 : -1)*count($this->childOutlines));
       
   434             } else {
       
   435                 $this->_outlineDictionary->Count = null;
       
   436             }
       
   437         } else {
       
   438             foreach ($this->childOutlines as $childOutline) {
       
   439                 if ($processedOutlines->contains($childOutline)) {
       
   440                     require_once 'Zend/Pdf/Exception.php';
       
   441                     throw new Zend_Pdf_Exception('Outlines cyclyc reference is detected.');
       
   442                 }
       
   443                 $lastChild = $childOutline->dumpOutline($factory, $updateChildNavigation, $this->_outlineDictionary, $lastChild, $processedOutlines);
       
   444             }
       
   445         }
       
   446 
       
   447         return $this->_outlineDictionary;
       
   448     }
       
   449 
       
   450     public function dump($level = 0)
       
   451     {
       
   452         printf(":%3d:%s:%s:%s%s  :\n", count($this->childOutlines),$this->isItalic()? 'i':' ', $this->isBold()? 'b':' ', str_pad('', 4*$level), $this->getTitle());
       
   453 
       
   454         if ($this->isOpen()  ||  true) {
       
   455             foreach ($this->childOutlines as $child) {
       
   456                 $child->dump($level + 1);
       
   457             }
       
   458         }
       
   459     }
       
   460 }