/** * Render the completed PDF to a string. * If $newSegmentOnly is true, then only appended part of PDF is returned. * * @param boolean $newSegmentOnly * @return string */ public function render($newSegmentOnly = false) { $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) { return ''; } else { return $this->_trailer->getPDFString(); } } // offset (from a start of PDF file) of new PDF file segment $segmentOffset = $this->_trailer->getPDFLength(); // new PDF file segment itself $pdfSegment = ''; // 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; // 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", $segmentOffset + strlen($pdfSegment), $updateInfo->getGenNum()); $pdfSegment .= $updateInfo->getObjectDump(); } $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; } } $xrefStartOffset = $segmentOffset + strlen($pdfSegment); $this->_trailer->Size->value = $this->_objFactory->getObjectCount(); $pdfSegment .= $xrefTableStr . $this->_trailer->toString() . "startxref\n" . $xrefStartOffset . "\n" . "%%EOF\n"; if ($newSegmentOnly) { return $pdfSegment; } else { return $this->_trailer->getPDFString() . $pdfSegment; } }