File indexing completed on 2025-01-19 05:21:23
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Pdf 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** Internally used classes */ 0023 // require_once 'Zend/Pdf/Element/Array.php'; 0024 // require_once 'Zend/Pdf/Element/Name.php'; 0025 // require_once 'Zend/Pdf/Element/Numeric.php'; 0026 0027 0028 /** Zend_Pdf_Resource_Image */ 0029 // require_once 'Zend/Pdf/Resource/Image.php'; 0030 0031 /** 0032 * TIFF image 0033 * 0034 * @package Zend_Pdf 0035 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0036 * @license http://framework.zend.com/license/new-bsd New BSD License 0037 */ 0038 class Zend_Pdf_Resource_Image_Tiff extends Zend_Pdf_Resource_Image 0039 { 0040 const TIFF_FIELD_TYPE_BYTE=1; 0041 const TIFF_FIELD_TYPE_ASCII=2; 0042 const TIFF_FIELD_TYPE_SHORT=3; 0043 const TIFF_FIELD_TYPE_LONG=4; 0044 const TIFF_FIELD_TYPE_RATIONAL=5; 0045 0046 const TIFF_TAG_IMAGE_WIDTH=256; 0047 const TIFF_TAG_IMAGE_LENGTH=257; //Height 0048 const TIFF_TAG_BITS_PER_SAMPLE=258; 0049 const TIFF_TAG_COMPRESSION=259; 0050 const TIFF_TAG_PHOTOMETRIC_INTERPRETATION=262; 0051 const TIFF_TAG_STRIP_OFFSETS=273; 0052 const TIFF_TAG_SAMPLES_PER_PIXEL=277; 0053 const TIFF_TAG_STRIP_BYTE_COUNTS=279; 0054 0055 const TIFF_COMPRESSION_UNCOMPRESSED = 1; 0056 const TIFF_COMPRESSION_CCITT1D = 2; 0057 const TIFF_COMPRESSION_GROUP_3_FAX = 3; 0058 const TIFF_COMPRESSION_GROUP_4_FAX = 4; 0059 const TIFF_COMPRESSION_LZW = 5; 0060 const TIFF_COMPRESSION_JPEG = 6; 0061 const TIFF_COMPRESSION_FLATE = 8; 0062 const TIFF_COMPRESSION_FLATE_OBSOLETE_CODE = 32946; 0063 const TIFF_COMPRESSION_PACKBITS = 32773; 0064 0065 const TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO=0; 0066 const TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO=1; 0067 const TIFF_PHOTOMETRIC_INTERPRETATION_RGB=2; 0068 const TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED=3; 0069 const TIFF_PHOTOMETRIC_INTERPRETATION_CMYK=5; 0070 const TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR=6; 0071 const TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB=8; 0072 0073 protected $_width; 0074 protected $_height; 0075 protected $_imageProperties; 0076 protected $_endianType; 0077 protected $_fileSize; 0078 protected $_bitsPerSample; 0079 protected $_compression; 0080 protected $_filter; 0081 protected $_colorCode; 0082 protected $_whiteIsZero; 0083 protected $_blackIsZero; 0084 protected $_colorSpace; 0085 protected $_imageDataOffset; 0086 protected $_imageDataLength; 0087 0088 const TIFF_ENDIAN_BIG=0; 0089 const TIFF_ENDIAN_LITTLE=1; 0090 0091 const UNPACK_TYPE_BYTE=0; 0092 const UNPACK_TYPE_SHORT=1; 0093 const UNPACK_TYPE_LONG=2; 0094 const UNPACK_TYPE_RATIONAL=3; 0095 0096 /** 0097 * Byte unpacking function 0098 * 0099 * Makes it possible to unpack bytes in one statement for enhanced logic readability. 0100 * 0101 * @param int $type 0102 * @param string $bytes 0103 * @throws Zend_Pdf_Exception 0104 */ 0105 protected function unpackBytes($type, $bytes) { 0106 if(!isset($this->_endianType)) { 0107 // require_once 'Zend/Pdf/Exception.php'; 0108 throw new Zend_Pdf_Exception("The unpackBytes function can only be used after the endianness of the file is known"); 0109 } 0110 switch($type) { 0111 case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE: 0112 $format = 'C'; 0113 $unpacked = unpack($format, $bytes); 0114 return $unpacked[1]; 0115 break; 0116 case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT: 0117 $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'v':'n'; 0118 $unpacked = unpack($format, $bytes); 0119 return $unpacked[1]; 0120 break; 0121 case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG: 0122 $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V':'N'; 0123 $unpacked = unpack($format, $bytes); 0124 return $unpacked[1]; 0125 break; 0126 case Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_RATIONAL: 0127 $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V2':'N2'; 0128 $unpacked = unpack($format, $bytes); 0129 return ($unpacked[1]/$unpacked[2]); 0130 break; 0131 } 0132 } 0133 0134 /** 0135 * Object constructor 0136 * 0137 * @param string $imageFileName 0138 * @throws Zend_Pdf_Exception 0139 */ 0140 public function __construct($imageFileName) 0141 { 0142 if (($imageFile = @fopen($imageFileName, 'rb')) === false ) { 0143 // require_once 'Zend/Pdf/Exception.php'; 0144 throw new Zend_Pdf_Exception( "Can not open '$imageFileName' file for reading." ); 0145 } 0146 0147 $byteOrderIndicator = fread($imageFile, 2); 0148 if($byteOrderIndicator == 'II') { 0149 $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE; 0150 } else if($byteOrderIndicator == 'MM') { 0151 $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_BIG; 0152 } else { 0153 // require_once 'Zend/Pdf/Exception.php'; 0154 throw new Zend_Pdf_Exception( "Not a tiff file or Tiff corrupt. No byte order indication found" ); 0155 } 0156 0157 $version = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); 0158 0159 if($version != 42) { 0160 // require_once 'Zend/Pdf/Exception.php'; 0161 throw new Zend_Pdf_Exception( "Not a tiff file or Tiff corrupt. Incorrect version number." ); 0162 } 0163 $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); 0164 0165 $fileStats = fstat($imageFile); 0166 $this->_fileSize = $fileStats['size']; 0167 0168 /* 0169 * Tiff files are stored as a series of Image File Directories (IFD) each direcctory 0170 * has a specific number of entries each 12 bytes in length. At the end of the directories 0171 * is four bytes pointing to the offset of the next IFD. 0172 */ 0173 0174 while($ifdOffset > 0) { 0175 if(fseek($imageFile, $ifdOffset, SEEK_SET) == -1 || $ifdOffset+2 >= $this->_fileSize) { 0176 // require_once 'Zend/Pdf/Exception.php'; 0177 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); 0178 } 0179 0180 $numDirEntries = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); 0181 0182 /* 0183 * Since we now know how many entries are in this (IFD) we can extract the data. 0184 * The format of a TIFF directory entry is: 0185 * 0186 * 2 bytes (short) tag code; See TIFF_TAG constants at the top for supported values. (There are many more in the spec) 0187 * 2 bytes (short) field type 0188 * 4 bytes (long) number of values, or value count. 0189 * 4 bytes (mixed) data if the data will fit into 4 bytes or an offset if the data is too large. 0190 */ 0191 for($dirEntryIdx = 1; $dirEntryIdx <= $numDirEntries; $dirEntryIdx++) { 0192 $tag = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); 0193 $fieldType = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); 0194 $valueCount = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); 0195 0196 switch($fieldType) { 0197 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: 0198 $fieldLength = $valueCount; 0199 break; 0200 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: 0201 $fieldLength = $valueCount; 0202 break; 0203 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: 0204 $fieldLength = $valueCount * 2; 0205 break; 0206 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: 0207 $fieldLength = $valueCount * 4; 0208 break; 0209 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_RATIONAL: 0210 $fieldLength = $valueCount * 8; 0211 break; 0212 default: 0213 $fieldLength = $valueCount; 0214 } 0215 0216 $offsetBytes = fread($imageFile, 4); 0217 0218 if($fieldLength <= 4) { 0219 switch($fieldType) { 0220 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: 0221 $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE, $offsetBytes); 0222 break; 0223 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: 0224 //Fall through to next case 0225 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: 0226 $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes); 0227 break; 0228 case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: 0229 //Fall through to next case 0230 default: 0231 $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, $offsetBytes); 0232 } 0233 } else { 0234 $refOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes); 0235 } 0236 /* 0237 * Linear tag processing is probably not the best way to do this. I've processed the tags according to the 0238 * Tiff 6 specification and make some assumptions about when tags will be < 4 bytes and fit into $value and when 0239 * they will be > 4 bytes and require seek/extraction of the offset. Same goes for extracting arrays of data, like 0240 * the data offsets and length. This should be fixed in the future. 0241 */ 0242 switch($tag) { 0243 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_WIDTH: 0244 $this->_width = $value; 0245 break; 0246 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_LENGTH: 0247 $this->_height = $value; 0248 break; 0249 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_BITS_PER_SAMPLE: 0250 if($valueCount>1) { 0251 $fp = ftell($imageFile); 0252 fseek($imageFile, $refOffset, SEEK_SET); 0253 $this->_bitsPerSample = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); 0254 fseek($imageFile, $fp, SEEK_SET); 0255 } else { 0256 $this->_bitsPerSample = $value; 0257 } 0258 break; 0259 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_COMPRESSION: 0260 $this->_compression = $value; 0261 switch($value) { 0262 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_UNCOMPRESSED: 0263 $this->_filter = 'None'; 0264 break; 0265 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_CCITT1D: 0266 //Fall through to next case 0267 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_3_FAX: 0268 //Fall through to next case 0269 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_4_FAX: 0270 $this->_filter = 'CCITTFaxDecode'; 0271 // require_once 'Zend/Pdf/Exception.php'; 0272 throw new Zend_Pdf_Exception("CCITTFaxDecode Compression Mode Not Currently Supported"); 0273 break; 0274 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_LZW: 0275 $this->_filter = 'LZWDecode'; 0276 // require_once 'Zend/Pdf/Exception.php'; 0277 throw new Zend_Pdf_Exception("LZWDecode Compression Mode Not Currently Supported"); 0278 break; 0279 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_JPEG: 0280 $this->_filter = 'DCTDecode'; //Should work, doesnt... 0281 // require_once 'Zend/Pdf/Exception.php'; 0282 throw new Zend_Pdf_Exception("JPEG Compression Mode Not Currently Supported"); 0283 break; 0284 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE: 0285 //fall through to next case 0286 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE_OBSOLETE_CODE: 0287 $this->_filter = 'FlateDecode'; 0288 // require_once 'Zend/Pdf/Exception.php'; 0289 throw new Zend_Pdf_Exception("ZIP/Flate Compression Mode Not Currently Supported"); 0290 break; 0291 case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_PACKBITS: 0292 $this->_filter = 'RunLengthDecode'; 0293 break; 0294 } 0295 break; 0296 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_PHOTOMETRIC_INTERPRETATION: 0297 $this->_colorCode = $value; 0298 $this->_whiteIsZero = false; 0299 $this->_blackIsZero = false; 0300 switch($value) { 0301 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO: 0302 $this->_whiteIsZero = true; 0303 $this->_colorSpace = 'DeviceGray'; 0304 break; 0305 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO: 0306 $this->_blackIsZero = true; 0307 $this->_colorSpace = 'DeviceGray'; 0308 break; 0309 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR: 0310 //fall through to next case 0311 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB: 0312 $this->_colorSpace = 'DeviceRGB'; 0313 break; 0314 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED: 0315 $this->_colorSpace = 'Indexed'; 0316 break; 0317 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CMYK: 0318 $this->_colorSpace = 'DeviceCMYK'; 0319 break; 0320 case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB: 0321 $this->_colorSpace = 'Lab'; 0322 break; 0323 default: 0324 // require_once 'Zend/Pdf/Exception.php'; 0325 throw new Zend_Pdf_Exception('TIFF: Unknown or Unsupported Color Type: '. $value); 0326 } 0327 break; 0328 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_OFFSETS: 0329 if($valueCount>1) { 0330 $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V*':'N*'; 0331 $fp = ftell($imageFile); 0332 fseek($imageFile, $refOffset, SEEK_SET); 0333 $stripOffsetsBytes = fread($imageFile, $fieldLength); 0334 $this->_imageDataOffset = unpack($format, $stripOffsetsBytes); 0335 fseek($imageFile, $fp, SEEK_SET); 0336 } else { 0337 $this->_imageDataOffset = $value; 0338 } 0339 break; 0340 case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_BYTE_COUNTS: 0341 if($valueCount>1) { 0342 $format = ($this->_endianType == Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE)?'V*':'N*'; 0343 $fp = ftell($imageFile); 0344 fseek($imageFile, $refOffset, SEEK_SET); 0345 $stripByteCountsBytes = fread($imageFile, $fieldLength); 0346 $this->_imageDataLength = unpack($format, $stripByteCountsBytes); 0347 fseek($imageFile, $fp, SEEK_SET); 0348 } else { 0349 $this->_imageDataLength = $value; 0350 } 0351 break; 0352 default: 0353 //For debugging. It should be harmless to ignore unknown tags, though there is some good info in them. 0354 //echo "Unknown tag detected: ". $tag . " value: ". $value; 0355 } 0356 } 0357 $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); 0358 } 0359 0360 if(!isset($this->_imageDataOffset) || !isset($this->_imageDataLength)) { 0361 // require_once 'Zend/Pdf/Exception.php'; 0362 throw new Zend_Pdf_Exception("TIFF: The image processed did not contain image data as expected."); 0363 } 0364 0365 $imageDataBytes = ''; 0366 if(is_array($this->_imageDataOffset)) { 0367 if(!is_array($this->_imageDataLength)) { 0368 // require_once 'Zend/Pdf/Exception.php'; 0369 throw new Zend_Pdf_Exception("TIFF: The image contained multiple data offsets but not multiple data lengths. Tiff may be corrupt."); 0370 } 0371 foreach($this->_imageDataOffset as $idx => $offset) { 0372 fseek($imageFile, $this->_imageDataOffset[$idx], SEEK_SET); 0373 $imageDataBytes .= fread($imageFile, $this->_imageDataLength[$idx]); 0374 } 0375 } else { 0376 fseek($imageFile, $this->_imageDataOffset, SEEK_SET); 0377 $imageDataBytes = fread($imageFile, $this->_imageDataLength); 0378 } 0379 if($imageDataBytes === '') { 0380 // require_once 'Zend/Pdf/Exception.php'; 0381 throw new Zend_Pdf_Exception("TIFF: No data. Image Corruption"); 0382 } 0383 0384 fclose($imageFile); 0385 0386 parent::__construct(); 0387 0388 $imageDictionary = $this->_resource->dictionary; 0389 if(!isset($this->_width) || !isset($this->_width)) { 0390 // require_once 'Zend/Pdf/Exception.php'; 0391 throw new Zend_Pdf_Exception("Problem reading tiff file. Tiff is probably corrupt."); 0392 } 0393 0394 $this->_imageProperties = array(); 0395 $this->_imageProperties['bitDepth'] = $this->_bitsPerSample; 0396 $this->_imageProperties['fileSize'] = $this->_fileSize; 0397 $this->_imageProperties['TIFFendianType'] = $this->_endianType; 0398 $this->_imageProperties['TIFFcompressionType'] = $this->_compression; 0399 $this->_imageProperties['TIFFwhiteIsZero'] = $this->_whiteIsZero; 0400 $this->_imageProperties['TIFFblackIsZero'] = $this->_blackIsZero; 0401 $this->_imageProperties['TIFFcolorCode'] = $this->_colorCode; 0402 $this->_imageProperties['TIFFimageDataOffset'] = $this->_imageDataOffset; 0403 $this->_imageProperties['TIFFimageDataLength'] = $this->_imageDataLength; 0404 $this->_imageProperties['PDFfilter'] = $this->_filter; 0405 $this->_imageProperties['PDFcolorSpace'] = $this->_colorSpace; 0406 0407 $imageDictionary->Width = new Zend_Pdf_Element_Numeric($this->_width); 0408 if($this->_whiteIsZero === true) { 0409 $imageDictionary->Decode = new Zend_Pdf_Element_Array(array(new Zend_Pdf_Element_Numeric(1), new Zend_Pdf_Element_Numeric(0))); 0410 } 0411 $imageDictionary->Height = new Zend_Pdf_Element_Numeric($this->_height); 0412 $imageDictionary->ColorSpace = new Zend_Pdf_Element_Name($this->_colorSpace); 0413 $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($this->_bitsPerSample); 0414 if(isset($this->_filter) && $this->_filter != 'None') { 0415 $imageDictionary->Filter = new Zend_Pdf_Element_Name($this->_filter); 0416 } 0417 0418 $this->_resource->value = $imageDataBytes; 0419 $this->_resource->skipFilters(); 0420 } 0421 /** 0422 * Image width (defined in Zend_Pdf_Resource_Image_Interface) 0423 */ 0424 public function getPixelWidth() { 0425 return $this->_width; 0426 } 0427 0428 /** 0429 * Image height (defined in Zend_Pdf_Resource_Image_Interface) 0430 */ 0431 public function getPixelHeight() { 0432 return $this->_height; 0433 } 0434 0435 /** 0436 * Image properties (defined in Zend_Pdf_Resource_Image_Interface) 0437 */ 0438 public function getProperties() { 0439 return $this->_imageProperties; 0440 } 0441 } 0442