/** * Object constructor * * @param string $imageFileName * @throws Zend_Pdf_Exception */ public function __construct($imageFileName) { if (!function_exists('gd_info')) { throw new Zend_Pdf_Exception('Image extension is not installed.'); } $gd_options = gd_info(); if (!$gd_options['JPG Support']) { throw new Zend_Pdf_Exception('JPG support is not configured properly.'); } if (($imageInfo = getimagesize($imageFileName)) === false) { throw new Zend_Pdf_Exception('Corrupted image or image doesn\'t exist.'); } if ($imageInfo[2] != IMAGETYPE_JPEG && $imageInfo[2] != IMAGETYPE_JPEG2000) { throw new Zend_Pdf_Exception('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 Zend_Pdf_Element_Numeric($imageInfo[0]); $imageDictionary->Height = new Zend_Pdf_Element_Numeric($imageInfo[1]); $imageDictionary->ColorSpace = new Zend_Pdf_Element_Name($colorSpace); $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($imageInfo['bits']); if ($imageInfo[2] == IMAGETYPE_JPEG) { $imageDictionary->Filter = new Zend_Pdf_Element_Name('DCTDecode'); } else { if ($imageInfo[2] == IMAGETYPE_JPEG2000) { $imageDictionary->Filter = new Zend_Pdf_Element_Name('JPXDecode'); } } if (($imageFile = @fopen($imageFileName, 'rb')) === false) { throw new Zend_Pdf_Exception("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']; }
/** * Object constructor * * @param string $imageFileName * @throws Zend_Pdf_Exception */ public function __construct($imageFileName) { if (($imageFile = @fopen($imageFileName, 'rb')) === false) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Can not open '{$imageFileName}' file for reading."); } $byteOrderIndicator = fread($imageFile, 2); if ($byteOrderIndicator == 'II') { $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE; } else { if ($byteOrderIndicator == 'MM') { $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_BIG; } else { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Not a tiff file or Tiff corrupt. No byte order indication found"); } } $version = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); if ($version != 42) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Not a tiff file or Tiff corrupt. Incorrect version number."); } $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::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) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Could not seek to the image file directory as indexed by the file. Likely cause is TIFF corruption. Offset: " . $ifdOffset); } $numDirEntries = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::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(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); $fieldType = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); $valueCount = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); switch ($fieldType) { case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: $fieldLength = $valueCount; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: $fieldLength = $valueCount; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: $fieldLength = $valueCount * 2; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: $fieldLength = $valueCount * 4; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_RATIONAL: $fieldLength = $valueCount * 8; break; default: $fieldLength = $valueCount; } $offsetBytes = fread($imageFile, 4); if ($fieldLength <= 4) { switch ($fieldType) { case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE, $offsetBytes); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: //Fall through to next case //Fall through to next case case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: //Fall through to next case //Fall through to next case default: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, $offsetBytes); } } else { $refOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::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 Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_WIDTH: $this->_width = $value; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_LENGTH: $this->_height = $value; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_BITS_PER_SAMPLE: if ($valueCount > 1) { $fp = ftell($imageFile); fseek($imageFile, $refOffset, SEEK_SET); $this->_bitsPerSample = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); fseek($imageFile, $fp, SEEK_SET); } else { $this->_bitsPerSample = $value; } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_COMPRESSION: $this->_compression = $value; switch ($value) { case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_UNCOMPRESSED: $this->_filter = 'None'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_CCITT1D: //Fall through to next case //Fall through to next case case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_3_FAX: //Fall through to next case //Fall through to next case case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_4_FAX: $this->_filter = 'CCITTFaxDecode'; // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("CCITTFaxDecode Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_LZW: $this->_filter = 'LZWDecode'; // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("LZWDecode Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_JPEG: $this->_filter = 'DCTDecode'; //Should work, doesnt... // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("JPEG Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE: //fall through to next case //fall through to next case case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE_OBSOLETE_CODE: $this->_filter = 'FlateDecode'; // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("ZIP/Flate Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_PACKBITS: $this->_filter = 'RunLengthDecode'; break; } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_PHOTOMETRIC_INTERPRETATION: $this->_colorCode = $value; $this->_whiteIsZero = false; $this->_blackIsZero = false; switch ($value) { case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO: $this->_whiteIsZero = true; $this->_colorSpace = 'DeviceGray'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO: $this->_blackIsZero = true; $this->_colorSpace = 'DeviceGray'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR: //fall through to next case //fall through to next case case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB: $this->_colorSpace = 'DeviceRGB'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED: $this->_colorSpace = 'Indexed'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CMYK: $this->_colorSpace = 'DeviceCMYK'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB: $this->_colorSpace = 'Lab'; break; default: // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('TIFF: Unknown or Unsupported Color Type: ' . $value); } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_OFFSETS: if ($valueCount > 1) { $format = $this->_endianType == Zend_Pdf_Resource_Image_Tiff::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 Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_BYTE_COUNTS: if ($valueCount > 1) { $format = $this->_endianType == Zend_Pdf_Resource_Image_Tiff::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(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); } if (!isset($this->_imageDataOffset) || !isset($this->_imageDataLength)) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("TIFF: The image processed did not contain image data as expected."); } $imageDataBytes = ''; if (is_array($this->_imageDataOffset)) { if (!is_array($this->_imageDataLength)) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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 === '') { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("TIFF: No data. Image Corruption"); } fclose($imageFile); parent::__construct(); $imageDictionary = $this->_resource->dictionary; if (!isset($this->_width) || !isset($this->_width)) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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 Zend_Pdf_Element_Numeric($this->_width); if ($this->_whiteIsZero === true) { $imageDictionary->Decode = new Zend_Pdf_Element_Array(array(new Zend_Pdf_Element_Numeric(1), new Zend_Pdf_Element_Numeric(0))); } $imageDictionary->Height = new Zend_Pdf_Element_Numeric($this->_height); $imageDictionary->ColorSpace = new Zend_Pdf_Element_Name($this->_colorSpace); $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($this->_bitsPerSample); if (isset($this->_filter) && $this->_filter != 'None') { $imageDictionary->Filter = new Zend_Pdf_Element_Name($this->_filter); } $this->_resource->value = $imageDataBytes; $this->_resource->skipFilters(); }
/** * 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) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('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))) != Zend_Pdf_Resource_Image_Png::PNG_INTERLACING_DISABLED) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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 (!feof($imageFile)) { $chunkLengthBytes = fread($imageFile, 4); if ($chunkLengthBytes === false) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Error ocuured while image file reading.'); } $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 Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_GRAY: $baseColor = ord(substr($trnsData, 1, 1)); $transparencyData = array(new Zend_Pdf_Element_Numeric($baseColor), new Zend_Pdf_Element_Numeric($baseColor)); break; case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_RGB: $red = ord(substr($trnsData, 1, 1)); $green = ord(substr($trnsData, 3, 1)); $blue = ord(substr($trnsData, 5, 1)); $transparencyData = array(new Zend_Pdf_Element_Numeric($red), new Zend_Pdf_Element_Numeric($red), new Zend_Pdf_Element_Numeric($green), new Zend_Pdf_Element_Numeric($green), new Zend_Pdf_Element_Numeric($blue), new Zend_Pdf_Element_Numeric($blue)); break; case Zend_Pdf_Resource_Image_Png::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 Zend_Pdf_Element_Numeric($trnsIdx), new Zend_Pdf_Element_Numeric($trnsIdx)); } break; case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_GRAY_ALPHA: // Fall through to the next case // Fall through to the next case case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_RGB_ALPHA: require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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 default: fseek($imageFile, $chunkLength + 4, SEEK_CUR); //Skip the section break; } } fclose($imageFile); $compressed = true; $imageDataTmp = ''; $smaskData = ''; switch ($color) { case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_RGB: $colorSpace = new Zend_Pdf_Element_Name('DeviceRGB'); break; case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_GRAY: $colorSpace = new Zend_Pdf_Element_Name('DeviceGray'); break; case Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_INDEXED: if (empty($paletteData)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("PNG Corruption: No palette data read for indexed type PNG."); } $colorSpace = new Zend_Pdf_Element_Array(); $colorSpace->items[] = new Zend_Pdf_Element_Name('Indexed'); $colorSpace->items[] = new Zend_Pdf_Element_Name('DeviceRGB'); $colorSpace->items[] = new Zend_Pdf_Element_Numeric(strlen($paletteData) / 3 - 1); $paletteObject = $this->_objectFactory->newObject(new Zend_Pdf_Element_String_Binary($paletteData)); $colorSpace->items[] = $paletteObject; break; case Zend_Pdf_Resource_Image_Png::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) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Alpha PNGs with bit depth > 8 are not yet supported"); } $colorSpace = new Zend_Pdf_Element_Name('DeviceGray'); require_once 'Zend/Pdf/ElementFactory.php'; $decodingObjFactory = Zend_Pdf_ElementFactory::createFactory(1); $decodingStream = $decodingObjFactory->newStreamObject($imageData); $decodingStream->dictionary->Filter = new Zend_Pdf_Element_Name('FlateDecode'); $decodingStream->dictionary->DecodeParms = new Zend_Pdf_Element_Dictionary(); $decodingStream->dictionary->DecodeParms->Predictor = new Zend_Pdf_Element_Numeric(15); $decodingStream->dictionary->DecodeParms->Columns = new Zend_Pdf_Element_Numeric($width); $decodingStream->dictionary->DecodeParms->Colors = new Zend_Pdf_Element_Numeric(2); //GreyAlpha $decodingStream->dictionary->DecodeParms->BitsPerComponent = new Zend_Pdf_Element_Numeric($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 Zend_Pdf_Resource_Image_Png::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) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Alpha PNGs with bit depth > 8 are not yet supported"); } $colorSpace = new Zend_Pdf_Element_Name('DeviceRGB'); require_once 'Zend/Pdf/ElementFactory.php'; $decodingObjFactory = Zend_Pdf_ElementFactory::createFactory(1); $decodingStream = $decodingObjFactory->newStreamObject($imageData); $decodingStream->dictionary->Filter = new Zend_Pdf_Element_Name('FlateDecode'); $decodingStream->dictionary->DecodeParms = new Zend_Pdf_Element_Dictionary(); $decodingStream->dictionary->DecodeParms->Predictor = new Zend_Pdf_Element_Numeric(15); $decodingStream->dictionary->DecodeParms->Columns = new Zend_Pdf_Element_Numeric($width); $decodingStream->dictionary->DecodeParms->Colors = new Zend_Pdf_Element_Numeric(4); //RGBA $decodingStream->dictionary->DecodeParms->BitsPerComponent = new Zend_Pdf_Element_Numeric($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: require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("PNG Corruption: Invalid color space."); } if (empty($imageData)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("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 Zend_Pdf_Element_Name('XObject'); $smaskStream->dictionary->Subtype = new Zend_Pdf_Element_Name('Image'); $smaskStream->dictionary->Width = new Zend_Pdf_Element_Numeric($width); $smaskStream->dictionary->Height = new Zend_Pdf_Element_Numeric($height); $smaskStream->dictionary->ColorSpace = new Zend_Pdf_Element_Name('DeviceGray'); $smaskStream->dictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($bits); $imageDictionary->SMask = $smaskStream; // Encode stream with FlateDecode filter $smaskStreamDecodeParms = array(); $smaskStreamDecodeParms['Predictor'] = new Zend_Pdf_Element_Numeric(15); $smaskStreamDecodeParms['Columns'] = new Zend_Pdf_Element_Numeric($width); $smaskStreamDecodeParms['Colors'] = new Zend_Pdf_Element_Numeric(1); $smaskStreamDecodeParms['BitsPerComponent'] = new Zend_Pdf_Element_Numeric(8); $smaskStream->dictionary->DecodeParms = new Zend_Pdf_Element_Dictionary($smaskStreamDecodeParms); $smaskStream->dictionary->Filter = new Zend_Pdf_Element_Name('FlateDecode'); } if (!empty($transparencyData)) { //This is experimental and not properly tested. $imageDictionary->Mask = new Zend_Pdf_Element_Array($transparencyData); } $imageDictionary->Width = new Zend_Pdf_Element_Numeric($width); $imageDictionary->Height = new Zend_Pdf_Element_Numeric($height); $imageDictionary->ColorSpace = $colorSpace; $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($bits); $imageDictionary->Filter = new Zend_Pdf_Element_Name('FlateDecode'); $decodeParms = array(); $decodeParms['Predictor'] = new Zend_Pdf_Element_Numeric(15); // Optimal prediction $decodeParms['Columns'] = new Zend_Pdf_Element_Numeric($width); $decodeParms['Colors'] = new Zend_Pdf_Element_Numeric($color == Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_RGB || $color == Zend_Pdf_Resource_Image_Png::PNG_CHANNEL_RGB_ALPHA ? 3 : 1); $decodeParms['BitsPerComponent'] = new Zend_Pdf_Element_Numeric($bits); $imageDictionary->DecodeParms = new Zend_Pdf_Element_Dictionary($decodeParms); //Include only the image IDAT section data. $this->_resource->value = $imageData; //Skip double compression if ($compressed) { $this->_resource->skipFilters(); } }
/** * Draw an image at the specified position on the page. * * @param Zend_Pdf_Image $image * @param float $x1 * @param float $y1 * @param float $x2 * @param float $y2 */ public function drawImage(Zend_Pdf_Resource_Image $image, $x1, $y1, $x2, $y2) { $this->_addProcSet('PDF'); $imageName = $this->_attachResource('XObject', $image->getResource()); $imageNameObj = new Zend_Pdf_Element_Name($imageName); $x1Obj = new Zend_Pdf_Element_Numeric($x1); $y1Obj = new Zend_Pdf_Element_Numeric($y1); $widthObj = new Zend_Pdf_Element_Numeric($x2 - $x1); $heightObj = new Zend_Pdf_Element_Numeric($y2 - $y1); $this->_contents .= "q\n" . '1 0 0 1 ' . $x1Obj->toString() . ' ' . $y1Obj->toString() . " cm\n" . $widthObj->toString() . ' 0 0 ' . $heightObj->toString() . " 0 0 cm\n" . $imageNameObj->toString() . " Do\n" . "Q\n"; }
public function __construct($imageFileName) { if (($imageFile = @fopen($imageFileName, 'rb')) === false) { throw new Zend_Pdf_Exception("Can not open '{$imageFileName}' file for reading."); } $byteOrderIndicator = fread($imageFile, 2); if ($byteOrderIndicator == 'II') { $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_LITTLE; } else { if ($byteOrderIndicator == 'MM') { $this->_endianType = Zend_Pdf_Resource_Image_Tiff::TIFF_ENDIAN_BIG; } else { throw new Zend_Pdf_Exception("Not a tiff file or Tiff corrupt. No byte order indication found"); } } $version = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); if ($version != 42) { throw new Zend_Pdf_Exception("Not a tiff file or Tiff corrupt. Incorrect version number."); } $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); $fileStats = fstat($imageFile); $this->_fileSize = $fileStats['size']; while ($ifdOffset > 0) { if (fseek($imageFile, $ifdOffset, SEEK_SET) == -1 || $ifdOffset + 2 >= $this->_fileSize) { throw new Zend_Pdf_Exception("Could not seek to the image file directory as indexed by the file. Likely cause is TIFF corruption. Offset: " . $ifdOffset); } $numDirEntries = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); for ($dirEntryIdx = 1; $dirEntryIdx <= $numDirEntries; $dirEntryIdx++) { $tag = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); $fieldType = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); $valueCount = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); switch ($fieldType) { case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: $fieldLength = $valueCount; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: $fieldLength = $valueCount; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: $fieldLength = $valueCount * 2; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: $fieldLength = $valueCount * 4; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_RATIONAL: $fieldLength = $valueCount * 8; break; default: $fieldLength = $valueCount; } $offsetBytes = fread($imageFile, 4); if ($fieldLength <= 4) { switch ($fieldType) { case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_BYTE: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_BYTE, $offsetBytes); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_ASCII: case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_LONG: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_FIELD_TYPE_SHORT: default: $value = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, $offsetBytes); } } else { $refOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, $offsetBytes); } switch ($tag) { case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_WIDTH: $this->_width = $value; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_IMAGE_LENGTH: $this->_height = $value; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_BITS_PER_SAMPLE: if ($valueCount > 1) { $fp = ftell($imageFile); fseek($imageFile, $refOffset, SEEK_SET); $this->_bitsPerSample = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_SHORT, fread($imageFile, 2)); fseek($imageFile, $fp, SEEK_SET); } else { $this->_bitsPerSample = $value; } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_COMPRESSION: $this->_compression = $value; switch ($value) { case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_UNCOMPRESSED: $this->_filter = 'None'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_CCITT1D: case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_3_FAX: case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_GROUP_4_FAX: $this->_filter = 'CCITTFaxDecode'; throw new Zend_Pdf_Exception("CCITTFaxDecode Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_LZW: $this->_filter = 'LZWDecode'; throw new Zend_Pdf_Exception("LZWDecode Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_JPEG: $this->_filter = 'DCTDecode'; throw new Zend_Pdf_Exception("JPEG Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE: case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_FLATE_OBSOLETE_CODE: $this->_filter = 'FlateDecode'; throw new Zend_Pdf_Exception("ZIP/Flate Compression Mode Not Currently Supported"); break; case Zend_Pdf_Resource_Image_Tiff::TIFF_COMPRESSION_PACKBITS: $this->_filter = 'RunLengthDecode'; break; } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_PHOTOMETRIC_INTERPRETATION: $this->_colorCode = $value; $this->_whiteIsZero = false; $this->_blackIsZero = false; switch ($value) { case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO: $this->_whiteIsZero = true; $this->_colorSpace = 'DeviceGray'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO: $this->_blackIsZero = true; $this->_colorSpace = 'DeviceGray'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_YCBCR: case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB: $this->_colorSpace = 'DeviceRGB'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_RGB_INDEXED: $this->_colorSpace = 'Indexed'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CMYK: $this->_colorSpace = 'DeviceCMYK'; break; case Zend_Pdf_Resource_Image_Tiff::TIFF_PHOTOMETRIC_INTERPRETATION_CIELAB: $this->_colorSpace = 'Lab'; break; default: throw new Zend_Pdf_Exception('TIFF: Unknown or Unsupported Color Type: ' . $value); } break; case Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_OFFSETS: if ($valueCount > 1) { $format = $this->_endianType == Zend_Pdf_Resource_Image_Tiff::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 Zend_Pdf_Resource_Image_Tiff::TIFF_TAG_STRIP_BYTE_COUNTS: if ($valueCount > 1) { $format = $this->_endianType == Zend_Pdf_Resource_Image_Tiff::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: } } $ifdOffset = $this->unpackBytes(Zend_Pdf_Resource_Image_Tiff::UNPACK_TYPE_LONG, fread($imageFile, 4)); } if (!isset($this->_imageDataOffset) || !isset($this->_imageDataLength)) { throw new Zend_Pdf_Exception("TIFF: The image processed did not contain image data as expected."); } $imageDataBytes = ''; if (is_array($this->_imageDataOffset)) { if (!is_array($this->_imageDataLength)) { throw new Zend_Pdf_Exception("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 Zend_Pdf_Exception("TIFF: No data. Image Corruption"); } fclose($imageFile); parent::__construct(); $imageDictionary = $this->_resource->dictionary; if (!isset($this->_width) || !isset($this->_width)) { throw new Zend_Pdf_Exception("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 Zend_Pdf_Element_Numeric($this->_width); if ($this->_whiteIsZero === true) { $imageDictionary->Decode = new Zend_Pdf_Element_Array(array(new Zend_Pdf_Element_Numeric(1), new Zend_Pdf_Element_Numeric(0))); } $imageDictionary->Height = new Zend_Pdf_Element_Numeric($this->_height); $imageDictionary->ColorSpace = new Zend_Pdf_Element_Name($this->_colorSpace); $imageDictionary->BitsPerComponent = new Zend_Pdf_Element_Numeric($this->_bitsPerSample); if (isset($this->_filter) && $this->_filter != 'None') { $imageDictionary->Filter = new Zend_Pdf_Element_Name($this->_filter); } $this->_resource->value = $imageDataBytes; $this->_resource->skipFilters(); }