File indexing completed on 2025-01-19 05:21:21
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 * @subpackage FileParser 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** @see Zend_Pdf_FileParser_Image */ 0024 // require_once 'Zend/Pdf/FileParser/Image.php'; 0025 0026 0027 /** 0028 * Abstract base class for Image file parsers. 0029 * 0030 * @package Zend_Pdf 0031 * @subpackage FileParser 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_Pdf_FileParser_Image_Png extends Zend_Pdf_FileParser_Image 0036 { 0037 protected $_isPNG; 0038 protected $_width; 0039 protected $_height; 0040 protected $_bits; 0041 protected $_color; 0042 protected $_compression; 0043 protected $_preFilter; 0044 protected $_interlacing; 0045 0046 protected $_imageData; 0047 protected $_paletteData; 0048 protected $_transparencyData; 0049 0050 /**** Public Interface ****/ 0051 0052 public function getWidth() { 0053 if(!$this->_isParsed) { 0054 $this->parse(); 0055 } 0056 return $this->_width; 0057 } 0058 0059 public function getHeight() { 0060 if(!$this->_isParsed) { 0061 $this->parse(); 0062 } 0063 return $this->_width; 0064 } 0065 0066 public function getBitDepth() { 0067 if(!$this->_isParsed) { 0068 $this->parse(); 0069 } 0070 return $this->_bits; 0071 } 0072 0073 public function getColorSpace() { 0074 if(!$this->_isParsed) { 0075 $this->parse(); 0076 } 0077 return $this->_color; 0078 } 0079 0080 public function getCompressionStrategy() { 0081 if(!$this->_isParsed) { 0082 $this->parse(); 0083 } 0084 return $this->_compression; 0085 } 0086 0087 public function getPaethFilter() { 0088 if(!$this->_isParsed) { 0089 $this->parse(); 0090 } 0091 return $this->_preFilter; 0092 } 0093 0094 public function getInterlacingMode() { 0095 if(!$this->_isParsed) { 0096 $this->parse(); 0097 } 0098 return $this->_interlacing; 0099 } 0100 0101 public function getRawImageData() { 0102 if(!$this->_isParsed) { 0103 $this->parse(); 0104 } 0105 return $this->_imageData; 0106 } 0107 0108 public function getRawPaletteData() { 0109 if(!$this->_isParsed) { 0110 $this->parse(); 0111 } 0112 return $this->_paletteData; 0113 } 0114 0115 public function getRawTransparencyData() { 0116 if(!$this->_isParsed) { 0117 $this->parse(); 0118 } 0119 return $this->_transparencyData; 0120 } 0121 0122 /* Semi-Concrete Class Implementation */ 0123 0124 /** 0125 * Verifies that the image file is in the expected format. 0126 * 0127 * @throws Zend_Pdf_Exception 0128 */ 0129 public function screen() 0130 { 0131 if ($this->_isScreened) { 0132 return; 0133 } 0134 return $this->_checkSignature(); 0135 } 0136 0137 /** 0138 * Reads and parses the image data from the file on disk. 0139 * 0140 * @throws Zend_Pdf_Exception 0141 */ 0142 public function parse() 0143 { 0144 if ($this->_isParsed) { 0145 return; 0146 } 0147 0148 /* Screen the font file first, if it hasn't been done yet. 0149 */ 0150 $this->screen(); 0151 0152 $this->_parseIHDRChunk(); 0153 $this->_parseChunks(); 0154 } 0155 0156 0157 protected function _parseSignature() { 0158 $this->moveToOffset(1); //Skip the first byte (%) 0159 if('PNG' != $this->readBytes(3)) { 0160 $this->_isPNG = false; 0161 } else { 0162 $this->_isPNG = true; 0163 } 0164 } 0165 0166 protected function _checkSignature() { 0167 if(!isset($this->_isPNG)) { 0168 $this->_parseSignature(); 0169 } 0170 return $this->_isPNG; 0171 } 0172 0173 protected function _parseChunks() { 0174 $this->moveToOffset(33); //Variable chunks start at the end of IHDR 0175 0176 //Start processing chunks. If there are no more bytes to read parsing is complete. 0177 $size = $this->getSize(); 0178 while($size - $this->getOffset() >= 8) { 0179 $chunkLength = $this->readUInt(4); 0180 if($chunkLength < 0 || ($chunkLength + $this->getOffset() + 4) > $size) { 0181 // require_once 'Zend/Pdf/Exception.php'; 0182 throw new Zend_Pdf_Exception("PNG Corrupt: Invalid Chunk Size In File."); 0183 } 0184 0185 $chunkType = $this->readBytes(4); 0186 $offset = $this->getOffset(); 0187 0188 //If we know how to process the chunk, do it here, else ignore the chunk and move on to the next 0189 switch($chunkType) { 0190 case 'IDAT': // This chunk may appear more than once. It contains the actual image data. 0191 $this->_parseIDATChunk($offset, $chunkLength); 0192 break; 0193 0194 case 'PLTE': // This chunk contains the image palette. 0195 $this->_parsePLTEChunk($offset, $chunkLength); 0196 break; 0197 0198 case 'tRNS': // This chunk contains non-alpha channel transparency data 0199 $this->_parseTRNSChunk($offset, $chunkLength); 0200 break; 0201 0202 case 'IEND': 0203 break 2; //End the loop too 0204 0205 //@TODO Implement the rest of the PNG chunks. (There are many not implemented here) 0206 } 0207 if($offset + $chunkLength + 4 < $size) { 0208 $this->moveToOffset($offset + $chunkLength + 4); //Skip past the data finalizer. (Don't rely on the parse to leave the offsets correct) 0209 } 0210 } 0211 if(empty($this->_imageData)) { 0212 // require_once 'Zend/Pdf/Exception.php'; 0213 throw new Zend_Pdf_Exception ( "This PNG is corrupt. All png must contain IDAT chunks." ); 0214 } 0215 } 0216 0217 protected function _parseIHDRChunk() { 0218 $this->moveToOffset(12); //IHDR must always start at offset 12 and run for 17 bytes 0219 if(!$this->readBytes(4) == 'IHDR') { 0220 // require_once 'Zend/Pdf/Exception.php'; 0221 throw new Zend_Pdf_Exception( "This PNG is corrupt. The first chunk in a PNG file must be IHDR." ); 0222 } 0223 $this->_width = $this->readUInt(4); 0224 $this->_height = $this->readUInt(4); 0225 $this->_bits = $this->readInt(1); 0226 $this->_color = $this->readInt(1); 0227 $this->_compression = $this->readInt(1); 0228 $this->_preFilter = $this->readInt(1); 0229 $this->_interlacing = $this->readInt(1); 0230 if($this->_interlacing != Zend_Pdf_Image::PNG_INTERLACING_DISABLED) { 0231 // require_once 'Zend/Pdf/Exception.php'; 0232 throw new Zend_Pdf_Exception( "Only non-interlaced images are currently supported." ); 0233 } 0234 } 0235 0236 protected function _parseIDATChunk($chunkOffset, $chunkLength) { 0237 $this->moveToOffset($chunkOffset); 0238 if(!isset($this->_imageData)) { 0239 $this->_imageData = $this->readBytes($chunkLength); 0240 } else { 0241 $this->_imageData .= $this->readBytes($chunkLength); 0242 } 0243 } 0244 0245 protected function _parsePLTEChunk($chunkOffset, $chunkLength) { 0246 $this->moveToOffset($chunkOffset); 0247 $this->_paletteData = $this->readBytes($chunkLength); 0248 } 0249 0250 protected function _parseTRNSChunk($chunkOffset, $chunkLength) { 0251 $this->moveToOffset($chunkOffset); 0252 0253 //Processing of tRNS data varies dependending on the color depth 0254 0255 switch($this->_color) { 0256 case Zend_Pdf_Image::PNG_CHANNEL_GRAY: 0257 $baseColor = $this->readInt(1); 0258 $this->_transparencyData = array($baseColor, $baseColor); 0259 break; 0260 0261 case Zend_Pdf_Image::PNG_CHANNEL_RGB: 0262 0263 //@TODO Fix this hack. 0264 //This parser cheats and only uses the lsb's (and only works with < 16 bit depth images) 0265 0266 /* 0267 From the standard: 0268 0269 For color type 2 (truecolor), the tRNS chunk contains a single RGB color value, stored in the format: 0270 0271 Red: 2 bytes, range 0 .. (2^bitdepth)-1 0272 Green: 2 bytes, range 0 .. (2^bitdepth)-1 0273 Blue: 2 bytes, range 0 .. (2^bitdepth)-1 0274 0275 (If the image bit depth is less than 16, the least significant bits are used and the others are 0.) 0276 Pixels of the specified color value are to be treated as transparent (equivalent to alpha value 0); 0277 all other pixels are to be treated as fully opaque (alpha value 2bitdepth-1). 0278 0279 */ 0280 0281 $red = $this->readInt(1); 0282 $this->skipBytes(1); 0283 $green = $this->readInt(1); 0284 $this->skipBytes(1); 0285 $blue = $this->readInt(1); 0286 0287 $this->_transparencyData = array($red, $red, $green, $green, $blue, $blue); 0288 0289 break; 0290 0291 case Zend_Pdf_Image::PNG_CHANNEL_INDEXED: 0292 0293 //@TODO Fix this hack. 0294 //This parser cheats too. It only masks the first color in the palette. 0295 0296 /* 0297 From the standard: 0298 0299 For color type 3 (indexed color), the tRNS chunk contains a series of one-byte alpha values, corresponding to entries in the PLTE chunk: 0300 0301 Alpha for palette index 0: 1 byte 0302 Alpha for palette index 1: 1 byte 0303 ...etc... 0304 0305 Each entry indicates that pixels of the corresponding palette index must be treated as having the specified alpha value. 0306 Alpha values have the same interpretation as in an 8-bit full alpha channel: 0 is fully transparent, 255 is fully opaque, 0307 regardless of image bit depth. The tRNS chunk must not contain more alpha values than there are palette entries, 0308 but tRNS can contain fewer values than there are palette entries. In this case, the alpha value for all remaining palette 0309 entries is assumed to be 255. In the common case in which only palette index 0 need be made transparent, only a one-byte 0310 tRNS chunk is needed. 0311 0312 */ 0313 0314 $tmpData = $this->readBytes($chunkLength); 0315 if(($trnsIdx = strpos($tmpData, "\0")) !== false) { 0316 $this->_transparencyData = array($trnsIdx, $trnsIdx); 0317 } 0318 0319 break; 0320 0321 case Zend_Pdf_Image::PNG_CHANNEL_GRAY_ALPHA: 0322 //Fall through to the next case 0323 case Zend_Pdf_Image::PNG_CHANNEL_RGB_ALPHA: 0324 // require_once 'Zend/Pdf/Exception.php'; 0325 throw new Zend_Pdf_Exception( "tRNS chunk illegal for Alpha Channel Images" ); 0326 break; 0327 } 0328 } 0329 }