Example #1
0
 /**
  * Object constructor.
  *
  * If resource is not a \Zend\Pdf\InternalType\AbstractTypeObject object,
  * then stream object with specified value is generated.
  *
  * @param \Zend\Pdf\InternalType\AbstractTypeObject|string $resource
  */
 public function __construct($resource)
 {
     $this->_objectFactory = ObjectFactory\ElementFactory::createFactory(1);
     if ($resource instanceof InternalType\AbstractTypeObject) {
         $this->_resource = $this->_objectFactory->newObject($resource);
     } else {
         $this->_resource = $this->_objectFactory->newStreamObject($resource);
     }
 }
Example #2
0
 /**
  * Creates or loads PDF document.
  *
  * If $source is null, then it creates a new document.
  *
  * If $source is a string and $load is false, then it loads document
  * from a binary string.
  *
  * If $source is a string and $load is true, then it loads document
  * from a file.
  * $revision used to roll back document to specified version
  * (0 - currtent version, 1 - previous version, 2 - ...)
  *
  * @param string  $source - PDF file to load
  * @param integer $revision
  * @throws \Zend\Pdf\Exception
  * @return \Zend\Pdf\PdfDocument
  */
 public function __construct($source = null, $revision = null, $load = false)
 {
     $this->_objFactory = ObjectFactory\ElementFactory::createFactory(1);
     if ($source !== null) {
         $this->_parser = new PdfParser\StructureParser($source, $this->_objFactory, $load);
         $this->_pdfHeaderVersion = $this->_parser->getPDFVersion();
         $this->_trailer = $this->_parser->getTrailer();
         if ($this->_trailer->Encrypt !== null) {
             throw new Exception('Encrypted document modification is not supported');
         }
         if ($revision !== null) {
             $this->rollback($revision);
         } else {
             $this->_loadPages($this->_trailer->Root->Pages);
         }
         $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
         $this->_loadOutlines($this->_trailer->Root);
         if ($this->_trailer->Info !== null) {
             $this->properties = $this->_trailer->Info->toPhp();
             if (isset($this->properties['Trapped'])) {
                 switch ($this->properties['Trapped']) {
                     case 'True':
                         $this->properties['Trapped'] = true;
                         break;
                     case 'False':
                         $this->properties['Trapped'] = false;
                         break;
                     case 'Unknown':
                         $this->properties['Trapped'] = null;
                         break;
                     default:
                         // Wrong property value
                         // Do nothing
                         break;
                 }
             }
             $this->_originalProperties = $this->properties;
         }
     } else {
         $this->_pdfHeaderVersion = self::PDF_VERSION;
         $trailerDictionary = new InternalType\DictionaryObject();
         /**
          * Document id
          */
         $docId = md5(uniqid(rand(), true));
         // 32 byte (128 bit) identifier
         $docIdLow = substr($docId, 0, 16);
         // first 16 bytes
         $docIdHigh = substr($docId, 16, 16);
         // second 16 bytes
         $trailerDictionary->ID = new InternalType\ArrayObject();
         $trailerDictionary->ID->items[] = new InternalType\BinaryStringObject($docIdLow);
         $trailerDictionary->ID->items[] = new InternalType\BinaryStringObject($docIdHigh);
         $trailerDictionary->Size = new InternalType\NumericObject(0);
         $this->_trailer = new Trailer\Generated($trailerDictionary);
         /**
          * Document catalog indirect object.
          */
         $docCatalog = $this->_objFactory->newObject(new InternalType\DictionaryObject());
         $docCatalog->Type = new InternalType\NameObject('Catalog');
         $docCatalog->Version = new InternalType\NameObject(self::PDF_VERSION);
         $this->_trailer->Root = $docCatalog;
         /**
          * Pages container
          */
         $docPages = $this->_objFactory->newObject(new InternalType\DictionaryObject());
         $docPages->Type = new InternalType\NameObject('Pages');
         $docPages->Kids = new InternalType\ArrayObject();
         $docPages->Count = new InternalType\NumericObject(0);
         $docCatalog->Pages = $docPages;
     }
 }
Example #3
0
File: Png.php Project: narixx/zf2
 /**
  * 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 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)) {
         throw new 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))) != self::PNG_INTERLACING_DISABLED) {
         throw new 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 (($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
                     case self::PNG_CHANNEL_RGB_ALPHA:
                         throw new 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 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 Pdf\Exception("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 Pdf\Exception("Alpha PNGs with bit depth > 8 are not yet supported");
             }
             $colorSpace = new InternalType\NameObject('DeviceGray');
             $decodingObjFactory = ObjectFactory\ElementFactory::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 Pdf\Exception("Alpha PNGs with bit depth > 8 are not yet supported");
             }
             $colorSpace = new InternalType\NameObject('DeviceRGB');
             $decodingObjFactory = ObjectFactory\ElementFactory::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 Pdf\Exception("PNG Corruption: Invalid color space.");
     }
     if (empty($imageData)) {
         throw new 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 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();
     }
 }