You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

453 lines
16 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 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-2014 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-2014 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. }