453 lines
16 KiB

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