Completed
Push — pr/238 ( 9ef5ec )
by Konrad
03:54
created

PDFObject::cleanContent()   B

Complexity

Conditions 11
Paths 64

Size

Total Lines 57
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 11.0044

Importance

Changes 0
Metric Value
cc 11
eloc 31
c 0
b 0
f 0
nc 64
nop 2
dl 0
loc 57
ccs 29
cts 30
cp 0.9667
crap 11.0044
rs 7.3166

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 18
    public function get($name)
98
    {
99 18
        return $this->header->get($name);
100
    }
101
102
    /**
103
     * @param string $name
104
     *
105
     * @return bool
106
     */
107 18
    public function has($name)
108
    {
109 18
        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 16
    public function getContent()
126
    {
127 16
        return $this->content;
128
    }
129
130
    /**
131
     * @param string $content
132
     */
133 10
    public function cleanContent($content, $char = 'X')
134
    {
135 10
        $char = $char[0];
136 10
        $content = str_replace(['\\\\', '\\)', '\\('], $char.$char, $content);
137
138
        // Remove image bloc with binary content
139 10
        preg_match_all('/\s(BI\s.*?(\sID\s).*?(\sEI))\s/s', $content, $matches, PREG_OFFSET_CAPTURE);
140 10
        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 10
        preg_match_all('/\[((\(.*?\)|[0-9\.\-\s]*)*)\]/s', $content, $matches, PREG_OFFSET_CAPTURE);
146 10
        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 10
        preg_match_all('/\((.*?)\)/s', $content, $matches, PREG_OFFSET_CAPTURE);
152 10
        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 10
        if ($parts = preg_split('/(<|>)/s', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)) {
158 10
            $content = '';
159 10
            $level = 0;
160 10
            foreach ($parts as $part) {
161 10
                if ('<' == $part) {
162 6
                    ++$level;
163
                }
164
165 10
                $content .= (0 == $level ? $part : str_repeat($char, \strlen($part)));
166
167 10
                if ('>' == $part) {
168 6
                    --$level;
169
                }
170
            }
171
        }
172
173
        // Clean BDC and EMC markup
174 10
        preg_match_all(
175 10
            '/(\/[A-Za-z0-9\_]*\s*'.preg_quote($char).'*BDC)/s',
176
            $content,
177
            $matches,
178 10
            PREG_OFFSET_CAPTURE
179
        );
180 10
        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 10
        preg_match_all('/\s(EMC)\s/s', $content, $matches, PREG_OFFSET_CAPTURE);
185 10
        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 10
        return $content;
190
    }
191
192
    /**
193
     * @param string $content
194
     *
195
     * @return array
196
     */
197 9
    public function getSectionsText($content)
198
    {
199 9
        $sections = [];
200 9
        $content = ' '.$content.' ';
201 9
        $textCleaned = $this->cleanContent($content, '_');
202
203
        // Extract text blocks.
204 9
        if (preg_match_all('/\s+BT[\s|\(|\[]+(.*?)\s*ET/s', $textCleaned, $matches, PREG_OFFSET_CAPTURE)) {
205 9
            foreach ($matches[1] as $part) {
206 9
                $text = $part[0];
207 9
                if ('' === $text) {
208
                    continue;
209
                }
210 9
                $offset = $part[1];
211 9
                $section = substr($content, $offset, \strlen($text));
212
213
                // Removes BDC and EMC markup.
214 9
                $section = preg_replace('/(\/[A-Za-z0-9]+\s*<<.*?)(>>\s*BDC)(.*?)(EMC\s+)/s', '${3}', $section.' ');
215
216 9
                $sections[] = $section;
217
            }
218
        }
219
220
        // Extract 'do' commands.
221 9
        if (preg_match_all('/(\/[A-Za-z0-9\.\-_]+\s+Do)\s/s', $textCleaned, $matches, PREG_OFFSET_CAPTURE)) {
222
            foreach ($matches[1] as $part) {
223
                $text = $part[0];
224
                $offset = $part[1];
225
                $section = substr($content, $offset, \strlen($text));
226
227
                $sections[] = $section;
228
            }
229
        }
230
231 9
        return $sections;
232
    }
233
234
    /**
235
     * @param Page $page
236
     *
237
     * @return string
238
     *
239
     * @throws \Exception
240
     */
241 3
    public function getText(Page $page = null)
242
    {
243 3
        $text = '';
244 3
        $sections = $this->getSectionsText($this->content);
245 3
        $current_font = null;
246
247 3
        foreach ($this->document->getObjects() as $obj) {
248 3
            if ($obj instanceof Font) {
249 3
                $current_font = $obj;
250 3
                break;
251
            }
252
        }
253
254 3
        if (null === $current_font) {
255
            $current_font = new Font($this->document);
256
        }
257
258 3
        $current_position_td = ['x' => false, 'y' => false];
259 3
        $current_position_tm = ['x' => false, 'y' => false];
260
261 3
        array_push(self::$recursionStack, $this->getUniqueId());
262
263 3
        foreach ($sections as $section) {
264 3
            $commands = $this->getCommandsText($section);
265
266 3
            foreach ($commands as $command) {
267 3
                switch ($command[self::OPERATOR]) {
268
                    // set character spacing
269 3
                    case 'Tc':
270 1
                        break;
271
272
                    // move text current point
273 3
                    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 1
                            $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 3
                    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 3
                    case 'Tf':
305 3
                        list($id) = preg_split('/\s/s', $command[self::COMMAND]);
306 3
                        $id = trim($id, '/');
307 3
                        if (null !== $page) {
308 3
                            $current_font = $page->getFont($id);
309
                        }
310 3
                        break;
311
312 3
                    case "'":
313 3
                    case 'Tj':
314 2
                        $command[self::COMMAND] = [$command];
315
                        // no break
316 3
                    case 'TJ':
317
                        // Skip if not previously defined, should never happened.
318 3
                        if (null === $current_font) {
319
                            // Fallback
320
                            // TODO : Improve
321 1
                            $text .= $command[self::COMMAND][0][self::COMMAND];
322 1
                            break;
323
                        }
324
325 3
                        $sub_text = $current_font->decodeText($command[self::COMMAND]);
326 3
                        $text .= $sub_text;
327 3
                        break;
328
329
                    // set leading
330 3
                    case 'TL':
331 1
                        $text .= ' ';
332 1
                        break;
333
334 3
                    case 'Tm':
335 3
                        $args = preg_split('/\s/s', $command[self::COMMAND]);
336 3
                        $y = array_pop($args);
337 3
                        $x = array_pop($args);
338 3
                        if (false !== $current_position_tm['x']) {
339 3
                            $delta = abs((float) $x - (float) ($current_position_tm['x']));
340 3
                            if ($delta > 10) {
341 3
                                $text .= "\t";
342
                            }
343
                        }
344 3
                        if (false !== $current_position_tm['y']) {
345 3
                            $delta = abs((float) $y - (float) ($current_position_tm['y']));
346 3
                            if ($delta > 10) {
347 2
                                $text .= "\n";
348
                            }
349
                        }
350 3
                        $current_position_tm = ['x' => $x, 'y' => $y];
351 3
                        break;
352
353
                    // set super/subscripting text rise
354 3
                    case 'Ts':
355
                        break;
356
357
                    // set word spacing
358 3
                    case 'Tw':
359
                        break;
360
361
                    // set horizontal scaling
362 3
                    case 'Tz':
363
                        $text .= "\n";
364
                        break;
365
366
                    // move to start of next line
367 3
                    case 'T*':
368 2
                        $text .= "\n";
369 2
                        break;
370
371 2
                    case 'Da':
372
                        break;
373
374 2
                    case 'Do':
375
                        if (null !== $page) {
376
                            $args = preg_split('/\s/s', $command[self::COMMAND]);
377
                            $id = trim(array_pop($args), '/ ');
378
                            $xobject = $page->getXObject($id);
379
380
                            // @todo $xobject could be a ElementXRef object, which would then throw an error
381
                            if (\is_object($xobject) && $xobject instanceof self && !\in_array($xobject->getUniqueId(), self::$recursionStack)) {
382
                                // Not a circular reference.
383
                                $text .= $xobject->getText($page);
384
                            }
385
                        }
386
                        break;
387
388 2
                    case 'rg':
389 2
                    case 'RG':
390 1
                        break;
391
392 2
                    case 're':
393
                        break;
394
395 2
                    case 'co':
396
                        break;
397
398 2
                    case 'cs':
399 1
                        break;
400
401 2
                    case 'gs':
402 2
                        break;
403
404 2
                    case 'en':
405
                        break;
406
407 2
                    case 'sc':
408 2
                    case 'SC':
409
                        break;
410
411 2
                    case 'g':
412 2
                    case 'G':
413 2
                        break;
414
415 1
                    case 'V':
416
                        break;
417
418 1
                    case 'vo':
419 1
                    case 'Vo':
420
                        break;
421
422
                    default:
423
                }
424
            }
425
        }
426
427 3
        array_pop(self::$recursionStack);
428
429 3
        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 9
    public function getCommandsText($text_part, &$offset = 0)
572
    {
573 9
        $commands = $matches = [];
574
575 9
        while ($offset < \strlen($text_part)) {
576 9
            $offset += strspn($text_part, "\x00\x09\x0a\x0c\x0d\x20", $offset);
577 9
            $char = $text_part[$offset];
578
579 9
            $operator = '';
580 9
            $type = '';
581 9
            $command = false;
582
583 9
            switch ($char) {
584 9
                case '/':
585 9
                    $type = $char;
586 9
                    if (preg_match(
587 9
                        '/^\/([A-Z0-9\._,\+]+\s+[0-9.\-]+)\s+([A-Z]+)\s*/si',
588 9
                        substr($text_part, $offset),
589
                        $matches
590
                    )
591
                    ) {
592 9
                        $operator = $matches[2];
593 9
                        $command = $matches[1];
594 9
                        $offset += \strlen($matches[0]);
595
                    } elseif (preg_match(
596 2
                        '/^\/([A-Z0-9\._,\+]+)\s+([A-Z]+)\s*/si',
597 2
                        substr($text_part, $offset),
598
                        $matches
599
                    )
600
                    ) {
601 2
                        $operator = $matches[2];
602 2
                        $command = $matches[1];
603 2
                        $offset += \strlen($matches[0]);
604
                    }
605 9
                    break;
606
607 9
                case '[':
608 9
                case ']':
609
                    // array object
610 9
                    $type = $char;
611 9
                    if ('[' == $char) {
612 9
                        ++$offset;
613
                        // get elements
614 9
                        $command = $this->getCommandsText($text_part, $offset);
615
616 9
                        if (preg_match('/^\s*[A-Z]{1,2}\s*/si', substr($text_part, $offset), $matches)) {
617 9
                            $operator = trim($matches[0]);
618 9
                            $offset += \strlen($matches[0]);
619
                        }
620
                    } else {
621 9
                        ++$offset;
622 9
                        break;
623
                    }
624 9
                    break;
625
626 9
                case '<':
627 9
                case '>':
628
                    // array object
629 2
                    $type = $char;
630 2
                    ++$offset;
631 2
                    if ('<' == $char) {
632 2
                        $strpos = strpos($text_part, '>', $offset);
633 2
                        $command = substr($text_part, $offset, ($strpos - $offset));
634 2
                        $offset = $strpos + 1;
635
                    }
636
637 2
                    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 2
                    break;
642
643 9
                case '(':
644 9
                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 9
                    if ('ET' == substr($text_part, $offset, 2)) {
687 1
                        break;
688
                    } elseif (preg_match(
689 9
                        '/^\s*(?P<data>([0-9\.\-]+\s*?)+)\s+(?P<id>[A-Z]{1,3})\s*/si',
690 9
                        substr($text_part, $offset),
691
                        $matches
692
                    )
693
                    ) {
694 9
                        $operator = trim($matches['id']);
695 9
                        $command = trim($matches['data']);
696 9
                        $offset += \strlen($matches[0]);
697 9
                    } elseif (preg_match('/^\s*([0-9\.\-]+\s*?)+\s*/si', substr($text_part, $offset), $matches)) {
698 9
                        $type = 'n';
699 9
                        $command = trim($matches[0]);
700 9
                        $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 9
            if (false !== $command) {
710 9
                $commands[] = [
711 9
                    self::TYPE => $type,
712 9
                    self::OPERATOR => $operator,
713 9
                    self::COMMAND => $command,
714
                ];
715
            } else {
716 9
                break;
717
            }
718
        }
719
720 9
        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 2
                switch ($header->get('Subtype')->getContent()) {
733 2
                    case 'Image':
734
                        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 16
                return new Pages($document, $header, $content);
744
745 17
            case 'Page':
746 16
                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 16
                $subtype = $header->get('Subtype')->getContent();
753 16
                $classname = '\Smalot\PdfParser\Font\Font'.$subtype;
754
755 16
                if (class_exists($classname)) {
756 16
                    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 3
    protected function getUniqueId()
772
    {
773 3
        return spl_object_hash($this);
774
    }
775
}
776