web/lib/Zend/Pdf/StringParser.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Pdf
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: StringParser.php 22311 2010-05-27 12:57:37Z padraic $
       
    20  */
       
    21 
       
    22 
       
    23 /** Internally used classes */
       
    24 require_once 'Zend/Pdf/Element/Array.php';
       
    25 require_once 'Zend/Pdf/Element/String/Binary.php';
       
    26 require_once 'Zend/Pdf/Element/Boolean.php';
       
    27 require_once 'Zend/Pdf/Element/Dictionary.php';
       
    28 require_once 'Zend/Pdf/Element/Name.php';
       
    29 require_once 'Zend/Pdf/Element/Null.php';
       
    30 require_once 'Zend/Pdf/Element/Numeric.php';
       
    31 require_once 'Zend/Pdf/Element/Object.php';
       
    32 require_once 'Zend/Pdf/Element/Object/Stream.php';
       
    33 require_once 'Zend/Pdf/Element/Reference.php';
       
    34 require_once 'Zend/Pdf/Element/String.php';
       
    35 
       
    36 
       
    37 /**
       
    38  * PDF string parser
       
    39  *
       
    40  * @package    Zend_Pdf
       
    41  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    42  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    43  */
       
    44 class Zend_Pdf_StringParser
       
    45 {
       
    46     /**
       
    47      * Source PDF
       
    48      *
       
    49      * @var string
       
    50      */
       
    51     public $data = '';
       
    52 
       
    53     /**
       
    54      * Current position in a data
       
    55      *
       
    56      * @var integer
       
    57      */
       
    58     public $offset = 0;
       
    59 
       
    60     /**
       
    61      * Current reference context
       
    62      *
       
    63      * @var Zend_Pdf_Element_Reference_Context
       
    64      */
       
    65     private $_context = null;
       
    66 
       
    67     /**
       
    68      * Array of elements of the currently parsed object/trailer
       
    69      *
       
    70      * @var array
       
    71      */
       
    72     private $_elements = array();
       
    73 
       
    74     /**
       
    75      * PDF objects factory.
       
    76      *
       
    77      * @var Zend_Pdf_ElementFactory_Interface
       
    78      */
       
    79     private $_objFactory = null;
       
    80 
       
    81 
       
    82     /**
       
    83      * Clean up resources.
       
    84      *
       
    85      * Clear current state to remove cyclic object references
       
    86      */
       
    87     public function cleanUp()
       
    88     {
       
    89         $this->_context = null;
       
    90         $this->_elements = array();
       
    91         $this->_objFactory = null;
       
    92     }
       
    93 
       
    94     /**
       
    95      * Character with code $chCode is white space
       
    96      *
       
    97      * @param integer $chCode
       
    98      * @return boolean
       
    99      */
       
   100     public static function isWhiteSpace($chCode)
       
   101     {
       
   102         if ($chCode == 0x00 || // null character
       
   103             $chCode == 0x09 || // Tab
       
   104             $chCode == 0x0A || // Line feed
       
   105             $chCode == 0x0C || // Form Feed
       
   106             $chCode == 0x0D || // Carriage return
       
   107             $chCode == 0x20    // Space
       
   108            ) {
       
   109             return true;
       
   110         } else {
       
   111             return false;
       
   112         }
       
   113     }
       
   114 
       
   115 
       
   116     /**
       
   117      * Character with code $chCode is a delimiter character
       
   118      *
       
   119      * @param integer $chCode
       
   120      * @return boolean
       
   121      */
       
   122     public static function isDelimiter($chCode )
       
   123     {
       
   124         if ($chCode == 0x28 || // '('
       
   125             $chCode == 0x29 || // ')'
       
   126             $chCode == 0x3C || // '<'
       
   127             $chCode == 0x3E || // '>'
       
   128             $chCode == 0x5B || // '['
       
   129             $chCode == 0x5D || // ']'
       
   130             $chCode == 0x7B || // '{'
       
   131             $chCode == 0x7D || // '}'
       
   132             $chCode == 0x2F || // '/'
       
   133             $chCode == 0x25    // '%'
       
   134            ) {
       
   135             return true;
       
   136         } else {
       
   137             return false;
       
   138         }
       
   139     }
       
   140 
       
   141 
       
   142     /**
       
   143      * Skip white space
       
   144      *
       
   145      * @param boolean $skipComment
       
   146      */
       
   147     public function skipWhiteSpace($skipComment = true)
       
   148     {
       
   149         if ($skipComment) {
       
   150             while (true) {
       
   151                 $this->offset += strspn($this->data, "\x00\t\n\f\r ", $this->offset);
       
   152 
       
   153                 if ($this->offset < strlen($this->data)  &&  $this->data[$this->offset] == '%') {
       
   154                     // Skip comment
       
   155                     $this->offset += strcspn($this->data, "\r\n", $this->offset);
       
   156                 } else {
       
   157                     // Non white space character not equal to '%' is found
       
   158                     return;
       
   159                 }
       
   160             }
       
   161         } else {
       
   162             $this->offset += strspn($this->data, "\x00\t\n\f\r ", $this->offset);
       
   163         }
       
   164 
       
   165 //        /** Original (non-optimized) implementation. */
       
   166 //
       
   167 //        while ($this->offset < strlen($this->data)) {
       
   168 //            if (strpos("\x00\t\n\f\r ", $this->data[$this->offset]) !== false) {
       
   169 //                $this->offset++;
       
   170 //            } else if (ord($this->data[$this->offset]) == 0x25 && $skipComment) { // '%'
       
   171 //                $this->skipComment();
       
   172 //            } else {
       
   173 //                return;
       
   174 //            }
       
   175 //        }
       
   176     }
       
   177 
       
   178 
       
   179     /**
       
   180      * Skip comment
       
   181      */
       
   182     public function skipComment()
       
   183     {
       
   184         while ($this->offset < strlen($this->data))
       
   185         {
       
   186             if (ord($this->data[$this->offset]) != 0x0A || // Line feed
       
   187                 ord($this->data[$this->offset]) != 0x0d    // Carriage return
       
   188                ) {
       
   189                 $this->offset++;
       
   190             } else {
       
   191                 return;
       
   192             }
       
   193         }
       
   194     }
       
   195 
       
   196 
       
   197     /**
       
   198      * Read comment line
       
   199      *
       
   200      * @return string
       
   201      */
       
   202     public function readComment()
       
   203     {
       
   204         $this->skipWhiteSpace(false);
       
   205 
       
   206         /** Check if it's a comment line */
       
   207         if ($this->data[$this->offset] != '%') {
       
   208             return '';
       
   209         }
       
   210 
       
   211         for ($start = $this->offset;
       
   212              $this->offset < strlen($this->data);
       
   213              $this->offset++) {
       
   214             if (ord($this->data[$this->offset]) == 0x0A || // Line feed
       
   215                 ord($this->data[$this->offset]) == 0x0d    // Carriage return
       
   216                ) {
       
   217                 break;
       
   218             }
       
   219         }
       
   220 
       
   221         return substr($this->data, $start, $this->offset-$start);
       
   222     }
       
   223 
       
   224 
       
   225     /**
       
   226      * Returns next lexeme from a pdf stream
       
   227      *
       
   228      * @return string
       
   229      */
       
   230     public function readLexeme()
       
   231     {
       
   232         // $this->skipWhiteSpace();
       
   233         while (true) {
       
   234             $this->offset += strspn($this->data, "\x00\t\n\f\r ", $this->offset);
       
   235 
       
   236             if ($this->offset < strlen($this->data)  &&  $this->data[$this->offset] == '%') {
       
   237                 $this->offset += strcspn($this->data, "\r\n", $this->offset);
       
   238             } else {
       
   239                 break;
       
   240             }
       
   241         }
       
   242 
       
   243         if ($this->offset >= strlen($this->data)) {
       
   244             return '';
       
   245         }
       
   246 
       
   247         if ( /* self::isDelimiter( ord($this->data[$start]) ) */
       
   248              strpos('()<>[]{}/%', $this->data[$this->offset]) !== false ) {
       
   249 
       
   250             switch (substr($this->data, $this->offset, 2)) {
       
   251                 case '<<':
       
   252                     $this->offset += 2;
       
   253                     return '<<';
       
   254                     break;
       
   255 
       
   256                 case '>>':
       
   257                     $this->offset += 2;
       
   258                     return '>>';
       
   259                     break;
       
   260 
       
   261                 default:
       
   262                     return $this->data[$this->offset++];
       
   263                     break;
       
   264             }
       
   265         } else {
       
   266             $start = $this->offset;
       
   267             $compare = '';
       
   268             if( version_compare( phpversion(), '5.2.5' ) >= 0) {
       
   269                 $compare = "()<>[]{}/%\x00\t\n\f\r ";
       
   270             } else {
       
   271                 $compare = "()<>[]{}/%\x00\t\n\r ";
       
   272             }
       
   273 
       
   274             $this->offset += strcspn($this->data, $compare, $this->offset);
       
   275  
       
   276             return substr($this->data, $start, $this->offset - $start);
       
   277         }
       
   278     }
       
   279 
       
   280 
       
   281     /**
       
   282      * Read elemental object from a PDF stream
       
   283      *
       
   284      * @return Zend_Pdf_Element
       
   285      * @throws Zend_Pdf_Exception
       
   286      */
       
   287     public function readElement($nextLexeme = null)
       
   288     {
       
   289         if ($nextLexeme === null) {
       
   290             $nextLexeme = $this->readLexeme();
       
   291         }
       
   292 
       
   293         /**
       
   294          * Note: readElement() method is a public method and could be invoked from other classes.
       
   295          * If readElement() is used not by Zend_Pdf_StringParser::getObject() method, then we should not care
       
   296          * about _elements member management.
       
   297          */
       
   298         switch ($nextLexeme) {
       
   299             case '(':
       
   300                 return ($this->_elements[] = $this->_readString());
       
   301 
       
   302             case '<':
       
   303                 return ($this->_elements[] = $this->_readBinaryString());
       
   304 
       
   305             case '/':
       
   306                 return ($this->_elements[] = new Zend_Pdf_Element_Name(
       
   307                                                     Zend_Pdf_Element_Name::unescape( $this->readLexeme() )
       
   308                                                                       ));
       
   309 
       
   310             case '[':
       
   311                 return ($this->_elements[] = $this->_readArray());
       
   312 
       
   313             case '<<':
       
   314                 return ($this->_elements[] = $this->_readDictionary());
       
   315 
       
   316             case ')':
       
   317                 // fall through to next case
       
   318             case '>':
       
   319                 // fall through to next case
       
   320             case ']':
       
   321                 // fall through to next case
       
   322             case '>>':
       
   323                 // fall through to next case
       
   324             case '{':
       
   325                 // fall through to next case
       
   326             case '}':
       
   327                 require_once 'Zend/Pdf/Exception.php';
       
   328                 throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X.',
       
   329                                                 $this->offset));
       
   330 
       
   331             default:
       
   332                 if (strcasecmp($nextLexeme, 'true') == 0) {
       
   333                     return ($this->_elements[] = new Zend_Pdf_Element_Boolean(true));
       
   334                 } else if (strcasecmp($nextLexeme, 'false') == 0) {
       
   335                     return ($this->_elements[] = new Zend_Pdf_Element_Boolean(false));
       
   336                 } else if (strcasecmp($nextLexeme, 'null') == 0) {
       
   337                     return ($this->_elements[] = new Zend_Pdf_Element_Null());
       
   338                 }
       
   339 
       
   340                 $ref = $this->_readReference($nextLexeme);
       
   341                 if ($ref !== null) {
       
   342                     return ($this->_elements[] = $ref);
       
   343                 }
       
   344 
       
   345                 return ($this->_elements[] = $this->_readNumeric($nextLexeme));
       
   346         }
       
   347     }
       
   348 
       
   349 
       
   350     /**
       
   351      * Read string PDF object
       
   352      * Also reads trailing ')' from a pdf stream
       
   353      *
       
   354      * @return Zend_Pdf_Element_String
       
   355      * @throws Zend_Pdf_Exception
       
   356      */
       
   357     private function _readString()
       
   358     {
       
   359         $start = $this->offset;
       
   360         $openedBrackets = 1;
       
   361 
       
   362         $this->offset += strcspn($this->data, '()\\', $this->offset);
       
   363 
       
   364         while ($this->offset < strlen($this->data)) {
       
   365             switch (ord( $this->data[$this->offset] )) {
       
   366                 case 0x28: // '(' - opened bracket in the string, needs balanced pair.
       
   367                     $this->offset++;
       
   368                     $openedBrackets++;
       
   369                     break;
       
   370 
       
   371                 case 0x29: // ')' - pair to the opened bracket
       
   372                     $this->offset++;
       
   373                     $openedBrackets--;
       
   374                     break;
       
   375 
       
   376                 case 0x5C: // '\\' - escape sequence, skip next char from a check
       
   377                     $this->offset += 2;
       
   378             }
       
   379 
       
   380             if ($openedBrackets == 0) {
       
   381                 break; // end of string
       
   382             }
       
   383 
       
   384             $this->offset += strcspn($this->data, '()\\', $this->offset);
       
   385         }
       
   386         if ($openedBrackets != 0) {
       
   387             require_once 'Zend/Pdf/Exception.php';
       
   388             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Unexpected end of file while string reading. Offset - 0x%X. \')\' expected.', $start));
       
   389         }
       
   390 
       
   391         return new Zend_Pdf_Element_String(Zend_Pdf_Element_String::unescape( substr($this->data,
       
   392                                                                                      $start,
       
   393                                                                                      $this->offset - $start - 1) ));
       
   394     }
       
   395 
       
   396 
       
   397     /**
       
   398      * Read binary string PDF object
       
   399      * Also reads trailing '>' from a pdf stream
       
   400      *
       
   401      * @return Zend_Pdf_Element_String_Binary
       
   402      * @throws Zend_Pdf_Exception
       
   403      */
       
   404     private function _readBinaryString()
       
   405     {
       
   406         $start = $this->offset;
       
   407 
       
   408         $this->offset += strspn($this->data, "\x00\t\n\f\r 0123456789abcdefABCDEF", $this->offset);
       
   409 
       
   410         if ($this->offset >= strlen($this->data) - 1) {
       
   411             require_once 'Zend/Pdf/Exception.php';
       
   412             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Unexpected end of file while reading binary string. Offset - 0x%X. \'>\' expected.', $start));
       
   413         }
       
   414 
       
   415         if ($this->data[$this->offset++] != '>') {
       
   416             require_once 'Zend/Pdf/Exception.php';
       
   417             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Unexpected character while binary string reading. Offset - 0x%X.', $this->offset));
       
   418         }
       
   419 
       
   420         return new Zend_Pdf_Element_String_Binary(
       
   421                        Zend_Pdf_Element_String_Binary::unescape( substr($this->data,
       
   422                                                                         $start,
       
   423                                                                         $this->offset - $start - 1) ));
       
   424     }
       
   425 
       
   426 
       
   427     /**
       
   428      * Read array PDF object
       
   429      * Also reads trailing ']' from a pdf stream
       
   430      *
       
   431      * @return Zend_Pdf_Element_Array
       
   432      * @throws Zend_Pdf_Exception
       
   433      */
       
   434     private function _readArray()
       
   435     {
       
   436         $elements = array();
       
   437 
       
   438         while ( strlen($nextLexeme = $this->readLexeme()) != 0 ) {
       
   439             if ($nextLexeme != ']') {
       
   440                 $elements[] = $this->readElement($nextLexeme);
       
   441             } else {
       
   442                 return new Zend_Pdf_Element_Array($elements);
       
   443             }
       
   444         }
       
   445 
       
   446         require_once 'Zend/Pdf/Exception.php';
       
   447         throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Unexpected end of file while array reading. Offset - 0x%X. \']\' expected.', $this->offset));
       
   448     }
       
   449 
       
   450 
       
   451     /**
       
   452      * Read dictionary PDF object
       
   453      * Also reads trailing '>>' from a pdf stream
       
   454      *
       
   455      * @return Zend_Pdf_Element_Dictionary
       
   456      * @throws Zend_Pdf_Exception
       
   457      */
       
   458     private function _readDictionary()
       
   459     {
       
   460         $dictionary = new Zend_Pdf_Element_Dictionary();
       
   461 
       
   462         while ( strlen($nextLexeme = $this->readLexeme()) != 0 ) {
       
   463             if ($nextLexeme != '>>') {
       
   464                 $nameStart = $this->offset - strlen($nextLexeme);
       
   465 
       
   466                 $name  = $this->readElement($nextLexeme);
       
   467                 $value = $this->readElement();
       
   468 
       
   469                 if (!$name instanceof Zend_Pdf_Element_Name) {
       
   470                     require_once 'Zend/Pdf/Exception.php';
       
   471                     throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Name object expected while dictionary reading. Offset - 0x%X.', $nameStart));
       
   472                 }
       
   473 
       
   474                 $dictionary->add($name, $value);
       
   475             } else {
       
   476                 return $dictionary;
       
   477             }
       
   478         }
       
   479 
       
   480         require_once 'Zend/Pdf/Exception.php';
       
   481         throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Unexpected end of file while dictionary reading. Offset - 0x%X. \'>>\' expected.', $this->offset));
       
   482     }
       
   483 
       
   484 
       
   485     /**
       
   486      * Read reference PDF object
       
   487      *
       
   488      * @param string $nextLexeme
       
   489      * @return Zend_Pdf_Element_Reference
       
   490      */
       
   491     private function _readReference($nextLexeme = null)
       
   492     {
       
   493         $start = $this->offset;
       
   494 
       
   495         if ($nextLexeme === null) {
       
   496             $objNum = $this->readLexeme();
       
   497         } else {
       
   498             $objNum = $nextLexeme;
       
   499         }
       
   500         if (!ctype_digit($objNum)) { // it's not a reference
       
   501             $this->offset = $start;
       
   502             return null;
       
   503         }
       
   504 
       
   505         $genNum = $this->readLexeme();
       
   506         if (!ctype_digit($genNum)) { // it's not a reference
       
   507             $this->offset = $start;
       
   508             return null;
       
   509         }
       
   510 
       
   511         $rMark  = $this->readLexeme();
       
   512         if ($rMark != 'R') { // it's not a reference
       
   513             $this->offset = $start;
       
   514             return null;
       
   515         }
       
   516 
       
   517         $ref = new Zend_Pdf_Element_Reference((int)$objNum, (int)$genNum, $this->_context, $this->_objFactory->resolve());
       
   518 
       
   519         return $ref;
       
   520     }
       
   521 
       
   522 
       
   523     /**
       
   524      * Read numeric PDF object
       
   525      *
       
   526      * @param string $nextLexeme
       
   527      * @return Zend_Pdf_Element_Numeric
       
   528      */
       
   529     private function _readNumeric($nextLexeme = null)
       
   530     {
       
   531         if ($nextLexeme === null) {
       
   532             $nextLexeme = $this->readLexeme();
       
   533         }
       
   534 
       
   535         return new Zend_Pdf_Element_Numeric($nextLexeme);
       
   536     }
       
   537 
       
   538 
       
   539     /**
       
   540      * Read inderect object from a PDF stream
       
   541      *
       
   542      * @param integer $offset
       
   543      * @param Zend_Pdf_Element_Reference_Context $context
       
   544      * @return Zend_Pdf_Element_Object
       
   545      */
       
   546     public function getObject($offset, Zend_Pdf_Element_Reference_Context $context)
       
   547     {
       
   548         if ($offset === null ) {
       
   549             return new Zend_Pdf_Element_Null();
       
   550         }
       
   551 
       
   552         // Save current offset to make getObject() reentrant
       
   553         $offsetSave = $this->offset;
       
   554 
       
   555         $this->offset    = $offset;
       
   556         $this->_context  = $context;
       
   557         $this->_elements = array();
       
   558 
       
   559         $objNum = $this->readLexeme();
       
   560         if (!ctype_digit($objNum)) {
       
   561             require_once 'Zend/Pdf/Exception.php';
       
   562             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Object number expected.', $this->offset - strlen($objNum)));
       
   563         }
       
   564 
       
   565         $genNum = $this->readLexeme();
       
   566         if (!ctype_digit($genNum)) {
       
   567             require_once 'Zend/Pdf/Exception.php';
       
   568             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. Object generation number expected.', $this->offset - strlen($genNum)));
       
   569         }
       
   570 
       
   571         $objKeyword = $this->readLexeme();
       
   572         if ($objKeyword != 'obj') {
       
   573             require_once 'Zend/Pdf/Exception.php';
       
   574             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'obj\' keyword expected.', $this->offset - strlen($objKeyword)));
       
   575         }
       
   576 
       
   577         $objValue = $this->readElement();
       
   578 
       
   579         $nextLexeme = $this->readLexeme();
       
   580 
       
   581         if( $nextLexeme == 'endobj' ) {
       
   582             /**
       
   583              * Object is not generated by factory (thus it's not marked as modified object).
       
   584              * But factory is assigned to the obect.
       
   585              */
       
   586             $obj = new Zend_Pdf_Element_Object($objValue, (int)$objNum, (int)$genNum, $this->_objFactory->resolve());
       
   587 
       
   588             foreach ($this->_elements as $element) {
       
   589                 $element->setParentObject($obj);
       
   590             }
       
   591 
       
   592             // Restore offset value
       
   593             $this->offset = $offsetSave;
       
   594 
       
   595             return $obj;
       
   596         }
       
   597 
       
   598         /**
       
   599          * It's a stream object
       
   600          */
       
   601         if ($nextLexeme != 'stream') {
       
   602             require_once 'Zend/Pdf/Exception.php';
       
   603             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endobj\' or \'stream\' keywords expected.', $this->offset - strlen($nextLexeme)));
       
   604         }
       
   605 
       
   606         if (!$objValue instanceof Zend_Pdf_Element_Dictionary) {
       
   607             require_once 'Zend/Pdf/Exception.php';
       
   608             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)));
       
   609         }
       
   610 
       
   611         /**
       
   612          * References are automatically dereferenced at this moment.
       
   613          */
       
   614         $streamLength = $objValue->Length->value;
       
   615 
       
   616         /**
       
   617          * 'stream' keyword must be followed by either cr-lf sequence or lf character only.
       
   618          * This restriction gives the possibility to recognize all cases exactly
       
   619          */
       
   620         if ($this->data[$this->offset] == "\r" &&
       
   621             $this->data[$this->offset + 1] == "\n"    ) {
       
   622             $this->offset += 2;
       
   623         } else if ($this->data[$this->offset] == "\n"    ) {
       
   624             $this->offset++;
       
   625         } else {
       
   626             require_once 'Zend/Pdf/Exception.php';
       
   627             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)));
       
   628         }
       
   629 
       
   630         $dataOffset = $this->offset;
       
   631 
       
   632         $this->offset += $streamLength;
       
   633 
       
   634         $nextLexeme = $this->readLexeme();
       
   635         if ($nextLexeme != 'endstream') {
       
   636             require_once 'Zend/Pdf/Exception.php';
       
   637             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endstream\' keyword expected.', $this->offset - strlen($nextLexeme)));
       
   638         }
       
   639 
       
   640         $nextLexeme = $this->readLexeme();
       
   641         if ($nextLexeme != 'endobj') {
       
   642             require_once 'Zend/Pdf/Exception.php';
       
   643             throw new Zend_Pdf_Exception(sprintf('PDF file syntax error. Offset - 0x%X. \'endobj\' keyword expected.', $this->offset - strlen($nextLexeme)));
       
   644         }
       
   645 
       
   646         $obj = new Zend_Pdf_Element_Object_Stream(substr($this->data,
       
   647                                                          $dataOffset,
       
   648                                                          $streamLength),
       
   649                                                   (int)$objNum,
       
   650                                                   (int)$genNum,
       
   651                                                   $this->_objFactory->resolve(),
       
   652                                                   $objValue);
       
   653 
       
   654         foreach ($this->_elements as $element) {
       
   655             $element->setParentObject($obj);
       
   656         }
       
   657 
       
   658         // Restore offset value
       
   659         $this->offset = $offsetSave;
       
   660 
       
   661         return $obj;
       
   662     }
       
   663 
       
   664 
       
   665     /**
       
   666      * Get length of source string
       
   667      *
       
   668      * @return integer
       
   669      */
       
   670     public function getLength()
       
   671     {
       
   672         return strlen($this->data);
       
   673     }
       
   674 
       
   675     /**
       
   676      * Get source string
       
   677      *
       
   678      * @return string
       
   679      */
       
   680     public function getString()
       
   681     {
       
   682         return $this->data;
       
   683     }
       
   684 
       
   685 
       
   686     /**
       
   687      * Parse integer value from a binary stream
       
   688      *
       
   689      * @param string $stream
       
   690      * @param integer $offset
       
   691      * @param integer $size
       
   692      * @return integer
       
   693      */
       
   694     public static function parseIntFromStream($stream, $offset, $size)
       
   695     {
       
   696         $value = 0;
       
   697         for ($count = 0; $count < $size; $count++) {
       
   698             $value *= 256;
       
   699             $value += ord($stream[$offset + $count]);
       
   700         }
       
   701 
       
   702         return $value;
       
   703     }
       
   704 
       
   705 
       
   706 
       
   707     /**
       
   708      * Set current context
       
   709      *
       
   710      * @param Zend_Pdf_Element_Reference_Context $context
       
   711      */
       
   712     public function setContext(Zend_Pdf_Element_Reference_Context $context)
       
   713     {
       
   714         $this->_context = $context;
       
   715     }
       
   716 
       
   717     /**
       
   718      * Object constructor
       
   719      *
       
   720      * Note: PHP duplicates string, which is sent by value, only of it's updated.
       
   721      * Thus we don't need to care about overhead
       
   722      *
       
   723      * @param string $pdfString
       
   724      * @param Zend_Pdf_ElementFactory_Interface $factory
       
   725      */
       
   726     public function __construct($source, Zend_Pdf_ElementFactory_Interface $factory)
       
   727     {
       
   728         $this->data         = $source;
       
   729         $this->_objFactory  = $factory;
       
   730     }
       
   731 }