See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401] [Versions 401 and 403]
1 <?php 2 3 /** 4 * This file is part of FPDI 5 * 6 * @package setasign\Fpdi 7 * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com) 8 * @license http://opensource.org/licenses/mit-license The MIT License 9 */ 10 11 namespace setasign\Fpdi\PdfParser\CrossReference; 12 13 use setasign\Fpdi\PdfParser\PdfParser; 14 use setasign\Fpdi\PdfParser\StreamReader; 15 16 /** 17 * Class LineReader 18 * 19 * This reader class read all cross-reference entries in a single run. 20 * It supports reading cross-references with e.g. invalid data (e.g. entries with a length < or > 20 bytes). 21 */ 22 class LineReader extends AbstractReader implements ReaderInterface 23 { 24 /** 25 * The object offsets. 26 * 27 * @var array 28 */ 29 protected $offsets; 30 31 /** 32 * LineReader constructor. 33 * 34 * @param PdfParser $parser 35 * @throws CrossReferenceException 36 */ 37 public function __construct(PdfParser $parser) 38 { 39 $this->read($this->extract($parser->getStreamReader())); 40 parent::__construct($parser); 41 } 42 43 /** 44 * @inheritdoc 45 */ 46 public function getOffsetFor($objectNumber) 47 { 48 if (isset($this->offsets[$objectNumber])) { 49 return $this->offsets[$objectNumber][0]; 50 } 51 52 return false; 53 } 54 55 /** 56 * Get all found offsets. 57 * 58 * @return array 59 */ 60 public function getOffsets() 61 { 62 return $this->offsets; 63 } 64 65 /** 66 * Extracts the cross reference data from the stream reader. 67 * 68 * @param StreamReader $reader 69 * @return string 70 * @throws CrossReferenceException 71 */ 72 protected function extract(StreamReader $reader) 73 { 74 $bytesPerCycle = 100; 75 $reader->reset(null, $bytesPerCycle); 76 77 $cycles = 0; 78 do { 79 // 6 = length of "trailer" - 1 80 $pos = \max(($bytesPerCycle * $cycles) - 6, 0); 81 $trailerPos = \strpos($reader->getBuffer(false), 'trailer', $pos); 82 $cycles++; 83 } while ($trailerPos === false && $reader->increaseLength($bytesPerCycle) !== false); 84 85 if ($trailerPos === false) { 86 throw new CrossReferenceException( 87 'Unexpected end of cross reference. "trailer"-keyword not found.', 88 CrossReferenceException::NO_TRAILER_FOUND 89 ); 90 } 91 92 $xrefContent = \substr($reader->getBuffer(false), 0, $trailerPos); 93 $reader->reset($reader->getPosition() + $trailerPos); 94 95 return $xrefContent; 96 } 97 98 /** 99 * Read the cross-reference entries. 100 * 101 * @param string $xrefContent 102 * @throws CrossReferenceException 103 */ 104 protected function read($xrefContent) 105 { 106 // get eol markers in the first 100 bytes 107 \preg_match_all("/(\r\n|\n|\r)/", \substr($xrefContent, 0, 100), $m); 108 109 if (\count($m[0]) === 0) { 110 throw new CrossReferenceException( 111 'No data found in cross-reference.', 112 CrossReferenceException::INVALID_DATA 113 ); 114 } 115 116 // count(array_count_values()) is faster then count(array_unique()) 117 // @see https://github.com/symfony/symfony/pull/23731 118 // can be reverted for php7.2 119 $differentLineEndings = \count(\array_count_values($m[0])); 120 if ($differentLineEndings > 1) { 121 $lines = \preg_split("/(\r\n|\n|\r)/", $xrefContent, -1, PREG_SPLIT_NO_EMPTY); 122 } else { 123 $lines = \explode($m[0][0], $xrefContent); 124 } 125 126 unset($differentLineEndings, $m); 127 if (!\is_array($lines)) { 128 $this->offsets = []; 129 return; 130 } 131 132 $start = 0; 133 $offsets = []; 134 135 // trim all lines and remove empty lines 136 $lines = \array_filter(\array_map('\trim', $lines)); 137 foreach ($lines as $line) { 138 $pieces = \explode(' ', $line); 139 140 switch (\count($pieces)) { 141 case 2: 142 $start = (int) $pieces[0]; 143 break; 144 145 case 3: 146 switch ($pieces[2]) { 147 case 'n': 148 $offsets[$start] = [(int) $pieces[0], (int) $pieces[1]]; 149 $start++; 150 break 2; 151 case 'f': 152 $start++; 153 break 2; 154 } 155 // fall through if pieces doesn't match 156 157 default: 158 throw new CrossReferenceException( 159 \sprintf('Unexpected data in xref table (%s)', \implode(' ', $pieces)), 160 CrossReferenceException::INVALID_DATA 161 ); 162 } 163 } 164 165 $this->offsets = $offsets; 166 } 167 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body