xref: /plugin/dw2pdf/vendor/setasign/fpdi/src/PdfParser/PdfParser.php (revision dc4d9dc689082c963d5c1d9ee679553326788c6e)
1*dc4d9dc6SAnna Dabrowska<?php
2*dc4d9dc6SAnna Dabrowska/**
3*dc4d9dc6SAnna Dabrowska * This file is part of FPDI
4*dc4d9dc6SAnna Dabrowska *
5*dc4d9dc6SAnna Dabrowska * @package   setasign\Fpdi
6*dc4d9dc6SAnna Dabrowska * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com)
7*dc4d9dc6SAnna Dabrowska * @license   http://opensource.org/licenses/mit-license The MIT License
8*dc4d9dc6SAnna Dabrowska */
9*dc4d9dc6SAnna Dabrowska
10*dc4d9dc6SAnna Dabrowskanamespace setasign\Fpdi\PdfParser;
11*dc4d9dc6SAnna Dabrowska
12*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\CrossReference\CrossReference;
13*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
14*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfArray;
15*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfBoolean;
16*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfDictionary;
17*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfHexString;
18*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
19*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
20*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfName;
21*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfNull;
22*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfNumeric;
23*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfString;
24*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfToken;
25*dc4d9dc6SAnna Dabrowskause setasign\Fpdi\PdfParser\Type\PdfType;
26*dc4d9dc6SAnna Dabrowska
27*dc4d9dc6SAnna Dabrowska/**
28*dc4d9dc6SAnna Dabrowska * A PDF parser class
29*dc4d9dc6SAnna Dabrowska *
30*dc4d9dc6SAnna Dabrowska * @package setasign\Fpdi\PdfParser
31*dc4d9dc6SAnna Dabrowska */
32*dc4d9dc6SAnna Dabrowskaclass PdfParser
33*dc4d9dc6SAnna Dabrowska{
34*dc4d9dc6SAnna Dabrowska    /**
35*dc4d9dc6SAnna Dabrowska     * @var StreamReader
36*dc4d9dc6SAnna Dabrowska     */
37*dc4d9dc6SAnna Dabrowska    protected $streamReader;
38*dc4d9dc6SAnna Dabrowska
39*dc4d9dc6SAnna Dabrowska    /**
40*dc4d9dc6SAnna Dabrowska     * @var Tokenizer
41*dc4d9dc6SAnna Dabrowska     */
42*dc4d9dc6SAnna Dabrowska    protected $tokenizer;
43*dc4d9dc6SAnna Dabrowska
44*dc4d9dc6SAnna Dabrowska    /**
45*dc4d9dc6SAnna Dabrowska     * The file header.
46*dc4d9dc6SAnna Dabrowska     *
47*dc4d9dc6SAnna Dabrowska     * @var string
48*dc4d9dc6SAnna Dabrowska     */
49*dc4d9dc6SAnna Dabrowska    protected $fileHeader;
50*dc4d9dc6SAnna Dabrowska
51*dc4d9dc6SAnna Dabrowska    /**
52*dc4d9dc6SAnna Dabrowska     * The offset to the file header.
53*dc4d9dc6SAnna Dabrowska     *
54*dc4d9dc6SAnna Dabrowska     * @var int
55*dc4d9dc6SAnna Dabrowska     */
56*dc4d9dc6SAnna Dabrowska    protected $fileHeaderOffset;
57*dc4d9dc6SAnna Dabrowska
58*dc4d9dc6SAnna Dabrowska    /**
59*dc4d9dc6SAnna Dabrowska     * @var CrossReference
60*dc4d9dc6SAnna Dabrowska     */
61*dc4d9dc6SAnna Dabrowska    protected $xref;
62*dc4d9dc6SAnna Dabrowska
63*dc4d9dc6SAnna Dabrowska    /**
64*dc4d9dc6SAnna Dabrowska     * All read objects.
65*dc4d9dc6SAnna Dabrowska     *
66*dc4d9dc6SAnna Dabrowska     * @var array
67*dc4d9dc6SAnna Dabrowska     */
68*dc4d9dc6SAnna Dabrowska    protected $objects = [];
69*dc4d9dc6SAnna Dabrowska
70*dc4d9dc6SAnna Dabrowska    /**
71*dc4d9dc6SAnna Dabrowska     * PdfParser constructor.
72*dc4d9dc6SAnna Dabrowska     *
73*dc4d9dc6SAnna Dabrowska     * @param StreamReader $streamReader
74*dc4d9dc6SAnna Dabrowska     */
75*dc4d9dc6SAnna Dabrowska    public function __construct(StreamReader $streamReader)
76*dc4d9dc6SAnna Dabrowska    {
77*dc4d9dc6SAnna Dabrowska        $this->streamReader = $streamReader;
78*dc4d9dc6SAnna Dabrowska        $this->tokenizer = new Tokenizer($streamReader);
79*dc4d9dc6SAnna Dabrowska    }
80*dc4d9dc6SAnna Dabrowska
81*dc4d9dc6SAnna Dabrowska    /**
82*dc4d9dc6SAnna Dabrowska     * Removes cycled references.
83*dc4d9dc6SAnna Dabrowska     *
84*dc4d9dc6SAnna Dabrowska     * @internal
85*dc4d9dc6SAnna Dabrowska     */
86*dc4d9dc6SAnna Dabrowska    public function cleanUp()
87*dc4d9dc6SAnna Dabrowska    {
88*dc4d9dc6SAnna Dabrowska        $this->xref = null;
89*dc4d9dc6SAnna Dabrowska    }
90*dc4d9dc6SAnna Dabrowska
91*dc4d9dc6SAnna Dabrowska    /**
92*dc4d9dc6SAnna Dabrowska     * Get the stream reader instance.
93*dc4d9dc6SAnna Dabrowska     *
94*dc4d9dc6SAnna Dabrowska     * @return StreamReader
95*dc4d9dc6SAnna Dabrowska     */
96*dc4d9dc6SAnna Dabrowska    public function getStreamReader()
97*dc4d9dc6SAnna Dabrowska    {
98*dc4d9dc6SAnna Dabrowska        return $this->streamReader;
99*dc4d9dc6SAnna Dabrowska    }
100*dc4d9dc6SAnna Dabrowska
101*dc4d9dc6SAnna Dabrowska    /**
102*dc4d9dc6SAnna Dabrowska     * Get the tokenizer instance.
103*dc4d9dc6SAnna Dabrowska     *
104*dc4d9dc6SAnna Dabrowska     * @return Tokenizer
105*dc4d9dc6SAnna Dabrowska     */
106*dc4d9dc6SAnna Dabrowska    public function getTokenizer()
107*dc4d9dc6SAnna Dabrowska    {
108*dc4d9dc6SAnna Dabrowska        return $this->tokenizer;
109*dc4d9dc6SAnna Dabrowska    }
110*dc4d9dc6SAnna Dabrowska
111*dc4d9dc6SAnna Dabrowska    /**
112*dc4d9dc6SAnna Dabrowska     * Resolves the file header.
113*dc4d9dc6SAnna Dabrowska     *
114*dc4d9dc6SAnna Dabrowska     * @throws PdfParserException
115*dc4d9dc6SAnna Dabrowska     * @return int
116*dc4d9dc6SAnna Dabrowska     */
117*dc4d9dc6SAnna Dabrowska    protected function resolveFileHeader()
118*dc4d9dc6SAnna Dabrowska    {
119*dc4d9dc6SAnna Dabrowska        if ($this->fileHeader) {
120*dc4d9dc6SAnna Dabrowska            return $this->fileHeaderOffset;
121*dc4d9dc6SAnna Dabrowska        }
122*dc4d9dc6SAnna Dabrowska
123*dc4d9dc6SAnna Dabrowska        $this->streamReader->reset(0);
124*dc4d9dc6SAnna Dabrowska        $offset = false;
125*dc4d9dc6SAnna Dabrowska        $maxIterations = 1000;
126*dc4d9dc6SAnna Dabrowska        while (true) {
127*dc4d9dc6SAnna Dabrowska            $buffer = $this->streamReader->getBuffer(false);
128*dc4d9dc6SAnna Dabrowska            $offset = \strpos($buffer, '%PDF-');
129*dc4d9dc6SAnna Dabrowska            if ($offset === false) {
130*dc4d9dc6SAnna Dabrowska                if (!$this->streamReader->increaseLength(100) || (--$maxIterations === 0)) {
131*dc4d9dc6SAnna Dabrowska                    throw new PdfParserException(
132*dc4d9dc6SAnna Dabrowska                        'Unable to find PDF file header.',
133*dc4d9dc6SAnna Dabrowska                        PdfParserException::FILE_HEADER_NOT_FOUND
134*dc4d9dc6SAnna Dabrowska                    );
135*dc4d9dc6SAnna Dabrowska                }
136*dc4d9dc6SAnna Dabrowska                continue;
137*dc4d9dc6SAnna Dabrowska            }
138*dc4d9dc6SAnna Dabrowska            break;
139*dc4d9dc6SAnna Dabrowska        }
140*dc4d9dc6SAnna Dabrowska
141*dc4d9dc6SAnna Dabrowska        $this->fileHeaderOffset = $offset;
142*dc4d9dc6SAnna Dabrowska        $this->streamReader->setOffset($offset);
143*dc4d9dc6SAnna Dabrowska
144*dc4d9dc6SAnna Dabrowska        $this->fileHeader = \trim($this->streamReader->readLine());
145*dc4d9dc6SAnna Dabrowska        return $this->fileHeaderOffset;
146*dc4d9dc6SAnna Dabrowska    }
147*dc4d9dc6SAnna Dabrowska
148*dc4d9dc6SAnna Dabrowska    /**
149*dc4d9dc6SAnna Dabrowska     * Get the cross reference instance.
150*dc4d9dc6SAnna Dabrowska     *
151*dc4d9dc6SAnna Dabrowska     * @return CrossReference
152*dc4d9dc6SAnna Dabrowska     * @throws CrossReferenceException
153*dc4d9dc6SAnna Dabrowska     * @throws PdfParserException
154*dc4d9dc6SAnna Dabrowska     */
155*dc4d9dc6SAnna Dabrowska    public function getCrossReference()
156*dc4d9dc6SAnna Dabrowska    {
157*dc4d9dc6SAnna Dabrowska        if ($this->xref === null) {
158*dc4d9dc6SAnna Dabrowska            $this->xref = new CrossReference($this, $this->resolveFileHeader());
159*dc4d9dc6SAnna Dabrowska        }
160*dc4d9dc6SAnna Dabrowska
161*dc4d9dc6SAnna Dabrowska        return $this->xref;
162*dc4d9dc6SAnna Dabrowska    }
163*dc4d9dc6SAnna Dabrowska
164*dc4d9dc6SAnna Dabrowska    /**
165*dc4d9dc6SAnna Dabrowska     * Get the PDF version.
166*dc4d9dc6SAnna Dabrowska     *
167*dc4d9dc6SAnna Dabrowska     * @return int[] An array of major and minor version.
168*dc4d9dc6SAnna Dabrowska     * @throws PdfParserException
169*dc4d9dc6SAnna Dabrowska     */
170*dc4d9dc6SAnna Dabrowska    public function getPdfVersion()
171*dc4d9dc6SAnna Dabrowska    {
172*dc4d9dc6SAnna Dabrowska        $this->resolveFileHeader();
173*dc4d9dc6SAnna Dabrowska
174*dc4d9dc6SAnna Dabrowska        if (\preg_match('/%PDF-(\d)\.(\d)/', $this->fileHeader, $result) === 0) {
175*dc4d9dc6SAnna Dabrowska            throw new PdfParserException(
176*dc4d9dc6SAnna Dabrowska                'Unable to extract PDF version from file header.',
177*dc4d9dc6SAnna Dabrowska                PdfParserException::PDF_VERSION_NOT_FOUND
178*dc4d9dc6SAnna Dabrowska            );
179*dc4d9dc6SAnna Dabrowska        }
180*dc4d9dc6SAnna Dabrowska        list(, $major, $minor) = $result;
181*dc4d9dc6SAnna Dabrowska
182*dc4d9dc6SAnna Dabrowska        $catalog = $this->getCatalog();
183*dc4d9dc6SAnna Dabrowska        if (isset($catalog->value['Version'])) {
184*dc4d9dc6SAnna Dabrowska            $versionParts = \explode('.', PdfName::unescape(PdfType::resolve($catalog->value['Version'], $this)->value));
185*dc4d9dc6SAnna Dabrowska            if (count($versionParts) === 2) {
186*dc4d9dc6SAnna Dabrowska                list($major, $minor) = $versionParts;
187*dc4d9dc6SAnna Dabrowska            }
188*dc4d9dc6SAnna Dabrowska        }
189*dc4d9dc6SAnna Dabrowska
190*dc4d9dc6SAnna Dabrowska        return [(int) $major, (int) $minor];
191*dc4d9dc6SAnna Dabrowska    }
192*dc4d9dc6SAnna Dabrowska
193*dc4d9dc6SAnna Dabrowska    /**
194*dc4d9dc6SAnna Dabrowska     * Get the catalog dictionary.
195*dc4d9dc6SAnna Dabrowska     *
196*dc4d9dc6SAnna Dabrowska     * @return PdfDictionary
197*dc4d9dc6SAnna Dabrowska     * @throws Type\PdfTypeException
198*dc4d9dc6SAnna Dabrowska     * @throws CrossReferenceException
199*dc4d9dc6SAnna Dabrowska     * @throws PdfParserException
200*dc4d9dc6SAnna Dabrowska     */
201*dc4d9dc6SAnna Dabrowska    public function getCatalog()
202*dc4d9dc6SAnna Dabrowska    {
203*dc4d9dc6SAnna Dabrowska        $xref = $this->getCrossReference();
204*dc4d9dc6SAnna Dabrowska        $trailer = $xref->getTrailer();
205*dc4d9dc6SAnna Dabrowska
206*dc4d9dc6SAnna Dabrowska        $catalog = PdfType::resolve(PdfDictionary::get($trailer, 'Root'), $this);
207*dc4d9dc6SAnna Dabrowska
208*dc4d9dc6SAnna Dabrowska        return PdfDictionary::ensure($catalog);
209*dc4d9dc6SAnna Dabrowska    }
210*dc4d9dc6SAnna Dabrowska
211*dc4d9dc6SAnna Dabrowska    /**
212*dc4d9dc6SAnna Dabrowska     * Get an indirect object by its object number.
213*dc4d9dc6SAnna Dabrowska     *
214*dc4d9dc6SAnna Dabrowska     * @param int $objectNumber
215*dc4d9dc6SAnna Dabrowska     * @param bool $cache
216*dc4d9dc6SAnna Dabrowska     * @return PdfIndirectObject
217*dc4d9dc6SAnna Dabrowska     * @throws CrossReferenceException
218*dc4d9dc6SAnna Dabrowska     * @throws PdfParserException
219*dc4d9dc6SAnna Dabrowska     */
220*dc4d9dc6SAnna Dabrowska    public function getIndirectObject($objectNumber, $cache = false)
221*dc4d9dc6SAnna Dabrowska    {
222*dc4d9dc6SAnna Dabrowska        $objectNumber = (int) $objectNumber;
223*dc4d9dc6SAnna Dabrowska        if (isset($this->objects[$objectNumber])) {
224*dc4d9dc6SAnna Dabrowska            return $this->objects[$objectNumber];
225*dc4d9dc6SAnna Dabrowska        }
226*dc4d9dc6SAnna Dabrowska
227*dc4d9dc6SAnna Dabrowska        $xref = $this->getCrossReference();
228*dc4d9dc6SAnna Dabrowska        $object = $xref->getIndirectObject($objectNumber);
229*dc4d9dc6SAnna Dabrowska
230*dc4d9dc6SAnna Dabrowska        if ($cache) {
231*dc4d9dc6SAnna Dabrowska            $this->objects[$objectNumber] = $object;
232*dc4d9dc6SAnna Dabrowska        }
233*dc4d9dc6SAnna Dabrowska
234*dc4d9dc6SAnna Dabrowska        return $object;
235*dc4d9dc6SAnna Dabrowska    }
236*dc4d9dc6SAnna Dabrowska
237*dc4d9dc6SAnna Dabrowska    /**
238*dc4d9dc6SAnna Dabrowska     * Read a PDF value.
239*dc4d9dc6SAnna Dabrowska     *
240*dc4d9dc6SAnna Dabrowska     * @param null|bool|string $token
241*dc4d9dc6SAnna Dabrowska     * @param null|string $expectedType
242*dc4d9dc6SAnna Dabrowska     * @return bool|PdfArray|PdfBoolean|PdfHexString|PdfName|PdfNull|PdfNumeric|PdfString|PdfToken|PdfIndirectObjectReference
243*dc4d9dc6SAnna Dabrowska     * @throws Type\PdfTypeException
244*dc4d9dc6SAnna Dabrowska     */
245*dc4d9dc6SAnna Dabrowska    public function readValue($token = null, $expectedType = null)
246*dc4d9dc6SAnna Dabrowska    {
247*dc4d9dc6SAnna Dabrowska        if ($token === null) {
248*dc4d9dc6SAnna Dabrowska            $token = $this->tokenizer->getNextToken();
249*dc4d9dc6SAnna Dabrowska        }
250*dc4d9dc6SAnna Dabrowska
251*dc4d9dc6SAnna Dabrowska        if ($token === false) {
252*dc4d9dc6SAnna Dabrowska            if ($expectedType !== null) {
253*dc4d9dc6SAnna Dabrowska                throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE);
254*dc4d9dc6SAnna Dabrowska            }
255*dc4d9dc6SAnna Dabrowska            return false;
256*dc4d9dc6SAnna Dabrowska        }
257*dc4d9dc6SAnna Dabrowska
258*dc4d9dc6SAnna Dabrowska        switch ($token) {
259*dc4d9dc6SAnna Dabrowska            case '(':
260*dc4d9dc6SAnna Dabrowska                $this->ensureExpectedType($token, $expectedType);
261*dc4d9dc6SAnna Dabrowska                return PdfString::parse($this->streamReader);
262*dc4d9dc6SAnna Dabrowska
263*dc4d9dc6SAnna Dabrowska            case '<':
264*dc4d9dc6SAnna Dabrowska                if ($this->streamReader->getByte() === '<') {
265*dc4d9dc6SAnna Dabrowska                    $this->ensureExpectedType('<<', $expectedType);
266*dc4d9dc6SAnna Dabrowska                    $this->streamReader->addOffset(1);
267*dc4d9dc6SAnna Dabrowska                    return PdfDictionary::parse($this->tokenizer, $this->streamReader, $this);
268*dc4d9dc6SAnna Dabrowska                }
269*dc4d9dc6SAnna Dabrowska
270*dc4d9dc6SAnna Dabrowska                $this->ensureExpectedType($token, $expectedType);
271*dc4d9dc6SAnna Dabrowska                return PdfHexString::parse($this->streamReader);
272*dc4d9dc6SAnna Dabrowska
273*dc4d9dc6SAnna Dabrowska            case '/':
274*dc4d9dc6SAnna Dabrowska                $this->ensureExpectedType($token, $expectedType);
275*dc4d9dc6SAnna Dabrowska                return PdfName::parse($this->tokenizer, $this->streamReader);
276*dc4d9dc6SAnna Dabrowska
277*dc4d9dc6SAnna Dabrowska            case '[':
278*dc4d9dc6SAnna Dabrowska                $this->ensureExpectedType($token, $expectedType);
279*dc4d9dc6SAnna Dabrowska                return PdfArray::parse($this->tokenizer, $this);
280*dc4d9dc6SAnna Dabrowska
281*dc4d9dc6SAnna Dabrowska            default:
282*dc4d9dc6SAnna Dabrowska                if (\is_numeric($token)) {
283*dc4d9dc6SAnna Dabrowska                    if (($token2 = $this->tokenizer->getNextToken()) !== false) {
284*dc4d9dc6SAnna Dabrowska                        if (\is_numeric($token2)) {
285*dc4d9dc6SAnna Dabrowska                            if (($token3 = $this->tokenizer->getNextToken()) !== false) {
286*dc4d9dc6SAnna Dabrowska                                switch ($token3) {
287*dc4d9dc6SAnna Dabrowska                                    case 'obj':
288*dc4d9dc6SAnna Dabrowska                                        if ($expectedType !== null && $expectedType !== PdfIndirectObject::class) {
289*dc4d9dc6SAnna Dabrowska                                            throw new Type\PdfTypeException(
290*dc4d9dc6SAnna Dabrowska                                                'Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE
291*dc4d9dc6SAnna Dabrowska                                            );
292*dc4d9dc6SAnna Dabrowska                                        }
293*dc4d9dc6SAnna Dabrowska
294*dc4d9dc6SAnna Dabrowska                                        return PdfIndirectObject::parse(
295*dc4d9dc6SAnna Dabrowska                                            $token,
296*dc4d9dc6SAnna Dabrowska                                            $token2,
297*dc4d9dc6SAnna Dabrowska                                            $this,
298*dc4d9dc6SAnna Dabrowska                                            $this->tokenizer,
299*dc4d9dc6SAnna Dabrowska                                            $this->streamReader
300*dc4d9dc6SAnna Dabrowska                                        );
301*dc4d9dc6SAnna Dabrowska                                    case 'R':
302*dc4d9dc6SAnna Dabrowska                                        if ($expectedType !== null &&
303*dc4d9dc6SAnna Dabrowska                                            $expectedType !== PdfIndirectObjectReference::class
304*dc4d9dc6SAnna Dabrowska                                        ) {
305*dc4d9dc6SAnna Dabrowska                                            throw new Type\PdfTypeException(
306*dc4d9dc6SAnna Dabrowska                                                'Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE
307*dc4d9dc6SAnna Dabrowska                                            );
308*dc4d9dc6SAnna Dabrowska                                        }
309*dc4d9dc6SAnna Dabrowska
310*dc4d9dc6SAnna Dabrowska                                        return PdfIndirectObjectReference::create($token, $token2);
311*dc4d9dc6SAnna Dabrowska                                }
312*dc4d9dc6SAnna Dabrowska
313*dc4d9dc6SAnna Dabrowska                                $this->tokenizer->pushStack($token3);
314*dc4d9dc6SAnna Dabrowska                            }
315*dc4d9dc6SAnna Dabrowska                        }
316*dc4d9dc6SAnna Dabrowska
317*dc4d9dc6SAnna Dabrowska                        $this->tokenizer->pushStack($token2);
318*dc4d9dc6SAnna Dabrowska                    }
319*dc4d9dc6SAnna Dabrowska
320*dc4d9dc6SAnna Dabrowska                    if ($expectedType !== null && $expectedType !== PdfNumeric::class) {
321*dc4d9dc6SAnna Dabrowska                        throw new Type\PdfTypeException(
322*dc4d9dc6SAnna Dabrowska                            'Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE
323*dc4d9dc6SAnna Dabrowska                        );
324*dc4d9dc6SAnna Dabrowska                    }
325*dc4d9dc6SAnna Dabrowska                    return PdfNumeric::create($token);
326*dc4d9dc6SAnna Dabrowska                }
327*dc4d9dc6SAnna Dabrowska
328*dc4d9dc6SAnna Dabrowska                if ($token === 'true' || $token === 'false') {
329*dc4d9dc6SAnna Dabrowska                    $this->ensureExpectedType($token, $expectedType);
330*dc4d9dc6SAnna Dabrowska                    return PdfBoolean::create($token === 'true');
331*dc4d9dc6SAnna Dabrowska                }
332*dc4d9dc6SAnna Dabrowska
333*dc4d9dc6SAnna Dabrowska                if ($token === 'null') {
334*dc4d9dc6SAnna Dabrowska                    $this->ensureExpectedType($token, $expectedType);
335*dc4d9dc6SAnna Dabrowska                    return new PdfNull();
336*dc4d9dc6SAnna Dabrowska                }
337*dc4d9dc6SAnna Dabrowska
338*dc4d9dc6SAnna Dabrowska                if ($expectedType !== null && $expectedType !== PdfToken::class) {
339*dc4d9dc6SAnna Dabrowska                    throw new Type\PdfTypeException(
340*dc4d9dc6SAnna Dabrowska                        'Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE
341*dc4d9dc6SAnna Dabrowska                    );
342*dc4d9dc6SAnna Dabrowska                }
343*dc4d9dc6SAnna Dabrowska
344*dc4d9dc6SAnna Dabrowska                $v = new PdfToken();
345*dc4d9dc6SAnna Dabrowska                $v->value = $token;
346*dc4d9dc6SAnna Dabrowska
347*dc4d9dc6SAnna Dabrowska                return $v;
348*dc4d9dc6SAnna Dabrowska        }
349*dc4d9dc6SAnna Dabrowska    }
350*dc4d9dc6SAnna Dabrowska
351*dc4d9dc6SAnna Dabrowska    /**
352*dc4d9dc6SAnna Dabrowska     * Ensures that the token will evaluate to an expected object type (or not).
353*dc4d9dc6SAnna Dabrowska     *
354*dc4d9dc6SAnna Dabrowska     * @param string $token
355*dc4d9dc6SAnna Dabrowska     * @param string|null $expectedType
356*dc4d9dc6SAnna Dabrowska     * @return bool
357*dc4d9dc6SAnna Dabrowska     * @throws Type\PdfTypeException
358*dc4d9dc6SAnna Dabrowska     */
359*dc4d9dc6SAnna Dabrowska    private function ensureExpectedType($token, $expectedType)
360*dc4d9dc6SAnna Dabrowska    {
361*dc4d9dc6SAnna Dabrowska        static $mapping = [
362*dc4d9dc6SAnna Dabrowska            '(' => PdfString::class,
363*dc4d9dc6SAnna Dabrowska            '<' => PdfHexString::class,
364*dc4d9dc6SAnna Dabrowska            '<<' => PdfDictionary::class,
365*dc4d9dc6SAnna Dabrowska            '/' => PdfName::class,
366*dc4d9dc6SAnna Dabrowska            '[' => PdfArray::class,
367*dc4d9dc6SAnna Dabrowska            'true' => PdfBoolean::class,
368*dc4d9dc6SAnna Dabrowska            'false' => PdfBoolean::class,
369*dc4d9dc6SAnna Dabrowska            'null' => PdfNull::class
370*dc4d9dc6SAnna Dabrowska        ];
371*dc4d9dc6SAnna Dabrowska
372*dc4d9dc6SAnna Dabrowska        if ($expectedType === null || $mapping[$token] === $expectedType) {
373*dc4d9dc6SAnna Dabrowska            return true;
374*dc4d9dc6SAnna Dabrowska        }
375*dc4d9dc6SAnna Dabrowska
376*dc4d9dc6SAnna Dabrowska        throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE);
377*dc4d9dc6SAnna Dabrowska    }
378*dc4d9dc6SAnna Dabrowska}
379