web/lib/Zend/Pdf/FileParser/Image/Png.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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  * @subpackage FileParser
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Png.php 22653 2010-07-22 18:41:39Z mabe $
       
    21  */
       
    22 
       
    23 /** @see Zend_Pdf_FileParser_Image */
       
    24 require_once 'Zend/Pdf/FileParser/Image.php';
       
    25 
       
    26 
       
    27 /**
       
    28  * Abstract base class for Image file parsers.
       
    29  *
       
    30  * @package    Zend_Pdf
       
    31  * @subpackage FileParser
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_Pdf_FileParser_Image_Png extends Zend_Pdf_FileParser_Image
       
    36 {
       
    37      protected $_isPNG;
       
    38      protected $_width;
       
    39      protected $_height;
       
    40      protected $_bits;
       
    41      protected $_color;
       
    42      protected $_compression;
       
    43      protected $_preFilter;
       
    44      protected $_interlacing;
       
    45 
       
    46      protected $_imageData;
       
    47      protected $_paletteData;
       
    48      protected $_transparencyData;
       
    49 
       
    50   /**** Public Interface ****/
       
    51 
       
    52      public function getWidth() {
       
    53           if(!$this->_isParsed) {
       
    54                $this->parse();
       
    55           }
       
    56           return $this->_width;
       
    57      }
       
    58 
       
    59      public function getHeight() {
       
    60           if(!$this->_isParsed) {
       
    61                $this->parse();
       
    62           }
       
    63           return $this->_width;
       
    64      }
       
    65 
       
    66      public function getBitDepth() {
       
    67           if(!$this->_isParsed) {
       
    68                $this->parse();
       
    69           }
       
    70           return $this->_bits;
       
    71      }
       
    72 
       
    73      public function getColorSpace() {
       
    74           if(!$this->_isParsed) {
       
    75                $this->parse();
       
    76           }
       
    77           return $this->_color;
       
    78      }
       
    79 
       
    80      public function getCompressionStrategy() {
       
    81           if(!$this->_isParsed) {
       
    82                $this->parse();
       
    83           }
       
    84           return $this->_compression;
       
    85      }
       
    86 
       
    87      public function getPaethFilter() {
       
    88           if(!$this->_isParsed) {
       
    89                $this->parse();
       
    90           }
       
    91           return $this->_preFilter;
       
    92      }
       
    93 
       
    94      public function getInterlacingMode() {
       
    95           if(!$this->_isParsed) {
       
    96                $this->parse();
       
    97           }
       
    98           return $this->_interlacing;
       
    99      }
       
   100 
       
   101      public function getRawImageData() {
       
   102           if(!$this->_isParsed) {
       
   103                $this->parse();
       
   104           }
       
   105           return $this->_imageData;
       
   106      }
       
   107 
       
   108      public function getRawPaletteData() {
       
   109           if(!$this->_isParsed) {
       
   110                $this->parse();
       
   111           }
       
   112           return $this->_paletteData;
       
   113      }
       
   114 
       
   115      public function getRawTransparencyData() {
       
   116           if(!$this->_isParsed) {
       
   117                $this->parse();
       
   118           }
       
   119           return $this->_transparencyData;
       
   120      }
       
   121 
       
   122   /* Semi-Concrete Class Implementation */
       
   123 
       
   124     /**
       
   125      * Verifies that the image file is in the expected format.
       
   126      *
       
   127      * @throws Zend_Pdf_Exception
       
   128      */
       
   129     public function screen()
       
   130     {
       
   131          if ($this->_isScreened) {
       
   132              return;
       
   133          }
       
   134          return $this->_checkSignature();
       
   135     }
       
   136 
       
   137     /**
       
   138      * Reads and parses the image data from the file on disk.
       
   139      *
       
   140      * @throws Zend_Pdf_Exception
       
   141      */
       
   142     public function parse()
       
   143     {
       
   144         if ($this->_isParsed) {
       
   145             return;
       
   146         }
       
   147 
       
   148         /* Screen the font file first, if it hasn't been done yet.
       
   149         */
       
   150         $this->screen();
       
   151 
       
   152         $this->_parseIHDRChunk();
       
   153         $this->_parseChunks();
       
   154     }
       
   155 
       
   156 
       
   157     protected function _parseSignature() {
       
   158          $this->moveToOffset(1); //Skip the first byte (%)
       
   159          if('PNG' != $this->readBytes(3)) {
       
   160                $this->_isPNG = false;
       
   161          } else {
       
   162                $this->_isPNG = true;
       
   163          }
       
   164     }
       
   165 
       
   166     protected function _checkSignature() {
       
   167          if(!isset($this->_isPNG)) {
       
   168               $this->_parseSignature();
       
   169          }
       
   170          return $this->_isPNG;
       
   171     }
       
   172 
       
   173     protected function _parseChunks() {
       
   174          $this->moveToOffset(33); //Variable chunks start at the end of IHDR
       
   175 
       
   176          //Start processing chunks. If there are no more bytes to read parsing is complete.
       
   177          $size = $this->getSize();
       
   178          while($size - $this->getOffset() >= 8) {
       
   179               $chunkLength = $this->readUInt(4);
       
   180               if($chunkLength < 0 || ($chunkLength + $this->getOffset() + 4) > $size) {
       
   181                    require_once 'Zend/Pdf/Exception.php';
       
   182                    throw new Zend_Pdf_Exception("PNG Corrupt: Invalid Chunk Size In File.");
       
   183               }
       
   184 
       
   185               $chunkType = $this->readBytes(4);
       
   186               $offset = $this->getOffset();
       
   187 
       
   188               //If we know how to process the chunk, do it here, else ignore the chunk and move on to the next
       
   189               switch($chunkType) {
       
   190                    case 'IDAT': // This chunk may appear more than once. It contains the actual image data.
       
   191                        $this->_parseIDATChunk($offset, $chunkLength);
       
   192                        break;
       
   193 
       
   194                    case 'PLTE': // This chunk contains the image palette.
       
   195                        $this->_parsePLTEChunk($offset, $chunkLength);
       
   196                        break;
       
   197 
       
   198                    case 'tRNS': // This chunk contains non-alpha channel transparency data
       
   199                        $this->_parseTRNSChunk($offset, $chunkLength);
       
   200                        break;
       
   201 
       
   202                    case 'IEND':
       
   203                        break 2; //End the loop too
       
   204 
       
   205                    //@TODO Implement the rest of the PNG chunks. (There are many not implemented here)
       
   206               }
       
   207               if($offset + $chunkLength + 4 < $size) {
       
   208                   $this->moveToOffset($offset + $chunkLength + 4); //Skip past the data finalizer. (Don't rely on the parse to leave the offsets correct)
       
   209               }
       
   210          }
       
   211          if(empty($this->_imageData)) {
       
   212               require_once 'Zend/Pdf/Exception.php';
       
   213               throw new Zend_Pdf_Exception ( "This PNG is corrupt. All png must contain IDAT chunks." );
       
   214          }
       
   215     }
       
   216 
       
   217     protected function _parseIHDRChunk() {
       
   218          $this->moveToOffset(12); //IHDR must always start at offset 12 and run for 17 bytes
       
   219          if(!$this->readBytes(4) == 'IHDR') {
       
   220               require_once 'Zend/Pdf/Exception.php';
       
   221               throw new Zend_Pdf_Exception( "This PNG is corrupt. The first chunk in a PNG file must be IHDR." );
       
   222          }
       
   223          $this->_width = $this->readUInt(4);
       
   224          $this->_height = $this->readUInt(4);
       
   225          $this->_bits = $this->readInt(1);
       
   226          $this->_color = $this->readInt(1);
       
   227          $this->_compression = $this->readInt(1);
       
   228          $this->_preFilter = $this->readInt(1);
       
   229          $this->_interlacing = $this->readInt(1);
       
   230          if($this->_interlacing != Zend_Pdf_Image::PNG_INTERLACING_DISABLED) {
       
   231               require_once 'Zend/Pdf/Exception.php';
       
   232               throw new Zend_Pdf_Exception( "Only non-interlaced images are currently supported." );
       
   233          }
       
   234     }
       
   235 
       
   236     protected function _parseIDATChunk($chunkOffset, $chunkLength) {
       
   237          $this->moveToOffset($chunkOffset);
       
   238          if(!isset($this->_imageData)) {
       
   239               $this->_imageData = $this->readBytes($chunkLength);
       
   240          } else {
       
   241               $this->_imageData .= $this->readBytes($chunkLength);
       
   242          }
       
   243     }
       
   244 
       
   245     protected function _parsePLTEChunk($chunkOffset, $chunkLength) {
       
   246          $this->moveToOffset($chunkOffset);
       
   247          $this->_paletteData = $this->readBytes($chunkLength);
       
   248     }
       
   249 
       
   250     protected function _parseTRNSChunk($chunkOffset, $chunkLength) {
       
   251          $this->moveToOffset($chunkOffset);
       
   252 
       
   253          //Processing of tRNS data varies dependending on the color depth
       
   254 
       
   255          switch($this->_color) {
       
   256              case Zend_Pdf_Image::PNG_CHANNEL_GRAY:
       
   257                   $baseColor = $this->readInt(1);
       
   258                   $this->_transparencyData = array($baseColor, $baseColor);
       
   259                   break;
       
   260 
       
   261              case Zend_Pdf_Image::PNG_CHANNEL_RGB:
       
   262 
       
   263                   //@TODO Fix this hack.
       
   264                   //This parser cheats and only uses the lsb's (and only works with < 16 bit depth images)
       
   265 
       
   266                   /*
       
   267                        From the standard:
       
   268 
       
   269                        For color type 2 (truecolor), the tRNS chunk contains a single RGB color value, stored in the format:
       
   270 
       
   271                        Red:   2 bytes, range 0 .. (2^bitdepth)-1
       
   272                        Green: 2 bytes, range 0 .. (2^bitdepth)-1
       
   273                        Blue:  2 bytes, range 0 .. (2^bitdepth)-1
       
   274 
       
   275                        (If the image bit depth is less than 16, the least significant bits are used and the others are 0.)
       
   276                        Pixels of the specified color value are to be treated as transparent (equivalent to alpha value 0);
       
   277                        all other pixels are to be treated as fully opaque (alpha value 2bitdepth-1).
       
   278 
       
   279                   */
       
   280 
       
   281                   $red = $this->readInt(1);
       
   282                   $this->skipBytes(1);
       
   283                   $green = $this->readInt(1);
       
   284                   $this->skipBytes(1);
       
   285                   $blue = $this->readInt(1);
       
   286 
       
   287                   $this->_transparencyData = array($red, $red, $green, $green, $blue, $blue);
       
   288 
       
   289                   break;
       
   290 
       
   291              case Zend_Pdf_Image::PNG_CHANNEL_INDEXED:
       
   292 
       
   293                   //@TODO Fix this hack.
       
   294                   //This parser cheats too. It only masks the first color in the palette.
       
   295 
       
   296                   /*
       
   297                        From the standard:
       
   298 
       
   299                        For color type 3 (indexed color), the tRNS chunk contains a series of one-byte alpha values, corresponding to entries in the PLTE chunk:
       
   300 
       
   301                           Alpha for palette index 0:  1 byte
       
   302                           Alpha for palette index 1:  1 byte
       
   303                           ...etc...
       
   304 
       
   305                        Each entry indicates that pixels of the corresponding palette index must be treated as having the specified alpha value.
       
   306                        Alpha values have the same interpretation as in an 8-bit full alpha channel: 0 is fully transparent, 255 is fully opaque,
       
   307                        regardless of image bit depth. The tRNS chunk must not contain more alpha values than there are palette entries,
       
   308                        but tRNS can contain fewer values than there are palette entries. In this case, the alpha value for all remaining palette
       
   309                        entries is assumed to be 255. In the common case in which only palette index 0 need be made transparent, only a one-byte
       
   310                        tRNS chunk is needed.
       
   311 
       
   312                   */
       
   313 
       
   314                   $tmpData = $this->readBytes($chunkLength);
       
   315                   if(($trnsIdx = strpos($tmpData, "\0")) !== false) {
       
   316                        $this->_transparencyData = array($trnsIdx, $trnsIdx);
       
   317                   }
       
   318 
       
   319                   break;
       
   320 
       
   321              case Zend_Pdf_Image::PNG_CHANNEL_GRAY_ALPHA:
       
   322                   //Fall through to the next case
       
   323              case Zend_Pdf_Image::PNG_CHANNEL_RGB_ALPHA:
       
   324                   require_once 'Zend/Pdf/Exception.php';
       
   325                   throw new Zend_Pdf_Exception( "tRNS chunk illegal for Alpha Channel Images" );
       
   326                   break;
       
   327          }
       
   328     }
       
   329 }