AutorÃa | Ultima modificación | Ver Log |
<?php/*** This file is part of FPDI** @package setasign\Fpdi* @copyright Copyright (c) 2024 Setasign GmbH & Co. KG (https://www.setasign.com)* @license http://opensource.org/licenses/mit-license The MIT License*/namespace setasign\Fpdi\PdfParser;/*** A stream reader class*/class StreamReader{/*** Creates a stream reader instance by a string value.** @param string $content* @param int $maxMemory* @return StreamReader*/public static function createByString($content, $maxMemory = 2097152){$h = \fopen('php://temp/maxmemory:' . ((int) $maxMemory), 'r+b');\fwrite($h, $content);\rewind($h);return new self($h, true);}/*** Creates a stream reader instance by a filename.** @param string $filename* @return StreamReader*/public static function createByFile($filename){$h = \fopen($filename, 'rb');return new self($h, true);}/*** Defines whether the stream should be closed when the stream reader instance is deconstructed or not.** @var bool*/protected $closeStream;/*** The stream resource.** @var resource*/protected $stream;/*** The byte-offset position in the stream.** @var int*/protected $position;/*** The byte-offset position in the buffer.** @var int*/protected $offset;/*** The buffer length.** @var int*/protected $bufferLength;/*** The total length of the stream.** @var int*/protected $totalLength;/*** The buffer.** @var string*/protected $buffer;/*** StreamReader constructor.** @param resource $stream* @param bool $closeStream Defines whether to close the stream resource if the instance is destructed or not.*/public function __construct($stream, $closeStream = false){if (!\is_resource($stream)) {throw new \InvalidArgumentException('No stream given.');}$metaData = \stream_get_meta_data($stream);if (!$metaData['seekable']) {throw new \InvalidArgumentException('Given stream is not seekable!');}if (fseek($stream, 0) === -1) {throw new \InvalidArgumentException('Given stream is not seekable!');}$this->stream = $stream;$this->closeStream = $closeStream;$this->reset();}/*** The destructor.*/public function __destruct(){$this->cleanUp();}/*** Closes the file handle.*/public function cleanUp(){if ($this->closeStream && is_resource($this->stream)) {\fclose($this->stream);}}/*** Returns the byte length of the buffer.** @param bool $atOffset* @return int*/public function getBufferLength($atOffset = false){if ($atOffset === false) {return $this->bufferLength;}return $this->bufferLength - $this->offset;}/*** Get the current position in the stream.** @return int*/public function getPosition(){return $this->position;}/*** Returns the current buffer.** @param bool $atOffset* @return string*/public function getBuffer($atOffset = true){if ($atOffset === false) {return $this->buffer;}$string = \substr($this->buffer, $this->offset);return (string) $string;}/*** Gets a byte at a specific position in the buffer.** If the position is invalid the method will return false.** If the $position parameter is set to null the value of $this->offset will be used.** @param int|null $position* @return string|bool*/public function getByte($position = null){$position = (int) ($position !== null ? $position : $this->offset);if ($position >= $this->bufferLength&& (!$this->increaseLength() || $position >= $this->bufferLength)) {return false;}return $this->buffer[$position];}/*** Returns a byte at a specific position, and set the offset to the next byte position.** If the position is invalid the method will return false.** If the $position parameter is set to null the value of $this->offset will be used.** @param int|null $position* @return string|bool*/public function readByte($position = null){if ($position !== null) {$position = (int) $position;// check if needed bytes are available in the current bufferif (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {$this->reset($position);$offset = $this->offset;} else {$offset = $position - $this->position;}} else {$offset = $this->offset;}if ($offset >= $this->bufferLength&& ((!$this->increaseLength()) || $offset >= $this->bufferLength)) {return false;}$this->offset = $offset + 1;return $this->buffer[$offset];}/*** Read bytes from the current or a specific offset position and set the internal pointer to the next byte.** If the position is invalid the method will return false.** If the $position parameter is set to null the value of $this->offset will be used.** @param int $length* @param int|null $position* @return string|false*/public function readBytes($length, $position = null){$length = (int) $length;if ($position !== null) {// check if needed bytes are available in the current bufferif (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {$this->reset($position, $length);$offset = $this->offset;} else {$offset = $position - $this->position;}} else {$offset = $this->offset;}if (($offset + $length) > $this->bufferLength&& ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength)) {return false;}$bytes = \substr($this->buffer, $offset, $length);$this->offset = $offset + $length;return $bytes;}/*** Read a line from the current position.** @param int $length* @return string|bool*/public function readLine($length = 1024){if ($this->ensureContent() === false) {return false;}$line = '';while ($this->ensureContent()) {$char = $this->readByte();if ($char === "\n") {break;}if ($char === "\r") {if ($this->getByte() === "\n") {$this->addOffset(1);}break;}$line .= $char;if (\strlen($line) >= $length) {break;}}return $line;}/*** Set the offset position in the current buffer.** @param int $offset*/public function setOffset($offset){if ($offset > $this->bufferLength || $offset < 0) {throw new \OutOfRangeException(\sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength));}$this->offset = (int) $offset;}/*** Returns the current offset in the current buffer.** @return int*/public function getOffset(){return $this->offset;}/*** Add an offset to the current offset.** @param int $offset*/public function addOffset($offset){$this->setOffset($this->offset + $offset);}/*** Make sure that there is at least one character beyond the current offset in the buffer.** @return bool*/public function ensureContent(){while ($this->offset >= $this->bufferLength) {if (!$this->increaseLength()) {return false;}}return true;}/*** Returns the stream.** @return resource*/public function getStream(){return $this->stream;}/*** Gets the total available length.** @return int*/public function getTotalLength(){if ($this->totalLength === null) {$stat = \fstat($this->stream);$this->totalLength = $stat['size'];}return $this->totalLength;}/*** Resets the buffer to a position and re-read the buffer with the given length.** If the $pos parameter is negative the start buffer position will be the $pos'th position from* the end of the file.** If the $pos parameter is negative and the absolute value is bigger then the totalLength of* the file $pos will set to zero.** @param int|null $pos Start position of the new buffer* @param int $length Length of the new buffer. Mustn't be negative*/public function reset($pos = 0, $length = 200){if ($pos === null) {$pos = $this->position + $this->offset;} elseif ($pos < 0) {$pos = \max(0, $this->getTotalLength() + $pos);}\fseek($this->stream, $pos);$this->position = $pos;$this->offset = 0;if ($length > 0) {$this->buffer = (string) \fread($this->stream, $length);} else {$this->buffer = '';}$this->bufferLength = \strlen($this->buffer);// If a stream wrapper is in use it is possible that// length values > 8096 will be ignored, so use the// increaseLength()-method to correct that behaviorif ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) {// increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer$this->buffer = (string) \substr($this->buffer, 0, $length);$this->bufferLength = \strlen($this->buffer);}}/*** Ensures bytes in the buffer with a specific length and location in the file.** @param int $pos* @param int $length* @see reset()*/public function ensure($pos, $length){if ($pos >= $this->position&& $pos < ($this->position + $this->bufferLength)&& ($this->position + $this->bufferLength) >= ($pos + $length)) {$this->offset = $pos - $this->position;} else {$this->reset($pos, $length);}}/*** Forcefully read more data into the buffer.** @param int $minLength* @return bool Returns false if the stream reaches the end*/public function increaseLength($minLength = 100){$length = \max($minLength, 100);if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) {return false;}$newLength = $this->bufferLength + $length;do {$this->buffer .= \fread($this->stream, $newLength - $this->bufferLength);$this->bufferLength = \strlen($this->buffer);} while (($this->bufferLength !== $newLength) && !\feof($this->stream));return true;}}