diff -r 5b37998e522e -r 162c1de6545a web/lib/Zend/Pdf/Page.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/Zend/Pdf/Page.php Fri Mar 11 15:05:35 2011 +0100 @@ -0,0 +1,773 @@ +getType()) { + case Zend_Pdf_Element::TYPE_DICTIONARY: + $this->_dictionary = $param1; + $this->_objFactory = $param2; + $this->_attached = true; + $this->_safeGS = false; + return; + break; + + case Zend_Pdf_Element::TYPE_NULL: + $this->_objFactory = $param2; + $pageWidth = $pageHeight = 0; + break; + + default: + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Unrecognized object type.'); + break; + + } + } else if ($param1 instanceof Zend_Pdf_Page && $param2 === null && $param3 === null) { + // Duplicate existing page. + // Let already existing content and resources to be shared between pages + // We don't give existing content modification functionality, so we don't need "deep copy" + $this->_objFactory = $param1->_objFactory; + $this->_attached = &$param1->_attached; + $this->_safeGS = false; + + $this->_dictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary()); + + foreach ($param1->_dictionary->getKeys() as $key) { + if ($key == 'Contents') { + // Clone Contents property + + $this->_dictionary->Contents = new Zend_Pdf_Element_Array(); + + if ($param1->_dictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) { + // Prepare array of content streams and add existing stream + $this->_dictionary->Contents->items[] = $param1->_dictionary->Contents; + } else { + // Clone array of the content streams + foreach ($param1->_dictionary->Contents->items as $srcContentStream) { + $this->_dictionary->Contents->items[] = $srcContentStream; + } + } + } else { + $this->_dictionary->$key = $param1->_dictionary->$key; + } + } + + return; + } else if (is_string($param1) && + ($param2 === null || $param2 instanceof Zend_Pdf_ElementFactory_Interface) && + $param3 === null) { + if ($param2 !== null) { + $this->_objFactory = $param2; + } else { + require_once 'Zend/Pdf/ElementFactory.php'; + $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1); + } + $this->_attached = false; + $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */ + + switch (strtolower($param1)) { + case 'a4': + $param1 = Zend_Pdf_Page::SIZE_A4; + break; + case 'a4-landscape': + $param1 = Zend_Pdf_Page::SIZE_A4_LANDSCAPE; + break; + case 'letter': + $param1 = Zend_Pdf_Page::SIZE_LETTER; + break; + case 'letter-landscape': + $param1 = Zend_Pdf_Page::SIZE_LETTER_LANDSCAPE; + break; + default: + // should be in "x:y" or "x:y:" form + } + + $pageDim = explode(':', $param1); + if(count($pageDim) == 2 || count($pageDim) == 3) { + $pageWidth = $pageDim[0]; + $pageHeight = $pageDim[1]; + } else { + /** + * @todo support of user defined pagesize notations, like: + * "210x297mm", "595x842", "8.5x11in", "612x792" + */ + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Wrong pagesize notation.'); + } + /** + * @todo support of pagesize recalculation to "default user space units" + */ + + } else if (is_numeric($param1) && is_numeric($param2) && + ($param3 === null || $param3 instanceof Zend_Pdf_ElementFactory_Interface)) { + if ($param3 !== null) { + $this->_objFactory = $param3; + } else { + require_once 'Zend/Pdf/ElementFactory.php'; + $this->_objFactory = Zend_Pdf_ElementFactory::createFactory(1); + } + + $this->_attached = false; + $this->_safeGS = true; /** New page created. That's users App responsibility to track GS changes */ + $pageWidth = $param1; + $pageHeight = $param2; + + } else { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Unrecognized method signature, wrong number of arguments or wrong argument types.'); + } + + $this->_dictionary = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary()); + $this->_dictionary->Type = new Zend_Pdf_Element_Name('Page'); + require_once 'Zend/Pdf.php'; + $this->_dictionary->LastModified = new Zend_Pdf_Element_String(Zend_Pdf::pdfDate()); + $this->_dictionary->Resources = new Zend_Pdf_Element_Dictionary(); + $this->_dictionary->MediaBox = new Zend_Pdf_Element_Array(); + $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0); + $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric(0); + $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageWidth); + $this->_dictionary->MediaBox->items[] = new Zend_Pdf_Element_Numeric($pageHeight); + $this->_dictionary->Contents = new Zend_Pdf_Element_Array(); + } + + + /** + * Attach resource to the canvas + * + * Method returns a name of the resource which can be used + * as a resource reference within drawing instructions stream + * Allowed types: 'ExtGState', 'ColorSpace', 'Pattern', 'Shading', + * 'XObject', 'Font', 'Properties' + * + * @param string $type + * @param Zend_Pdf_Resource $resource + * @return string + */ + protected function _attachResource($type, Zend_Pdf_Resource $resource) + { + // Check that Resources dictionary contains appropriate resource set + if ($this->_dictionary->Resources->$type === null) { + $this->_dictionary->Resources->touch(); + $this->_dictionary->Resources->$type = new Zend_Pdf_Element_Dictionary(); + } else { + $this->_dictionary->Resources->$type->touch(); + } + + // Check, that resource is already attached to resource set. + $resObject = $resource->getResource(); + foreach ($this->_dictionary->Resources->$type->getKeys() as $ResID) { + if ($this->_dictionary->Resources->$type->$ResID === $resObject) { + return $ResID; + } + } + + $idCounter = 1; + do { + $newResName = $type[0] . $idCounter++; + } while ($this->_dictionary->Resources->$type->$newResName !== null); + + $this->_dictionary->Resources->$type->$newResName = $resObject; + $this->_objFactory->attach($resource->getFactory()); + + return $newResName; + } + + /** + * Add procedureSet to the Page description + * + * @param string $procSetName + */ + protected function _addProcSet($procSetName) + { + // Check that Resources dictionary contains ProcSet entry + if ($this->_dictionary->Resources->ProcSet === null) { + $this->_dictionary->Resources->touch(); + $this->_dictionary->Resources->ProcSet = new Zend_Pdf_Element_Array(); + } else { + $this->_dictionary->Resources->ProcSet->touch(); + } + + foreach ($this->_dictionary->Resources->ProcSet->items as $procSetEntry) { + if ($procSetEntry->value == $procSetName) { + // Procset is already included into a ProcSet array + return; + } + } + + $this->_dictionary->Resources->ProcSet->items[] = new Zend_Pdf_Element_Name($procSetName); + } + + /** + * Returns dictionaries of used resources. + * + * Used for canvas implementations interoperability + * + * Structure of the returned array: + * array( + * => array( + * => , + * => , + * => , + * ... + * ), + * => array( + * => , + * => , + * => , + * ... + * ), + * ... + * 'ProcSet' => array() + * ) + * + * where ProcSet array is a list of used procedure sets names (strings). + * Allowed procedure set names: 'PDF', 'Text', 'ImageB', 'ImageC', 'ImageI' + * + * @internal + * @return array + */ + public function getResources() + { + $resources = array(); + $resDictionary = $this->_dictionary->Resources; + + foreach ($resDictionary->getKeys() as $resType) { + $resources[$resType] = array(); + + if ($resType == 'ProcSet') { + foreach ($resDictionary->ProcSet->items as $procSetEntry) { + $resources[$resType][] = $procSetEntry->value; + } + } else { + $resMap = $resDictionary->$resType; + + foreach ($resMap->getKeys() as $resId) { + $resources[$resType][$resId] =new Zend_Pdf_Resource_Unified($resMap->$resId); + } + } + } + + return $resources; + } + + /** + * Get drawing instructions stream + * + * It has to be returned as a PDF stream object to make it reusable. + * + * @internal + * @returns Zend_Pdf_Resource_ContentStream + */ + public function getContents() + { + /** @todo implementation */ + } + + /** + * Return the height of this page in points. + * + * @return float + */ + public function getHeight() + { + return $this->_dictionary->MediaBox->items[3]->value - + $this->_dictionary->MediaBox->items[1]->value; + } + + /** + * Return the width of this page in points. + * + * @return float + */ + public function getWidth() + { + return $this->_dictionary->MediaBox->items[2]->value - + $this->_dictionary->MediaBox->items[0]->value; + } + + /** + * Clone page, extract it and dependent objects from the current document, + * so it can be used within other docs. + */ + public function __clone() + { + $factory = Zend_Pdf_ElementFactory::createFactory(1); + $processed = array(); + + // Clone dictionary object. + // Do it explicitly to prevent sharing page attributes between different + // results of clonePage() operation (other resources are still shared) + $dictionary = new Zend_Pdf_Element_Dictionary(); + foreach ($this->_dictionary->getKeys() as $key) { + $dictionary->$key = $this->_dictionary->$key->makeClone($factory->getFactory(), + $processed, + Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES); + } + + $this->_dictionary = $factory->newObject($dictionary); + $this->_objFactory = $factory; + $this->_attached = false; + $this->_style = null; + $this->_font = null; + } + + /** + * Clone page, extract it and dependent objects from the current document, + * so it can be used within other docs. + * + * @internal + * @param Zend_Pdf_ElementFactory_Interface $factory + * @param array $processed + * @return Zend_Pdf_Page + */ + public function clonePage($factory, &$processed) + { + // Clone dictionary object. + // Do it explicitly to prevent sharing page attributes between different + // results of clonePage() operation (other resources are still shared) + $dictionary = new Zend_Pdf_Element_Dictionary(); + foreach ($this->_dictionary->getKeys() as $key) { + $dictionary->$key = $this->_dictionary->$key->makeClone($factory->getFactory(), + $processed, + Zend_Pdf_Element::CLONE_MODE_SKIP_PAGES); + } + + $clonedPage = new Zend_Pdf_Page($factory->newObject($dictionary), $factory); + $clonedPage->_attached = false; + + return $clonedPage; + } + + /** + * Retrive PDF file reference to the page + * + * @internal + * @return Zend_Pdf_Element_Dictionary + */ + public function getPageDictionary() + { + return $this->_dictionary; + } + + /** + * Dump current drawing instructions into the content stream. + * + * @todo Don't forget to close all current graphics operations (like path drawing) + * + * @throws Zend_Pdf_Exception + */ + public function flush() + { + if ($this->_saveCount != 0) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Saved graphics state is not restored'); + } + + if ($this->_contents == '') { + return; + } + + if ($this->_dictionary->Contents->getType() != Zend_Pdf_Element::TYPE_ARRAY) { + /** + * It's a stream object. + * Prepare Contents page attribute for update. + */ + $this->_dictionary->touch(); + + $currentPageContents = $this->_dictionary->Contents; + $this->_dictionary->Contents = new Zend_Pdf_Element_Array(); + $this->_dictionary->Contents->items[] = $currentPageContents; + } else { + $this->_dictionary->Contents->touch(); + } + + if ((!$this->_safeGS) && (count($this->_dictionary->Contents->items) != 0)) { + /** + * Page already has some content which is not treated as safe. + * + * Add save/restore GS operators + */ + $this->_addProcSet('PDF'); + + $newContentsArray = new Zend_Pdf_Element_Array(); + $newContentsArray->items[] = $this->_objFactory->newStreamObject(" q\n"); + foreach ($this->_dictionary->Contents->items as $contentStream) { + $newContentsArray->items[] = $contentStream; + } + $newContentsArray->items[] = $this->_objFactory->newStreamObject(" Q\n"); + + $this->_dictionary->touch(); + $this->_dictionary->Contents = $newContentsArray; + + $this->_safeGS = true; + } + + $this->_dictionary->Contents->items[] = + $this->_objFactory->newStreamObject($this->_contents); + + $this->_contents = ''; + } + + /** + * Prepare page to be rendered into PDF. + * + * @todo Don't forget to close all current graphics operations (like path drawing) + * + * @param Zend_Pdf_ElementFactory_Interface $objFactory + * @throws Zend_Pdf_Exception + */ + public function render(Zend_Pdf_ElementFactory_Interface $objFactory) + { + $this->flush(); + + if ($objFactory === $this->_objFactory) { + // Page is already attached to the document. + return; + } + + if ($this->_attached) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Page is attached to other documen. Use clone $page to get it context free.'); + } else { + $objFactory->attach($this->_objFactory); + } + } + + /** + * Extract resources attached to the page + * + * This method is not intended to be used in userland, but helps to optimize some document wide operations + * + * returns array of Zend_Pdf_Element_Dictionary objects + * + * @internal + * @return array + */ + public function extractResources() + { + return $this->_dictionary->Resources; + } + + /** + * Extract fonts attached to the page + * + * returns array of Zend_Pdf_Resource_Font_Extracted objects + * + * @return array + * @throws Zend_Pdf_Exception + */ + public function extractFonts() + { + if ($this->_dictionary->Resources->Font === null) { + // Page doesn't have any font attached + // Return empty array + return array(); + } + + $fontResources = $this->_dictionary->Resources->Font; + + $fontResourcesUnique = array(); + foreach ($fontResources->getKeys() as $fontResourceName) { + $fontDictionary = $fontResources->$fontResourceName; + + if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference || + $fontDictionary instanceof Zend_Pdf_Element_Object) ) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.'); + } + + $fontResourcesUnique[spl_object_hash($fontDictionary->getObject())] = $fontDictionary; + } + + $fonts = array(); + require_once 'Zend/Pdf/Exception.php'; + foreach ($fontResourcesUnique as $resourceId => $fontDictionary) { + try { + require_once 'Zend/Pdf/Resource/Font/Extracted.php'; + // Try to extract font + $extractedFont = new Zend_Pdf_Resource_Font_Extracted($fontDictionary); + + $fonts[$resourceId] = $extractedFont; + } catch (Zend_Pdf_Exception $e) { + if ($e->getMessage() != 'Unsupported font type.') { + throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e); + } + } + } + + return $fonts; + } + + /** + * Extract font attached to the page by specific font name + * + * $fontName should be specified in UTF-8 encoding + * + * @return Zend_Pdf_Resource_Font_Extracted|null + * @throws Zend_Pdf_Exception + */ + public function extractFont($fontName) + { + if ($this->_dictionary->Resources->Font === null) { + // Page doesn't have any font attached + return null; + } + + $fontResources = $this->_dictionary->Resources->Font; + + $fontResourcesUnique = array(); + + require_once 'Zend/Pdf/Exception.php'; + foreach ($fontResources->getKeys() as $fontResourceName) { + $fontDictionary = $fontResources->$fontResourceName; + + if (! ($fontDictionary instanceof Zend_Pdf_Element_Reference || + $fontDictionary instanceof Zend_Pdf_Element_Object) ) { + require_once 'Zend/Pdf/Exception.php'; + throw new Zend_Pdf_Exception('Font dictionary has to be an indirect object or object reference.'); + } + + $resourceId = spl_object_hash($fontDictionary->getObject()); + if (isset($fontResourcesUnique[$resourceId])) { + continue; + } else { + // Mark resource as processed + $fontResourcesUnique[$resourceId] = 1; + } + + if ($fontDictionary->BaseFont->value != $fontName) { + continue; + } + + try { + // Try to extract font + require_once 'Zend/Pdf/Resource/Font/Extracted.php'; + return new Zend_Pdf_Resource_Font_Extracted($fontDictionary); + } catch (Zend_Pdf_Exception $e) { + if ($e->getMessage() != 'Unsupported font type.') { + throw new Zend_Pdf_Exception($e->getMessage(), $e->getCode(), $e); + } + + // Continue searhing font with specified name + } + } + + return null; + } + + /** + * + * @param Zend_Pdf_Annotation $annotation + * @return Zend_Pdf_Page + */ + public function attachAnnotation(Zend_Pdf_Annotation $annotation) + { + $annotationDictionary = $annotation->getResource(); + if (!$annotationDictionary instanceof Zend_Pdf_Element_Object && + !$annotationDictionary instanceof Zend_Pdf_Element_Reference) { + $annotationDictionary = $this->_objFactory->newObject($annotationDictionary); + } + + if ($this->_dictionary->Annots === null) { + $this->_dictionary->touch(); + $this->_dictionary->Annots = new Zend_Pdf_Element_Array(); + } else { + $this->_dictionary->Annots->touch(); + } + + $this->_dictionary->Annots->items[] = $annotationDictionary; + + $annotationDictionary->touch(); + $annotationDictionary->P = $this->_dictionary; + + return $this; + } +} +