File indexing completed on 2024-12-22 05:36:56

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Pdf
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 
0023 /** Zend_Pdf_ElementFactory_Interface */
0024 // require_once 'Zend/Pdf/ElementFactory/Interface.php';
0025 
0026 /**
0027  * PDF element factory.
0028  * Responsibility is to log PDF changes
0029  *
0030  * @package    Zend_Pdf
0031  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0032  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0033  */
0034 class Zend_Pdf_ElementFactory implements Zend_Pdf_ElementFactory_Interface
0035 {
0036     /**
0037      * List of the modified objects.
0038      * Also contains new and removed objects
0039      *
0040      * Array: ojbectNumber => Zend_Pdf_Element_Object
0041      *
0042      * @var array
0043      */
0044     private $_modifiedObjects = array();
0045 
0046     /**
0047      * List of the removed objects
0048      *
0049      * Array: ojbectNumber => Zend_Pdf_Element_Object
0050      *
0051      * @var SplObjectStorage
0052      */
0053     private $_removedObjects;
0054 
0055     /**
0056      * List of registered objects.
0057      * Used for resources clean up when factory is destroyed.
0058      *
0059      * Array of Zend_Pdf_Element objects
0060      *
0061      * @var array
0062      */
0063     private $_registeredObjects = array();
0064 
0065     /**
0066      * PDF object counter.
0067      * Actually it's an object number for new PDF object
0068      *
0069      * @var integer
0070      */
0071     private $_objectCount;
0072 
0073 
0074     /**
0075      * List of the attached object factories.
0076      * Array of Zend_Pdf_ElementFactory_Interface objects
0077      *
0078      * @var array
0079      */
0080     private $_attachedFactories = array();
0081 
0082 
0083     /**
0084      * Factory internal id
0085      *
0086      * @var integer
0087      */
0088     private $_factoryId;
0089 
0090     /**
0091      * Identity, used for factory id generation
0092      *
0093      * @var integer
0094      */
0095     private static $_identity = 0;
0096 
0097 
0098     /**
0099      * Internal cache to save calculated shifts
0100      *
0101      * @var array
0102      */
0103     private $_shiftCalculationCache = array();
0104 
0105 
0106     /**
0107      * Object constructor
0108      *
0109      * @param integer $objCount
0110      */
0111     public function __construct($objCount)
0112     {
0113         $this->_objectCount    = (int)$objCount;
0114         $this->_factoryId      = self::$_identity++;
0115         $this->_removedObjects = new SplObjectStorage();
0116     }
0117 
0118 
0119     /**
0120      * Get factory
0121      *
0122      * @return Zend_Pdf_ElementFactory_Interface
0123      */
0124     public function getFactory()
0125     {
0126         return $this;
0127     }
0128 
0129     /**
0130      * Factory generator
0131      *
0132      * @param integer $objCount
0133      * @return Zend_Pdf_ElementFactory_Interface
0134      */
0135     static public function createFactory($objCount)
0136     {
0137         // require_once 'Zend/Pdf/ElementFactory/Proxy.php';
0138         return new Zend_Pdf_ElementFactory_Proxy(new Zend_Pdf_ElementFactory($objCount));
0139     }
0140 
0141     /**
0142      * Close factory and clean-up resources
0143      *
0144      * @internal
0145      */
0146     public function close()
0147     {
0148         $this->_modifiedObjects   = null;
0149         $this->_removedObjects    = null;
0150         $this->_attachedFactories = null;
0151 
0152         foreach ($this->_registeredObjects as $obj) {
0153             $obj->cleanUp();
0154         }
0155         $this->_registeredObjects = null;
0156     }
0157 
0158     /**
0159      * Get source factory object
0160      *
0161      * @return Zend_Pdf_ElementFactory
0162      */
0163     public function resolve()
0164     {
0165         return $this;
0166     }
0167 
0168     /**
0169      * Get factory ID
0170      *
0171      * @return integer
0172      */
0173     public function getId()
0174     {
0175         return $this->_factoryId;
0176     }
0177 
0178     /**
0179      * Set object counter
0180      *
0181      * @param integer $objCount
0182      */
0183     public function setObjectCount($objCount)
0184     {
0185         $this->_objectCount = (int)$objCount;
0186     }
0187 
0188     /**
0189      * Get object counter
0190      *
0191      * @return integer
0192      */
0193     public function getObjectCount()
0194     {
0195         $count = $this->_objectCount;
0196 
0197         foreach ($this->_attachedFactories as $attached) {
0198             $count += $attached->getObjectCount() - 1; // -1 as "0" object is a special case and shared between factories
0199         }
0200 
0201         return $count;
0202     }
0203 
0204 
0205     /**
0206      * Attach factory to the current;
0207      *
0208      * @param Zend_Pdf_ElementFactory_Interface $factory
0209      */
0210     public function attach(Zend_Pdf_ElementFactory_Interface $factory)
0211     {
0212         if ( $factory === $this || isset($this->_attachedFactories[$factory->getId()])) {
0213             /**
0214              * Don't attach factory twice.
0215              * We do not check recusively because of nature of attach operation
0216              * (Pages are always attached to the Documents, Fonts are always attached
0217              * to the pages even if pages already use Document level object factory and so on)
0218              */
0219             return;
0220         }
0221 
0222         $this->_attachedFactories[$factory->getId()] = $factory;
0223     }
0224 
0225 
0226     /**
0227      * Calculate object enumeration shift.
0228      *
0229      * @param Zend_Pdf_ElementFactory_Interface $factory
0230      * @return integer
0231      */
0232     public function calculateShift(Zend_Pdf_ElementFactory_Interface $factory)
0233     {
0234         if ($factory === $this) {
0235             return 0;
0236         }
0237 
0238         if (isset($this->_shiftCalculationCache[$factory->_factoryId])) {
0239             return $this->_shiftCalculationCache[$factory->_factoryId];
0240         }
0241 
0242         $shift = $this->_objectCount - 1;
0243 
0244         foreach ($this->_attachedFactories as $subFactory) {
0245             $subFactoryShift = $subFactory->calculateShift($factory);
0246 
0247             if ($subFactoryShift != -1) {
0248                 // context found
0249                 $this->_shiftCalculationCache[$factory->_factoryId] = $shift + $subFactoryShift;
0250                 return $shift + $subFactoryShift;
0251             } else {
0252                 $shift += $subFactory->getObjectCount()-1;
0253             }
0254         }
0255 
0256         $this->_shiftCalculationCache[$factory->_factoryId] = -1;
0257         return -1;
0258     }
0259 
0260     /**
0261      * Clean enumeration shift cache.
0262      * Has to be used after PDF render operation to let followed updates be correct.
0263      */
0264     public function cleanEnumerationShiftCache()
0265     {
0266         $this->_shiftCalculationCache = array();
0267 
0268         foreach ($this->_attachedFactories as $attached) {
0269             $attached->cleanEnumerationShiftCache();
0270         }
0271     }
0272 
0273     /**
0274      * Retrive object enumeration shift.
0275      *
0276      * @param Zend_Pdf_ElementFactory_Interface $factory
0277      * @return integer
0278      * @throws Zend_Pdf_Exception
0279      */
0280     public function getEnumerationShift(Zend_Pdf_ElementFactory_Interface $factory)
0281     {
0282         if (($shift = $this->calculateShift($factory)) == -1) {
0283             // require_once 'Zend/Pdf/Exception.php';
0284             throw new Zend_Pdf_Exception('Wrong object context');
0285         }
0286 
0287         return $shift;
0288     }
0289 
0290     /**
0291      * Mark object as modified in context of current factory.
0292      *
0293      * @param Zend_Pdf_Element_Object $obj
0294      * @throws Zend_Pdf_Exception
0295      */
0296     public function markAsModified(Zend_Pdf_Element_Object $obj)
0297     {
0298         if ($obj->getFactory() !== $this) {
0299             // require_once 'Zend/Pdf/Exception.php';
0300             throw new Zend_Pdf_Exception('Object is not generated by this factory');
0301         }
0302 
0303         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
0304     }
0305 
0306 
0307     /**
0308      * Remove object in context of current factory.
0309      *
0310      * @param Zend_Pdf_Element_Object $obj
0311      * @throws Zend_Pdf_Exception
0312      */
0313     public function remove(Zend_Pdf_Element_Object $obj)
0314     {
0315         if (!$obj->compareFactory($this)) {
0316             // require_once 'Zend/Pdf/Exception.php';
0317             throw new Zend_Pdf_Exception('Object is not generated by this factory');
0318         }
0319 
0320         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
0321         $this->_removedObjects->attach($obj);
0322     }
0323 
0324 
0325     /**
0326      * Generate new Zend_Pdf_Element_Object
0327      *
0328      * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
0329      *
0330      * @param Zend_Pdf_Element $objectValue
0331      * @return Zend_Pdf_Element_Object
0332      */
0333     public function newObject(Zend_Pdf_Element $objectValue)
0334     {
0335         // require_once 'Zend/Pdf/Element/Object.php';
0336         $obj = new Zend_Pdf_Element_Object($objectValue, $this->_objectCount++, 0, $this);
0337         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
0338         return $obj;
0339     }
0340 
0341     /**
0342      * Generate new Zend_Pdf_Element_Object_Stream
0343      *
0344      * @todo Reusage of the freed object. It's not a support of new feature, but only improvement.
0345      *
0346      * @param mixed $objectValue
0347      * @return Zend_Pdf_Element_Object_Stream
0348      */
0349     public function newStreamObject($streamValue)
0350     {
0351         // require_once 'Zend/Pdf/Element/Object/Stream.php';
0352         $obj = new Zend_Pdf_Element_Object_Stream($streamValue, $this->_objectCount++, 0, $this);
0353         $this->_modifiedObjects[$obj->getObjNum()] = $obj;
0354         return $obj;
0355     }
0356 
0357 
0358     /**
0359      * Enumerate modified objects.
0360      * Returns array of Zend_Pdf_UpdateInfoContainer
0361      *
0362      * @param Zend_Pdf_ElementFactory_Interface $rootFactory
0363      * @return array
0364      */
0365     public function listModifiedObjects($rootFactory = null)
0366     {
0367         if ($rootFactory == null) {
0368             $rootFactory = $this;
0369             $shift = 0;
0370         } else {
0371             $shift = $rootFactory->getEnumerationShift($this);
0372         }
0373 
0374         ksort($this->_modifiedObjects);
0375 
0376         $result = array();
0377         // require_once 'Zend/Pdf/UpdateInfoContainer.php';
0378         foreach ($this->_modifiedObjects as $objNum => $obj) {
0379             if ($this->_removedObjects->contains($obj)) {
0380                             $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
0381                                                                            $obj->getGenNum()+1,
0382                                                                            true);
0383             } else {
0384                 $result[$objNum+$shift] = new Zend_Pdf_UpdateInfoContainer($objNum + $shift,
0385                                                                            $obj->getGenNum(),
0386                                                                            false,
0387                                                                            $obj->dump($rootFactory));
0388             }
0389         }
0390 
0391         foreach ($this->_attachedFactories as $factory) {
0392             $result += $factory->listModifiedObjects($rootFactory);
0393         }
0394 
0395         return $result;
0396     }
0397 
0398     /**
0399      * Register object in the factory
0400      *
0401      * It's used to clear "parent object" referencies when factory is closed and clean up resources
0402      *
0403      * @param string $refString
0404      * @param Zend_Pdf_Element_Object $obj
0405      */
0406     public function registerObject(Zend_Pdf_Element_Object $obj, $refString)
0407     {
0408         $this->_registeredObjects[$refString] = $obj;
0409     }
0410 
0411     /**
0412      * Fetch object specified by reference
0413      *
0414      * @param string $refString
0415      * @return Zend_Pdf_Element_Object|null
0416      */
0417     public function fetchObject($refString)
0418     {
0419         if (!isset($this->_registeredObjects[$refString])) {
0420             return null;
0421         }
0422         return $this->_registeredObjects[$refString];
0423     }
0424 
0425 
0426     /**
0427      * Check if PDF file was modified
0428      *
0429      * @return boolean
0430      */
0431     public function isModified()
0432     {
0433         if (count($this->_modifiedObjects) != 0) {
0434             return true;
0435         }
0436 
0437         foreach ($this->_attachedFactories as $subFactory) {
0438             if ($subFactory->isModified()) {
0439                 return true;
0440             }
0441         }
0442 
0443         return false;
0444     }
0445 }
0446