PageRenderTime 63ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/PDF/InternalType/StreamObject.php

https://github.com/giorgiosironi/zf2
PHP | 413 lines | 225 code | 53 blank | 135 comment | 42 complexity | 76aa75890cbc7689d3d65d3b0fc90c36 MD5 | raw file
  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. * @package Zend_PDF_Internal
  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$
  21. */
  22. /**
  23. * @namespace
  24. */
  25. namespace Zend\PDF\InternalType;
  26. use Zend\PDF\InternalType\StreamFilter\Compression as CompressionFilter;
  27. use Zend\PDF\ObjectFactory;
  28. use Zend\PDF;
  29. /**
  30. * PDF file 'stream object' element implementation
  31. *
  32. * @uses \Zend\PDF\InternalType\AbstractTypeObject
  33. * @uses \Zend\PDF\InternalType\DictionaryObject
  34. * @uses \Zend\PDF\InternalType\NumericObject
  35. * @uses \Zend\PDF\InternalType\IndirectObject
  36. * @uses \Zend\PDF\InternalType\StreamContent
  37. * @uses \Zend\PDF\Exception
  38. * @uses \Zend\PDF\InternalType\StreamFilter
  39. * @uses \Zend\PDF\InternalType\StreamFilter\Compression;
  40. * @category Zend
  41. * @package Zend_PDF
  42. * @package Zend_PDF_Internal
  43. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  44. * @license http://framework.zend.com/license/new-bsd New BSD License
  45. */
  46. class StreamObject extends IndirectObject
  47. {
  48. /**
  49. * StreamObject dictionary
  50. * Required enries:
  51. * Length
  52. *
  53. * @var \Zend\PDF\InternalType\DictionaryObject
  54. */
  55. private $_dictionary;
  56. /**
  57. * Flag which signals, that stream is decoded
  58. *
  59. * @var boolean
  60. */
  61. private $_streamDecoded;
  62. /**
  63. * Stored original stream object dictionary.
  64. * Used to decode stream during an access time.
  65. *
  66. * The only properties, which affect decoding, are sored here.
  67. *
  68. * @var array|null
  69. */
  70. private $_originalDictionary = null;
  71. /**
  72. * Object constructor
  73. *
  74. * @param mixed $val
  75. * @param integer $objNum
  76. * @param integer $genNum
  77. * @param \Zend\PDF\ObjectFactory\ObjectFactory $factory
  78. * @param \Zend\PDF\InternalType\DictionaryObject|null $dictionary
  79. * @throws \Zend\PDF\Exception
  80. */
  81. public function __construct($val, $objNum, $genNum, ObjectFactory\ObjectFactory $factory, $dictionary = null)
  82. {
  83. parent::__construct(new StreamContent($val), $objNum, $genNum, $factory);
  84. if ($dictionary === null) {
  85. $this->_dictionary = new DictionaryObject();
  86. $this->_dictionary->Length = new NumericObject(strlen( $val ));
  87. $this->_streamDecoded = true;
  88. } else {
  89. $this->_dictionary = $dictionary;
  90. $this->_streamDecoded = false;
  91. }
  92. }
  93. /**
  94. * Store original dictionary information in $_originalDictionary class member.
  95. * Used to store information and to normalize filters information before defiltering.
  96. *
  97. */
  98. private function _storeOriginalDictionary()
  99. {
  100. $this->_originalDictionary = array();
  101. $this->_originalDictionary['Filter'] = array();
  102. $this->_originalDictionary['DecodeParms'] = array();
  103. if ($this->_dictionary->Filter === null) {
  104. // Do nothing.
  105. } else if ($this->_dictionary->Filter->getType() == AbstractTypeObject::TYPE_ARRAY) {
  106. foreach ($this->_dictionary->Filter->items as $id => $filter) {
  107. $this->_originalDictionary['Filter'][$id] = $filter->value;
  108. $this->_originalDictionary['DecodeParms'][$id] = array();
  109. if ($this->_dictionary->DecodeParms !== null ) {
  110. if ($this->_dictionary->DecodeParms->items[$id] !== null &&
  111. $this->_dictionary->DecodeParms->items[$id]->value !== null ) {
  112. foreach ($this->_dictionary->DecodeParms->items[$id]->getKeys() as $paramKey) {
  113. $this->_originalDictionary['DecodeParms'][$id][$paramKey] =
  114. $this->_dictionary->DecodeParms->items[$id]->$paramKey->value;
  115. }
  116. }
  117. }
  118. }
  119. } else if ($this->_dictionary->Filter->getType() != AbstractTypeObject::TYPE_NULL) {
  120. $this->_originalDictionary['Filter'][0] = $this->_dictionary->Filter->value;
  121. $this->_originalDictionary['DecodeParms'][0] = array();
  122. if ($this->_dictionary->DecodeParms !== null ) {
  123. foreach ($this->_dictionary->DecodeParms->getKeys() as $paramKey) {
  124. $this->_originalDictionary['DecodeParms'][0][$paramKey] =
  125. $this->_dictionary->DecodeParms->$paramKey->value;
  126. }
  127. }
  128. }
  129. if ($this->_dictionary->F !== null) {
  130. $this->_originalDictionary['F'] = $this->_dictionary->F->value;
  131. }
  132. $this->_originalDictionary['FFilter'] = array();
  133. $this->_originalDictionary['FDecodeParms'] = array();
  134. if ($this->_dictionary->FFilter === null) {
  135. // Do nothing.
  136. } else if ($this->_dictionary->FFilter->getType() == AbstractTypeObject::TYPE_ARRAY) {
  137. foreach ($this->_dictionary->FFilter->items as $id => $filter) {
  138. $this->_originalDictionary['FFilter'][$id] = $filter->value;
  139. $this->_originalDictionary['FDecodeParms'][$id] = array();
  140. if ($this->_dictionary->FDecodeParms !== null ) {
  141. if ($this->_dictionary->FDecodeParms->items[$id] !== null &&
  142. $this->_dictionary->FDecodeParms->items[$id]->value !== null) {
  143. foreach ($this->_dictionary->FDecodeParms->items[$id]->getKeys() as $paramKey) {
  144. $this->_originalDictionary['FDecodeParms'][$id][$paramKey] =
  145. $this->_dictionary->FDecodeParms->items[$id]->items[$paramKey]->value;
  146. }
  147. }
  148. }
  149. }
  150. } else {
  151. $this->_originalDictionary['FFilter'][0] = $this->_dictionary->FFilter->value;
  152. $this->_originalDictionary['FDecodeParms'][0] = array();
  153. if ($this->_dictionary->FDecodeParms !== null ) {
  154. foreach ($this->_dictionary->FDecodeParms->getKeys() as $paramKey) {
  155. $this->_originalDictionary['FDecodeParms'][0][$paramKey] =
  156. $this->_dictionary->FDecodeParms->items[$paramKey]->value;
  157. }
  158. }
  159. }
  160. }
  161. /**
  162. * Decode stream
  163. *
  164. * @throws \Zend\PDF\Exception
  165. */
  166. private function _decodeStream()
  167. {
  168. if ($this->_originalDictionary === null) {
  169. $this->_storeOriginalDictionary();
  170. }
  171. /**
  172. * All applied stream filters must be processed to decode stream.
  173. * If we don't recognize any of applied filetrs an exception should be thrown here
  174. */
  175. if (isset($this->_originalDictionary['F'])) {
  176. /** @todo Check, how external files can be processed. */
  177. throw new PDF\Exception('External filters are not supported now.');
  178. }
  179. foreach ($this->_originalDictionary['Filter'] as $id => $filterName ) {
  180. $valueRef = &$this->_value->value->getRef();
  181. $this->_value->value->touch();
  182. switch ($filterName) {
  183. case 'ASCIIHexDecode':
  184. $valueRef = StreamFilter\ASCIIHex::decode($valueRef);
  185. break;
  186. case 'ASCII85Decode':
  187. $valueRef = StreamFilter\ASCII85::decode($valueRef);
  188. break;
  189. case 'FlateDecode':
  190. $valueRef = CompressionFilter\Flate::decode($valueRef,
  191. $this->_originalDictionary['DecodeParms'][$id]);
  192. break;
  193. case 'LZWDecode':
  194. $valueRef = CompressionFilter\LZW::decode($valueRef,
  195. $this->_originalDictionary['DecodeParms'][$id]);
  196. break;
  197. case 'RunLengthDecode':
  198. $valueRef = StreamFilter\RunLength::decode($valueRef);
  199. break;
  200. default:
  201. throw new PDF\Exception('Unknown stream filter: \'' . $filterName . '\'.');
  202. }
  203. }
  204. $this->_streamDecoded = true;
  205. }
  206. /**
  207. * Encode stream
  208. *
  209. * @throws \Zend\PDF\Exception
  210. */
  211. private function _encodeStream()
  212. {
  213. /**
  214. * All applied stream filters must be processed to encode stream.
  215. * If we don't recognize any of applied filetrs an exception should be thrown here
  216. */
  217. if (isset($this->_originalDictionary['F'])) {
  218. /** @todo Check, how external files can be processed. */
  219. throw new PDF\Exception('External filters are not supported now.');
  220. }
  221. $filters = array_reverse($this->_originalDictionary['Filter'], true);
  222. foreach ($filters as $id => $filterName ) {
  223. $valueRef = &$this->_value->value->getRef();
  224. $this->_value->value->touch();
  225. switch ($filterName) {
  226. case 'ASCIIHexDecode':
  227. $valueRef = StreamFilter\ASCIIHex::encode($valueRef);
  228. break;
  229. case 'ASCII85Decode':
  230. $valueRef = StreamFilter\ASCII85::encode($valueRef);
  231. break;
  232. case 'FlateDecode':
  233. $valueRef = CompressionFilter\Flate::encode($valueRef,
  234. $this->_originalDictionary['DecodeParms'][$id]);
  235. break;
  236. case 'LZWDecode':
  237. $valueRef = CompressionFilter\LZW::encode($valueRef,
  238. $this->_originalDictionary['DecodeParms'][$id]);
  239. break;
  240. case 'RunLengthDecode':
  241. $valueRef = StreamFilter\RunLength::encode($valueRef);
  242. break;
  243. default:
  244. throw new PDF\Exception('Unknown stream filter: \'' . $filterName . '\'.');
  245. }
  246. }
  247. $this->_streamDecoded = false;
  248. }
  249. /**
  250. * Get handler
  251. *
  252. * @param string $property
  253. * @return mixed
  254. * @throws \Zend\PDF\Exception
  255. */
  256. public function __get($property)
  257. {
  258. if ($property == 'dictionary') {
  259. /**
  260. * If stream is note decoded yet, then store original decoding options (do it only once).
  261. */
  262. if (( !$this->_streamDecoded ) && ($this->_originalDictionary === null)) {
  263. $this->_storeOriginalDictionary();
  264. }
  265. return $this->_dictionary;
  266. }
  267. if ($property == 'value') {
  268. if (!$this->_streamDecoded) {
  269. $this->_decodeStream();
  270. }
  271. return $this->_value->value->getRef();
  272. }
  273. throw new PDF\Exception('Unknown stream object property requested.');
  274. }
  275. /**
  276. * Set handler
  277. *
  278. * @param string $property
  279. * @param mixed $value
  280. */
  281. public function __set($property, $value)
  282. {
  283. if ($property == 'value') {
  284. $valueRef = &$this->_value->value->getRef();
  285. $valueRef = $value;
  286. $this->_value->value->touch();
  287. $this->_streamDecoded = true;
  288. return;
  289. }
  290. throw new PDF\Exception('Unknown stream object property: \'' . $property . '\'.');
  291. }
  292. /**
  293. * Treat stream data as already encoded
  294. */
  295. public function skipFilters()
  296. {
  297. $this->_streamDecoded = false;
  298. }
  299. /**
  300. * Call handler
  301. *
  302. * @param string $method
  303. * @param array $args
  304. * @return mixed
  305. */
  306. public function __call($method, $args)
  307. {
  308. if (!$this->_streamDecoded) {
  309. $this->_decodeStream();
  310. }
  311. switch (count($args)) {
  312. case 0:
  313. return $this->_value->$method();
  314. case 1:
  315. return $this->_value->$method($args[0]);
  316. default:
  317. throw new PDF\Exception('Unsupported number of arguments');
  318. }
  319. }
  320. /**
  321. * Dump object to a string to save within PDF file
  322. *
  323. * $factory parameter defines operation context.
  324. *
  325. * @param \Zend\PDF\ObjectFactory\ObjectFactory $factory
  326. * @return string
  327. */
  328. public function dump(ObjectFactory\ObjectFactory $factory)
  329. {
  330. $shift = $factory->getEnumerationShift($this->_factory);
  331. if ($this->_streamDecoded) {
  332. $this->_storeOriginalDictionary();
  333. $this->_encodeStream();
  334. } else if ($this->_originalDictionary != null) {
  335. $startDictionary = $this->_originalDictionary;
  336. $this->_storeOriginalDictionary();
  337. $newDictionary = $this->_originalDictionary;
  338. if ($startDictionary !== $newDictionary) {
  339. $this->_originalDictionary = $startDictionary;
  340. $this->_decodeStream();
  341. $this->_originalDictionary = $newDictionary;
  342. $this->_encodeStream();
  343. }
  344. }
  345. // Update stream length
  346. $this->dictionary->Length->value = $this->_value->length();
  347. return $this->_objNum + $shift . " " . $this->_genNum . " obj \n"
  348. . $this->dictionary->toString($factory) . "\n"
  349. . $this->_value->toString($factory) . "\n"
  350. . "endobj\n";
  351. }
  352. /**
  353. * Clean up resources, used by object
  354. */
  355. public function cleanUp()
  356. {
  357. $this->_dictionary = null;
  358. $this->_value = null;
  359. }
  360. }