Completed
Pull Request — master (#312)
by
unknown
09:12
created

PDFObject::getText()   F

Complexity

Conditions 52
Paths 696

Size

Total Lines 189
Code Lines 119

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 105
CRAP Score 57.2812

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 52
eloc 119
c 1
b 1
f 0
nc 696
nop 1
dl 0
loc 189
ccs 105
cts 120
cp 0.875
crap 57.2812
rs 0.3377

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @file
5
 *          This file is part of the PdfParser library.
6
 *
7
 * @author  Sébastien MALOT <[email protected]>
8
 * @date    2017-01-03
9
 *
10
 * @license LGPLv3
11
 * @url     <https://github.com/smalot/pdfparser>
12
 *
13
 *  PdfParser is a pdf library written in PHP, extraction oriented.
14
 *  Copyright (C) 2017 - Sébastien MALOT <[email protected]>
15
 *
16
 *  This program is free software: you can redistribute it and/or modify
17
 *  it under the terms of the GNU Lesser General Public License as published by
18
 *  the Free Software Foundation, either version 3 of the License, or
19
 *  (at your option) any later version.
20
 *
21
 *  This program is distributed in the hope that it will be useful,
22
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 *  GNU Lesser General Public License for more details.
25
 *
26
 *  You should have received a copy of the GNU Lesser General Public License
27
 *  along with this program.
28
 *  If not, see <http://www.pdfparser.org/sites/default/LICENSE.txt>.
29
 */
30
31
namespace Smalot\PdfParser;
32
33
use Smalot\PdfParser\XObject\Form;
34
use Smalot\PdfParser\XObject\Image;
35
36
/**
37
 * Class PDFObject
38
 */
39
class PDFObject
40
{
41
    const TYPE = 't';
42
43
    const OPERATOR = 'o';
44
45
    const COMMAND = 'c';
46
47
    /**
48
     * The recursion stack.
49
     *
50
     * @var array
51
     */
52
    public static $recursionStack = [];
53
54
    /**
55
     * @var Document
56
     */
57
    protected $document = null;
58
59
    /**
60
     * @var Header
61
     */
62
    protected $header = null;
63
64
    /**
65
     * @var string
66
     */
67
    protected $content = null;
68
69
    /**
70
     * @param Header $header
71
     * @param string $content
72
     */
73 28
    public function __construct(Document $document, Header $header = null, $content = null)
74
    {
75 28
        $this->document = $document;
76 28
        $this->header = null !== $header ? $header : new Header();
77 28
        $this->content = $content;
78 28
    }
79
80 25
    public function init()
81
    {
82 25
    }
83
84
    /**
85
     * @return Header|null
86
     */
87 25
    public function getHeader()
88
    {
89 25
        return $this->header;
90
    }
91
92
    /**
93
     * @param string $name
94
     *
95
     * @return Element|PDFObject
96
     */
97 19
    public function get($name)
98
    {
99 19
        return $this->header->get($name);
100
    }
101
102
    /**
103
     * @param string $name
104
     *
105
     * @return bool
106
     */
107 19
    public function has($name)
108
    {
109 19
        return $this->header->has($name);
110
    }
111
112
    /**
113
     * @param bool $deep
114
     *
115
     * @return array
116
     */
117 1
    public function getDetails($deep = true)
118
    {
119 1
        return $this->header->getDetails($deep);
120
    }
121
122
    /**
123
     * @return string|null
124
     */
125 17
    public function getContent()
126
    {
127 17
        return $this->content;
128
    }
129
130
    /**
131
     * @param string $content
132
     */
133 11
    public function cleanContent($content, $char = 'X')
134
    {
135 11
        $char = $char[0];
136 11
        $content = str_replace(['\\\\', '\\)', '\\('], $char.$char, $content);
137
138
        // Remove image bloc with binary content
139 11
        preg_match_all('/\s(BI\s.*?(\sID\s).*?(\sEI))\s/s', $content, $matches, PREG_OFFSET_CAPTURE);
140 11
        foreach ($matches[0] as $part) {
141
            $content = substr_replace($content, str_repeat($char, \strlen($part[0])), $part[1], \strlen($part[0]));
142
        }
143
144
        // Clean content in square brackets [.....]
145 11
        preg_match_all('/\[((\(.*?\)|[0-9\.\-\s]*)*)\]/s', $content, $matches, PREG_OFFSET_CAPTURE);
146 11
        foreach ($matches[1] as $part) {
147 10
            $content = substr_replace($content, str_repeat($char, \strlen($part[0])), $part[1], \strlen($part[0]));
148
        }
149
150
        // Clean content in round brackets (.....)
151 11
        preg_match_all('/\((.*?)\)/s', $content, $matches, PREG_OFFSET_CAPTURE);
152 11
        foreach ($matches[1] as $part) {
153 10
            $content = substr_replace($content, str_repeat($char, \strlen($part[0])), $part[1], \strlen($part[0]));
154
        }
155
156
        // Clean structure
157 11
        if ($parts = preg_split('/(<|>)/s', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)) {
158 11
            $content = '';
159 11
            $level = 0;
160 11
            foreach ($parts as $part) {
161 11
                if ('<' == $part) {
162 7
                    ++$level;
163
                }
164
165 11
                $content .= (0 == $level ? $part : str_repeat($char, \strlen($part)));
166
167 11
                if ('>' == $part) {
168 7
                    --$level;
169
                }
170
            }
171
        }
172
173
        // Clean BDC and EMC markup
174 11
        preg_match_all(
175 11
            '/(\/[A-Za-z0-9\_]*\s*'.preg_quote($char).'*BDC)/s',
176
            $content,
177
            $matches,
178 11
            PREG_OFFSET_CAPTURE
179
        );
180 11
        foreach ($matches[1] as $part) {
181 2
            $content = substr_replace($content, str_repeat($char, \strlen($part[0])), $part[1], \strlen($part[0]));
182
        }
183
184 11
        preg_match_all('/\s(EMC)\s/s', $content, $matches, PREG_OFFSET_CAPTURE);
185 11
        foreach ($matches[1] as $part) {
186 6
            $content = substr_replace($content, str_repeat($char, \strlen($part[0])), $part[1], \strlen($part[0]));
187
        }
188
189 11
        return $content;
190
    }
191
192
    /**
193
     * @param string $content
194
     *
195
     * @return array
196
     */
197 10
    public function getSectionsText($content)
198
    {
199 10
        $sections = [];
200 10
        $content = ' '.$content.' ';
201 10
        $textCleaned = $this->cleanContent($content, '_');
202
203
        // Extract text blocks.
204 10
        if (preg_match_all('/\s+BT[\s|\(|\[]+(.*?)\s*ET/s', $textCleaned, $matches, PREG_OFFSET_CAPTURE)) {
205 10
            foreach ($matches[1] as $part) {
206 10
                $text = $part[0];
207 10
                if ('' === $text) {
208
                    continue;
209
                }
210 10
                $offset = $part[1];
211 10
                $section = substr($content, $offset, \strlen($text));
212
213
                // Removes BDC and EMC markup.
214 10
                $section = preg_replace('/(\/[A-Za-z0-9]+\s*<<.*?)(>>\s*BDC)(.*?)(EMC\s+)/s', '${3}', $section.' ');
215
216 10
                $sections[] = $section;
217
            }
218
        }
219
220
        // Extract 'do' commands.
221 10
        if (preg_match_all('/(\/[A-Za-z0-9\.\-_]+\s+Do)\s/s', $textCleaned, $matches, PREG_OFFSET_CAPTURE)) {
222 2
            foreach ($matches[1] as $part) {
223 2
                $text = $part[0];
224 2
                $offset = $part[1];
225 2
                $section = substr($content, $offset, \strlen($text));
226
227 2
                $sections[] = $section;
228
            }
229
        }
230
231 10
        return $sections;
232
    }
233
234
    /**
235
     * @param Page $page
236
     *
237
     * @return string
238
     *
239
     * @throws \Exception
240
     */
241 4
    public function getText(Page $page = null)
242
    {
243 4
        $text = '';
244 4
        $sections = $this->getSectionsText($this->content);
245 4
        $current_font = null;
246
247 4
        foreach ($this->document->getObjects() as $obj) {
248 4
            if ($obj instanceof Font) {
249 4
                $current_font = $obj;
250 4
                break;
251
            }
252
        }
253
254 4
        if (null === $current_font) {
255
            $current_font = new Font($this->document);
256
        }
257
258 4
        $current_position_td = ['x' => false, 'y' => false];
259 4
        $current_position_tm = ['x' => false, 'y' => false];
260
261 4
        array_push(self::$recursionStack, $this->getUniqueId());
262
263 4
        foreach ($sections as $section) {
264 4
            $commands = $this->getCommandsText($section);
265
266 4
            foreach ($commands as $command) {
267 4
                switch ($command[self::OPERATOR]) {
268
                    // set character spacing
269 4
                    case 'Tc':
270 1
                        break;
271
272
                    // move text current point
273 4
                    case 'Td':
274 2
                        $args = preg_split('/\s/s', $command[self::COMMAND]);
275 2
                        $y = array_pop($args);
0 ignored issues
show
Bug introduced by
It seems like $args can also be of type false; however, parameter $array of array_pop() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

275
                        $y = array_pop(/** @scrutinizer ignore-type */ $args);
Loading history...
276 2
                        $x = array_pop($args);
277 2
                        if (((float) $x <= 0) ||
278 2
                            (false !== $current_position_td['y'] && (float) $y < (float) ($current_position_td['y']))
279
                        ) {
280
                            // vertical offset
281 2
                            $text .= "\n";
282 2
                        } elseif (false !== $current_position_td['x'] && (float) $x > (float) (
283 2
                                $current_position_td['x']
284
                            )
285
                        ) {
286
                            // horizontal offset
287 2
                            $text .= ' ';
288
                        }
289 2
                        $current_position_td = ['x' => $x, 'y' => $y];
290 2
                        break;
291
292
                    // move text current point and set leading
293 4
                    case 'TD':
294 1
                        $args = preg_split('/\s/s', $command[self::COMMAND]);
295 1
                        $y = array_pop($args);
296 1
                        $x = array_pop($args);
297 1
                        if ((float) $y < 0) {
298 1
                            $text .= "\n";
299
                        } elseif ((float) $x <= 0) {
300
                            $text .= ' ';
301
                        }
302 1
                        break;
303
304 4
                    case 'Tf':
305 4
                        list($id) = preg_split('/\s/s', $command[self::COMMAND]);
306 4
                        $id = trim($id, '/');
307 4
                        if (null !== $page) {
308 4
                            $current_font = $page->getFont($id);
309
                        }
310 4
                        break;
311
312 4
                    case "'":
313 4
                    case 'Tj':
314 2
                        $command[self::COMMAND] = [$command];
315
                        // no break
316 4
                    case 'TJ':
317
                        // Skip if not previously defined, should never happened.
318 4
                        if (null === $current_font) {
319
                            // Fallback
320
                            // TODO : Improve
321 1
                            $text .= $command[self::COMMAND][0][self::COMMAND];
322 1
                            break;
323
                        }
324
325 4
                        $sub_text = $current_font->decodeText($command[self::COMMAND]);
326 4
                        $text .= $sub_text;
327 4
                        break;
328
329
                    // set leading
330 4
                    case 'TL':
331 1
                        $text .= ' ';
332 1
                        break;
333
334 4
                    case 'Tm':
335 4
                        $args = preg_split('/\s/s', $command[self::COMMAND]);
336 4
                        $y = array_pop($args);
337 4
                        $x = array_pop($args);
338 4
                        if (false !== $current_position_tm['x']) {
339 4
                            $delta = abs((float) $x - (float) ($current_position_tm['x']));
340 4
                            if ($delta > 10) {
341 3
                                $text .= "\t";
342
                            }
343
                        }
344 4
                        if (false !== $current_position_tm['y']) {
345 4
                            $delta = abs((float) $y - (float) ($current_position_tm['y']));
346 4
                            if ($delta > 10) {
347 3
                                $text .= "\n";
348
                            }
349
                        }
350 4
                        $current_position_tm = ['x' => $x, 'y' => $y];
351 4
                        break;
352
353
                    // set super/subscripting text rise
354 4
                    case 'Ts':
355
                        break;
356
357
                    // set word spacing
358 4
                    case 'Tw':
359
                        break;
360
361
                    // set horizontal scaling
362 4
                    case 'Tz':
363
                        $text .= "\n";
364
                        break;
365
366
                    // move to start of next line
367 4
                    case 'T*':
368 2
                        $text .= "\n";
369 2
                        break;
370
371 3
                    case 'Da':
372
                        break;
373
374 3
                    case 'Do':
375 2
                        if (null !== $page) {
376 2
                            $args = preg_split('/\s/s', $command[self::COMMAND]);
377 2
                            $id = trim(array_pop($args), '/ ');
378 2
                            $xobject = $page->getXObject($id);
379
380
                            // @todo $xobject could be a ElementXRef object, which would then throw an error
381 2
                            if (\is_object($xobject) && $xobject instanceof self && !\in_array($xobject->getUniqueId(), self::$recursionStack)) {
382
                                // Not a circular reference.
383 2
                                $text .= $xobject->getText($page);
384
                            }
385
                        }
386 2
                        break;
387
388 3
                    case 'rg':
389 3
                    case 'RG':
390 1
                        break;
391
392 3
                    case 're':
393
                        break;
394
395 3
                    case 'co':
396
                        break;
397
398 3
                    case 'cs':
399 1
                        break;
400
401 3
                    case 'gs':
402 2
                        break;
403
404 3
                    case 'en':
405
                        break;
406
407 3
                    case 'sc':
408 3
                    case 'SC':
409
                        break;
410
411 3
                    case 'g':
412 3
                    case 'G':
413 2
                        break;
414
415 2
                    case 'V':
416
                        break;
417
418 2
                    case 'vo':
419 2
                    case 'Vo':
420
                        break;
421
422
                    default:
423
                }
424
            }
425
        }
426
427 4
        array_pop(self::$recursionStack);
428
429 4
        return $text.' ';
430
    }
431
432
    /**
433
     * @param Page $page
434
     *
435
     * @return array
436
     *
437
     * @throws \Exception
438
     */
439
    public function getTextArray(Page $page = null)
440
    {
441
        $text = [];
442
        $sections = $this->getSectionsText($this->content);
443
        $current_font = new Font($this->document);
444
445
        foreach ($sections as $section) {
446
            $commands = $this->getCommandsText($section);
447
448
            foreach ($commands as $command) {
449
                switch ($command[self::OPERATOR]) {
450
                    // set character spacing
451
                    case 'Tc':
452
                        break;
453
454
                    // move text current point
455
                    case 'Td':
456
                        break;
457
458
                    // move text current point and set leading
459
                    case 'TD':
460
                        break;
461
462
                    case 'Tf':
463
                        list($id) = preg_split('/\s/s', $command[self::COMMAND]);
464
                        $id = trim($id, '/');
465
                        $current_font = $page->getFont($id);
0 ignored issues
show
Bug introduced by
The method getFont() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

465
                        /** @scrutinizer ignore-call */ 
466
                        $current_font = $page->getFont($id);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
466
                        break;
467
468
                    case "'":
469
                    case 'Tj':
470
                        $command[self::COMMAND] = [$command];
471
                        // no break
472
                    case 'TJ':
473
                        // Skip if not previously defined, should never happened.
474
                        if (null === $current_font) {
475
                            // Fallback
476
                            // TODO : Improve
477
                            $text[] = $command[self::COMMAND][0][self::COMMAND];
478
                            break;
479
                        }
480
481
                        $sub_text = $current_font->decodeText($command[self::COMMAND]);
482
                        $text[] = $sub_text;
483
                        break;
484
485
                    // set leading
486
                    case 'TL':
487
                        break;
488
489
                    case 'Tm':
490
                        break;
491
492
                    // set super/subscripting text rise
493
                    case 'Ts':
494
                        break;
495
496
                    // set word spacing
497
                    case 'Tw':
498
                        break;
499
500
                    // set horizontal scaling
501
                    case 'Tz':
502
                        //$text .= "\n";
503
                        break;
504
505
                    // move to start of next line
506
                    case 'T*':
507
                        //$text .= "\n";
508
                        break;
509
510
                    case 'Da':
511
                        break;
512
513
                    case 'Do':
514
                        if (null !== $page) {
515
                            $args = preg_split('/\s/s', $command[self::COMMAND]);
516
                            $id = trim(array_pop($args), '/ ');
0 ignored issues
show
Bug introduced by
It seems like $args can also be of type false; however, parameter $array of array_pop() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

516
                            $id = trim(array_pop(/** @scrutinizer ignore-type */ $args), '/ ');
Loading history...
517
                            if ($xobject = $page->getXObject($id)) {
518
                                $text[] = $xobject->getText($page);
519
                            }
520
                        }
521
                        break;
522
523
                    case 'rg':
524
                    case 'RG':
525
                        break;
526
527
                    case 're':
528
                        break;
529
530
                    case 'co':
531
                        break;
532
533
                    case 'cs':
534
                        break;
535
536
                    case 'gs':
537
                        break;
538
539
                    case 'en':
540
                        break;
541
542
                    case 'sc':
543
                    case 'SC':
544
                        break;
545
546
                    case 'g':
547
                    case 'G':
548
                        break;
549
550
                    case 'V':
551
                        break;
552
553
                    case 'vo':
554
                    case 'Vo':
555
                        break;
556
557
                    default:
558
                }
559
            }
560
        }
561
562
        return $text;
563
    }
564
565
    /**
566
     * @param string $text_part
567
     * @param int    $offset
568
     *
569
     * @return array
570
     */
571 10
    public function getCommandsText($text_part, &$offset = 0)
572
    {
573 10
        $commands = $matches = [];
574
575 10
        while ($offset < \strlen($text_part)) {
576 10
            $offset += strspn($text_part, "\x00\x09\x0a\x0c\x0d\x20", $offset);
577 10
            $char = $text_part[$offset];
578
579 10
            $operator = '';
580 10
            $type = '';
581 10
            $command = false;
582
583 10
            switch ($char) {
584 10
                case '/':
585 10
                    $type = $char;
586 10
                    if (preg_match(
587 10
                        '/^\/([A-Z0-9\._,\+]+\s+[0-9.\-]+)\s+([A-Z]+)\s*/si',
588 10
                        substr($text_part, $offset),
589
                        $matches
590
                    )
591
                    ) {
592 10
                        $operator = $matches[2];
593 10
                        $command = $matches[1];
594 10
                        $offset += \strlen($matches[0]);
595
                    } elseif (preg_match(
596 3
                        '/^\/([A-Z0-9\._,\+]+)\s+([A-Z]+)\s*/si',
597 3
                        substr($text_part, $offset),
598
                        $matches
599
                    )
600
                    ) {
601 3
                        $operator = $matches[2];
602 3
                        $command = $matches[1];
603 3
                        $offset += \strlen($matches[0]);
604
                    }
605 10
                    break;
606
607 10
                case '[':
608 10
                case ']':
609
                    // array object
610 10
                    $type = $char;
611 10
                    if ('[' == $char) {
612 10
                        ++$offset;
613
                        // get elements
614 10
                        $command = $this->getCommandsText($text_part, $offset);
615
616 10
                        if (preg_match('/^\s*[A-Z]{1,2}\s*/si', substr($text_part, $offset), $matches)) {
617 10
                            $operator = trim($matches[0]);
618 10
                            $offset += \strlen($matches[0]);
619
                        }
620
                    } else {
621 10
                        ++$offset;
622 10
                        break;
623
                    }
624 10
                    break;
625
626 10
                case '<':
627 10
                case '>':
628
                    // array object
629 3
                    $type = $char;
630 3
                    ++$offset;
631 3
                    if ('<' == $char) {
632 3
                        $strpos = strpos($text_part, '>', $offset);
633 3
                        $command = substr($text_part, $offset, ($strpos - $offset));
634 3
                        $offset = $strpos + 1;
635
                    }
636
637 3
                    if (preg_match('/^\s*[A-Z]{1,2}\s*/si', substr($text_part, $offset), $matches)) {
638 2
                        $operator = trim($matches[0]);
639 2
                        $offset += \strlen($matches[0]);
640
                    }
641 3
                    break;
642
643 10
                case '(':
644 10
                case ')':
645 9
                    ++$offset;
646 9
                    $type = $char;
647 9
                    $strpos = $offset;
648 9
                    if ('(' == $char) {
649 9
                        $open_bracket = 1;
650 9
                        while ($open_bracket > 0) {
651 9
                            if (!isset($text_part[$strpos])) {
652
                                break;
653
                            }
654 9
                            $ch = $text_part[$strpos];
655 9
                            switch ($ch) {
656 9
                                case '\\':
657
                                 // REVERSE SOLIDUS (5Ch) (Backslash)
658
                                    // skip next character
659 8
                                    ++$strpos;
660 8
                                    break;
661
662 9
                                case '(':
663
                                 // LEFT PARENHESIS (28h)
664
                                    ++$open_bracket;
665
                                    break;
666
667 9
                                case ')':
668
                                 // RIGHT PARENTHESIS (29h)
669 9
                                    --$open_bracket;
670 9
                                    break;
671
                            }
672 9
                            ++$strpos;
673
                        }
674 9
                        $command = substr($text_part, $offset, ($strpos - $offset - 1));
675 9
                        $offset = $strpos;
676
677 9
                        if (preg_match('/^\s*([A-Z\']{1,2})\s*/si', substr($text_part, $offset), $matches)) {
678 8
                            $operator = $matches[1];
679 8
                            $offset += \strlen($matches[0]);
680
                        }
681
                    }
682 9
                    break;
683
684
                default:
685
686 10
                    if ('ET' == substr($text_part, $offset, 2)) {
687 1
                        break;
688
                    } elseif (preg_match(
689 10
                        '/^\s*(?P<data>([0-9\.\-]+\s*?)+)\s+(?P<id>[A-Z]{1,3})\s*/si',
690 10
                        substr($text_part, $offset),
691
                        $matches
692
                    )
693
                    ) {
694 10
                        $operator = trim($matches['id']);
695 10
                        $command = trim($matches['data']);
696 10
                        $offset += \strlen($matches[0]);
697 10
                    } elseif (preg_match('/^\s*([0-9\.\-]+\s*?)+\s*/si', substr($text_part, $offset), $matches)) {
698 10
                        $type = 'n';
699 10
                        $command = trim($matches[0]);
700 10
                        $offset += \strlen($matches[0]);
701 8
                    } elseif (preg_match('/^\s*([A-Z\*]+)\s*/si', substr($text_part, $offset), $matches)) {
702 8
                        $type = '';
703 8
                        $operator = $matches[1];
704 8
                        $command = '';
705 8
                        $offset += \strlen($matches[0]);
706
                    }
707
            }
708
709 10
            if (false !== $command) {
710 10
                $commands[] = [
711 10
                    self::TYPE => $type,
712 10
                    self::OPERATOR => $operator,
713 10
                    self::COMMAND => $command,
714
                ];
715
            } else {
716 10
                break;
717
            }
718
        }
719
720 10
        return $commands;
721
    }
722
723
    /**
724
     * @param string $content
725
     *
726
     * @return PDFObject
727
     */
728 17
    public static function factory(Document $document, Header $header, $content)
729
    {
730 17
        switch ($header->get('Type')->getContent()) {
731 17
            case 'XObject':
732 3
                switch ($header->get('Subtype')->getContent()) {
733 3
                    case 'Image':
734 2
                        return new Image($document, $header, $content);
735
736 2
                    case 'Form':
737 2
                        return new Form($document, $header, $content);
738
                }
739
740
                return new self($document, $header, $content);
741
742 17
            case 'Pages':
743 17
                return new Pages($document, $header, $content);
744
745 17
            case 'Page':
746 17
                return new Page($document, $header, $content);
747
748 17
            case 'Encoding':
749 2
                return new Encoding($document, $header, $content);
750
751 17
            case 'Font':
752 17
                $subtype = $header->get('Subtype')->getContent();
753 17
                $classname = '\Smalot\PdfParser\Font\Font'.$subtype;
754
755 17
                if (class_exists($classname)) {
756 17
                    return new $classname($document, $header, $content);
757
                }
758
759
                return new Font($document, $header, $content);
760
761
            default:
762 17
                return new self($document, $header, $content);
763
        }
764
    }
765
766
    /**
767
     * Returns unique id identifying the object.
768
     *
769
     * @return string
770
     */
771 4
    protected function getUniqueId()
772
    {
773 4
        return spl_object_hash($this);
774
    }
775
}
776