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 }