/** * 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::createFactory(1); if ($resource instanceof InternalType\AbstractTypeObject) { $this->_resource = $this->_objectFactory->newObject($resource); } else { $this->_resource = $this->_objectFactory->newStreamObject($resource); } }
/** * * @param \Zend\Pdf\Annotation\AbstractAnnotation $annotation * @return \Zend\Pdf\Page */ public function attachAnnotation(Annotation\AbstractAnnotation $annotation) { $annotationDictionary = $annotation->getResource(); if (!$annotationDictionary instanceof InternalType\IndirectObject && !$annotationDictionary instanceof InternalType\IndirectObjectReference) { $annotationDictionary = $this->_objFactory->newObject($annotationDictionary); } if ($this->_pageDictionary->Annots === null) { $this->_pageDictionary->touch(); $this->_pageDictionary->Annots = new InternalType\ArrayObject(); } else { $this->_pageDictionary->Annots->touch(); } $this->_pageDictionary->Annots->items[] = $annotationDictionary; $annotationDictionary->touch(); $annotationDictionary->P = $this->_pageDictionary; return $this; }
/** * Detach PDF object from the factory (if applicable), clone it and attach to new factory. * * @param \Zend\Pdf\ObjectFactory $factory The factory to attach * @param array &$processed List of already processed indirect objects, used to avoid objects duplication * @param integer $mode Cloning mode (defines filter for objects cloning) * @returns \Zend\Pdf\InternalType\AbstractTypeObject */ public function makeClone(ObjectFactory $factory, array &$processed, $mode) { $id = spl_object_hash($this); if (isset($processed[$id])) { // Do nothing if object is already processed // return it return $processed[$id]; } // Create obect with null value and register it in $processed container $processed[$id] = $clonedObject = $factory->newObject(new NullObject()); // Pecursively process actual data $clonedObject->_value = $this->_value->makeClone($factory, $processed, $mode); if ($clonedObject->_value instanceof NullObject) { // Do not store null objects within $processed container since it may be filtered // by $mode parameter but used in some future pass unset($processed[$id]); // Return direct null object return $clonedObject->_value; } return $clonedObject; }
/** * Dump Outline and its child outlines into PDF structures * * Returns dictionary indirect object or reference * * @internal * @param \Zend\Pdf\ObjectFactory $factory object factory for newly created indirect objects * @param boolean $updateNavigation Update navigation flag * @param \Zend\Pdf\InternalType\AbstractTypeObject $parent Parent outline dictionary reference * @param \Zend\Pdf\InternalType\AbstractTypeObject $prev Previous outline dictionary reference * @param SplObjectStorage $processedOutlines List of already processed outlines * @return \Zend\Pdf\InternalType\AbstractTypeObject * @throws \Zend\Pdf\Exception */ public function dumpOutline(ObjectFactory $factory, $updateNavigation, InternalType\AbstractTypeObject $parent, InternalType\AbstractTypeObject $prev = null, \SplObjectStorage $processedOutlines = null) { if ($processedOutlines === null) { $processedOutlines = new \SplObjectStorage(); } $processedOutlines->attach($this); $outlineDictionary = $factory->newObject(new InternalType\DictionaryObject()); $outlineDictionary->Title = new InternalType\StringObject($this->getTitle()); $target = $this->getTarget(); if ($target === null) { // Do nothing } else { if ($target instanceof Pdf\Destination\AbstractDestination) { $outlineDictionary->Dest = $target->getResource(); } else { if ($target instanceof Action\AbstractAction) { $outlineDictionary->A = $target->getResource(); } else { throw new Exception\CorruptedPdfException('Outline target has to be \\Zend\\Pdf\\Destination, \\Zend\\Pdf\\Action object or null'); } } } $color = $this->getColor(); if ($color !== null) { $components = $color->getComponents(); $colorComponentElements = array(new InternalType\NumericObject($components[0]), new InternalType\NumericObject($components[1]), new InternalType\NumericObject($components[2])); $outlineDictionary->C = new InternalType\ArrayObject($colorComponentElements); } if ($this->isItalic() || $this->isBold()) { $outlineDictionary->F = new InternalType\NumericObject(($this->isItalic() ? 1 : 0) | ($this->isBold() ? 2 : 0)); // Bit 2 - Bold } $outlineDictionary->Parent = $parent; $outlineDictionary->Prev = $prev; $lastChild = null; foreach ($this->childOutlines as $childOutline) { if ($processedOutlines->contains($childOutline)) { throw new Exception\CorruptedPdfException('Outlines cyclyc reference is detected.'); } if ($lastChild === null) { $lastChild = $childOutline->dumpOutline($factory, true, $outlineDictionary, null, $processedOutlines); $outlineDictionary->First = $lastChild; } else { $childOutlineDictionary = $childOutline->dumpOutline($factory, true, $outlineDictionary, $lastChild, $processedOutlines); $lastChild->Next = $childOutlineDictionary; $lastChild = $childOutlineDictionary; } } $outlineDictionary->Last = $lastChild; if (count($this->childOutlines) != 0) { $outlineDictionary->Count = new InternalType\NumericObject(($this->isOpen() ? 1 : -1) * count($this->childOutlines)); } return $outlineDictionary; }
/** * Render the completed PDF to a string. * If $newSegmentOnly is true, then only appended part of PDF is returned. * * @param boolean $newSegmentOnly * @param resource $outputStream * @return string * @throws \Zend\Pdf\Exception */ public function render($newSegmentOnly = false, $outputStream = null) { // Save document properties if necessary if ($this->properties != $this->_originalProperties) { $docInfo = $this->_objFactory->newObject(new InternalType\DictionaryObject()); foreach ($this->properties as $key => $value) { switch ($key) { case 'Trapped': switch ($value) { case true: $docInfo->{$key} = new InternalType\NameObject('True'); break; case false: $docInfo->{$key} = new InternalType\NameObject('False'); break; case null: $docInfo->{$key} = new InternalType\NameObject('Unknown'); break; default: throw new Exception\LogicException('Wrong Trapped document property vale: \'' . $value . '\'. Only true, false and null values are allowed.'); break; } case 'CreationDate': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'ModDate': $docInfo->{$key} = new InternalType\StringObject((string) $value); break; case 'Title': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'Author': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'Subject': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'Keywords': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'Creator': // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted // break intentionally omitted case 'Producer': if (extension_loaded('mbstring') === true) { $detected = mb_detect_encoding($value); if ($detected !== 'ASCII') { $value = chr(254) . chr(255) . mb_convert_encoding($value, 'UTF-16', $detected); } } $docInfo->{$key} = new InternalType\StringObject((string) $value); break; default: // Set property using PDF type based on PHP type $docInfo->{$key} = InternalType\AbstractTypeObject::phpToPDF($value); break; } } $this->_trailer->Info = $docInfo; } $this->_dumpPages(); $this->_dumpNamedDestinations(); $this->_dumpOutlines(); // Check, that PDF file was modified // File is always modified by _dumpPages() now, but future implementations may eliminate this. if (!$this->_objFactory->isModified()) { if ($newSegmentOnly) { // Do nothing, return return ''; } if ($outputStream === null) { return $this->_trailer->getPDFString(); } else { $pdfData = $this->_trailer->getPDFString(); while (strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false) { $pdfData = substr($pdfData, $byteCount); } return ''; } } // offset (from a start of PDF file) of new PDF file segment $offset = $this->_trailer->getPDFLength(); // Last Object number in a list of free objects $lastFreeObject = $this->_trailer->getLastFreeObject(); // Array of cross-reference table subsections $xrefTable = array(); // Object numbers of first objects in each subsection $xrefSectionStartNums = array(); // Last cross-reference table subsection $xrefSection = array(); // Dummy initialization of the first element (specail case - header of linked list of free objects). $xrefSection[] = 0; $xrefSectionStartNums[] = 0; // Object number of last processed PDF object. // Used to manage cross-reference subsections. // Initialized by zero (specail case - header of linked list of free objects). $lastObjNum = 0; if ($outputStream !== null) { if (!$newSegmentOnly) { $pdfData = $this->_trailer->getPDFString(); while (strlen($pdfData) > 0 && ($byteCount = fwrite($outputStream, $pdfData)) != false) { $pdfData = substr($pdfData, $byteCount); } } } else { $pdfSegmentBlocks = $newSegmentOnly ? array() : array($this->_trailer->getPDFString()); } // Iterate objects to create new reference table foreach ($this->_objFactory->listModifiedObjects() as $updateInfo) { $objNum = $updateInfo->getObjNum(); if ($objNum - $lastObjNum != 1) { // Save cross-reference table subsection and start new one $xrefTable[] = $xrefSection; $xrefSection = array(); $xrefSectionStartNums[] = $objNum; } if ($updateInfo->isFree()) { // Free object cross-reference table entry $xrefSection[] = sprintf("%010d %05d f \n", $lastFreeObject, $updateInfo->getGenNum()); $lastFreeObject = $objNum; } else { // In-use object cross-reference table entry $xrefSection[] = sprintf("%010d %05d n \n", $offset, $updateInfo->getGenNum()); $pdfBlock = $updateInfo->getObjectDump(); $offset += strlen($pdfBlock); if ($outputStream === null) { $pdfSegmentBlocks[] = $pdfBlock; } else { while (strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false) { $pdfBlock = substr($pdfBlock, $byteCount); } } } $lastObjNum = $objNum; } // Save last cross-reference table subsection $xrefTable[] = $xrefSection; // Modify first entry (specail case - header of linked list of free objects). $xrefTable[0][0] = sprintf("%010d 65535 f \n", $lastFreeObject); $xrefTableStr = "xref\n"; foreach ($xrefTable as $sectId => $xrefSection) { $xrefTableStr .= sprintf("%d %d \n", $xrefSectionStartNums[$sectId], count($xrefSection)); foreach ($xrefSection as $xrefTableEntry) { $xrefTableStr .= $xrefTableEntry; } } $this->_trailer->Size->value = $this->_objFactory->getObjectCount(); $pdfBlock = $xrefTableStr . $this->_trailer->toString() . "startxref\n" . $offset . "\n" . "%%EOF\n"; $this->_objFactory->cleanEnumerationShiftCache(); if ($outputStream === null) { $pdfSegmentBlocks[] = $pdfBlock; return implode('', $pdfSegmentBlocks); } else { while (strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false) { $pdfBlock = substr($pdfBlock, $byteCount); } return ''; } }
/** * Generate new \Zend\Pdf\InternalType\IndirectObject * * @todo Reusage of the freed object. It's not a support of new feature, but only improvement. * * @param \Zend\Pdf\InternalType\AbstractTypeObject $objectValue * @return \Zend\Pdf\InternalType\IndirectObject */ public function newObject(InternalType\AbstractTypeObject $objectValue) { return $this->_factory->newObject($objectValue); }