File indexing completed on 2025-01-19 05:21:21

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 /** Internally used classes */
0024 // require_once 'Zend/Pdf/Element/Stream.php';
0025 // require_once 'Zend/Pdf/Element/Dictionary.php';
0026 // require_once 'Zend/Pdf/Element/Numeric.php';
0027 
0028 
0029 /** Zend_Pdf_Element_Object */
0030 // require_once 'Zend/Pdf/Element/Object.php';
0031 
0032 /**
0033  * PDF file 'stream object' element implementation
0034  *
0035  * @category   Zend
0036  * @package    Zend_Pdf
0037  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0038  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0039  */
0040 class Zend_Pdf_Element_Object_Stream extends Zend_Pdf_Element_Object
0041 {
0042     /**
0043      * StreamObject dictionary
0044      * Required enries:
0045      * Length
0046      *
0047      * @var Zend_Pdf_Element_Dictionary
0048      */
0049     private $_dictionary;
0050 
0051     /**
0052      * Flag which signals, that stream is decoded
0053      *
0054      * @var boolean
0055      */
0056     private $_streamDecoded;
0057 
0058     /**
0059      * Stored original stream object dictionary.
0060      * Used to decode stream at access time.
0061      *
0062      * The only properties affecting decoding are sored here.
0063      *
0064      * @var array|null
0065      */
0066     private $_initialDictionaryData = null;
0067 
0068     /**
0069      * Object constructor
0070      *
0071      * @param mixed $val
0072      * @param integer $objNum
0073      * @param integer $genNum
0074      * @param Zend_Pdf_ElementFactory $factory
0075      * @param Zend_Pdf_Element_Dictionary|null $dictionary
0076      * @throws Zend_Pdf_Exception
0077      */
0078     public function __construct($val, $objNum, $genNum, Zend_Pdf_ElementFactory $factory, $dictionary = null)
0079     {
0080         parent::__construct(new Zend_Pdf_Element_Stream($val), $objNum, $genNum, $factory);
0081 
0082         if ($dictionary === null) {
0083             $this->_dictionary    = new Zend_Pdf_Element_Dictionary();
0084             $this->_dictionary->Length = new Zend_Pdf_Element_Numeric(strlen( $val ));
0085             $this->_streamDecoded = true;
0086         } else {
0087             $this->_dictionary    = $dictionary;
0088             $this->_streamDecoded = false;
0089         }
0090     }
0091 
0092 
0093     /**
0094      * Extract dictionary data which are used to store information and to normalize filters
0095      * information before defiltering.
0096      *
0097      * @return array
0098      */
0099     private function _extractDictionaryData()
0100     {
0101         $dictionaryArray = array();
0102 
0103         $dictionaryArray['Filter']      = array();
0104         $dictionaryArray['DecodeParms'] = array();
0105         if ($this->_dictionary->Filter === null) {
0106             // Do nothing.
0107         } else if ($this->_dictionary->Filter->getType() == Zend_Pdf_Element::TYPE_ARRAY) {
0108             foreach ($this->_dictionary->Filter->items as $id => $filter) {
0109                 $dictionaryArray['Filter'][$id]      = $filter->value;
0110                 $dictionaryArray['DecodeParms'][$id] = array();
0111 
0112                 if ($this->_dictionary->DecodeParms !== null ) {
0113                     if ($this->_dictionary->DecodeParms->items[$id] !== null &&
0114                         $this->_dictionary->DecodeParms->items[$id]->value !== null ) {
0115                         foreach ($this->_dictionary->DecodeParms->items[$id]->getKeys() as $paramKey) {
0116                             $dictionaryArray['DecodeParms'][$id][$paramKey] =
0117                                   $this->_dictionary->DecodeParms->items[$id]->$paramKey->value;
0118                         }
0119                     }
0120                 }
0121             }
0122         } else if ($this->_dictionary->Filter->getType() != Zend_Pdf_Element::TYPE_NULL) {
0123             $dictionaryArray['Filter'][0]      = $this->_dictionary->Filter->value;
0124             $dictionaryArray['DecodeParms'][0] = array();
0125             if ($this->_dictionary->DecodeParms !== null ) {
0126                 foreach ($this->_dictionary->DecodeParms->getKeys() as $paramKey) {
0127                     $dictionaryArray['DecodeParms'][0][$paramKey] =
0128                           $this->_dictionary->DecodeParms->$paramKey->value;
0129                 }
0130             }
0131         }
0132 
0133         if ($this->_dictionary->F !== null) {
0134             $dictionaryArray['F'] = $this->_dictionary->F->value;
0135         }
0136 
0137         $dictionaryArray['FFilter']      = array();
0138         $dictionaryArray['FDecodeParms'] = array();
0139         if ($this->_dictionary->FFilter === null) {
0140             // Do nothing.
0141         } else if ($this->_dictionary->FFilter->getType() == Zend_Pdf_Element::TYPE_ARRAY) {
0142             foreach ($this->_dictionary->FFilter->items as $id => $filter) {
0143                 $dictionaryArray['FFilter'][$id]      = $filter->value;
0144                 $dictionaryArray['FDecodeParms'][$id] = array();
0145 
0146                 if ($this->_dictionary->FDecodeParms !== null ) {
0147                     if ($this->_dictionary->FDecodeParms->items[$id] !== null &&
0148                         $this->_dictionary->FDecodeParms->items[$id]->value !== null) {
0149                         foreach ($this->_dictionary->FDecodeParms->items[$id]->getKeys() as $paramKey) {
0150                             $dictionaryArray['FDecodeParms'][$id][$paramKey] =
0151                                   $this->_dictionary->FDecodeParms->items[$id]->items[$paramKey]->value;
0152                         }
0153                     }
0154                 }
0155             }
0156         } else {
0157             $dictionaryArray['FFilter'][0]      = $this->_dictionary->FFilter->value;
0158             $dictionaryArray['FDecodeParms'][0] = array();
0159             if ($this->_dictionary->FDecodeParms !== null ) {
0160                 foreach ($this->_dictionary->FDecodeParms->getKeys() as $paramKey) {
0161                     $dictionaryArray['FDecodeParms'][0][$paramKey] =
0162                           $this->_dictionary->FDecodeParms->items[$paramKey]->value;
0163                 }
0164             }
0165         }
0166 
0167         return $dictionaryArray;
0168     }
0169 
0170     /**
0171      * Decode stream
0172      *
0173      * @throws Zend_Pdf_Exception
0174      */
0175     private function _decodeStream()
0176     {
0177         if ($this->_initialDictionaryData === null) {
0178             $this->_initialDictionaryData = $this->_extractDictionaryData();
0179         }
0180 
0181         /**
0182          * All applied stream filters must be processed to decode stream.
0183          * If we don't recognize any of applied filetrs an exception should be thrown here
0184          */
0185         if (isset($this->_initialDictionaryData['F'])) {
0186             /** @todo Check, how external files can be processed. */
0187             // require_once 'Zend/Pdf/Exception.php';
0188             throw new Zend_Pdf_Exception('External filters are not supported now.');
0189         }
0190 
0191         foreach ($this->_initialDictionaryData['Filter'] as $id => $filterName ) {
0192             $valueRef = &$this->_value->value->getRef();
0193             $this->_value->value->touch();
0194             switch ($filterName) {
0195                 case 'ASCIIHexDecode':
0196                     // require_once 'Zend/Pdf/Filter/AsciiHex.php';
0197                     $valueRef = Zend_Pdf_Filter_AsciiHex::decode($valueRef);
0198                     break;
0199 
0200                 case 'ASCII85Decode':
0201                     // require_once 'Zend/Pdf/Filter/Ascii85.php';
0202                     $valueRef = Zend_Pdf_Filter_Ascii85::decode($valueRef);
0203                     break;
0204 
0205                 case 'FlateDecode':
0206                     // require_once 'Zend/Pdf/Filter/Compression/Flate.php';
0207                     $valueRef = Zend_Pdf_Filter_Compression_Flate::decode($valueRef,
0208                                                                           $this->_initialDictionaryData['DecodeParms'][$id]);
0209                     break;
0210 
0211                 case 'LZWDecode':
0212                     // require_once 'Zend/Pdf/Filter/Compression/Lzw.php';
0213                     $valueRef = Zend_Pdf_Filter_Compression_Lzw::decode($valueRef,
0214                                                                         $this->_initialDictionaryData['DecodeParms'][$id]);
0215                     break;
0216 
0217                 case 'RunLengthDecode':
0218                     // require_once 'Zend/Pdf/Filter/RunLength.php';
0219                     $valueRef = Zend_Pdf_Filter_RunLength::decode($valueRef);
0220                     break;
0221 
0222                 default:
0223                     // require_once 'Zend/Pdf/Exception.php';
0224                     throw new Zend_Pdf_Exception('Unknown stream filter: \'' . $filterName . '\'.');
0225             }
0226         }
0227 
0228         $this->_streamDecoded = true;
0229     }
0230 
0231     /**
0232      * Encode stream
0233      *
0234      * @throws Zend_Pdf_Exception
0235      */
0236     private function _encodeStream()
0237     {
0238         /**
0239          * All applied stream filters must be processed to encode stream.
0240          * If we don't recognize any of applied filetrs an exception should be thrown here
0241          */
0242         if (isset($this->_initialDictionaryData['F'])) {
0243             /** @todo Check, how external files can be processed. */
0244             // require_once 'Zend/Pdf/Exception.php';
0245             throw new Zend_Pdf_Exception('External filters are not supported now.');
0246         }
0247 
0248         $filters = array_reverse($this->_initialDictionaryData['Filter'], true);
0249 
0250         foreach ($filters as $id => $filterName ) {
0251             $valueRef = &$this->_value->value->getRef();
0252             $this->_value->value->touch();
0253             switch ($filterName) {
0254                 case 'ASCIIHexDecode':
0255                     // require_once 'Zend/Pdf/Filter/AsciiHex.php';
0256                     $valueRef = Zend_Pdf_Filter_AsciiHex::encode($valueRef);
0257                     break;
0258 
0259                 case 'ASCII85Decode':
0260                     // require_once 'Zend/Pdf/Filter/Ascii85.php';
0261                     $valueRef = Zend_Pdf_Filter_Ascii85::encode($valueRef);
0262                     break;
0263 
0264                 case 'FlateDecode':
0265                     // require_once 'Zend/Pdf/Filter/Compression/Flate.php';
0266                     $valueRef = Zend_Pdf_Filter_Compression_Flate::encode($valueRef,
0267                                                                           $this->_initialDictionaryData['DecodeParms'][$id]);
0268                     break;
0269 
0270                 case 'LZWDecode':
0271                     // require_once 'Zend/Pdf/Filter/Compression/Lzw.php';
0272                     $valueRef = Zend_Pdf_Filter_Compression_Lzw::encode($valueRef,
0273                                                                         $this->_initialDictionaryData['DecodeParms'][$id]);
0274                     break;
0275 
0276                  case 'RunLengthDecode':
0277                     // require_once 'Zend/Pdf/Filter/RunLength.php';
0278                     $valueRef = Zend_Pdf_Filter_RunLength::encode($valueRef);
0279                     break;
0280 
0281                default:
0282                     // require_once 'Zend/Pdf/Exception.php';
0283                     throw new Zend_Pdf_Exception('Unknown stream filter: \'' . $filterName . '\'.');
0284             }
0285         }
0286 
0287         $this->_streamDecoded = false;
0288     }
0289 
0290     /**
0291      * Get handler
0292      *
0293      * @param string $property
0294      * @return mixed
0295      * @throws Zend_Pdf_Exception
0296      */
0297     public function __get($property)
0298     {
0299         if ($property == 'dictionary') {
0300             /**
0301              * If stream is not decoded yet, then store original decoding options (do it only once).
0302              */
0303             if (( !$this->_streamDecoded ) && ($this->_initialDictionaryData === null)) {
0304                 $this->_initialDictionaryData = $this->_extractDictionaryData();
0305             }
0306 
0307             return $this->_dictionary;
0308         }
0309 
0310         if ($property == 'value') {
0311             if (!$this->_streamDecoded) {
0312                 $this->_decodeStream();
0313             }
0314 
0315             return $this->_value->value->getRef();
0316         }
0317 
0318         // require_once 'Zend/Pdf/Exception.php';
0319         throw new Zend_Pdf_Exception('Unknown stream object property requested.');
0320     }
0321 
0322 
0323     /**
0324      * Set handler
0325      *
0326      * @param string $property
0327      * @param  mixed $value
0328      */
0329     public function __set($property, $value)
0330     {
0331         if ($property == 'value') {
0332             $valueRef = &$this->_value->value->getRef();
0333             $valueRef = $value;
0334             $this->_value->value->touch();
0335 
0336             $this->_streamDecoded = true;
0337 
0338             return;
0339         }
0340 
0341         // require_once 'Zend/Pdf/Exception.php';
0342         throw new Zend_Pdf_Exception('Unknown stream object property: \'' . $property . '\'.');
0343     }
0344 
0345 
0346     /**
0347      * Treat stream data as already encoded
0348      */
0349     public function skipFilters()
0350     {
0351         $this->_streamDecoded = false;
0352     }
0353 
0354 
0355     /**
0356      * Call handler
0357      *
0358      * @param string $method
0359      * @param array  $args
0360      * @return mixed
0361      */
0362     public function __call($method, $args)
0363     {
0364         if (!$this->_streamDecoded) {
0365             $this->_decodeStream();
0366         }
0367 
0368         switch (count($args)) {
0369             case 0:
0370                 return $this->_value->$method();
0371             case 1:
0372                 return $this->_value->$method($args[0]);
0373             default:
0374                 // require_once 'Zend/Pdf/Exception.php';
0375                 throw new Zend_Pdf_Exception('Unsupported number of arguments');
0376         }
0377     }
0378 
0379     /**
0380      * Detach PDF object from the factory (if applicable), clone it and attach to new factory.
0381      *
0382      * @param Zend_Pdf_ElementFactory $factory  The factory to attach
0383      * @param array &$processed  List of already processed indirect objects, used to avoid objects duplication
0384      * @param integer $mode  Cloning mode (defines filter for objects cloning)
0385      * @returns Zend_Pdf_Element
0386      */
0387     public function makeClone(Zend_Pdf_ElementFactory $factory, array &$processed, $mode)
0388     {
0389         $id = spl_object_hash($this);
0390         if (isset($processed[$id])) {
0391             // Do nothing if object is already processed
0392             // return it
0393             return $processed[$id];
0394         }
0395 
0396         $streamValue      = $this->_value;
0397         $streamDictionary = $this->_dictionary->makeClone($factory, $processed, $mode);
0398 
0399         // Make new empty instance of stream object and register it in $processed container
0400         $processed[$id] = $clonedObject = $factory->newStreamObject('');
0401 
0402         // Copy current object data and state
0403         $clonedObject->_dictionary            = $this->_dictionary->makeClone($factory, $processed, $mode);
0404         $clonedObject->_value                 = $this->_value->makeClone($factory, $processed, $mode);
0405         $clonedObject->_initialDictionaryData = $this->_initialDictionaryData;
0406         $clonedObject->_streamDecoded         = $this->_streamDecoded;
0407 
0408         return  $clonedObject;
0409     }
0410 
0411     /**
0412      * Dump object to a string to save within PDF file
0413      *
0414      * $factory parameter defines operation context.
0415      *
0416      * @param Zend_Pdf_ElementFactory $factory
0417      * @return string
0418      */
0419     public function dump(Zend_Pdf_ElementFactory $factory)
0420     {
0421         $shift = $factory->getEnumerationShift($this->_factory);
0422 
0423         if ($this->_streamDecoded) {
0424             $this->_initialDictionaryData = $this->_extractDictionaryData();
0425             $this->_encodeStream();
0426         } else if ($this->_initialDictionaryData != null) {
0427             $newDictionary   = $this->_extractDictionaryData();
0428 
0429             if ($this->_initialDictionaryData !== $newDictionary) {
0430                 $this->_decodeStream();
0431                 $this->_initialDictionaryData = $newDictionary;
0432                 $this->_encodeStream();
0433             }
0434         }
0435 
0436         // Update stream length
0437         $this->dictionary->Length->value = $this->_value->length();
0438 
0439         return  $this->_objNum + $shift . " " . $this->_genNum . " obj \n"
0440              .  $this->dictionary->toString($factory) . "\n"
0441              .  $this->_value->toString($factory) . "\n"
0442              . "endobj\n";
0443     }
0444 
0445     /**
0446      * Clean up resources, used by object
0447      */
0448     public function cleanUp()
0449     {
0450         $this->_dictionary = null;
0451         $this->_value      = null;
0452     }
0453 }