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\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
     * @return int|false
46
     */
47
    public function getOffsetFor($objectNumber)
48
    {
49
        if (isset($this->offsets[$objectNumber])) {
50
            return $this->offsets[$objectNumber][0];
51
        }
52
 
53
        return false;
54
    }
55
 
56
    /**
57
     * Get all found offsets.
58
     *
59
     * @return array
60
     */
61
    public function getOffsets()
62
    {
63
        return $this->offsets;
64
    }
65
 
66
    /**
67
     * Extracts the cross reference data from the stream reader.
68
     *
69
     * @param StreamReader $reader
70
     * @return string
71
     * @throws CrossReferenceException
72
     */
73
    protected function extract(StreamReader $reader)
74
    {
75
        $bytesPerCycle = 100;
76
        $reader->reset(null, $bytesPerCycle);
77
 
78
        $cycles = 0;
79
        do {
80
            // 6 = length of "trailer" - 1
81
            $pos = \max(($bytesPerCycle * $cycles) - 6, 0);
82
            $trailerPos = \strpos($reader->getBuffer(false), 'trailer', $pos);
83
            $cycles++;
84
        } while ($trailerPos === false && $reader->increaseLength($bytesPerCycle) !== false);
85
 
86
        if ($trailerPos === false) {
87
            throw new CrossReferenceException(
88
                'Unexpected end of cross reference. "trailer"-keyword not found.',
89
                CrossReferenceException::NO_TRAILER_FOUND
90
            );
91
        }
92
 
93
        $xrefContent = \substr($reader->getBuffer(false), 0, $trailerPos);
94
        $reader->reset($reader->getPosition() + $trailerPos);
95
 
96
        return $xrefContent;
97
    }
98
 
99
    /**
100
     * Read the cross-reference entries.
101
     *
102
     * @param string $xrefContent
103
     * @throws CrossReferenceException
104
     */
105
    protected function read($xrefContent)
106
    {
107
        // get eol markers in the first 100 bytes
108
        \preg_match_all("/(\r\n|\n|\r)/", \substr($xrefContent, 0, 100), $m);
109
 
110
        if (\count($m[0]) === 0) {
111
            throw new CrossReferenceException(
112
                'No data found in cross-reference.',
113
                CrossReferenceException::INVALID_DATA
114
            );
115
        }
116
 
117
        // count(array_count_values()) is faster then count(array_unique())
118
        // @see https://github.com/symfony/symfony/pull/23731
119
        // can be reverted for php7.2
120
        $differentLineEndings = \count(\array_count_values($m[0]));
121
        if ($differentLineEndings > 1) {
122
            $lines = \preg_split("/(\r\n|\n|\r)/", $xrefContent, -1, PREG_SPLIT_NO_EMPTY);
123
        } else {
124
            $lines = \explode($m[0][0], $xrefContent);
125
        }
126
 
127
        unset($differentLineEndings, $m);
128
        if (!\is_array($lines)) {
129
            $this->offsets = [];
130
            return;
131
        }
132
 
133
        $start = 0;
134
        $offsets = [];
135
 
136
        // trim all lines and remove empty lines
137
        $lines = \array_filter(\array_map('\trim', $lines));
138
        foreach ($lines as $line) {
139
            $pieces = \explode(' ', $line);
140
 
141
            switch (\count($pieces)) {
142
                case 2:
143
                    $start = (int) $pieces[0];
144
                    break;
145
 
146
                case 3:
147
                    switch ($pieces[2]) {
148
                        case 'n':
149
                            $offsets[$start] = [(int) $pieces[0], (int) $pieces[1]];
150
                            $start++;
151
                            break 2;
152
                        case 'f':
153
                            $start++;
154
                            break 2;
155
                    }
156
                    // fall through if pieces doesn't match
157
 
158
                default:
159
                    throw new CrossReferenceException(
160
                        \sprintf('Unexpected data in xref table (%s)', \implode(' ', $pieces)),
161
                        CrossReferenceException::INVALID_DATA
162
                    );
163
            }
164
        }
165
 
166
        $this->offsets = $offsets;
167
    }
168
}