/** * Object constructor. * * If resource is not a Zend_Pdf_Element object, then stream object with specified value is * generated. * * @param Zend_Pdf_Element|string $resource */ public function __construct($resource) { $this->_objectFactory = Zend_Pdf_ElementFactory::createFactory(1); if ($resource instanceof Zend_Pdf_Element) { $this->_resource = $this->_objectFactory->newObject($resource); } else { $this->_resource = $this->_objectFactory->newStreamObject($resource); } }
/** * Object constructor. * * If resource is not a Zend_Pdf_Element object, then stream object with specified value is * generated. * * @param Zend_Pdf_Element|string $resource */ public function __construct($resource) { if ($resource instanceof Zend_Pdf_Element_Object) { $this->_objectFactory = $resource->getFactory(); $this->_resource = $resource; return; } require_once 'Zend/Pdf/ElementFactory.php'; $this->_objectFactory = Zend_Pdf_ElementFactory::createFactory(1); if ($resource instanceof Zend_Pdf_Element) { $this->_resource = $this->_objectFactory->newObject($resource); } else { $this->_resource = $this->_objectFactory->newStreamObject($resource); } }
/** * Attach factory to the current; * * @param Zend_Pdf_ElementFactory_Interface $factory */ public function attach(Zend_Pdf_ElementFactory_Interface $factory) { if ($factory === $this || isset($this->_attachedFactories[$factory->getId()])) { /** * Don't attach factory twice. * We do not check recusively because of nature of attach operation * (Pages are always attached to the Documents, Fonts are always attached * to the pages even if pages already use Document level object factory and so on) */ return; } $this->_attachedFactories[$factory->getId()] = $factory; }
/** * * @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->_pageDictionary->Annots === null) { $this->_pageDictionary->touch(); $this->_pageDictionary->Annots = new Zend_Pdf_Element_Array(); } else { $this->_pageDictionary->Annots->touch(); } $this->_pageDictionary->Annots->items[] = $annotationDictionary; $annotationDictionary->touch(); $annotationDictionary->P = $this->_pageDictionary; return $this; }
/** * Read inderect object from a PDF stream * * @param integer $offset * @param Zend_Pdf_Element_Reference_Context $context * @return Zend_Pdf_Element_Object */ public function getObject($offset, Zend_Pdf_Element_Reference_Context $context) { if ($offset === null) { return new Zend_Pdf_Element_Null(); } // Save current offset to make getObject() reentrant $offsetSave = $this->offset; $this->offset = $offset; $this->_context = $context; $this->_elements = array(); $objNum = $this->readLexeme(); if (!ctype_digit($objNum)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Object number expected.', $this->offset - strlen($objNum))); } $genNum = $this->readLexeme(); if (!ctype_digit($genNum)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Object generation number expected.', $this->offset - strlen($genNum))); } $objKeyword = $this->readLexeme(); if ($objKeyword != 'obj') { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'obj\' keyword expected.', $this->offset - strlen($objKeyword))); } $objValue = $this->readElement(); $nextLexeme = $this->readLexeme(); if ($nextLexeme == 'endobj') { /** * Object is not generated by factory (thus it's not marked as modified object). * But factory is assigned to the obect. */ $obj = new Zend_Pdf_Element_Object($objValue, (int) $objNum, (int) $genNum, $this->_objFactory->resolve()); foreach ($this->_elements as $element) { $element->setParentObject($obj); } // Restore offset value $this->offset = $offsetSave; return $obj; } /** * It's a stream object */ if ($nextLexeme != 'stream') { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endobj\' or \'stream\' keywords expected.', $this->offset - strlen($nextLexeme))); } if (!$objValue instanceof Zend_Pdf_Element_Dictionary) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Stream extent must be preceded by stream dictionary.', $this->offset - strlen($nextLexeme))); } /** * References are automatically dereferenced at this moment. */ $streamLength = $objValue->Length->value; /** * 'stream' keyword must be followed by either cr-lf sequence or lf character only. * This restriction gives the possibility to recognize all cases exactly */ if ($this->data[$this->offset] == "\r" && $this->data[$this->offset + 1] == "\n") { $this->offset += 2; } else { if ($this->data[$this->offset] == "\n") { $this->offset++; } else { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'stream\' must be followed by either cr-lf sequence or lf character only.', $this->offset - strlen($nextLexeme))); } } $dataOffset = $this->offset; $this->offset += $streamLength; $nextLexeme = $this->readLexeme(); if ($nextLexeme != 'endstream') { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endstream\' keyword expected.', $this->offset - strlen($nextLexeme))); } $nextLexeme = $this->readLexeme(); if ($nextLexeme != 'endobj') { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endobj\' keyword expected.', $this->offset - strlen($nextLexeme))); } $obj = new Zend_Pdf_Element_Object_Stream(substr($this->data, $dataOffset, $streamLength), (int) $objNum, (int) $genNum, $this->_objFactory->resolve(), $objValue); foreach ($this->_elements as $element) { $element->setParentObject($obj); } // Restore offset value $this->offset = $offsetSave; return $obj; }
/** * Check if PDF file was modified * * @return boolean */ public function isModified() { return $this->_factory->isModified(); }
/** * 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) { throw new Zend_Pdf_Exception('Page is attached to one documen, but rendered in context of another.'); /** * @todo Page cloning must be implemented here instead of exception. * PDF objects (ex. fonts) can be shared between pages. * Thus all referenced objects, which can be modified, must be cloned recursively, * to avoid producing wrong object references in a context of source PDF. */ //... } else { $objFactory->attach($this->_objFactory); } }
/** * Dump Outline and its child outlines into PDF structures * * Returns dictionary indirect object or reference * * @internal * @param Zend_Pdf_ElementFactory $factory object factory for newly created indirect objects * @param boolean $updateNavigation Update navigation flag * @param Zend_Pdf_Element $parent Parent outline dictionary reference * @param Zend_Pdf_Element $prev Previous outline dictionary reference * @param SplObjectStorage $processedOutlines List of already processed outlines * @return Zend_Pdf_Element * @throws Zend_Pdf_Exception */ public function dumpOutline(Zend_Pdf_ElementFactory_Interface $factory, $updateNavigation, Zend_Pdf_Element $parent, Zend_Pdf_Element $prev = null, SplObjectStorage $processedOutlines = null) { if ($processedOutlines === null) { $processedOutlines = new SplObjectStorage(); } $processedOutlines->attach($this); $outlineDictionary = $factory->newObject(new Zend_Pdf_Element_Dictionary()); $outlineDictionary->Title = new Zend_Pdf_Element_String($this->getTitle()); $target = $this->getTarget(); if ($target === null) { // Do nothing } else { if ($target instanceof Zend_Pdf_Destination) { $outlineDictionary->Dest = $target->getResource(); } else { if ($target instanceof Zend_Pdf_Action) { $outlineDictionary->A = $target->getResource(); } else { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('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 Zend_Pdf_Element_Numeric($components[0]), new Zend_Pdf_Element_Numeric($components[1]), new Zend_Pdf_Element_Numeric($components[2])); $outlineDictionary->C = new Zend_Pdf_Element_Array($colorComponentElements); } if ($this->isItalic() || $this->isBold()) { $outlineDictionary->F = new Zend_Pdf_Element_Numeric(($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)) { require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('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 Zend_Pdf_Element_Numeric(($this->isOpen() ? 1 : -1) * count($this->childOutlines)); } return $outlineDictionary; }
/** * Object constructor * * Note: PHP duplicates string, which is sent by value, only of it's updated. * Thus we don't need to care about overhead * * @param mixed $source * @param Zend_Pdf_ElementFactory_Interface $factory * @param boolean $load * @throws Zend_Exception */ public function __construct($source, Zend_Pdf_ElementFactory_Interface $factory, $load) { if ($load) { if (($pdfFile = @fopen($source, 'rb')) === false) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Can not open '{$source}' file for reading."); } $data = ''; $byteCount = filesize($source); while ($byteCount > 0 && !feof($pdfFile)) { $nextBlock = fread($pdfFile, $byteCount); if ($nextBlock === false) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Error occured while '{$source}' file reading."); } $data .= $nextBlock; $byteCount -= strlen($nextBlock); } if ($byteCount != 0) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception("Error occured while '{$source}' file reading."); } fclose($pdfFile); $this->_stringParser = new Zend_Pdf_StringParser($data, $factory); } else { $this->_stringParser = new Zend_Pdf_StringParser($source, $factory); } $pdfVersionComment = $this->_stringParser->readComment(); if (substr($pdfVersionComment, 0, 5) != '%PDF-') { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('File is not a PDF.'); } $pdfVersion = substr($pdfVersionComment, 5); if (version_compare($pdfVersion, '0.9', '<') || version_compare($pdfVersion, '1.61', '>=')) { /** * @todo * To support PDF versions 1.5 (Acrobat 6) and PDF version 1.7 (Acrobat 7) * Stream compression filter must be implemented (for compressed object streams). * Cross reference streams must be implemented */ require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('Unsupported PDF version. Zend_Pdf supports PDF 1.0-1.4. Current version - \'%f\'', $pdfVersion)); } $this->_pdfVersion = $pdfVersion; $this->_stringParser->offset = strrpos($this->_stringParser->data, '%%EOF'); if ($this->_stringParser->offset === false || strlen($this->_stringParser->data) - $this->_stringParser->offset > 7) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Pdf file syntax error. End-of-fle marker expected at the end of file.'); } $this->_stringParser->offset--; /** * Go to end of cross-reference table offset */ while (Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset])) && $this->_stringParser->offset > 0) { $this->_stringParser->offset--; } /** * Go to the start of cross-reference table offset */ while (!Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset])) && $this->_stringParser->offset > 0) { $this->_stringParser->offset--; } /** * Go to the end of 'startxref' keyword */ while (Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset])) && $this->_stringParser->offset > 0) { $this->_stringParser->offset--; } /** * Go to the white space (eol marker) before 'startxref' keyword */ $this->_stringParser->offset -= 9; $nextLexeme = $this->_stringParser->readLexeme(); if ($nextLexeme != 'startxref') { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('Pdf file syntax error. \'startxref\' keyword expected. Offset - 0x%X.', $this->_stringParser->offset - strlen($nextLexeme))); } $startXref = $this->_stringParser->readLexeme(); if (!ctype_digit($startXref)) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('Pdf file syntax error. Cross-reference table offset must contain only digits. Offset - 0x%X.', $this->_stringParser->offset - strlen($nextLexeme))); } $this->_trailer = $this->_loadXRefTable($startXref); $factory->setObjectCount($this->_trailer->Size->value); }
/** * Render the completed PDF to a string. * If $newSegmentOnly is true and it's not a new document, * 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) { if ($this->_isNewDocument) { // Drop full document first time even $newSegmentOnly is set to true $newSegmentOnly = false; $this->_isNewDocument = false; } // Save document properties if necessary if ($this->properties != $this->_originalProperties) { $docInfo = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary()); foreach ($this->properties as $key => $value) { switch ($key) { case 'Trapped': switch ($value) { case true: $docInfo->$key = new Zend_Pdf_Element_Name('True'); break; case false: $docInfo->$key = new Zend_Pdf_Element_Name('False'); break; case null: $docInfo->$key = new Zend_Pdf_Element_Name('Unknown'); break; default: require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Wrong Trapped document property vale: \'' . $value . '\'. Only true, false and null values are allowed.'); break; } case 'CreationDate': // break intentionally omitted case 'ModDate': $docInfo->$key = new Zend_Pdf_Element_String((string)$value); break; case 'Title': // break intentionally omitted case 'Author': // break intentionally omitted case 'Subject': // break intentionally omitted case 'Keywords': // break intentionally omitted case 'Creator': // break intentionally omitted case 'Producer': if (extension_loaded('mbstring') === true) { $detected = mb_detect_encoding($value); if ($detected !== 'ASCII') { $value = "\xfe\xff" . mb_convert_encoding($value, 'UTF-16', $detected); } } $docInfo->$key = new Zend_Pdf_Element_String((string)$value); break; default: // Set property using PDF type based on PHP type $docInfo->$key = Zend_Pdf_Element::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 ''; } }
public function setEmbeddedJS($javascript = null) { /* * Names container */ if (null === $javascript || '' === $javascript) { throw new Zend_Pdf_Exception("Javascript must be set."); } $this->setJavaScript($javascript); $js_code = array("S" => new Zend_Pdf_Element_Name('JavaScript'), "JS" => new Zend_Pdf_Element_String($this->getJavascript())); $embed[] = new Zend_Pdf_Element_String('EmbeddedJS'); $embed[] = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary($js_code)); $js_ref = $this->_objFactory->newObject(new Zend_Pdf_Element_Dictionary(array('Names' => new Zend_Pdf_Element_Array($embed)))); $this->_trailer->Root->Names = new Zend_Pdf_Element_Dictionary(array('JavaScript' => $js_ref)); }
/** * Set the transparancy * * $alpha == 0 - transparent * $alpha == 1 - opaque * * Transparency modes, supported by PDF: * Normal (default), Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, * SoftLight, Difference, Exclusion * * @param float $alpha * @param string $mode * @throws Zend_Pdf_Exception * @return Zend_Pdf_Page */ public function setAlpha($alpha, $mode = 'Normal') { if (!in_array($mode, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion'))) { throw new Zend_Pdf_Exception('Unsupported transparency mode.'); } if (!is_numeric($alpha) || $alpha < 0 || $alpha > 1) { throw new Zend_Pdf_Exception('Alpha value must be numeric between 0 (transparent) and 1 (opaque).'); } $this->_addProcSet('Text'); $this->_addProcSet('PDF'); $resources = $this->_pageDictionary->Resources; // Check if Resources dictionary contains ExtGState entry if ($resources->ExtGState === null) { $resources->touch(); $resources->ExtGState = new Zend_Pdf_Element_Dictionary(); } else { $resources->ExtGState->touch(); } $idCounter = 1; do { $gStateName = 'GS' . $idCounter++; } while ($resources->ExtGState->{$gStateName} !== null); $gStateDictionary = new Zend_Pdf_Element_Dictionary(); $gStateDictionary->Type = new Zend_Pdf_Element_Name('ExtGState'); $gStateDictionary->BM = new Zend_Pdf_Element_Name($mode); $gStateDictionary->CA = new Zend_Pdf_Element_Numeric($alpha); $gStateDictionary->ca = new Zend_Pdf_Element_Numeric($alpha); $resources->ExtGState->{$gStateName} = $this->_objFactory->newObject($gStateDictionary); $gStateNameObj = new Zend_Pdf_Element_Name($gStateName); $this->_contents .= $gStateNameObj->toString() . " gs\n"; return $this; }
/** * 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 */ public function render($newSegmentOnly = false, $outputStream = null) { $this->_dumpPages(); // 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"; if ($outputStream === null) { $pdfSegmentBlocks[] = $pdfBlock; return implode('', $pdfSegmentBlocks); } else { while (strlen($pdfBlock) > 0 && ($byteCount = fwrite($outputStream, $pdfBlock)) != false) { $pdfBlock = substr($pdfBlock, $byteCount); } return ''; } }