Example #1
0
 /**
  * Object constructor
  *
  * @param string $imageFileName
  * @throws \Zend\Pdf\Exception
  */
 public function __construct($imageFileName)
 {
     if (!function_exists('gd_info')) {
         throw new Exception\RuntimeException('Image extension is not installed.');
     }
     $gd_options = gd_info();
     if ((!isset($gd_options['JPG Support']) || $gd_options['JPG Support'] != true) && (!isset($gd_options['JPEG Support']) || $gd_options['JPEG Support'] != true)) {
         throw new Exception\RuntimeException('JPG support is not configured properly.');
     }
     if (!is_readable($imageFileName)) {
         throw new Exception\IOException("File '{$imageFileName}' is not readable.");
     }
     if (($imageInfo = getimagesize($imageFileName)) === false) {
         throw new Exception\CorruptedImageException('Corrupted image.');
     }
     if ($imageInfo[2] != IMAGETYPE_JPEG && $imageInfo[2] != IMAGETYPE_JPEG2000) {
         throw new Exception\DomainException('ImageType is not JPG');
     }
     parent::__construct();
     switch ($imageInfo['channels']) {
         case 3:
             $colorSpace = 'DeviceRGB';
             break;
         case 4:
             $colorSpace = 'DeviceCMYK';
             break;
         default:
             $colorSpace = 'DeviceGray';
             break;
     }
     $imageDictionary = $this->_resource->dictionary;
     $imageDictionary->Width = new InternalType\NumericObject($imageInfo[0]);
     $imageDictionary->Height = new InternalType\NumericObject($imageInfo[1]);
     $imageDictionary->ColorSpace = new InternalType\NameObject($colorSpace);
     $imageDictionary->BitsPerComponent = new InternalType\NumericObject($imageInfo['bits']);
     if ($imageInfo[2] == IMAGETYPE_JPEG) {
         $imageDictionary->Filter = new InternalType\NameObject('DCTDecode');
     } else {
         if ($imageInfo[2] == IMAGETYPE_JPEG2000) {
             $imageDictionary->Filter = new InternalType\NameObject('JPXDecode');
         }
     }
     if (($imageFile = @fopen($imageFileName, 'rb')) === false) {
         throw new Exception\IOException("Can not open '{$imageFileName}' file for reading.");
     }
     $byteCount = filesize($imageFileName);
     $this->_resource->value = '';
     while ($byteCount > 0 && ($nextBlock = fread($imageFile, $byteCount)) != false) {
         $this->_resource->value .= $nextBlock;
         $byteCount -= strlen($nextBlock);
     }
     fclose($imageFile);
     $this->_resource->skipFilters();
     $this->_width = $imageInfo[0];
     $this->_height = $imageInfo[1];
     $this->_imageProperties = array();
     $this->_imageProperties['bitDepth'] = $imageInfo['bits'];
     $this->_imageProperties['jpegImageType'] = $imageInfo[2];
     $this->_imageProperties['jpegColorType'] = $imageInfo['channels'];
 }
Example #2
0
File: Tiff.php Project: rexmac/zf2
 /**
  * Object constructor
  *
  * @param string $imageFileName
  * @throws \Zend\Pdf\Exception
  */
 public function __construct($imageFileName)
 {
     if (($imageFile = @fopen($imageFileName, 'rb')) === false) {
         throw new Exception\IOException("Can not open '{$imageFileName}' file for reading.");
     }
     $byteOrderIndicator = fread($imageFile, 2);
     if ($byteOrderIndicator == 'II') {
         $this->_endianType = self::TIFF_ENDIAN_LITTLE;
     } else {
         if ($byteOrderIndicator == 'MM') {
             $this->_endianType = self::TIFF_ENDIAN_BIG;
         } else {
             throw new Exception\DomainException('Not a tiff file or Tiff corrupt. No byte order indication found');
         }
     }
     $version = $this->unpackBytes(self::UNPACK_TYPE_SHORT, fread($imageFile, 2));
     if ($version != 42) {
         throw new Exception\DomainException('Not a tiff file or Tiff corrupt. Incorrect version number.');
     }
     $ifdOffset = $this->unpackBytes(self::UNPACK_TYPE_LONG, fread($imageFile, 4));
     $fileStats = fstat($imageFile);
     $this->_fileSize = $fileStats['size'];
     /*
      * Tiff files are stored as a series of Image File Directories (IFD) each direcctory
      * has a specific number of entries each 12 bytes in length. At the end of the directories
      * is four bytes pointing to the offset of the next IFD.
      */
     while ($ifdOffset > 0) {
         if (fseek($imageFile, $ifdOffset, SEEK_SET) == -1 || $ifdOffset + 2 >= $this->_fileSize) {
             throw new Exception\CorruptedImageException("Could not seek to the image file directory as indexed by the file. Likely cause is TIFF corruption. Offset: " . $ifdOffset);
         }
         $numDirEntries = $this->unpackBytes(self::UNPACK_TYPE_SHORT, fread($imageFile, 2));
         /*
          * Since we now know how many entries are in this (IFD) we can extract the data.
          * The format of a TIFF directory entry is:
          *
          * 2 bytes (short) tag code; See TIFF_TAG constants at the top for supported values. (There are many more in the spec)
          * 2 bytes (short) field type
          * 4 bytes (long) number of values, or value count.
          * 4 bytes (mixed) data if the data will fit into 4 bytes or an offset if the data is too large.
          */
         for ($dirEntryIdx = 1; $dirEntryIdx <= $numDirEntries; $dirEntryIdx++) {
             $tag = $this->unpackBytes(self::UNPACK_TYPE_SHORT, fread($imageFile, 2));
             $fieldType = $this->unpackBytes(self::UNPACK_TYPE_SHORT, fread($imageFile, 2));
             $valueCount = $this->unpackBytes(self::UNPACK_TYPE_LONG, fread($imageFile, 4));
             switch ($fieldType) {
                 case self::TIFF_FIELD_TYPE_BYTE:
                     $fieldLength = $valueCount;
                     break;
                 case self::TIFF_FIELD_TYPE_ASCII:
                     $fieldLength = $valueCount;
                     break;
                 case self::TIFF_FIELD_TYPE_SHORT:
                     $fieldLength = $valueCount * 2;
                     break;
                 case self::TIFF_FIELD_TYPE_LONG:
                     $fieldLength = $valueCount * 4;
                     break;
                 case self::TIFF_FIELD_TYPE_RATIONAL:
                     $fieldLength = $valueCount * 8;
                     break;
                 default:
                     $fieldLength = $valueCount;
             }
             $offsetBytes = fread($imageFile, 4);
             if ($fieldLength <= 4) {
                 switch ($fieldType) {
                     case self::TIFF_FIELD_TYPE_BYTE:
                         $value = $this->unpackBytes(self::UNPACK_TYPE_BYTE, $offsetBytes);
                         break;
                     case self::TIFF_FIELD_TYPE_ASCII:
                         //Fall through to next case
                     //Fall through to next case
                     case self::TIFF_FIELD_TYPE_LONG:
                         $value = $this->unpackBytes(self::UNPACK_TYPE_LONG, $offsetBytes);
                         break;
                     case self::TIFF_FIELD_TYPE_SHORT:
                         //Fall through to next case
                     //Fall through to next case
                     default:
                         $value = $this->unpackBytes(self::UNPACK_TYPE_SHORT, $offsetBytes);
                 }
             } else {
                 $refOffset = $this->unpackBytes(self::UNPACK_TYPE_LONG, $offsetBytes);
             }
             /*
              * Linear tag processing is probably not the best way to do this. I've processed the tags according to the
              * Tiff 6 specification and make some assumptions about when tags will be < 4 bytes and fit into $value and when
              * they will be > 4 bytes and require seek/extraction of the offset. Same goes for extracting arrays of data, like
              * the data offsets and length. This should be fixed in the future.
              */
             switch ($tag) {
                 case self::TIFF_TAG_IMAGE_WIDTH:
                     $this->_width = $value;
                     break;
                 case self::TIFF_TAG_IMAGE_LENGTH:
                     $this->_height = $value;
                     break;
                 case self::TIFF_TAG_BITS_PER_SAMPLE:
                     if ($valueCount > 1) {
                         $fp = ftell($imageFile);
                         fseek($imageFile, $refOffset, SEEK_SET);
                         $this->_bitsPerSample = $this->unpackBytes(self::UNPACK_TYPE_SHORT, fread($imageFile, 2));
                         fseek($imageFile, $fp, SEEK_SET);
                     } else {
                         $this->_bitsPerSample = $value;
                     }
                     break;
                 case self::TIFF_TAG_COMPRESSION:
                     $this->_compression = $value;
                     switch ($value) {
                         case self::TIFF_COMPRESSION_UNCOMPRESSED:
                             $this->_filter = 'None';
                             break;
                         case self::TIFF_COMPRESSION_CCITT1D:
                             //Fall through to next case
                         //Fall through to next case
                         case self::TIFF_COMPRESSION_GROUP_3_FAX:
                             //Fall through to next case
                         //Fall through to next case
                         case self::TIFF_COMPRESSION_GROUP_4_FAX:
                             $this->_filter = 'CCITTFaxDecode';
                             throw new Exception\NotImplementedException('CCITTFaxDecode Compression Mode Not Currently Supported');
                             break;
                         case self::TIFF_COMPRESSION_LZW:
                             $this->_filter = 'LZWDecode';
                             throw new Exception\NotImplementedException('LZWDecode Compression Mode Not Currently Supported');
                             break;
                         case self::TIFF_COMPRESSION_JPEG:
                             $this->_filter = 'DCTDecode';
                             //Should work, doesnt...
                             throw new Exception\NotImplementedException('JPEG Compression Mode Not Currently Supported');
                             break;
                         case self::TIFF_COMPRESSION_FLATE:
                             //fall through to next case
                         //fall through to next case
                         case self::TIFF_COMPRESSION_FLATE_OBSOLETE_CODE:
                             $this->_filter = 'FlateDecode';
                             throw new Exception\NotImplementedException('ZIP/Flate Compression Mode Not Currently Supported');
                             break;
                         case self::TIFF_COMPRESSION_PACKBITS:
                             $this->_filter = 'RunLengthDecode';
                             break;
                     }
                     break;
                 case self::TIFF_TAG_PHOTOMETRIC_INTERPRETATION:
                     $this->_colorCode = $value;
                     $this->_whiteIsZero = false;
                     $this->_blackIsZero = false;
                     switch ($value) {
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO:
                             $this->_whiteIsZero = true;
                             $this->_colorSpace = 'DeviceGray';
                             break;
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO:
                             $this->_blackIsZero = true;
                             $this->_colorSpace = 'DeviceGray';
                             break;
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR:
                             //fall through to next case
                         //fall through to next case
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_RGB:
                             $this->_colorSpace = 'DeviceRGB';
                             break;
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED:
                             $this->_colorSpace = 'Indexed';
                             break;
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_CMYK:
                             $this->_colorSpace = 'DeviceCMYK';
                             break;
                         case self::TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB:
                             $this->_colorSpace = 'Lab';
                             break;
                         default:
                             throw new Exception\NotImplementedException('TIFF: Unknown or Unsupported Color Type: ' . $value);
                     }
                     break;
                 case self::TIFF_TAG_STRIP_OFFSETS:
                     if ($valueCount > 1) {
                         $format = $this->_endianType == self::TIFF_ENDIAN_LITTLE ? 'V*' : 'N*';
                         $fp = ftell($imageFile);
                         fseek($imageFile, $refOffset, SEEK_SET);
                         $stripOffsetsBytes = fread($imageFile, $fieldLength);
                         $this->_imageDataOffset = unpack($format, $stripOffsetsBytes);
                         fseek($imageFile, $fp, SEEK_SET);
                     } else {
                         $this->_imageDataOffset = $value;
                     }
                     break;
                 case self::TIFF_TAG_STRIP_BYTE_COUNTS:
                     if ($valueCount > 1) {
                         $format = $this->_endianType == self::TIFF_ENDIAN_LITTLE ? 'V*' : 'N*';
                         $fp = ftell($imageFile);
                         fseek($imageFile, $refOffset, SEEK_SET);
                         $stripByteCountsBytes = fread($imageFile, $fieldLength);
                         $this->_imageDataLength = unpack($format, $stripByteCountsBytes);
                         fseek($imageFile, $fp, SEEK_SET);
                     } else {
                         $this->_imageDataLength = $value;
                     }
                     break;
                 default:
                     //For debugging. It should be harmless to ignore unknown tags, though there is some good info in them.
                     //echo "Unknown tag detected: ". $tag . " value: ". $value;
             }
         }
         $ifdOffset = $this->unpackBytes(self::UNPACK_TYPE_LONG, fread($imageFile, 4));
     }
     if (!isset($this->_imageDataOffset) || !isset($this->_imageDataLength)) {
         throw new Exception\CorruptedImageException('TIFF: The image processed did not contain image data as expected.');
     }
     $imageDataBytes = '';
     if (is_array($this->_imageDataOffset)) {
         if (!is_array($this->_imageDataLength)) {
             throw new Exception\CorruptedImageException('TIFF: The image contained multiple data offsets but not multiple data lengths. Tiff may be corrupt.');
         }
         foreach ($this->_imageDataOffset as $idx => $offset) {
             fseek($imageFile, $this->_imageDataOffset[$idx], SEEK_SET);
             $imageDataBytes .= fread($imageFile, $this->_imageDataLength[$idx]);
         }
     } else {
         fseek($imageFile, $this->_imageDataOffset, SEEK_SET);
         $imageDataBytes = fread($imageFile, $this->_imageDataLength);
     }
     if ($imageDataBytes === '') {
         throw new Exception\CorruptedImageException('TIFF: No data. Image Corruption');
     }
     fclose($imageFile);
     parent::__construct();
     $imageDictionary = $this->_resource->dictionary;
     if (!isset($this->_width) || !isset($this->_height)) {
         throw new Exception\CorruptedImageException('Problem reading tiff file. Tiff is probably corrupt.');
     }
     $this->_imageProperties = array();
     $this->_imageProperties['bitDepth'] = $this->_bitsPerSample;
     $this->_imageProperties['fileSize'] = $this->_fileSize;
     $this->_imageProperties['TIFFendianType'] = $this->_endianType;
     $this->_imageProperties['TIFFcompressionType'] = $this->_compression;
     $this->_imageProperties['TIFFwhiteIsZero'] = $this->_whiteIsZero;
     $this->_imageProperties['TIFFblackIsZero'] = $this->_blackIsZero;
     $this->_imageProperties['TIFFcolorCode'] = $this->_colorCode;
     $this->_imageProperties['TIFFimageDataOffset'] = $this->_imageDataOffset;
     $this->_imageProperties['TIFFimageDataLength'] = $this->_imageDataLength;
     $this->_imageProperties['PDFfilter'] = $this->_filter;
     $this->_imageProperties['PDFcolorSpace'] = $this->_colorSpace;
     $imageDictionary->Width = new InternalType\NumericObject($this->_width);
     if ($this->_whiteIsZero === true) {
         $imageDictionary->Decode = new InternalType\ArrayObject(array(new InternalType\NumericObject(1), new InternalType\NumericObject(0)));
     }
     $imageDictionary->Height = new InternalType\NumericObject($this->_height);
     $imageDictionary->ColorSpace = new InternalType\NameObject($this->_colorSpace);
     $imageDictionary->BitsPerComponent = new InternalType\NumericObject($this->_bitsPerSample);
     if (isset($this->_filter) && $this->_filter != 'None') {
         $imageDictionary->Filter = new InternalType\NameObject($this->_filter);
     }
     $this->_resource->value = $imageDataBytes;
     $this->_resource->skipFilters();
 }
Example #3
0
 /**
  * Object constructor
  *
  * @param string $imageFileName
  * @throws \Zend\Pdf\Exception
  * @todo Add compression conversions to support compression strategys other than PNG_COMPRESSION_DEFAULT_STRATEGY.
  * @todo Add pre-compression filtering.
  * @todo Add interlaced image handling.
  * @todo Add support for 16-bit images. Requires PDF version bump to 1.5 at least.
  * @todo Add processing for all PNG chunks defined in the spec. gAMA etc.
  * @todo Fix tRNS chunk support for Indexed Images to a SMask.
  */
 public function __construct($imageFileName)
 {
     if (($imageFile = @fopen($imageFileName, 'rb')) === false) {
         throw new Exception\IOException("Can not open '{$imageFileName}' file for reading.");
     }
     parent::__construct();
     //Check if the file is a PNG
     fseek($imageFile, 1, SEEK_CUR);
     //First signature byte (%)
     if ('PNG' != fread($imageFile, 3)) {
         throw new Exception\DomainException('Image is not a PNG');
     }
     fseek($imageFile, 12, SEEK_CUR);
     //Signature bytes (Includes the IHDR chunk) IHDR processed linerarly because it doesnt contain a variable chunk length
     $wtmp = unpack('Ni', fread($imageFile, 4));
     //Unpack a 4-Byte Long
     $width = $wtmp['i'];
     $htmp = unpack('Ni', fread($imageFile, 4));
     $height = $htmp['i'];
     $bits = ord(fread($imageFile, 1));
     //Higher than 8 bit depths are only supported in later versions of PDF.
     $color = ord(fread($imageFile, 1));
     $compression = ord(fread($imageFile, 1));
     $prefilter = ord(fread($imageFile, 1));
     if (($interlacing = ord(fread($imageFile, 1))) != self::PNG_INTERLACING_DISABLED) {
         throw new Exception\NotImplementedException('Only non-interlaced images are currently supported.');
     }
     $this->_width = $width;
     $this->_height = $height;
     $this->_imageProperties = array();
     $this->_imageProperties['bitDepth'] = $bits;
     $this->_imageProperties['pngColorType'] = $color;
     $this->_imageProperties['pngFilterType'] = $prefilter;
     $this->_imageProperties['pngCompressionType'] = $compression;
     $this->_imageProperties['pngInterlacingType'] = $interlacing;
     fseek($imageFile, 4, SEEK_CUR);
     //4 Byte Ending Sequence
     $imageData = '';
     /*
      * The following loop processes PNG chunks. 4 Byte Longs are packed first give the chunk length
      * followed by the chunk signature, a four byte code. IDAT and IEND are manditory in any PNG.
      */
     while (($chunkLengthBytes = fread($imageFile, 4)) !== false) {
         $chunkLengthtmp = unpack('Ni', $chunkLengthBytes);
         $chunkLength = $chunkLengthtmp['i'];
         $chunkType = fread($imageFile, 4);
         switch ($chunkType) {
             case 'IDAT':
                 //Image Data
                 /*
                  * Reads the actual image data from the PNG file. Since we know at this point that the compression
                  * strategy is the default strategy, we also know that this data is Zip compressed. We will either copy
                  * the data directly to the PDF and provide the correct FlateDecode predictor, or decompress the data
                  * decode the filters and output the data as a raw pixel map.
                  */
                 $imageData .= fread($imageFile, $chunkLength);
                 fseek($imageFile, 4, SEEK_CUR);
                 break;
             case 'PLTE':
                 //Palette
                 $paletteData = fread($imageFile, $chunkLength);
                 fseek($imageFile, 4, SEEK_CUR);
                 break;
             case 'tRNS':
                 //Basic (non-alpha channel) transparency.
                 $trnsData = fread($imageFile, $chunkLength);
                 switch ($color) {
                     case self::PNG_CHANNEL_GRAY:
                         $baseColor = ord(substr($trnsData, 1, 1));
                         $transparencyData = array(new InternalType\NumericObject($baseColor), new InternalType\NumericObject($baseColor));
                         break;
                     case self::PNG_CHANNEL_RGB:
                         $red = ord(substr($trnsData, 1, 1));
                         $green = ord(substr($trnsData, 3, 1));
                         $blue = ord(substr($trnsData, 5, 1));
                         $transparencyData = array(new InternalType\NumericObject($red), new InternalType\NumericObject($red), new InternalType\NumericObject($green), new InternalType\NumericObject($green), new InternalType\NumericObject($blue), new InternalType\NumericObject($blue));
                         break;
                     case self::PNG_CHANNEL_INDEXED:
                         //Find the first transparent color in the index, we will mask that. (This is a bit of a hack. This should be a SMask and mask all entries values).
                         if (($trnsIdx = strpos($trnsData, "")) !== false) {
                             $transparencyData = array(new InternalType\NumericObject($trnsIdx), new InternalType\NumericObject($trnsIdx));
                         }
                         break;
                     case self::PNG_CHANNEL_GRAY_ALPHA:
                         // Fall through to the next case
                         // Fall through to the next case
                     // Fall through to the next case
                     // Fall through to the next case
                     case self::PNG_CHANNEL_RGB_ALPHA:
                         throw new Exception\CorruptedImageException("tRNS chunk illegal for Alpha Channel Images");
                         break;
                 }
                 fseek($imageFile, 4, SEEK_CUR);
                 //4 Byte Ending Sequence
                 break;
             case 'IEND':
                 break 2;
                 //End the loop too
                 //End the loop too
             //End the loop too
             //End the loop too
             default:
                 fseek($imageFile, $chunkLength + 4, SEEK_CUR);
                 //Skip the section
                 break;
         }
     }
     fclose($imageFile);
     $compressed = true;
     $imageDataTmp = '';
     $smaskData = '';
     switch ($color) {
         case self::PNG_CHANNEL_RGB:
             $colorSpace = new InternalType\NameObject('DeviceRGB');
             break;
         case self::PNG_CHANNEL_GRAY:
             $colorSpace = new InternalType\NameObject('DeviceGray');
             break;
         case self::PNG_CHANNEL_INDEXED:
             if (empty($paletteData)) {
                 throw new Exception\CorruptedImageException("PNG Corruption: No palette data read for indexed type PNG.");
             }
             $colorSpace = new InternalType\ArrayObject();
             $colorSpace->items[] = new InternalType\NameObject('Indexed');
             $colorSpace->items[] = new InternalType\NameObject('DeviceRGB');
             $colorSpace->items[] = new InternalType\NumericObject(strlen($paletteData) / 3 - 1);
             $paletteObject = $this->_objectFactory->newObject(new InternalType\BinaryStringObject($paletteData));
             $colorSpace->items[] = $paletteObject;
             break;
         case self::PNG_CHANNEL_GRAY_ALPHA:
             /*
              * To decode PNG's with alpha data we must create two images from one. One image will contain the Gray data
              * the other will contain the Gray transparency overlay data. The former will become the object data and the latter
              * will become the Shadow Mask (SMask).
              */
             if ($bits > 8) {
                 throw new Exception\NotImplementedException('Alpha PNGs with bit depth > 8 are not yet supported');
             }
             $colorSpace = new InternalType\NameObject('DeviceGray');
             $decodingObjFactory = ObjectFactory::createFactory(1);
             $decodingStream = $decodingObjFactory->newStreamObject($imageData);
             $decodingStream->dictionary->Filter = new InternalType\NameObject('FlateDecode');
             $decodingStream->dictionary->DecodeParms = new InternalType\DictionaryObject();
             $decodingStream->dictionary->DecodeParms->Predictor = new InternalType\NumericObject(15);
             $decodingStream->dictionary->DecodeParms->Columns = new InternalType\NumericObject($width);
             $decodingStream->dictionary->DecodeParms->Colors = new InternalType\NumericObject(2);
             //GreyAlpha
             $decodingStream->dictionary->DecodeParms->BitsPerComponent = new InternalType\NumericObject($bits);
             $decodingStream->skipFilters();
             $pngDataRawDecoded = $decodingStream->value;
             //Iterate every pixel and copy out gray data and alpha channel (this will be slow)
             for ($pixel = 0, $pixelcount = $width * $height; $pixel < $pixelcount; $pixel++) {
                 $imageDataTmp .= $pngDataRawDecoded[$pixel * 2];
                 $smaskData .= $pngDataRawDecoded[$pixel * 2 + 1];
             }
             $compressed = false;
             $imageData = $imageDataTmp;
             //Overwrite image data with the gray channel without alpha
             break;
         case self::PNG_CHANNEL_RGB_ALPHA:
             /*
              * To decode PNG's with alpha data we must create two images from one. One image will contain the RGB data
              * the other will contain the Gray transparency overlay data. The former will become the object data and the latter
              * will become the Shadow Mask (SMask).
              */
             if ($bits > 8) {
                 throw new Exception\NotImplementedException('Alpha PNGs with bit depth > 8 are not yet supported');
             }
             $colorSpace = new InternalType\NameObject('DeviceRGB');
             $decodingObjFactory = ObjectFactory::createFactory(1);
             $decodingStream = $decodingObjFactory->newStreamObject($imageData);
             $decodingStream->dictionary->Filter = new InternalType\NameObject('FlateDecode');
             $decodingStream->dictionary->DecodeParms = new InternalType\DictionaryObject();
             $decodingStream->dictionary->DecodeParms->Predictor = new InternalType\NumericObject(15);
             $decodingStream->dictionary->DecodeParms->Columns = new InternalType\NumericObject($width);
             $decodingStream->dictionary->DecodeParms->Colors = new InternalType\NumericObject(4);
             //RGBA
             $decodingStream->dictionary->DecodeParms->BitsPerComponent = new InternalType\NumericObject($bits);
             $decodingStream->skipFilters();
             $pngDataRawDecoded = $decodingStream->value;
             //Iterate every pixel and copy out rgb data and alpha channel (this will be slow)
             for ($pixel = 0, $pixelcount = $width * $height; $pixel < $pixelcount; $pixel++) {
                 $imageDataTmp .= $pngDataRawDecoded[$pixel * 4 + 0] . $pngDataRawDecoded[$pixel * 4 + 1] . $pngDataRawDecoded[$pixel * 4 + 2];
                 $smaskData .= $pngDataRawDecoded[$pixel * 4 + 3];
             }
             $compressed = false;
             $imageData = $imageDataTmp;
             //Overwrite image data with the RGB channel without alpha
             break;
         default:
             throw new Exception\CorruptedImageException('PNG Corruption: Invalid color space.');
     }
     if (empty($imageData)) {
         throw new Exception\CorruptedImageException('Corrupt PNG Image. Mandatory IDAT chunk not found.');
     }
     $imageDictionary = $this->_resource->dictionary;
     if (!empty($smaskData)) {
         /*
          * Includes the Alpha transparency data as a Gray Image, then assigns the image as the Shadow Mask for the main image data.
          */
         $smaskStream = $this->_objectFactory->newStreamObject($smaskData);
         $smaskStream->dictionary->Type = new InternalType\NameObject('XObject');
         $smaskStream->dictionary->Subtype = new InternalType\NameObject('Image');
         $smaskStream->dictionary->Width = new InternalType\NumericObject($width);
         $smaskStream->dictionary->Height = new InternalType\NumericObject($height);
         $smaskStream->dictionary->ColorSpace = new InternalType\NameObject('DeviceGray');
         $smaskStream->dictionary->BitsPerComponent = new InternalType\NumericObject($bits);
         $imageDictionary->SMask = $smaskStream;
         // Encode stream with FlateDecode filter
         $smaskStreamDecodeParms = array();
         $smaskStreamDecodeParms['Predictor'] = new InternalType\NumericObject(15);
         $smaskStreamDecodeParms['Columns'] = new InternalType\NumericObject($width);
         $smaskStreamDecodeParms['Colors'] = new InternalType\NumericObject(1);
         $smaskStreamDecodeParms['BitsPerComponent'] = new InternalType\NumericObject(8);
         $smaskStream->dictionary->DecodeParms = new InternalType\DictionaryObject($smaskStreamDecodeParms);
         $smaskStream->dictionary->Filter = new InternalType\NameObject('FlateDecode');
     }
     if (!empty($transparencyData)) {
         //This is experimental and not properly tested.
         $imageDictionary->Mask = new InternalType\ArrayObject($transparencyData);
     }
     $imageDictionary->Width = new InternalType\NumericObject($width);
     $imageDictionary->Height = new InternalType\NumericObject($height);
     $imageDictionary->ColorSpace = $colorSpace;
     $imageDictionary->BitsPerComponent = new InternalType\NumericObject($bits);
     $imageDictionary->Filter = new InternalType\NameObject('FlateDecode');
     $decodeParms = array();
     $decodeParms['Predictor'] = new InternalType\NumericObject(15);
     // Optimal prediction
     $decodeParms['Columns'] = new InternalType\NumericObject($width);
     $decodeParms['Colors'] = new InternalType\NumericObject($color == self::PNG_CHANNEL_RGB || $color == self::PNG_CHANNEL_RGB_ALPHA ? 3 : 1);
     $decodeParms['BitsPerComponent'] = new InternalType\NumericObject($bits);
     $imageDictionary->DecodeParms = new InternalType\DictionaryObject($decodeParms);
     //Include only the image IDAT section data.
     $this->_resource->value = $imageData;
     //Skip double compression
     if ($compressed) {
         $this->_resource->skipFilters();
     }
 }