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.

442 lines
21 KiB

11 years ago
9 years ago
11 years ago
10 years ago
11 years ago
9 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/Array.php';
  23. // require_once 'Zend/Pdf/Element/Name.php';
  24. // require_once 'Zend/Pdf/Element/Numeric.php';
  25. /** Zend_Pdf_Resource_Image */
  26. // require_once 'Zend/Pdf/Resource/Image.php';
  27. /**
  28. * TIFF image
  29. *
  30. * @package Zend_Pdf
  31. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  32. * @license http://framework.zend.com/license/new-bsd New BSD License
  33. */
  34. class Zend_Pdf_Resource_Image_Tiff extends Zend_Pdf_Resource_Image
  35. {
  36. const TIFF_FIELD_TYPE_BYTE=1;
  37. const TIFF_FIELD_TYPE_ASCII=2;
  38. const TIFF_FIELD_TYPE_SHORT=3;
  39. const TIFF_FIELD_TYPE_LONG=4;
  40. const TIFF_FIELD_TYPE_RATIONAL=5;
  41. const TIFF_TAG_IMAGE_WIDTH=256;
  42. const TIFF_TAG_IMAGE_LENGTH=257; //Height
  43. const TIFF_TAG_BITS_PER_SAMPLE=258;
  44. const TIFF_TAG_COMPRESSION=259;
  45. const TIFF_TAG_PHOTOMETRIC_INTERPRETATION=262;
  46. const TIFF_TAG_STRIP_OFFSETS=273;
  47. const TIFF_TAG_SAMPLES_PER_PIXEL=277;
  48. const TIFF_TAG_STRIP_BYTE_COUNTS=279;
  49. const TIFF_COMPRESSION_UNCOMPRESSED = 1;
  50. const TIFF_COMPRESSION_CCITT1D = 2;
  51. const TIFF_COMPRESSION_GROUP_3_FAX = 3;
  52. const TIFF_COMPRESSION_GROUP_4_FAX = 4;
  53. const TIFF_COMPRESSION_LZW = 5;
  54. const TIFF_COMPRESSION_JPEG = 6;
  55. const TIFF_COMPRESSION_FLATE = 8;
  56. const TIFF_COMPRESSION_FLATE_OBSOLETE_CODE = 32946;
  57. const TIFF_COMPRESSION_PACKBITS = 32773;
  58. const TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO=0;
  59. const TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO=1;
  60. const TIFF_PHOTOMETRIC_INTERPRETATION_RGB=2;
  61. const TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED=3;
  62. const TIFF_PHOTOMETRIC_INTERPRETATION_CMYK=5;
  63. const TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR=6;
  64. const TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB=8;
  65. protected $_width;
  66. protected $_height;
  67. protected $_imageProperties;
  68. protected $_endianType;
  69. protected $_fileSize;
  70. protected $_bitsPerSample;
  71. protected $_compression;
  72. protected $_filter;
  73. protected $_colorCode;
  74. protected $_whiteIsZero;
  75. protected $_blackIsZero;
  76. protected $_colorSpace;
  77. protected $_imageDataOffset;
  78. protected $_imageDataLength;
  79. const TIFF_ENDIAN_BIG=0;
  80. const TIFF_ENDIAN_LITTLE=1;
  81. const UNPACK_TYPE_BYTE=0;
  82. const UNPACK_TYPE_SHORT=1;
  83. const UNPACK_TYPE_LONG=2;
  84. const UNPACK_TYPE_RATIONAL=3;
  85. /**
  86. * Byte unpacking function
  87. *
  88. * Makes it possible to unpack bytes in one statement for enhanced logic readability.
  89. *
  90. * @param int $type
  91. * @param string $bytes
  92. * @throws Zend_Pdf_Exception
  93. */
  94. protected function unpackBytes($type, $bytes) {
  95. if(!isset($this->_endianType)) {
  96. // require_once 'Zend/Pdf/Exception.php';
  97. throw new Zend_Pdf_Exception("The unpackBytes function can only be used after the endianness of the file is known");
  98. }
  99. switch($type) {
  100. case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE:
  101. $format = 'C';
  102. $unpacked = unpack($format, $bytes);
  103. return $unpacked[1];
  104. break;
  105. case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT:
  106. $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'v':'n';
  107. $unpacked = unpack($format, $bytes);
  108. return $unpacked[1];
  109. break;
  110. case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG:
  111. $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V':'N';
  112. $unpacked = unpack($format, $bytes);
  113. return $unpacked[1];
  114. break;
  115. case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_RATIONAL:
  116. $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V2':'N2';
  117. $unpacked = unpack($format, $bytes);
  118. return ($unpacked[1]/$unpacked[2]);
  119. break;
  120. }
  121. }
  122. /**
  123. * Object constructor
  124. *
  125. * @param string $imageFileName
  126. * @throws Zend_Pdf_Exception
  127. */
  128. public function __construct($imageFileName)
  129. {
  130. if (($imageFile = @fopen($imageFileName, 'rb')) === false ) {
  131. // require_once 'Zend/Pdf/Exception.php';
  132. throw new Zend_Pdf_Exception( "Can not open '$imageFileName' file for reading." );
  133. }
  134. $byteOrderIndicator = fread($imageFile, 2);
  135. if($byteOrderIndicator == 'II') {
  136. $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE;
  137. } else if($byteOrderIndicator == 'MM') {
  138. $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_BIG;
  139. } else {
  140. // require_once 'Zend/Pdf/Exception.php';
  141. throw new Zend_Pdf_Exception( "Not a tiff file or Tiff corrupt. No byte order indication found" );
  142. }
  143. $version = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2));
  144. if($version != 42) {
  145. // require_once 'Zend/Pdf/Exception.php';
  146. throw new Zend_Pdf_Exception( "Not a tiff file or Tiff corrupt. Incorrect version number." );
  147. }
  148. $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4));
  149. $fileStats = fstat($imageFile);
  150. $this->_fileSize = $fileStats['size'];
  151. /*
  152. * Tiff files are stored as a series of Image File Directories (IFD) each direcctory
  153. * has a specific number of entries each 12 bytes in length. At the end of the directories
  154. * is four bytes pointing to the offset of the next IFD.
  155. */
  156. while($ifdOffset > 0) {
  157. if(fseek($imageFile, $ifdOffset, SEEK_SET) == -1 || $ifdOffset+2 >= $this->_fileSize) {
  158. // require_once 'Zend/Pdf/Exception.php';
  159. throw new Zend_Pdf_Exception("Could not seek to the image file directory as indexed by the file. Likely cause is TIFF corruption. Offset: ". $ifdOffset);
  160. }
  161. $numDirEntries = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2));
  162. /*
  163. * Since we now know how many entries are in this (IFD) we can extract the data.
  164. * The format of a TIFF directory entry is:
  165. *
  166. * 2 bytes (short) tag code; See TIFF_TAG constants at the top for supported values. (There are many more in the spec)
  167. * 2 bytes (short) field type
  168. * 4 bytes (long) number of values, or value count.
  169. * 4 bytes (mixed) data if the data will fit into 4 bytes or an offset if the data is too large.
  170. */
  171. for($dirEntryIdx = 1; $dirEntryIdx <= $numDirEntries; $dirEntryIdx++) {
  172. $tag = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2));
  173. $fieldType = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2));
  174. $valueCount = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4));
  175. switch($fieldType) {
  176. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE:
  177. $fieldLength = $valueCount;
  178. break;
  179. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII:
  180. $fieldLength = $valueCount;
  181. break;
  182. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT:
  183. $fieldLength = $valueCount * 2;
  184. break;
  185. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG:
  186. $fieldLength = $valueCount * 4;
  187. break;
  188. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_RATIONAL:
  189. $fieldLength = $valueCount * 8;
  190. break;
  191. default:
  192. $fieldLength = $valueCount;
  193. }
  194. $offsetBytes = fread($imageFile, 4);
  195. if($fieldLength <= 4) {
  196. switch($fieldType) {
  197. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE:
  198. $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE, $offsetBytes);
  199. break;
  200. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII:
  201. //Fall through to next case
  202. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG:
  203. $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes);
  204. break;
  205. case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT:
  206. //Fall through to next case
  207. default:
  208. $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, $offsetBytes);
  209. }
  210. } else {
  211. $refOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes);
  212. }
  213. /*
  214. * Linear tag processing is probably not the best way to do this. I've processed the tags according to the
  215. * Tiff 6 specification and make some assumptions about when tags will be < 4 bytes and fit into $value and when
  216. * they will be > 4 bytes and require seek/extraction of the offset. Same goes for extracting arrays of data, like
  217. * the data offsets and length. This should be fixed in the future.
  218. */
  219. switch($tag) {
  220. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_WIDTH:
  221. $this->_width = $value;
  222. break;
  223. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_LENGTH:
  224. $this->_height = $value;
  225. break;
  226. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_BITS_PER_SAMPLE:
  227. if($valueCount>1) {
  228. $fp = ftell($imageFile);
  229. fseek($imageFile, $refOffset, SEEK_SET);
  230. $this->_bitsPerSample = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2));
  231. fseek($imageFile, $fp, SEEK_SET);
  232. } else {
  233. $this->_bitsPerSample = $value;
  234. }
  235. break;
  236. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_COMPRESSION:
  237. $this->_compression = $value;
  238. switch($value) {
  239. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_UNCOMPRESSED:
  240. $this->_filter = 'None';
  241. break;
  242. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_CCITT1D:
  243. //Fall through to next case
  244. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_3_FAX:
  245. //Fall through to next case
  246. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_4_FAX:
  247. $this->_filter = 'CCITTFaxDecode';
  248. // require_once 'Zend/Pdf/Exception.php';
  249. throw new Zend_Pdf_Exception("CCITTFaxDecode Compression Mode Not Currently Supported");
  250. break;
  251. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_LZW:
  252. $this->_filter = 'LZWDecode';
  253. // require_once 'Zend/Pdf/Exception.php';
  254. throw new Zend_Pdf_Exception("LZWDecode Compression Mode Not Currently Supported");
  255. break;
  256. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_JPEG:
  257. $this->_filter = 'DCTDecode'; //Should work, doesnt...
  258. // require_once 'Zend/Pdf/Exception.php';
  259. throw new Zend_Pdf_Exception("JPEG Compression Mode Not Currently Supported");
  260. break;
  261. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE:
  262. //fall through to next case
  263. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE_OBSOLETE_CODE:
  264. $this->_filter = 'FlateDecode';
  265. // require_once 'Zend/Pdf/Exception.php';
  266. throw new Zend_Pdf_Exception("ZIP/Flate Compression Mode Not Currently Supported");
  267. break;
  268. case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_PACKBITS:
  269. $this->_filter = 'RunLengthDecode';
  270. break;
  271. }
  272. break;
  273. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_PHOTOMETRIC_INTERPRETATION:
  274. $this->_colorCode = $value;
  275. $this->_whiteIsZero = false;
  276. $this->_blackIsZero = false;
  277. switch($value) {
  278. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO:
  279. $this->_whiteIsZero = true;
  280. $this->_colorSpace = 'DeviceGray';
  281. break;
  282. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO:
  283. $this->_blackIsZero = true;
  284. $this->_colorSpace = 'DeviceGray';
  285. break;
  286. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR:
  287. //fall through to next case
  288. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB:
  289. $this->_colorSpace = 'DeviceRGB';
  290. break;
  291. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED:
  292. $this->_colorSpace = 'Indexed';
  293. break;
  294. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CMYK:
  295. $this->_colorSpace = 'DeviceCMYK';
  296. break;
  297. case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB:
  298. $this->_colorSpace = 'Lab';
  299. break;
  300. default:
  301. // require_once 'Zend/Pdf/Exception.php';
  302. throw new Zend_Pdf_Exception('TIFF: Unknown or Unsupported Color Type: '. $value);
  303. }
  304. break;
  305. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_OFFSETS:
  306. if($valueCount>1) {
  307. $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V*':'N*';
  308. $fp = ftell($imageFile);
  309. fseek($imageFile, $refOffset, SEEK_SET);
  310. $stripOffsetsBytes = fread($imageFile, $fieldLength);
  311. $this->_imageDataOffset = unpack($format, $stripOffsetsBytes);
  312. fseek($imageFile, $fp, SEEK_SET);
  313. } else {
  314. $this->_imageDataOffset = $value;
  315. }
  316. break;
  317. case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_BYTE_COUNTS:
  318. if($valueCount>1) {
  319. $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V*':'N*';
  320. $fp = ftell($imageFile);
  321. fseek($imageFile, $refOffset, SEEK_SET);
  322. $stripByteCountsBytes = fread($imageFile, $fieldLength);
  323. $this->_imageDataLength = unpack($format, $stripByteCountsBytes);
  324. fseek($imageFile, $fp, SEEK_SET);
  325. } else {
  326. $this->_imageDataLength = $value;
  327. }
  328. break;
  329. default:
  330. //For debugging. It should be harmless to ignore unknown tags, though there is some good info in them.
  331. //echo "Unknown tag detected: ". $tag . " value: ". $value;
  332. }
  333. }
  334. $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4));
  335. }
  336. if(!isset($this->_imageDataOffset) || !isset($this->_imageDataLength)) {
  337. // require_once 'Zend/Pdf/Exception.php';
  338. throw new Zend_Pdf_Exception("TIFF: The image processed did not contain image data as expected.");
  339. }
  340. $imageDataBytes = '';
  341. if(is_array($this->_imageDataOffset)) {
  342. if(!is_array($this->_imageDataLength)) {
  343. // require_once 'Zend/Pdf/Exception.php';
  344. throw new Zend_Pdf_Exception("TIFF: The image contained multiple data offsets but not multiple data lengths. Tiff may be corrupt.");
  345. }
  346. foreach($this->_imageDataOffset as $idx => $offset) {
  347. fseek($imageFile, $this->_imageDataOffset[$idx], SEEK_SET);
  348. $imageDataBytes .= fread($imageFile, $this->_imageDataLength[$idx]);
  349. }
  350. } else {
  351. fseek($imageFile, $this->_imageDataOffset, SEEK_SET);
  352. $imageDataBytes = fread($imageFile, $this->_imageDataLength);
  353. }
  354. if($imageDataBytes === '') {
  355. // require_once 'Zend/Pdf/Exception.php';
  356. throw new Zend_Pdf_Exception("TIFF: No data. Image Corruption");
  357. }
  358. fclose($imageFile);
  359. parent::__construct();
  360. $imageDictionary = $this->_resource->dictionary;
  361. if(!isset($this->_width) || !isset($this->_width)) {
  362. // require_once 'Zend/Pdf/Exception.php';
  363. throw new Zend_Pdf_Exception("Problem reading tiff file. Tiff is probably corrupt.");
  364. }
  365. $this->_imageProperties = array();
  366. $this->_imageProperties['bitDepth'] = $this->_bitsPerSample;
  367. $this->_imageProperties['fileSize'] = $this->_fileSize;
  368. $this->_imageProperties['TIFFendianType'] = $this->_endianType;
  369. $this->_imageProperties['TIFFcompressionType'] = $this->_compression;
  370. $this->_imageProperties['TIFFwhiteIsZero'] = $this->_whiteIsZero;
  371. $this->_imageProperties['TIFFblackIsZero'] = $this->_blackIsZero;
  372. $this->_imageProperties['TIFFcolorCode'] = $this->_colorCode;
  373. $this->_imageProperties['TIFFimageDataOffset'] = $this->_imageDataOffset;
  374. $this->_imageProperties['TIFFimageDataLength'] = $this->_imageDataLength;
  375. $this->_imageProperties['PDFfilter'] = $this->_filter;
  376. $this->_imageProperties['PDFcolorSpace'] = $this->_colorSpace;
  377. $imageDictionary->Width = new Zend_Pdf_Element_Numeric($this->_width);
  378. if($this->_whiteIsZero === true) {
  379. $imageDictionary->Decode = new Zend_Pdf_Element_Array(array(new Zend_Pdf_Element_Numeric(1), new Zend_Pdf_Element_Numeric(0)));
  380. }
  381. $imageDictionary->Height = new Zend_Pdf_Element_Numeric($this->_height);
  382. $imageDictionary->ColorSpace = new Zend_Pdf_Element_Name($this->_colorSpace);
  383. $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($this->_bitsPerSample);
  384. if(isset($this->_filter) && $this->_filter != 'None') {
  385. $imageDictionary->Filter = new Zend_Pdf_Element_Name($this->_filter);
  386. }
  387. $this->_resource->value = $imageDataBytes;
  388. $this->_resource->skipFilters();
  389. }
  390. /**
  391. * Image width (defined in Zend_Pdf_Resource_Image_Interface)
  392. */
  393. public function getPixelWidth() {
  394. return $this->_width;
  395. }
  396. /**
  397. * Image height (defined in Zend_Pdf_Resource_Image_Interface)
  398. */
  399. public function getPixelHeight() {
  400. return $this->_height;
  401. }
  402. /**
  403. * Image properties (defined in Zend_Pdf_Resource_Image_Interface)
  404. */
  405. public function getProperties() {
  406. return $this->_imageProperties;
  407. }
  408. }