web/lib/Zend/Pdf/ElementFactory.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  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: ElementFactory.php 22797 2010-08-06 15:02:12Z alexander $
       
    20  */
       
    21 
       
    22 
       
    23 /** Zend_Pdf_ElementFactory_Interface */
       
    24 require_once 'Zend/Pdf/ElementFactory/Interface.php';
       
    25 
       
    26 /**
       
    27  * PDF element factory.
       
    28  * Responsibility is to log PDF changes
       
    29  *
       
    30  * @package    Zend_Pdf
       
    31  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    32  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    33  */
       
    34 class Zend_Pdf_ElementFactory implements Zend_Pdf_ElementFactory_Interface
       
    35 {
       
    36     /**
       
    37      * List of the modified objects.
       
    38      * Also contains new and removed objects
       
    39      *
       
    40      * Array: ojbectNumber => Zend_Pdf_Element_Object
       
    41      *
       
    42      * @var array
       
    43      */
       
    44     private $_modifiedObjects = array();
       
    45 
       
    46     /**
       
    47      * List of the removed objects
       
    48      *
       
    49      * Array: ojbectNumber => Zend_Pdf_Element_Object
       
    50      *
       
    51      * @var SplObjectStorage
       
    52      */
       
    53     private $_removedObjects;
       
    54 
       
    55     /**
       
    56      * List of registered objects.
       
    57      * Used for resources clean up when factory is destroyed.
       
    58      *
       
    59      * Array of Zend_Pdf_Element objects
       
    60      *
       
    61      * @var array
       
    62      */
       
    63     private $_registeredObjects = array();
       
    64 
       
    65     /**
       
    66      * PDF object counter.
       
    67      * Actually it's an object number for new PDF object
       
    68      *
       
    69      * @var integer
       
    70      */
       
    71     private $_objectCount;
       
    72 
       
    73 
       
    74     /**
       
    75      * List of the attached object factories.
       
    76      * Array of Zend_Pdf_ElementFactory_Interface objects
       
    77      *
       
    78      * @var array
       
    79      */
       
    80     private $_attachedFactories = array();
       
    81 
       
    82 
       
    83     /**
       
    84      * Factory internal id
       
    85      *
       
    86      * @var integer
       
    87      */
       
    88     private $_factoryId;
       
    89 
       
    90     /**
       
    91      * Identity, used for factory id generation
       
    92      *
       
    93      * @var integer
       
    94      */
       
    95     private static $_identity = 0;
       
    96 
       
    97 
       
    98     /**
       
    99      * Internal cache to save calculated shifts
       
   100      *
       
   101      * @var array
       
   102      */
       
   103     private $_shiftCalculationCache = array();
       
   104 
       
   105 
       
   106     /**
       
   107      * Object constructor
       
   108      *
       
   109      * @param integer $objCount
       
   110      */
       
   111     public function __construct($objCount)
       
   112     {
       
   113         $this->_objectCount    = (int)$objCount;
       
   114         $this->_factoryId      = self::$_identity++;
       
   115         $this->_removedObjects = new SplObjectStorage();
       
   116     }
       
   117 
       
   118 
       
   119     /**
       
   120      * Get factory
       
   121      *
       
   122      * @return Zend_Pdf_ElementFactory_Interface
       
   123      */
       
   124     public function getFactory()
       
   125     {
       
   126         return $this;
       
   127     }
       
   128 
       
   129     /**
       
   130      * Factory generator
       
   131      *
       
   132      * @param integer $objCount
       
   133      * @return Zend_Pdf_ElementFactory_Interface
       
   134      */
       
   135     static public function createFactory($objCount)
       
   136     {
       
   137         require_once 'Zend/Pdf/ElementFactory/Proxy.php';
       
   138         return new Zend_Pdf_ElementFactory_Proxy(new Zend_Pdf_ElementFactory($objCount));
       
   139     }
       
   140 
       
   141     /**
       
   142      * Close factory and clean-up resources
       
   143      *
       
   144      * @internal
       
   145      */
       
   146     public function close()
       
   147     {
       
   148         $this->_modifiedObjects   = null;
       
   149         $this->_removedObjects    = null;
       
   150         $this->_attachedFactories = null;
       
   151 
       
   152         foreach ($this->_registeredObjects as $obj) {
       
   153             $obj->cleanUp();
       
   154         }
       
   155         $this->_registeredObjects = null;
       
   156     }
       
   157 
       
   158     /**
       
   159      * Get source factory object
       
   160      *
       
   161      * @return Zend_Pdf_ElementFactory
       
   162      */
       
   163     public function resolve()
       
   164     {
       
   165         return $this;
       
   166     }
       
   167 
       
   168     /**
       
   169      * Get factory ID
       
   170      *
       
   171      * @return integer
       
   172      */
       
   173     public function getId()
       
   174     {
       
   175         return $this->_factoryId;
       
   176     }
       
   177 
       
   178     /**
       
   179      * Set object counter
       
   180      *
       
   181      * @param integer $objCount
       
   182      */
       
   183     public function setObjectCount($objCount)
       
   184     {
       
   185         $this->_objectCount = (int)$objCount;
       
   186     }
       
   187 
       
   188     /**
       
   189      * Get object counter
       
   190      *
       
   191      * @return integer
       
   192      */
       
   193     public function getObjectCount()
       
   194     {
       
   195         $count = $this->_objectCount;
       
   196 
       
   197         foreach ($this->_attachedFactories as $attached) {
       
   198             $count += $attached->getObjectCount() - 1; // -1 as "0" object is a special case and shared between factories
       
   199         }
       
   200 
       
   201         return $count;
       
   202     }
       
   203 
       
   204 
       
   205     /**
       
   206      * Attach factory to the current;
       
   207      *
       
   208      * @param Zend_Pdf_ElementFactory_Interface $factory
       
   209      */
       
   210     public function attach(Zend_Pdf_ElementFactory_Interface $factory)
       
   211     {
       
   212         if ( $factory === $this || isset($this->_attachedFactories[$factory->getId()])) {
       
   213             /**
       
   214              * Don't attach factory twice.
       
   215              * We do not check recusively because of nature of attach operation
       
   216              * (Pages are always attached to the Documents, Fonts are always attached
       
   217              * to the pages even if pages already use Document level object factory and so on)
       
   218              */
       
   219             return;
       
   220         }
       
   221 
       
   222         $this->_attachedFactories[$factory->getId()] = $factory;
       
   223     }
       
   224 
       
   225 
       
   226     /**
       
   227      * Calculate object enumeration shift.
       
   228      *
       
   229      * @param Zend_Pdf_ElementFactory_Interface $factory
       
   230      * @return integer
       
   231      */
       
   232     public function calculateShift(Zend_Pdf_ElementFactory_Interface $factory)
       
   233     {
       
   234         if ($factory === $this) {
       
   235             return 0;
       
   236         }
       
   237 
       
   238         if (isset($this->_shiftCalculationCache[$factory->_factoryId])) {
       
   239             return $this->_shiftCalculationCache[$factory->_factoryId];
       
   240         }
       
   241 
       
   242         $shift = $this->_objectCount - 1;
       
   243 
       
   244         foreach ($this->_attachedFactories as $subFactory) {
       
   245             $subFactoryShift = $subFactory->calculateShift($factory);
       
   246 
       
   247             if ($subFactoryShift != -1) {
       
   248                 // context found
       
   249                 $this->_shiftCalculationCache[$factory->_factoryId] = $shift + $subFactoryShift;
       
   250                 return $shift + $subFactoryShift;
       
   251             } else {
       
   252                 $shift += $subFactory->getObjectCount()-1;
       
   253             }
       
   254         }
       
   255 
       
   256         $this->_shiftCalculationCache[$factory->_factoryId] = -1;
       
   257         return -1;
       
   258     }
       
   259 
       
   260     /**
       
   261      * Clean enumeration shift cache.
       
   262      * Has to be used after PDF render operation to let followed updates be correct.
       
   263      */
       
   264     public function cleanEnumerationShiftCache()
       
   265     {
       
   266         $this->_shiftCalculationCache = array();
       
   267 
       
   268         foreach ($this->_attachedFactories as $attached) {
       
   269             $attached->cleanEnumerationShiftCache();
       
   270         }
       
   271     }
       
   272 
       
   273     /**
       
   274      * Retrive object enumeration shift.
       
   275      *
       
   276      * @param Zend_Pdf_ElementFactory_Interface $factory
       
   277      * @return integer
       
   278      * @throws Zend_Pdf_Exception
       
   279      */
       
   280     public function getEnumerationShift(Zend_Pdf_ElementFactory_Interface $factory)
       
   281     {
       
   282         if (($shift = $this->calculateShift($factory)) == -1) {
       
   283             require_once 'Zend/Pdf/Exception.php';
       
   284             throw new Zend_Pdf_Exception('Wrong object context');
       
   285         }
       
   286 
       
   287         return $shift;
       
   288     }
       
   289 
       
   290     /**
       
   291      * Mark object as modified in context of current factory.
       
   292      *
       
   293      * @param Zend_Pdf_Element_Object $obj
       
   294      * @throws Zend_Pdf_Exception
       
   295      */
       
   296     public function markAsModified(Zend_Pdf_Element_Object $obj)
       
   297     {
       
   298         if ($obj->getFactory() !== $this) {
       
   299             require_once 'Zend/Pdf/Exception.php';
       
   300             throw new Zend_Pdf_Exception('Object is not generated by this factory');
       
   301         }
       
   302 
       
   303         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
       
   304     }
       
   305 
       
   306 
       
   307     /**
       
   308      * Remove object in context of current factory.
       
   309      *
       
   310      * @param Zend_Pdf_Element_Object $obj
       
   311      * @throws Zend_Pdf_Exception
       
   312      */
       
   313     public function remove(Zend_Pdf_Element_Object $obj)
       
   314     {
       
   315         if (!$obj->compareFactory($this)) {
       
   316             require_once 'Zend/Pdf/Exception.php';
       
   317             throw new Zend_Pdf_Exception('Object is not generated by this factory');
       
   318         }
       
   319 
       
   320         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
       
   321         $this->_removedObjects->attach($obj);
       
   322     }
       
   323 
       
   324 
       
   325     /**
       
   326      * Generate new Zend_Pdf_Element_Object
       
   327      *
       
   328      * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
       
   329      *
       
   330      * @param Zend_Pdf_Element $objectValue
       
   331      * @return Zend_Pdf_Element_Object
       
   332      */
       
   333     public function newObject(Zend_Pdf_Element $objectValue)
       
   334     {
       
   335         require_once 'Zend/Pdf/Element/Object.php';
       
   336         $obj = new Zend_Pdf_Element_Object($objectValue, $this->_objectCount++, 0, $this);
       
   337         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
       
   338         return $obj;
       
   339     }
       
   340 
       
   341     /**
       
   342      * Generate new Zend_Pdf_Element_Object_Stream
       
   343      *
       
   344      * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
       
   345      *
       
   346      * @param mixed $objectValue
       
   347      * @return Zend_Pdf_Element_Object_Stream
       
   348      */
       
   349     public function newStreamObject($streamValue)
       
   350     {
       
   351         require_once 'Zend/Pdf/Element/Object/Stream.php';
       
   352         $obj = new Zend_Pdf_Element_Object_Stream($streamValue, $this->_objectCount++, 0, $this);
       
   353         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
       
   354         return $obj;
       
   355     }
       
   356 
       
   357 
       
   358     /**
       
   359      * Enumerate modified objects.
       
   360      * Returns array of Zend_Pdf_UpdateInfoContainer
       
   361      *
       
   362      * @param Zend_Pdf_ElementFactory_Interface $rootFactory
       
   363      * @return array
       
   364      */
       
   365     public function listModifiedObjects($rootFactory = null)
       
   366     {
       
   367         if ($rootFactory == null) {
       
   368             $rootFactory = $this;
       
   369             $shift = 0;
       
   370         } else {
       
   371             $shift = $rootFactory->getEnumerationShift($this);
       
   372         }
       
   373 
       
   374         ksort($this->_modifiedObjects);
       
   375 
       
   376         $result = array();
       
   377         require_once 'Zend/Pdf/UpdateInfoContainer.php';
       
   378         foreach ($this->_modifiedObjects as $objNum => $obj) {
       
   379             if ($this->_removedObjects->contains($obj)) {
       
   380                             $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
       
   381                                                                            $obj->getGenNum()+1,
       
   382                                                                            true);
       
   383             } else {
       
   384                 $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
       
   385                                                                            $obj->getGenNum(),
       
   386                                                                            false,
       
   387                                                                            $obj->dump($rootFactory));
       
   388             }
       
   389         }
       
   390 
       
   391         foreach ($this->_attachedFactories as $factory) {
       
   392             $result += $factory->listModifiedObjects($rootFactory);
       
   393         }
       
   394 
       
   395         return $result;
       
   396     }
       
   397 
       
   398     /**
       
   399      * Register object in the factory
       
   400      *
       
   401      * It's used to clear "parent object" referencies when factory is closed and clean up resources
       
   402      *
       
   403      * @param string $refString
       
   404      * @param Zend_Pdf_Element_Object $obj
       
   405      */
       
   406     public function registerObject(Zend_Pdf_Element_Object $obj, $refString)
       
   407     {
       
   408         $this->_registeredObjects[$refString] = $obj;
       
   409     }
       
   410 
       
   411     /**
       
   412      * Fetch object specified by reference
       
   413      *
       
   414      * @param string $refString
       
   415      * @return Zend_Pdf_Element_Object|null
       
   416      */
       
   417     public function fetchObject($refString)
       
   418     {
       
   419         if (!isset($this->_registeredObjects[$refString])) {
       
   420             return null;
       
   421         }
       
   422         return $this->_registeredObjects[$refString];
       
   423     }
       
   424 
       
   425 
       
   426     /**
       
   427      * Check if PDF file was modified
       
   428      *
       
   429      * @return boolean
       
   430      */
       
   431     public function isModified()
       
   432     {
       
   433         if (count($this->_modifiedObjects) != 0) {
       
   434             return true;
       
   435         }
       
   436 
       
   437         foreach ($this->_attachedFactories as $subFactory) {
       
   438             if ($subFactory->isModified()) {
       
   439                 return true;
       
   440             }
       
   441         }
       
   442 
       
   443         return false;
       
   444     }
       
   445 }
       
   446