/** * Get next generation number for free object * * @param integer $objNum * @return unknown */ public function getNewGeneration($objNum) { if (isset($this->_usedObjects[$objNum])) { throw new Zend_Pdf_Exception('Object is not free'); } if (isset($this->_generations[$objNum])) { return $this->_generations[$objNum]; } if (isset($this->_parent)) { return $this->_parent->getNewGeneration($objNum); } throw new Zend_Pdf_Exception('Object not found.'); }
/** * Get next generation number for free object * * @param integer $objNum * @return unknown */ public function getNewGeneration($objNum) { if (isset($this->_usedObjects[$objNum])) { // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Object is not free'); } if (isset($this->_generations[$objNum])) { return $this->_generations[$objNum]; } if (isset($this->_parent)) { return $this->_parent->getNewGeneration($objNum); } // require_once 'Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Object not found.'); }
/** * Load XReference table and referenced objects * * @param integer $offset * @throws Zend_Pdf_Exception * @return Zend_Pdf_Trailer_Keeper */ private function _loadXRefTable($offset) { $this->_stringParser->offset = $offset; require_once LIB_DIR . '/Zend/Pdf/Element/Reference/Table.php'; $refTable = new Zend_Pdf_Element_Reference_Table(); require_once LIB_DIR . '/Zend/Pdf/Element/Reference/Context.php'; $context = new Zend_Pdf_Element_Reference_Context($this->_stringParser, $refTable); $this->_stringParser->setContext($context); $nextLexeme = $this->_stringParser->readLexeme(); if ($nextLexeme == 'xref') { /** * Common cross-reference table */ $this->_stringParser->skipWhiteSpace(); while (($nextLexeme = $this->_stringParser->readLexeme()) != 'trailer') { if (!ctype_digit($nextLexeme)) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_stringParser->offset - strlen($nextLexeme))); } $objNum = (int) $nextLexeme; $refCount = $this->_stringParser->readLexeme(); if (!ctype_digit($refCount)) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_stringParser->offset - strlen($refCount))); } $this->_stringParser->skipWhiteSpace(); while ($refCount > 0) { $objectOffset = substr($this->_stringParser->data, $this->_stringParser->offset, 10); if (!ctype_digit($objectOffset)) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_stringParser->offset)); } // Force $objectOffset to be treated as decimal instead of octal number for ($numStart = 0; $numStart < strlen($objectOffset) - 1; $numStart++) { if ($objectOffset[$numStart] != '0') { break; } } $objectOffset = substr($objectOffset, $numStart); $this->_stringParser->offset += 10; if (strpos("\t\n\f\r ", $this->_stringParser->data[$this->_stringParser->offset]) === false) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $genNumber = substr($this->_stringParser->data, $this->_stringParser->offset, 5); if (!ctype_digit($objectOffset)) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_stringParser->offset)); } // Force $objectOffset to be treated as decimal instead of octal number for ($numStart = 0; $numStart < strlen($genNumber) - 1; $numStart++) { if ($genNumber[$numStart] != '0') { break; } } $genNumber = substr($genNumber, $numStart); $this->_stringParser->offset += 5; if (strpos("\t\n\f\r ", $this->_stringParser->data[$this->_stringParser->offset]) === false) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $inUseKey = $this->_stringParser->data[$this->_stringParser->offset]; $this->_stringParser->offset++; switch ($inUseKey) { case 'f': // free entry unset($this->_refTable[$objNum . ' ' . $genNumber . ' R']); $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, false); break; case 'n': // in-use entry $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, true); } if (!Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset]))) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; if (!Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset]))) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $refCount--; $objNum++; } } $trailerDictOffset = $this->_stringParser->offset; $trailerDict = $this->_stringParser->readElement(); if (!$trailerDict instanceof Zend_Pdf_Element_Dictionary) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Dictionary expected after \'trailer\' keyword.', $trailerDictOffset)); } } else { $xrefStream = $this->_stringParser->getObject($offset, $context); if (!$xrefStream instanceof Zend_Pdf_Element_Object_Stream) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference stream expected.', $offset)); } $trailerDict = $xrefStream->dictionary; if ($trailerDict->Type->value != 'XRef') { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference stream object must have /Type property assigned to /XRef.', $offset)); } if ($trailerDict->W === null || $trailerDict->W->getType() != Zend_Pdf_Element::TYPE_ARRAY) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross reference stream dictionary doesn\'t have W entry or it\'s not an array.', $offset)); } $entryField1Size = $trailerDict->W->items[0]->value; $entryField2Size = $trailerDict->W->items[1]->value; $entryField3Size = $trailerDict->W->items[2]->value; if ($entryField2Size == 0 || $entryField3Size == 0) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Wrong W dictionary entry. Only type field of stream entries has default value and could be zero length.', $offset)); } $xrefStreamData = $xrefStream->value; if ($trailerDict->Index !== null) { if ($trailerDict->Index->getType() != Zend_Pdf_Element::TYPE_ARRAY) { require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross reference stream dictionary Index entry must be an array.', $offset)); } $sections = count($trailerDict->Index->items) / 2; } else { $sections = 1; } $streamOffset = 0; $size = $entryField1Size + $entryField2Size + $entryField3Size; $entries = strlen($xrefStreamData) / $size; for ($count = 0; $count < $sections; $count++) { if ($trailerDict->Index !== null) { $objNum = $trailerDict->Index->items[$count * 2]->value; $entries = $trailerDict->Index->items[$count * 2 + 1]->value; } else { $objNum = 0; $entries = $trailerDict->Size->value; } for ($count2 = 0; $count2 < $entries; $count2++) { if ($entryField1Size == 0) { $type = 1; } else { if ($entryField1Size == 1) { // Optimyze one-byte field case $type = ord($xrefStreamData[$streamOffset++]); } else { $type = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField1Size); $streamOffset += $entryField1Size; } } if ($entryField2Size == 1) { // Optimyze one-byte field case $field2 = ord($xrefStreamData[$streamOffset++]); } else { $field2 = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField2Size); $streamOffset += $entryField2Size; } if ($entryField3Size == 1) { // Optimyze one-byte field case $field3 = ord($xrefStreamData[$streamOffset++]); } else { $field3 = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField3Size); $streamOffset += $entryField3Size; } switch ($type) { case 0: // Free object $refTable->addReference($objNum . ' ' . $field3 . ' R', $field2, false); // Debug output: // echo "Free object - $objNum $field3 R, next free - $field2\n"; break; case 1: // In use object $refTable->addReference($objNum . ' ' . $field3 . ' R', $field2, true); // Debug output: // echo "In-use object - $objNum $field3 R, offset - $field2\n"; break; case 2: // Object in an object stream // Debug output: // echo "Compressed object - $objNum 0 R, object stream - $field2 0 R, offset - $field3\n"; break; } $objNum++; } } // $streamOffset . ' ' . strlen($xrefStreamData) . "\n"; // "$entries\n"; require_once LIB_DIR . '/Zend/Pdf/Exception.php'; throw new Zend_Pdf_Exception('Cross-reference streams are not supported yet.'); } require_once LIB_DIR . '/Zend/Pdf/Trailer/Keeper.php'; $trailerObj = new Zend_Pdf_Trailer_Keeper($trailerDict, $context); if ($trailerDict->Prev instanceof Zend_Pdf_Element_Numeric || $trailerDict->Prev instanceof Zend_Pdf_Element_Reference) { $trailerObj->setPrev($this->_loadXRefTable($trailerDict->Prev->value)); $context->getRefTable()->setParent($trailerObj->getPrev()->getRefTable()); } /** * We set '/Prev' dictionary property to the current cross-reference section offset. * It doesn't correspond to the actual data, but is true when trailer will be used * as a trailer for next generated PDF section. */ $trailerObj->Prev = new Zend_Pdf_Element_Numeric($offset); return $trailerObj; }
/** * Load XReference table and referenced objects * * @param integer $offset * @throws Zend_Pdf_Exception * @return Zend_Pdf_Trailer_Keeper */ private function _loadXRefTable($offset) { $this->_pushContext(); $this->_current = $offset; $refTable = new Zend_Pdf_Element_Reference_Table(); $context = new Zend_Pdf_Element_Reference_Context($this, $refTable); $this->_currentContext = $context; $nextLexeme = $this->_readLexeme(); if ($nextLexeme == 'xref') { /** * Common cross-reference table */ $this->_skipWhiteSpace(); while (($nextLexeme = $this->_readLexeme()) != 'trailer') { if (!ctype_digit($nextLexeme)) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_current - strlen($nextLexeme))); } $objNum = (int) $nextLexeme; $refCount = $this->_readLexeme(); if (!ctype_digit($refCount)) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_current - strlen($refCount))); } $this->_skipWhiteSpace(); while ($refCount > 0) { $objectOffset = substr($this->_pdfString, $this->_current, 10); if (!ctype_digit($objectOffset)) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_current)); } // Force $objectOffset to be treated as decimal instead of octal number for ($numStart = 0; $numStart < strlen($objectOffset) - 1; $numStart++) { if ($objectOffset[$numStart] != '0') { break; } } $objectOffset = substr($objectOffset, $numStart); $this->_current += 10; if (!self::isWhiteSpace(ord($this->_pdfString[$this->_current]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_current)); } $this->_current++; $genNumber = substr($this->_pdfString, $this->_current, 5); if (!ctype_digit($objectOffset)) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_current)); } // Force $objectOffset to be treated as decimal instead of octal number for ($numStart = 0; $numStart < strlen($genNumber) - 1; $numStart++) { if ($genNumber[$numStart] != '0') { break; } } $genNumber = substr($genNumber, $numStart); $this->_current += 5; if (!self::isWhiteSpace(ord($this->_pdfString[$this->_current]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_current)); } $this->_current++; $inUseKey = $this->_pdfString[$this->_current]; $this->_current++; switch ($inUseKey) { case 'f': // free entry unset($this->_refTable[$objNum . ' ' . $genNumber . ' R']); $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, false); break; case 'n': // in-use entry $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, true); } if (!self::isWhiteSpace(ord($this->_pdfString[$this->_current]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_current)); } $this->_current++; if (!self::isWhiteSpace(ord($this->_pdfString[$this->_current]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_current)); } $this->_current++; $refCount--; $objNum++; } } $trailerDictOffset = $this->_current; $trailerDict = $this->_readElementalObject(); if (!$trailerDict instanceof Zend_Pdf_Element_Dictionary) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Dictionary expected after \'trailer\' keyword.', $trailerDictOffset)); } $trailerObj = new Zend_Pdf_Trailer_Keeper($trailerDict, $context); if ($trailerDict->Prev instanceof Zend_Pdf_Element_Numeric || $trailerDict->Prev instanceof Zend_Pdf_Element_Reference) { $trailerObj->setPrev($this->_loadXRefTable($trailerDict->Prev->value)); $context->getRefTable()->setParent($trailerObj->getPrev()->getRefTable()); } /** * We set '/Prev' dictionary property to the current cross-reference section offset. * It doesn't correspond to the actual data, but is true when trailer will be used * as trailer for next generated PDF section. */ $trailerObj->Prev = new Zend_Pdf_Element_Numeric($offset); } else { throw new Zend_Exception(sprintf('Pdf file syntax error. Offset - 0x%X. \'xref\' lexeme expected.', $this->_current - strlen($nextLexeme))); /** * @todo Cross-Reference stream object must be loaded here. */ } $this->_popContext(); return $trailerObj; }
private function _loadXRefTable($offset) { $this->_stringParser->offset = $offset; $refTable = new Zend_Pdf_Element_Reference_Table(); $context = new Zend_Pdf_Element_Reference_Context($this->_stringParser, $refTable); $this->_stringParser->setContext($context); $nextLexeme = $this->_stringParser->readLexeme(); if ($nextLexeme == 'xref') { $this->_stringParser->skipWhiteSpace(); while (($nextLexeme = $this->_stringParser->readLexeme()) != 'trailer') { if (!ctype_digit($nextLexeme)) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_stringParser->offset - strlen($nextLexeme))); } $objNum = (int) $nextLexeme; $refCount = $this->_stringParser->readLexeme(); if (!ctype_digit($refCount)) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference table subheader values must contain only digits.', $this->_stringParser->offset - strlen($refCount))); } $this->_stringParser->skipWhiteSpace(); while ($refCount > 0) { $objectOffset = substr($this->_stringParser->data, $this->_stringParser->offset, 10); if (!ctype_digit($objectOffset)) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_stringParser->offset)); } for ($numStart = 0; $numStart < strlen($objectOffset) - 1; $numStart++) { if ($objectOffset[$numStart] != '0') { break; } } $objectOffset = substr($objectOffset, $numStart); $this->_stringParser->offset += 10; if (strpos("\t\n\f\r ", $this->_stringParser->data[$this->_stringParser->offset]) === false) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $genNumber = substr($this->_stringParser->data, $this->_stringParser->offset, 5); if (!ctype_digit($objectOffset)) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Offset must contain only digits.', $this->_stringParser->offset)); } for ($numStart = 0; $numStart < strlen($genNumber) - 1; $numStart++) { if ($genNumber[$numStart] != '0') { break; } } $genNumber = substr($genNumber, $numStart); $this->_stringParser->offset += 5; if (strpos("\t\n\f\r ", $this->_stringParser->data[$this->_stringParser->offset]) === false) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $inUseKey = $this->_stringParser->data[$this->_stringParser->offset]; $this->_stringParser->offset++; switch ($inUseKey) { case 'f': unset($this->_refTable[$objNum . ' ' . $genNumber . ' R']); $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, false); break; case 'n': $refTable->addReference($objNum . ' ' . $genNumber . ' R', $objectOffset, true); } if (!Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; if (!Zend_Pdf_StringParser::isWhiteSpace(ord($this->_stringParser->data[$this->_stringParser->offset]))) { throw new Zend_Pdf_Exception(sprintf('PDF file cross-reference table syntax error. Offset - 0x%X. Value separator must be white space.', $this->_stringParser->offset)); } $this->_stringParser->offset++; $refCount--; $objNum++; } } $trailerDictOffset = $this->_stringParser->offset; $trailerDict = $this->_stringParser->readElement(); if (!$trailerDict instanceof Zend_Pdf_Element_Dictionary) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Dictionary expected after \'trailer\' keyword.', $trailerDictOffset)); } } else { $xrefStream = $this->_stringParser->getObject($offset, $context); if (!$xrefStream instanceof Zend_Pdf_Element_Object_Stream) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference stream expected.', $offset)); } $trailerDict = $xrefStream->dictionary; if ($trailerDict->Type->value != 'XRef') { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross-reference stream object must have /Type property assigned to /XRef.', $offset)); } if ($trailerDict->W === null || $trailerDict->W->getType() != Zend_Pdf_Element::TYPE_ARRAY) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross reference stream dictionary doesn\'t have W entry or it\'s not an array.', $offset)); } $entryField1Size = $trailerDict->W->items[0]->value; $entryField2Size = $trailerDict->W->items[1]->value; $entryField3Size = $trailerDict->W->items[2]->value; if ($entryField2Size == 0 || $entryField3Size == 0) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Wrong W dictionary entry. Only type field of stream entries has default value and could be zero length.', $offset)); } $xrefStreamData = $xrefStream->value; if ($trailerDict->Index !== null) { if ($trailerDict->Index->getType() != Zend_Pdf_Element::TYPE_ARRAY) { throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Cross reference stream dictionary Index entry must be an array.', $offset)); } $sections = count($trailerDict->Index->items) / 2; } else { $sections = 1; } $streamOffset = 0; $size = $entryField1Size + $entryField2Size + $entryField3Size; $entries = strlen($xrefStreamData) / $size; for ($count = 0; $count < $sections; $count++) { if ($trailerDict->Index !== null) { $objNum = $trailerDict->Index->items[$count * 2]->value; $entries = $trailerDict->Index->items[$count * 2 + 1]->value; } else { $objNum = 0; $entries = $trailerDict->Size->value; } for ($count2 = 0; $count2 < $entries; $count2++) { if ($entryField1Size == 0) { $type = 1; } else { if ($entryField1Size == 1) { $type = ord($xrefStreamData[$streamOffset++]); } else { $type = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField1Size); $streamOffset += $entryField1Size; } } if ($entryField2Size == 1) { $field2 = ord($xrefStreamData[$streamOffset++]); } else { $field2 = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField2Size); $streamOffset += $entryField2Size; } if ($entryField3Size == 1) { $field3 = ord($xrefStreamData[$streamOffset++]); } else { $field3 = Zend_Pdf_StringParser::parseIntFromStream($xrefStreamData, $streamOffset, $entryField3Size); $streamOffset += $entryField3Size; } switch ($type) { case 0: $refTable->addReference($objNum . ' ' . $field3 . ' R', $field2, false); break; case 1: $refTable->addReference($objNum . ' ' . $field3 . ' R', $field2, true); break; case 2: break; } $objNum++; } } throw new Zend_Pdf_Exception('Cross-reference streams are not supported yet.'); } $trailerObj = new Zend_Pdf_Trailer_Keeper($trailerDict, $context); if ($trailerDict->Prev instanceof Zend_Pdf_Element_Numeric || $trailerDict->Prev instanceof Zend_Pdf_Element_Reference) { $trailerObj->setPrev($this->_loadXRefTable($trailerDict->Prev->value)); $context->getRefTable()->setParent($trailerObj->getPrev()->getRefTable()); } $trailerObj->Prev = new Zend_Pdf_Element_Numeric($offset); return $trailerObj; }