Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
/**
4
 * This file is part of FPDI
5
 *
6
 * @package   setasign\Fpdi
7
 * @copyright Copyright (c) 2023 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
        if (fseek($stream, 0) === -1) {
117
            throw new \InvalidArgumentException(
118
                'Given stream is not seekable!'
119
            );
120
        }
121
 
122
        $this->stream = $stream;
123
        $this->closeStream = $closeStream;
124
        $this->reset();
125
    }
126
 
127
    /**
128
     * The destructor.
129
     */
130
    public function __destruct()
131
    {
132
        $this->cleanUp();
133
    }
134
 
135
    /**
136
     * Closes the file handle.
137
     */
138
    public function cleanUp()
139
    {
140
        if ($this->closeStream && is_resource($this->stream)) {
141
            \fclose($this->stream);
142
        }
143
    }
144
 
145
    /**
146
     * Returns the byte length of the buffer.
147
     *
148
     * @param bool $atOffset
149
     * @return int
150
     */
151
    public function getBufferLength($atOffset = false)
152
    {
153
        if ($atOffset === false) {
154
            return $this->bufferLength;
155
        }
156
 
157
        return $this->bufferLength - $this->offset;
158
    }
159
 
160
    /**
161
     * Get the current position in the stream.
162
     *
163
     * @return int
164
     */
165
    public function getPosition()
166
    {
167
        return $this->position;
168
    }
169
 
170
    /**
171
     * Returns the current buffer.
172
     *
173
     * @param bool $atOffset
174
     * @return string
175
     */
176
    public function getBuffer($atOffset = true)
177
    {
178
        if ($atOffset === false) {
179
            return $this->buffer;
180
        }
181
 
182
        $string = \substr($this->buffer, $this->offset);
183
 
184
        return (string) $string;
185
    }
186
 
187
    /**
188
     * Gets a byte at a specific position in the buffer.
189
     *
190
     * If the position is invalid the method will return false.
191
     *
192
     * If the $position parameter is set to null the value of $this->offset will be used.
193
     *
194
     * @param int|null $position
195
     * @return string|bool
196
     */
197
    public function getByte($position = null)
198
    {
199
        $position = (int) ($position !== null ? $position : $this->offset);
200
        if (
201
            $position >= $this->bufferLength
202
            && (!$this->increaseLength() || $position >= $this->bufferLength)
203
        ) {
204
            return false;
205
        }
206
 
207
        return $this->buffer[$position];
208
    }
209
 
210
    /**
211
     * Returns a byte at a specific position, and set the offset to the next byte position.
212
     *
213
     * If the position is invalid the method will return false.
214
     *
215
     * If the $position parameter is set to null the value of $this->offset will be used.
216
     *
217
     * @param int|null $position
218
     * @return string|bool
219
     */
220
    public function readByte($position = null)
221
    {
222
        if ($position !== null) {
223
            $position = (int) $position;
224
            // check if needed bytes are available in the current buffer
225
            if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
226
                $this->reset($position);
227
                $offset = $this->offset;
228
            } else {
229
                $offset = $position - $this->position;
230
            }
231
        } else {
232
            $offset = $this->offset;
233
        }
234
 
235
        if (
236
            $offset >= $this->bufferLength
237
            && ((!$this->increaseLength()) || $offset >= $this->bufferLength)
238
        ) {
239
            return false;
240
        }
241
 
242
        $this->offset = $offset + 1;
243
        return $this->buffer[$offset];
244
    }
245
 
246
    /**
247
     * Read bytes from the current or a specific offset position and set the internal pointer to the next byte.
248
     *
249
     * If the position is invalid the method will return false.
250
     *
251
     * If the $position parameter is set to null the value of $this->offset will be used.
252
     *
253
     * @param int $length
254
     * @param int|null $position
255
     * @return string|false
256
     */
257
    public function readBytes($length, $position = null)
258
    {
259
        $length = (int) $length;
260
        if ($position !== null) {
261
            // check if needed bytes are available in the current buffer
262
            if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
263
                $this->reset($position, $length);
264
                $offset = $this->offset;
265
            } else {
266
                $offset = $position - $this->position;
267
            }
268
        } else {
269
            $offset = $this->offset;
270
        }
271
 
272
        if (
273
            ($offset + $length) > $this->bufferLength
274
            && ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength)
275
        ) {
276
            return false;
277
        }
278
 
279
        $bytes = \substr($this->buffer, $offset, $length);
280
        $this->offset = $offset + $length;
281
 
282
        return $bytes;
283
    }
284
 
285
    /**
286
     * Read a line from the current position.
287
     *
288
     * @param int $length
289
     * @return string|bool
290
     */
291
    public function readLine($length = 1024)
292
    {
293
        if ($this->ensureContent() === false) {
294
            return false;
295
        }
296
 
297
        $line = '';
298
        while ($this->ensureContent()) {
299
            $char = $this->readByte();
300
 
301
            if ($char === "\n") {
302
                break;
303
            }
304
 
305
            if ($char === "\r") {
306
                if ($this->getByte() === "\n") {
307
                    $this->addOffset(1);
308
                }
309
                break;
310
            }
311
 
312
            $line .= $char;
313
 
314
            if (\strlen($line) >= $length) {
315
                break;
316
            }
317
        }
318
 
319
        return $line;
320
    }
321
 
322
    /**
323
     * Set the offset position in the current buffer.
324
     *
325
     * @param int $offset
326
     */
327
    public function setOffset($offset)
328
    {
329
        if ($offset > $this->bufferLength || $offset < 0) {
330
            throw new \OutOfRangeException(
331
                \sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength)
332
            );
333
        }
334
 
335
        $this->offset = (int) $offset;
336
    }
337
 
338
    /**
339
     * Returns the current offset in the current buffer.
340
     *
341
     * @return int
342
     */
343
    public function getOffset()
344
    {
345
        return $this->offset;
346
    }
347
 
348
    /**
349
     * Add an offset to the current offset.
350
     *
351
     * @param int $offset
352
     */
353
    public function addOffset($offset)
354
    {
355
        $this->setOffset($this->offset + $offset);
356
    }
357
 
358
    /**
359
     * Make sure that there is at least one character beyond the current offset in the buffer.
360
     *
361
     * @return bool
362
     */
363
    public function ensureContent()
364
    {
365
        while ($this->offset >= $this->bufferLength) {
366
            if (!$this->increaseLength()) {
367
                return false;
368
            }
369
        }
370
        return true;
371
    }
372
 
373
    /**
374
     * Returns the stream.
375
     *
376
     * @return resource
377
     */
378
    public function getStream()
379
    {
380
        return $this->stream;
381
    }
382
 
383
    /**
384
     * Gets the total available length.
385
     *
386
     * @return int
387
     */
388
    public function getTotalLength()
389
    {
390
        if ($this->totalLength === null) {
391
            $stat = \fstat($this->stream);
392
            $this->totalLength = $stat['size'];
393
        }
394
 
395
        return $this->totalLength;
396
    }
397
 
398
    /**
399
     * Resets the buffer to a position and re-read the buffer with the given length.
400
     *
401
     * If the $pos parameter is negative the start buffer position will be the $pos'th position from
402
     * the end of the file.
403
     *
404
     * If the $pos parameter is negative and the absolute value is bigger then the totalLength of
405
     * the file $pos will set to zero.
406
     *
407
     * @param int|null $pos Start position of the new buffer
408
     * @param int $length Length of the new buffer. Mustn't be negative
409
     */
410
    public function reset($pos = 0, $length = 200)
411
    {
412
        if ($pos === null) {
413
            $pos = $this->position + $this->offset;
414
        } elseif ($pos < 0) {
415
            $pos = \max(0, $this->getTotalLength() + $pos);
416
        }
417
 
418
        \fseek($this->stream, $pos);
419
 
420
        $this->position = $pos;
421
        $this->buffer = $length > 0 ? \fread($this->stream, $length) : '';
422
        $this->bufferLength = \strlen($this->buffer);
423
        $this->offset = 0;
424
 
425
        // If a stream wrapper is in use it is possible that
426
        // length values > 8096 will be ignored, so use the
427
        // increaseLength()-method to correct that behavior
428
        if ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) {
429
            // increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer
430
            $this->buffer = \substr($this->buffer, 0, $length);
431
            $this->bufferLength = \strlen($this->buffer);
432
        }
433
    }
434
 
435
    /**
436
     * Ensures bytes in the buffer with a specific length and location in the file.
437
     *
438
     * @param int $pos
439
     * @param int $length
440
     * @see reset()
441
     */
442
    public function ensure($pos, $length)
443
    {
444
        if (
445
            $pos >= $this->position
446
            && $pos < ($this->position + $this->bufferLength)
447
            && ($this->position + $this->bufferLength) >= ($pos + $length)
448
        ) {
449
            $this->offset = $pos - $this->position;
450
        } else {
451
            $this->reset($pos, $length);
452
        }
453
    }
454
 
455
    /**
456
     * Forcefully read more data into the buffer.
457
     *
458
     * @param int $minLength
459
     * @return bool Returns false if the stream reaches the end
460
     */
461
    public function increaseLength($minLength = 100)
462
    {
463
        $length = \max($minLength, 100);
464
 
465
        if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) {
466
            return false;
467
        }
468
 
469
        $newLength = $this->bufferLength + $length;
470
        do {
471
            $this->buffer .= \fread($this->stream, $newLength - $this->bufferLength);
472
            $this->bufferLength = \strlen($this->buffer);
473
        } while (($this->bufferLength !== $newLength) && !\feof($this->stream));
474
 
475
        return true;
476
    }
477
}