diff -r 000000000000 -r 4eba9c11703f web/Zend/Pdf/FileParser/Image/Png.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/Zend/Pdf/FileParser/Image/Png.php Mon Dec 13 18:29:26 2010 +0100 @@ -0,0 +1,329 @@ +_isParsed) { + $this->parse(); + } + return $this->_width; + } + + public function getHeight() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_width; + } + + public function getBitDepth() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_bits; + } + + public function getColorSpace() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_color; + } + + public function getCompressionStrategy() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_compression; + } + + public function getPaethFilter() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_preFilter; + } + + public function getInterlacingMode() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_interlacing; + } + + public function getRawImageData() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_imageData; + } + + public function getRawPaletteData() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_paletteData; + } + + public function getRawTransparencyData() { + if(!$this->_isParsed) { + $this->parse(); + } + return $this->_transparencyData; + } + + /* Semi-Concrete Class Implementation */ + + /** + * Verifies that the image file is in the expected format. + * + * @throws Zend_Pdf_Exception + */ + public function screen() + { + if ($this->_isScreened) { + return; + } + return $this->_checkSignature(); + } + + /** + * Reads and parses the image data from the file on disk. + * + * @throws Zend_Pdf_Exception + */ + public function parse() + { + if ($this->_isParsed) { + return; + } + + /* Screen the font file first, if it hasn't been done yet. + */ + $this->screen(); + + $this->_parseIHDRChunk(); + $this->_parseChunks(); + } + + + protected function _parseSignature() { + $this->moveToOffset(1); //Skip the first byte (%) + if('PNG' != $this->readBytes(3)) { + $this->_isPNG = false; + } else { + $this->_isPNG = true; + } + } + + protected function _checkSignature() { + if(!isset($this->_isPNG)) { + $this->_parseSignature(); + } + return $this->_isPNG; + } + + protected function _parseChunks() { + $this->moveToOffset(33); //Variable chunks start at the end of IHDR + + //Start processing chunks. If there are no more bytes to read parsing is complete. + $size = $this->getSize(); + while($size - $this->getOffset() >= 8) { + $chunkLength = $this->readUInt(4); + if($chunkLength < 0 || ($chunkLength + $this->getOffset() + 4) > $size) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception("PNG Corrupt: Invalid Chunk Size In File."); + } + + $chunkType = $this->readBytes(4); + $offset = $this->getOffset(); + + //If we know how to process the chunk, do it here, else ignore the chunk and move on to the next + switch($chunkType) { + case 'IDAT': // This chunk may appear more than once. It contains the actual image data. + $this->_parseIDATChunk($offset, $chunkLength); + break; + + case 'PLTE': // This chunk contains the image palette. + $this->_parsePLTEChunk($offset, $chunkLength); + break; + + case 'tRNS': // This chunk contains non-alpha channel transparency data + $this->_parseTRNSChunk($offset, $chunkLength); + break; + + case 'IEND': + break 2; //End the loop too + + //@TODO Implement the rest of the PNG chunks. (There are many not implemented here) + } + if($offset + $chunkLength + 4 < $size) { + $this->moveToOffset($offset + $chunkLength + 4); //Skip past the data finalizer. (Don't rely on the parse to leave the offsets correct) + } + } + if(empty($this->_imageData)) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception ( "This PNG is corrupt. All png must contain IDAT chunks." ); + } + } + + protected function _parseIHDRChunk() { + $this->moveToOffset(12); //IHDR must always start at offset 12 and run for 17 bytes + if(!$this->readBytes(4) == 'IHDR') { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception( "This PNG is corrupt. The first chunk in a PNG file must be IHDR." ); + } + $this->_width = $this->readUInt(4); + $this->_height = $this->readUInt(4); + $this->_bits = $this->readInt(1); + $this->_color = $this->readInt(1); + $this->_compression = $this->readInt(1); + $this->_preFilter = $this->readInt(1); + $this->_interlacing = $this->readInt(1); + if($this->_interlacing != Zend_Pdf_Image::PNG_INTERLACING_DISABLED) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception( "Only non-interlaced images are currently supported." ); + } + } + + protected function _parseIDATChunk($chunkOffset, $chunkLength) { + $this->moveToOffset($chunkOffset); + if(!isset($this->_imageData)) { + $this->_imageData = $this->readBytes($chunkLength); + } else { + $this->_imageData .= $this->readBytes($chunkLength); + } + } + + protected function _parsePLTEChunk($chunkOffset, $chunkLength) { + $this->moveToOffset($chunkOffset); + $this->_paletteData = $this->readBytes($chunkLength); + } + + protected function _parseTRNSChunk($chunkOffset, $chunkLength) { + $this->moveToOffset($chunkOffset); + + //Processing of tRNS data varies dependending on the color depth + + switch($this->_color) { + case Zend_Pdf_Image::PNG_CHANNEL_GRAY: + $baseColor = $this->readInt(1); + $this->_transparencyData = array($baseColor, $baseColor); + break; + + case Zend_Pdf_Image::PNG_CHANNEL_RGB: + + //@TODO Fix this hack. + //This parser cheats and only uses the lsb's (and only works with < 16 bit depth images) + + /* + From the standard: + + For color type 2 (truecolor), the tRNS chunk contains a single RGB color value, stored in the format: + + Red: 2 bytes, range 0 .. (2^bitdepth)-1 + Green: 2 bytes, range 0 .. (2^bitdepth)-1 + Blue: 2 bytes, range 0 .. (2^bitdepth)-1 + + (If the image bit depth is less than 16, the least significant bits are used and the others are 0.) + Pixels of the specified color value are to be treated as transparent (equivalent to alpha value 0); + all other pixels are to be treated as fully opaque (alpha value 2bitdepth-1). + + */ + + $red = $this->readInt(1); + $this->skipBytes(1); + $green = $this->readInt(1); + $this->skipBytes(1); + $blue = $this->readInt(1); + + $this->_transparencyData = array($red, $red, $green, $green, $blue, $blue); + + break; + + case Zend_Pdf_Image::PNG_CHANNEL_INDEXED: + + //@TODO Fix this hack. + //This parser cheats too. It only masks the first color in the palette. + + /* + From the standard: + + For color type 3 (indexed color), the tRNS chunk contains a series of one-byte alpha values, corresponding to entries in the PLTE chunk: + + Alpha for palette index 0: 1 byte + Alpha for palette index 1: 1 byte + ...etc... + + Each entry indicates that pixels of the corresponding palette index must be treated as having the specified alpha value. + Alpha values have the same interpretation as in an 8-bit full alpha channel: 0 is fully transparent, 255 is fully opaque, + regardless of image bit depth. The tRNS chunk must not contain more alpha values than there are palette entries, + but tRNS can contain fewer values than there are palette entries. In this case, the alpha value for all remaining palette + entries is assumed to be 255. In the common case in which only palette index 0 need be made transparent, only a one-byte + tRNS chunk is needed. + + */ + + $tmpData = $this->readBytes($chunkLength); + if(($trnsIdx = strpos($tmpData, "\0")) !== false) { + $this->_transparencyData = array($trnsIdx, $trnsIdx); + } + + break; + + case Zend_Pdf_Image::PNG_CHANNEL_GRAY_ALPHA: + //Fall through to the next case + case Zend_Pdf_Image::PNG_CHANNEL_RGB_ALPHA: + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception( "tRNS chunk illegal for Alpha Channel Images" ); + break; + } + } +}