|
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: Stream.php 22909 2010-08-27 19:57:48Z alexander $ |
|
20 */ |
|
21 |
|
22 |
|
23 /** Internally used classes */ |
|
24 require_once 'Zend/Pdf/Element/Stream.php'; |
|
25 require_once 'Zend/Pdf/Element/Dictionary.php'; |
|
26 require_once 'Zend/Pdf/Element/Numeric.php'; |
|
27 |
|
28 |
|
29 /** Zend_Pdf_Element_Object */ |
|
30 require_once 'Zend/Pdf/Element/Object.php'; |
|
31 |
|
32 /** |
|
33 * PDF file 'stream object' element implementation |
|
34 * |
|
35 * @category Zend |
|
36 * @package Zend_Pdf |
|
37 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
38 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
39 */ |
|
40 class Zend_Pdf_Element_Object_Stream extends Zend_Pdf_Element_Object |
|
41 { |
|
42 /** |
|
43 * StreamObject dictionary |
|
44 * Required enries: |
|
45 * Length |
|
46 * |
|
47 * @var Zend_Pdf_Element_Dictionary |
|
48 */ |
|
49 private $_dictionary; |
|
50 |
|
51 /** |
|
52 * Flag which signals, that stream is decoded |
|
53 * |
|
54 * @var boolean |
|
55 */ |
|
56 private $_streamDecoded; |
|
57 |
|
58 /** |
|
59 * Stored original stream object dictionary. |
|
60 * Used to decode stream at access time. |
|
61 * |
|
62 * The only properties affecting decoding are sored here. |
|
63 * |
|
64 * @var array|null |
|
65 */ |
|
66 private $_initialDictionaryData = null; |
|
67 |
|
68 /** |
|
69 * Object constructor |
|
70 * |
|
71 * @param mixed $val |
|
72 * @param integer $objNum |
|
73 * @param integer $genNum |
|
74 * @param Zend_Pdf_ElementFactory $factory |
|
75 * @param Zend_Pdf_Element_Dictionary|null $dictionary |
|
76 * @throws Zend_Pdf_Exception |
|
77 */ |
|
78 public function __construct($val, $objNum, $genNum, Zend_Pdf_ElementFactory $factory, $dictionary = null) |
|
79 { |
|
80 parent::__construct(new Zend_Pdf_Element_Stream($val), $objNum, $genNum, $factory); |
|
81 |
|
82 if ($dictionary === null) { |
|
83 $this->_dictionary = new Zend_Pdf_Element_Dictionary(); |
|
84 $this->_dictionary->Length = new Zend_Pdf_Element_Numeric(strlen( $val )); |
|
85 $this->_streamDecoded = true; |
|
86 } else { |
|
87 $this->_dictionary = $dictionary; |
|
88 $this->_streamDecoded = false; |
|
89 } |
|
90 } |
|
91 |
|
92 |
|
93 /** |
|
94 * Extract dictionary data which are used to store information and to normalize filters |
|
95 * information before defiltering. |
|
96 * |
|
97 * @return array |
|
98 */ |
|
99 private function _extractDictionaryData() |
|
100 { |
|
101 $dictionaryArray = array(); |
|
102 |
|
103 $dictionaryArray['Filter'] = array(); |
|
104 $dictionaryArray['DecodeParms'] = array(); |
|
105 if ($this->_dictionary->Filter === null) { |
|
106 // Do nothing. |
|
107 } else if ($this->_dictionary->Filter->getType() == Zend_Pdf_Element::TYPE_ARRAY) { |
|
108 foreach ($this->_dictionary->Filter->items as $id => $filter) { |
|
109 $dictionaryArray['Filter'][$id] = $filter->value; |
|
110 $dictionaryArray['DecodeParms'][$id] = array(); |
|
111 |
|
112 if ($this->_dictionary->DecodeParms !== null ) { |
|
113 if ($this->_dictionary->DecodeParms->items[$id] !== null && |
|
114 $this->_dictionary->DecodeParms->items[$id]->value !== null ) { |
|
115 foreach ($this->_dictionary->DecodeParms->items[$id]->getKeys() as $paramKey) { |
|
116 $dictionaryArray['DecodeParms'][$id][$paramKey] = |
|
117 $this->_dictionary->DecodeParms->items[$id]->$paramKey->value; |
|
118 } |
|
119 } |
|
120 } |
|
121 } |
|
122 } else if ($this->_dictionary->Filter->getType() != Zend_Pdf_Element::TYPE_NULL) { |
|
123 $dictionaryArray['Filter'][0] = $this->_dictionary->Filter->value; |
|
124 $dictionaryArray['DecodeParms'][0] = array(); |
|
125 if ($this->_dictionary->DecodeParms !== null ) { |
|
126 foreach ($this->_dictionary->DecodeParms->getKeys() as $paramKey) { |
|
127 $dictionaryArray['DecodeParms'][0][$paramKey] = |
|
128 $this->_dictionary->DecodeParms->$paramKey->value; |
|
129 } |
|
130 } |
|
131 } |
|
132 |
|
133 if ($this->_dictionary->F !== null) { |
|
134 $dictionaryArray['F'] = $this->_dictionary->F->value; |
|
135 } |
|
136 |
|
137 $dictionaryArray['FFilter'] = array(); |
|
138 $dictionaryArray['FDecodeParms'] = array(); |
|
139 if ($this->_dictionary->FFilter === null) { |
|
140 // Do nothing. |
|
141 } else if ($this->_dictionary->FFilter->getType() == Zend_Pdf_Element::TYPE_ARRAY) { |
|
142 foreach ($this->_dictionary->FFilter->items as $id => $filter) { |
|
143 $dictionaryArray['FFilter'][$id] = $filter->value; |
|
144 $dictionaryArray['FDecodeParms'][$id] = array(); |
|
145 |
|
146 if ($this->_dictionary->FDecodeParms !== null ) { |
|
147 if ($this->_dictionary->FDecodeParms->items[$id] !== null && |
|
148 $this->_dictionary->FDecodeParms->items[$id]->value !== null) { |
|
149 foreach ($this->_dictionary->FDecodeParms->items[$id]->getKeys() as $paramKey) { |
|
150 $dictionaryArray['FDecodeParms'][$id][$paramKey] = |
|
151 $this->_dictionary->FDecodeParms->items[$id]->items[$paramKey]->value; |
|
152 } |
|
153 } |
|
154 } |
|
155 } |
|
156 } else { |
|
157 $dictionaryArray['FFilter'][0] = $this->_dictionary->FFilter->value; |
|
158 $dictionaryArray['FDecodeParms'][0] = array(); |
|
159 if ($this->_dictionary->FDecodeParms !== null ) { |
|
160 foreach ($this->_dictionary->FDecodeParms->getKeys() as $paramKey) { |
|
161 $dictionaryArray['FDecodeParms'][0][$paramKey] = |
|
162 $this->_dictionary->FDecodeParms->items[$paramKey]->value; |
|
163 } |
|
164 } |
|
165 } |
|
166 |
|
167 return $dictionaryArray; |
|
168 } |
|
169 |
|
170 /** |
|
171 * Decode stream |
|
172 * |
|
173 * @throws Zend_Pdf_Exception |
|
174 */ |
|
175 private function _decodeStream() |
|
176 { |
|
177 if ($this->_initialDictionaryData === null) { |
|
178 $this->_initialDictionaryData = $this->_extractDictionaryData(); |
|
179 } |
|
180 |
|
181 /** |
|
182 * All applied stream filters must be processed to decode stream. |
|
183 * If we don't recognize any of applied filetrs an exception should be thrown here |
|
184 */ |
|
185 if (isset($this->_initialDictionaryData['F'])) { |
|
186 /** @todo Check, how external files can be processed. */ |
|
187 require_once 'Zend/Pdf/Exception.php'; |
|
188 throw new Zend_Pdf_Exception('External filters are not supported now.'); |
|
189 } |
|
190 |
|
191 foreach ($this->_initialDictionaryData['Filter'] as $id => $filterName ) { |
|
192 $valueRef = &$this->_value->value->getRef(); |
|
193 $this->_value->value->touch(); |
|
194 switch ($filterName) { |
|
195 case 'ASCIIHexDecode': |
|
196 require_once 'Zend/Pdf/Filter/AsciiHex.php'; |
|
197 $valueRef = Zend_Pdf_Filter_AsciiHex::decode($valueRef); |
|
198 break; |
|
199 |
|
200 case 'ASCII85Decode': |
|
201 require_once 'Zend/Pdf/Filter/Ascii85.php'; |
|
202 $valueRef = Zend_Pdf_Filter_Ascii85::decode($valueRef); |
|
203 break; |
|
204 |
|
205 case 'FlateDecode': |
|
206 require_once 'Zend/Pdf/Filter/Compression/Flate.php'; |
|
207 $valueRef = Zend_Pdf_Filter_Compression_Flate::decode($valueRef, |
|
208 $this->_initialDictionaryData['DecodeParms'][$id]); |
|
209 break; |
|
210 |
|
211 case 'LZWDecode': |
|
212 require_once 'Zend/Pdf/Filter/Compression/Lzw.php'; |
|
213 $valueRef = Zend_Pdf_Filter_Compression_Lzw::decode($valueRef, |
|
214 $this->_initialDictionaryData['DecodeParms'][$id]); |
|
215 break; |
|
216 |
|
217 case 'RunLengthDecode': |
|
218 require_once 'Zend/Pdf/Filter/RunLength.php'; |
|
219 $valueRef = Zend_Pdf_Filter_RunLength::decode($valueRef); |
|
220 break; |
|
221 |
|
222 default: |
|
223 require_once 'Zend/Pdf/Exception.php'; |
|
224 throw new Zend_Pdf_Exception('Unknown stream filter: \'' . $filterName . '\'.'); |
|
225 } |
|
226 } |
|
227 |
|
228 $this->_streamDecoded = true; |
|
229 } |
|
230 |
|
231 /** |
|
232 * Encode stream |
|
233 * |
|
234 * @throws Zend_Pdf_Exception |
|
235 */ |
|
236 private function _encodeStream() |
|
237 { |
|
238 /** |
|
239 * All applied stream filters must be processed to encode stream. |
|
240 * If we don't recognize any of applied filetrs an exception should be thrown here |
|
241 */ |
|
242 if (isset($this->_initialDictionaryData['F'])) { |
|
243 /** @todo Check, how external files can be processed. */ |
|
244 require_once 'Zend/Pdf/Exception.php'; |
|
245 throw new Zend_Pdf_Exception('External filters are not supported now.'); |
|
246 } |
|
247 |
|
248 $filters = array_reverse($this->_initialDictionaryData['Filter'], true); |
|
249 |
|
250 foreach ($filters as $id => $filterName ) { |
|
251 $valueRef = &$this->_value->value->getRef(); |
|
252 $this->_value->value->touch(); |
|
253 switch ($filterName) { |
|
254 case 'ASCIIHexDecode': |
|
255 require_once 'Zend/Pdf/Filter/AsciiHex.php'; |
|
256 $valueRef = Zend_Pdf_Filter_AsciiHex::encode($valueRef); |
|
257 break; |
|
258 |
|
259 case 'ASCII85Decode': |
|
260 require_once 'Zend/Pdf/Filter/Ascii85.php'; |
|
261 $valueRef = Zend_Pdf_Filter_Ascii85::encode($valueRef); |
|
262 break; |
|
263 |
|
264 case 'FlateDecode': |
|
265 require_once 'Zend/Pdf/Filter/Compression/Flate.php'; |
|
266 $valueRef = Zend_Pdf_Filter_Compression_Flate::encode($valueRef, |
|
267 $this->_initialDictionaryData['DecodeParms'][$id]); |
|
268 break; |
|
269 |
|
270 case 'LZWDecode': |
|
271 require_once 'Zend/Pdf/Filter/Compression/Lzw.php'; |
|
272 $valueRef = Zend_Pdf_Filter_Compression_Lzw::encode($valueRef, |
|
273 $this->_initialDictionaryData['DecodeParms'][$id]); |
|
274 break; |
|
275 |
|
276 case 'RunLengthDecode': |
|
277 require_once 'Zend/Pdf/Filter/RunLength.php'; |
|
278 $valueRef = Zend_Pdf_Filter_RunLength::encode($valueRef); |
|
279 break; |
|
280 |
|
281 default: |
|
282 require_once 'Zend/Pdf/Exception.php'; |
|
283 throw new Zend_Pdf_Exception('Unknown stream filter: \'' . $filterName . '\'.'); |
|
284 } |
|
285 } |
|
286 |
|
287 $this->_streamDecoded = false; |
|
288 } |
|
289 |
|
290 /** |
|
291 * Get handler |
|
292 * |
|
293 * @param string $property |
|
294 * @return mixed |
|
295 * @throws Zend_Pdf_Exception |
|
296 */ |
|
297 public function __get($property) |
|
298 { |
|
299 if ($property == 'dictionary') { |
|
300 /** |
|
301 * If stream is not decoded yet, then store original decoding options (do it only once). |
|
302 */ |
|
303 if (( !$this->_streamDecoded ) && ($this->_initialDictionaryData === null)) { |
|
304 $this->_initialDictionaryData = $this->_extractDictionaryData(); |
|
305 } |
|
306 |
|
307 return $this->_dictionary; |
|
308 } |
|
309 |
|
310 if ($property == 'value') { |
|
311 if (!$this->_streamDecoded) { |
|
312 $this->_decodeStream(); |
|
313 } |
|
314 |
|
315 return $this->_value->value->getRef(); |
|
316 } |
|
317 |
|
318 require_once 'Zend/Pdf/Exception.php'; |
|
319 throw new Zend_Pdf_Exception('Unknown stream object property requested.'); |
|
320 } |
|
321 |
|
322 |
|
323 /** |
|
324 * Set handler |
|
325 * |
|
326 * @param string $property |
|
327 * @param mixed $value |
|
328 */ |
|
329 public function __set($property, $value) |
|
330 { |
|
331 if ($property == 'value') { |
|
332 $valueRef = &$this->_value->value->getRef(); |
|
333 $valueRef = $value; |
|
334 $this->_value->value->touch(); |
|
335 |
|
336 $this->_streamDecoded = true; |
|
337 |
|
338 return; |
|
339 } |
|
340 |
|
341 require_once 'Zend/Pdf/Exception.php'; |
|
342 throw new Zend_Pdf_Exception('Unknown stream object property: \'' . $property . '\'.'); |
|
343 } |
|
344 |
|
345 |
|
346 /** |
|
347 * Treat stream data as already encoded |
|
348 */ |
|
349 public function skipFilters() |
|
350 { |
|
351 $this->_streamDecoded = false; |
|
352 } |
|
353 |
|
354 |
|
355 /** |
|
356 * Call handler |
|
357 * |
|
358 * @param string $method |
|
359 * @param array $args |
|
360 * @return mixed |
|
361 */ |
|
362 public function __call($method, $args) |
|
363 { |
|
364 if (!$this->_streamDecoded) { |
|
365 $this->_decodeStream(); |
|
366 } |
|
367 |
|
368 switch (count($args)) { |
|
369 case 0: |
|
370 return $this->_value->$method(); |
|
371 case 1: |
|
372 return $this->_value->$method($args[0]); |
|
373 default: |
|
374 require_once 'Zend/Pdf/Exception.php'; |
|
375 throw new Zend_Pdf_Exception('Unsupported number of arguments'); |
|
376 } |
|
377 } |
|
378 |
|
379 /** |
|
380 * Detach PDF object from the factory (if applicable), clone it and attach to new factory. |
|
381 * |
|
382 * @param Zend_Pdf_ElementFactory $factory The factory to attach |
|
383 * @param array &$processed List of already processed indirect objects, used to avoid objects duplication |
|
384 * @param integer $mode Cloning mode (defines filter for objects cloning) |
|
385 * @returns Zend_Pdf_Element |
|
386 */ |
|
387 public function makeClone(Zend_Pdf_ElementFactory $factory, array &$processed, $mode) |
|
388 { |
|
389 $id = spl_object_hash($this); |
|
390 if (isset($processed[$id])) { |
|
391 // Do nothing if object is already processed |
|
392 // return it |
|
393 return $processed[$id]; |
|
394 } |
|
395 |
|
396 $streamValue = $this->_value; |
|
397 $streamDictionary = $this->_dictionary->makeClone($factory, $processed, $mode); |
|
398 |
|
399 // Make new empty instance of stream object and register it in $processed container |
|
400 $processed[$id] = $clonedObject = $factory->newStreamObject(''); |
|
401 |
|
402 // Copy current object data and state |
|
403 $clonedObject->_dictionary = $this->_dictionary->makeClone($factory, $processed, $mode); |
|
404 $clonedObject->_value = $this->_value->makeClone($factory, $processed, $mode); |
|
405 $clonedObject->_initialDictionaryData = $this->_initialDictionaryData; |
|
406 $clonedObject->_streamDecoded = $this->_streamDecoded; |
|
407 |
|
408 return $clonedObject; |
|
409 } |
|
410 |
|
411 /** |
|
412 * Dump object to a string to save within PDF file |
|
413 * |
|
414 * $factory parameter defines operation context. |
|
415 * |
|
416 * @param Zend_Pdf_ElementFactory $factory |
|
417 * @return string |
|
418 */ |
|
419 public function dump(Zend_Pdf_ElementFactory $factory) |
|
420 { |
|
421 $shift = $factory->getEnumerationShift($this->_factory); |
|
422 |
|
423 if ($this->_streamDecoded) { |
|
424 $this->_initialDictionaryData = $this->_extractDictionaryData(); |
|
425 $this->_encodeStream(); |
|
426 } else if ($this->_initialDictionaryData != null) { |
|
427 $newDictionary = $this->_extractDictionaryData(); |
|
428 |
|
429 if ($this->_initialDictionaryData !== $newDictionary) { |
|
430 $this->_decodeStream(); |
|
431 $this->_initialDictionaryData = $newDictionary; |
|
432 $this->_encodeStream(); |
|
433 } |
|
434 } |
|
435 |
|
436 // Update stream length |
|
437 $this->dictionary->Length->value = $this->_value->length(); |
|
438 |
|
439 return $this->_objNum + $shift . " " . $this->_genNum . " obj \n" |
|
440 . $this->dictionary->toString($factory) . "\n" |
|
441 . $this->_value->toString($factory) . "\n" |
|
442 . "endobj\n"; |
|
443 } |
|
444 |
|
445 /** |
|
446 * Clean up resources, used by object |
|
447 */ |
|
448 public function cleanUp() |
|
449 { |
|
450 $this->_dictionary = null; |
|
451 $this->_value = null; |
|
452 } |
|
453 } |