See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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; 12 13 /** 14 * A stream reader class 15 */ 16 class StreamReader 17 { 18 /** 19 * Creates a stream reader instance by a string value. 20 * 21 * @param string $content 22 * @param int $maxMemory 23 * @return StreamReader 24 */ 25 public static function createByString($content, $maxMemory = 2097152) 26 { 27 $h = \fopen('php://temp/maxmemory:' . ((int) $maxMemory), 'r+b'); 28 \fwrite($h, $content); 29 \rewind($h); 30 31 return new self($h, true); 32 } 33 34 /** 35 * Creates a stream reader instance by a filename. 36 * 37 * @param string $filename 38 * @return StreamReader 39 */ 40 public static function createByFile($filename) 41 { 42 $h = \fopen($filename, 'rb'); 43 return new self($h, true); 44 } 45 46 /** 47 * Defines whether the stream should be closed when the stream reader instance is deconstructed or not. 48 * 49 * @var bool 50 */ 51 protected $closeStream; 52 53 /** 54 * The stream resource. 55 * 56 * @var resource 57 */ 58 protected $stream; 59 60 /** 61 * The byte-offset position in the stream. 62 * 63 * @var int 64 */ 65 protected $position; 66 67 /** 68 * The byte-offset position in the buffer. 69 * 70 * @var int 71 */ 72 protected $offset; 73 74 /** 75 * The buffer length. 76 * 77 * @var int 78 */ 79 protected $bufferLength; 80 81 /** 82 * The total length of the stream. 83 * 84 * @var int 85 */ 86 protected $totalLength; 87 88 /** 89 * The buffer. 90 * 91 * @var string 92 */ 93 protected $buffer; 94 95 /** 96 * StreamReader constructor. 97 * 98 * @param resource $stream 99 * @param bool $closeStream Defines whether to close the stream resource if the instance is destructed or not. 100 */ 101 public function __construct($stream, $closeStream = false) 102 { 103 if (!\is_resource($stream)) { 104 throw new \InvalidArgumentException( 105 'No stream given.' 106 ); 107 } 108 109 $metaData = \stream_get_meta_data($stream); 110 if (!$metaData['seekable']) { 111 throw new \InvalidArgumentException( 112 'Given stream is not seekable!' 113 ); 114 } 115 116 $this->stream = $stream; 117 $this->closeStream = $closeStream; 118 $this->reset(); 119 } 120 121 /** 122 * The destructor. 123 */ 124 public function __destruct() 125 { 126 $this->cleanUp(); 127 } 128 129 /** 130 * Closes the file handle. 131 */ 132 public function cleanUp() 133 { 134 if ($this->closeStream && is_resource($this->stream)) { 135 \fclose($this->stream); 136 } 137 } 138 139 /** 140 * Returns the byte length of the buffer. 141 * 142 * @param bool $atOffset 143 * @return int 144 */ 145 public function getBufferLength($atOffset = false) 146 { 147 if ($atOffset === false) { 148 return $this->bufferLength; 149 } 150 151 return $this->bufferLength - $this->offset; 152 } 153 154 /** 155 * Get the current position in the stream. 156 * 157 * @return int 158 */ 159 public function getPosition() 160 { 161 return $this->position; 162 } 163 164 /** 165 * Returns the current buffer. 166 * 167 * @param bool $atOffset 168 * @return string 169 */ 170 public function getBuffer($atOffset = true) 171 { 172 if ($atOffset === false) { 173 return $this->buffer; 174 } 175 176 $string = \substr($this->buffer, $this->offset); 177 178 return (string) $string; 179 } 180 181 /** 182 * Gets a byte at a specific position in the buffer. 183 * 184 * If the position is invalid the method will return false. 185 * 186 * If the $position parameter is set to null the value of $this->offset will be used. 187 * 188 * @param int|null $position 189 * @return string|bool 190 */ 191 public function getByte($position = null) 192 { 193 $position = (int) ($position !== null ? $position : $this->offset); 194 if ( 195 $position >= $this->bufferLength 196 && (!$this->increaseLength() || $position >= $this->bufferLength) 197 ) { 198 return false; 199 } 200 201 return $this->buffer[$position]; 202 } 203 204 /** 205 * Returns a byte at a specific position, and set the offset to the next byte position. 206 * 207 * If the position is invalid the method will return false. 208 * 209 * If the $position parameter is set to null the value of $this->offset will be used. 210 * 211 * @param int|null $position 212 * @return string|bool 213 */ 214 public function readByte($position = null) 215 { 216 if ($position !== null) { 217 $position = (int) $position; 218 // check if needed bytes are available in the current buffer 219 if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) { 220 $this->reset($position); 221 $offset = $this->offset; 222 } else { 223 $offset = $position - $this->position; 224 } 225 } else { 226 $offset = $this->offset; 227 } 228 229 if ( 230 $offset >= $this->bufferLength 231 && ((!$this->increaseLength()) || $offset >= $this->bufferLength) 232 ) { 233 return false; 234 } 235 236 $this->offset = $offset + 1; 237 return $this->buffer[$offset]; 238 } 239 240 /** 241 * Read bytes from the current or a specific offset position and set the internal pointer to the next byte. 242 * 243 * If the position is invalid the method will return false. 244 * 245 * If the $position parameter is set to null the value of $this->offset will be used. 246 * 247 * @param int $length 248 * @param int|null $position 249 * @return string|false 250 */ 251 public function readBytes($length, $position = null) 252 { 253 $length = (int) $length; 254 if ($position !== null) { 255 // check if needed bytes are available in the current buffer 256 if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) { 257 $this->reset($position, $length); 258 $offset = $this->offset; 259 } else { 260 $offset = $position - $this->position; 261 } 262 } else { 263 $offset = $this->offset; 264 } 265 266 if ( 267 ($offset + $length) > $this->bufferLength 268 && ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength) 269 ) { 270 return false; 271 } 272 273 $bytes = \substr($this->buffer, $offset, $length); 274 $this->offset = $offset + $length; 275 276 return $bytes; 277 } 278 279 /** 280 * Read a line from the current position. 281 * 282 * @param int $length 283 * @return string|bool 284 */ 285 public function readLine($length = 1024) 286 { 287 if ($this->ensureContent() === false) { 288 return false; 289 } 290 291 $line = ''; 292 while ($this->ensureContent()) { 293 $char = $this->readByte(); 294 295 if ($char === "\n") { 296 break; 297 } 298 299 if ($char === "\r") { 300 if ($this->getByte() === "\n") { 301 $this->addOffset(1); 302 } 303 break; 304 } 305 306 $line .= $char; 307 308 if (\strlen($line) >= $length) { 309 break; 310 } 311 } 312 313 return $line; 314 } 315 316 /** 317 * Set the offset position in the current buffer. 318 * 319 * @param int $offset 320 */ 321 public function setOffset($offset) 322 { 323 if ($offset > $this->bufferLength || $offset < 0) { 324 throw new \OutOfRangeException( 325 \sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength) 326 ); 327 } 328 329 $this->offset = (int) $offset; 330 } 331 332 /** 333 * Returns the current offset in the current buffer. 334 * 335 * @return int 336 */ 337 public function getOffset() 338 { 339 return $this->offset; 340 } 341 342 /** 343 * Add an offset to the current offset. 344 * 345 * @param int $offset 346 */ 347 public function addOffset($offset) 348 { 349 $this->setOffset($this->offset + $offset); 350 } 351 352 /** 353 * Make sure that there is at least one character beyond the current offset in the buffer. 354 * 355 * @return bool 356 */ 357 public function ensureContent() 358 { 359 while ($this->offset >= $this->bufferLength) { 360 if (!$this->increaseLength()) { 361 return false; 362 } 363 } 364 return true; 365 } 366 367 /** 368 * Returns the stream. 369 * 370 * @return resource 371 */ 372 public function getStream() 373 { 374 return $this->stream; 375 } 376 377 /** 378 * Gets the total available length. 379 * 380 * @return int 381 */ 382 public function getTotalLength() 383 { 384 if ($this->totalLength === null) { 385 $stat = \fstat($this->stream); 386 $this->totalLength = $stat['size']; 387 } 388 389 return $this->totalLength; 390 } 391 392 /** 393 * Resets the buffer to a position and re-read the buffer with the given length. 394 * 395 * If the $pos parameter is negative the start buffer position will be the $pos'th position from 396 * the end of the file. 397 * 398 * If the $pos parameter is negative and the absolute value is bigger then the totalLength of 399 * the file $pos will set to zero. 400 * 401 * @param int|null $pos Start position of the new buffer 402 * @param int $length Length of the new buffer. Mustn't be negative 403 */ 404 public function reset($pos = 0, $length = 200) 405 { 406 if ($pos === null) { 407 $pos = $this->position + $this->offset; 408 } elseif ($pos < 0) { 409 $pos = \max(0, $this->getTotalLength() + $pos); 410 } 411 412 \fseek($this->stream, $pos); 413 414 $this->position = $pos; 415 $this->buffer = $length > 0 ? \fread($this->stream, $length) : ''; 416 $this->bufferLength = \strlen($this->buffer); 417 $this->offset = 0; 418 419 // If a stream wrapper is in use it is possible that 420 // length values > 8096 will be ignored, so use the 421 // increaseLength()-method to correct that behavior 422 if ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) { 423 // increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer 424 $this->buffer = \substr($this->buffer, 0, $length); 425 $this->bufferLength = \strlen($this->buffer); 426 } 427 } 428 429 /** 430 * Ensures bytes in the buffer with a specific length and location in the file. 431 * 432 * @param int $pos 433 * @param int $length 434 * @see reset() 435 */ 436 public function ensure($pos, $length) 437 { 438 if ( 439 $pos >= $this->position 440 && $pos < ($this->position + $this->bufferLength) 441 && ($this->position + $this->bufferLength) >= ($pos + $length) 442 ) { 443 $this->offset = $pos - $this->position; 444 } else { 445 $this->reset($pos, $length); 446 } 447 } 448 449 /** 450 * Forcefully read more data into the buffer. 451 * 452 * @param int $minLength 453 * @return bool Returns false if the stream reaches the end 454 */ 455 public function increaseLength($minLength = 100) 456 { 457 $length = \max($minLength, 100); 458 459 if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) { 460 return false; 461 } 462 463 $newLength = $this->bufferLength + $length; 464 do { 465 $this->buffer .= \fread($this->stream, $newLength - $this->bufferLength); 466 $this->bufferLength = \strlen($this->buffer); 467 } while (($this->bufferLength !== $newLength) && !\feof($this->stream)); 468 469 return true; 470 } 471 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body