Completed
Push — master ( 6cd3ef...06c905 )
by Hannes
02:54
created

TCPDI   F

Complexity

Total Complexity 178

Size/Duplication

Total Lines 813
Duplicated Lines 6.52 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 178
lcom 1
cbo 3
dl 53
loc 813
rs 1.787
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A setSourceFile() 0 10 2
A setSourceData() 0 11 2
A _getPdfParser() 0 4 1
A getPDFVersion() 0 3 1
A setPDFVersion() 0 3 1
C importPage() 0 75 16
A setPageFormatFromTemplatePage() 0 13 3
A AddPage() 0 10 3
A AddTOC() 0 9 2
B importAnnotations() 0 20 9
A importAnnotation() 0 12 2
D _getannotsrefs() 0 44 21
A getLastUsedPageBox() 0 3 1
A useTemplate() 0 15 5
B _putimportedobjects() 0 29 8
F _putformxobjects() 32 108 33
A _newobj() 0 14 3
F pdf_write_value() 16 121 24
B _straightOut() 0 22 7
A _enddoc() 0 4 1
A _closeParsers() 0 7 3
A cleanUp() 0 7 2
A _putstream() 0 3 1
A _getxobjectdict() 5 10 3
D _unescape() 0 58 22
A hex2str() 0 3 1
A str2hex() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TCPDI often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TCPDI, and based on these observations, apply Extract Interface, too.

1
<?php
2
//
3
//  TCPDI - Version 1.1
4
//  Based on FPDI - Version 1.4.4
5
//
6
//    Copyright 2004-2013 Setasign - Jan Slabon
7
//
8
//  Licensed under the Apache License, Version 2.0 (the "License");
9
//  you may not use this file except in compliance with the License.
10
//  You may obtain a copy of the License at
11
//
12
//      http://www.apache.org/licenses/LICENSE-2.0
13
//
14
//  Unless required by applicable law or agreed to in writing, software
15
//  distributed under the License is distributed on an "AS IS" BASIS,
16
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
//  See the License for the specific language governing permissions and
18
//  limitations under the License.
19
//
20
21
// Dummy shim to allow unmodified use of fpdf_tpl
22
class FPDF extends TCPDF {}
23
24
require_once('fpdf_tpl.php');
25
26
require_once('tcpdi_parser.php');
27
28
29
class TCPDI extends FPDF_TPL {
30
    /**
31
     * Actual filename
32
     * @var string
33
     */
34
    public $current_filename;
35
36
    /**
37
     * Parser-Objects
38
     * @var array
39
     */
40
    public $parsers = array();
41
42
    /**
43
     * Current parser
44
     * @var object
45
     */
46
    public $current_parser;
47
48
    /**
49
     * object stack
50
     * @var array
51
     */
52
    protected $_obj_stack = array();
53
54
    /**
55
     * done object stack
56
     * @var array
57
     */
58
    protected $_don_obj_stack = array();
59
60
    /**
61
     * Current Object Id.
62
     * @var integer
63
     */
64
    protected $_current_obj_id;
65
66
    /**
67
     * The name of the last imported page box
68
     * @var string
69
     */
70
    public $lastUsedPageBox;
71
72
    /**
73
     * Cache for imported pages/template ids
74
     * @var array
75
     */
76
    protected $_importedPages = array();
77
78
    /**
79
     * Cache for imported page annotations
80
     * @var array
81
     */
82
    protected $_importedAnnots  = array();
83
84
    /**
85
     * Number of TOC pages, used for annotation offset
86
     * @var integer
87
     */
88
    protected $_numTOCpages  = 0;
89
90
    /**
91
     * First TOC page, used for annotation offset
92
     * @var integer
93
     */
94
    protected $_TOCpagenum  = 0;
95
96
    /**
97
     * Set a source-file
98
     *
99
     * @param string $filename a valid filename
100
     * @return int number of available pages
101
     */
102
    public function setSourceFile($filename) {
103
        $this->current_filename = $filename;
104
105
        if (!isset($this->parsers[$filename]))
106
            $this->parsers[$filename] = $this->_getPdfParser($filename);
107
        $this->current_parser =& $this->parsers[$filename];
108
        $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
109
110
        return $this->parsers[$filename]->getPageCount();
111
    }
112
113
    /**
114
     * Set a source-file PDF data
115
     *
116
     * @param string $pdfdata The PDF file content
117
     * @return int number of available pages
118
     */
119
    public function setSourceData($pdfdata) {
120
        $filename = uniqid('tcpdi-');
121
        $this->current_filename = $filename;
122
123
        if (!isset($this->parsers[$filename]))
124
            $this->parsers[$filename] = new tcpdi_parser($pdfdata, $filename);
125
        $this->current_parser =& $this->parsers[$filename];
126
        $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
127
128
        return $this->parsers[$filename]->getPageCount();
129
    }
130
131
    /**
132
     * Returns a PDF parser object
133
     *
134
     * @param string $filename
135
     * @return fpdi_pdf_parser
136
     */
137
    protected function _getPdfParser($filename) {
138
        $data = file_get_contents($filename);
139
        return new tcpdi_parser($data, $filename);
140
    }
141
142
    /**
143
     * Get the current PDF version
144
     *
145
     * @return string
146
     */
147
    public function getPDFVersion() {
148
        return $this->PDFVersion;
149
    }
150
151
    /**
152
     * Set the PDF version
153
     *
154
     * @return string
155
     */
156
    public function setPDFVersion($version = '1.3') {
157
        $this->PDFVersion = $version;
158
    }
159
160
    /**
161
     * Import a page
162
     *
163
     * @param int $pageno pagenumber
164
     * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
165
     */
166
    public function importPage($pageno, $boxName = '/CropBox') {
167
        if ($this->_intpl) {
168
            return $this->error('Please import the desired pages before creating a new template.');
169
        }
170
171
        $fn = $this->current_filename;
172
173
        // check if page already imported
174
        $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
175
        if (isset($this->_importedPages[$pageKey]))
176
            return $this->_importedPages[$pageKey];
177
178
        $parser =& $this->parsers[$fn];
179
        $parser->setPageno($pageno);
180
181
        if (!in_array($boxName, $parser->availableBoxes))
182
            return $this->Error(sprintf('Unknown box: %s', $boxName));
183
184
        $pageboxes = $parser->getPageBoxes($pageno, $this->k);
185
186
        /**
187
         * MediaBox
188
         * CropBox: Default -> MediaBox
189
         * BleedBox: Default -> CropBox
190
         * TrimBox: Default -> CropBox
191
         * ArtBox: Default -> CropBox
192
         */
193
        if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
194
            $boxName = '/CropBox';
195
        if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
196
            $boxName = '/MediaBox';
197
198
        if (!isset($pageboxes[$boxName]))
199
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by TCPDI::importPage of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
200
201
        $this->lastUsedPageBox = $boxName;
202
203
        $box = $pageboxes[$boxName];
204
205
        $this->tpl++;
206
        $this->tpls[$this->tpl] = array();
207
        $tpl =& $this->tpls[$this->tpl];
208
        $tpl['parser'] =& $parser;
209
        $tpl['resources'] = $parser->getPageResources();
210
        $tpl['buffer'] = $parser->getContent();
211
        $tpl['box'] = $box;
212
213
        // To build an array that can be used by PDF_TPL::useTemplate()
214
        $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
215
216
        // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
217
        $tpl['x'] = 0;
218
        $tpl['y'] = 0;
219
220
        // handle rotated pages
221
        $rotation = $parser->getPageRotation($pageno);
222
        $tpl['_rotationAngle'] = 0;
223
        if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
224
            $steps = $angle / 90;
225
226
            $_w = $tpl['w'];
227
            $_h = $tpl['h'];
228
            $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
229
            $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
230
231
            if ($angle < 0)
232
                $angle += 360;
233
234
            $tpl['_rotationAngle'] = $angle * -1;
235
        }
236
237
        $this->_importedPages[$pageKey] = $this->tpl;
238
239
        return $this->tpl;
240
    }
241
242
    public function setPageFormatFromTemplatePage($pageno, $orientation) {
243
        $fn = $this->current_filename;
244
        $parser =& $this->parsers[$fn];
245
        $parser->setPageno($pageno);
246
        $boxes = $parser->getPageBoxes($pageno, $this->k);
247
        foreach ($boxes as $name => $box) {
248
            if ($name[0] == '/') {
249
                $boxes[substr($name, 1)] = $box;
250
                unset($boxes[$name]);
251
            }
252
        }
253
        $this->setPageFormat($boxes, $orientation);
254
    }
255
256
    /* Wrapper for AddPage() which tracks TOC pages to offset annotations later */
257
    public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
258
        if ($this->inxobj) {
259
            // we are inside an XObject template
260
            return;
261
        }
262
        parent::AddPage($orientation, $format, $keepmargins, $tocpage);
263
        if ($this->tocpage) {
264
            $this->_numTOCpages++;
265
        }
266
    }
267
268
    /* Wrapper for AddTOC() which tracks TOC position to offset annotations later */
269
    public function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
270
        if (!TCPDF_STATIC::empty_string($page)) {
271
            $this->_TOCpagenum = $page;
0 ignored issues
show
Documentation Bug introduced by
The property $_TOCpagenum was declared of type integer, but $page is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
272
        } else {
273
            $this->_TOCpagenum = $this->page;
274
        }
275
276
        parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color);
277
    }
278
279
    public function importAnnotations($pageno) {
280
        $fn = $this->current_filename;
281
        $parser =& $this->parsers[$fn];
282
        $parser->setPageno($pageno);
283
        $annots = $parser->getPageAnnotations();
284
285
        if (is_array($annots) && $annots[0] == PDF_TYPE_OBJECT // We got an object
286
                && is_array($annots[1]) && $annots[1][0] == PDF_TYPE_ARRAY // It's an array
287
                && is_array($annots[1][1]) && count($annots[1][1] > 1) // It's not empty - there are annotations for this page
288
        ) {
289
            if (!isset($this->_obj_stack[$fn])) {
290
                $this->_obj_stack[$fn] = array();
291
            }
292
293
            $this->_importedAnnots[$this->page] = array();
294
            foreach ($annots[1][1] as $annot) {
295
                $this->importAnnotation($annot);
296
            }
297
        }
298
    }
299
300
    public function importAnnotation($annotation) {
301
        $fn = $this->current_filename;
302
        $old_id = $annotation[1];
303
        $value = array(PDF_TYPE_OBJREF, $old_id, 0);
304
        if (!isset($this->_don_obj_stack[$fn][$old_id])) {
305
            $this->_newobj(false, true);
306
            $this->_obj_stack[$fn][$old_id] = array($this->n, $value);
307
            $this->_don_obj_stack[$fn][$old_id] = array($this->n, $value);
308
        }
309
        $objid = $this->_don_obj_stack[$fn][$old_id][0];
310
        $this->_importedAnnots[$this->page][] = $objid;
311
    }
312
313
    /**
314
     * Get references to page annotations.
315
     * @param $n (int) page number
316
     * @return string
317
     * @protected
318
     * @author Nicola Asuni
319
     * @since 5.0.010 (2010-05-17)
320
     */
321
    protected function _getannotsrefs($n) {
322
        if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) {
323
            // Offset page number to account for TOC being inserted before page containing annotations.
324
            $n -= $this->_numTOCpages;
325
        }
326
        if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
327
            return '';
328
        }
329
        $out = ' /Annots [';
330
        if (isset($this->_importedAnnots[$n])) {
331
            foreach ($this->_importedAnnots[$n] as $key => $val) {
332
                $out .= ' '.$val.' 0 R';
333
            }
334
        }
335
        if (isset($this->PageAnnots[$n])) {
336
            foreach ($this->PageAnnots[$n] as $key => $val) {
337
                if (!in_array($val['n'], $this->radio_groups)) {
338
                    $out .= ' '.$val['n'].' 0 R';
339
                }
340
            }
341
            // add radiobutton groups
342
            if (isset($this->radiobutton_groups[$n])) {
343
                foreach ($this->radiobutton_groups[$n] as $key => $data) {
344
                    if (isset($data['n'])) {
345
                        $out .= ' '.$data['n'].' 0 R';
346
                    }
347
                }
348
            }
349
        }
350
        if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
351
            // set reference for signature object
352
            $out .= ' '.$this->sig_obj_id.' 0 R';
353
        }
354
        if (!empty($this->empty_signature_appearance)) {
355
            foreach ($this->empty_signature_appearance as $esa) {
356
                if ($esa['page'] == $n) {
357
                    // set reference for empty signature objects
358
                    $out .= ' '.$esa['objid'].' 0 R';
359
                }
360
            }
361
        }
362
        $out .= ' ]';
363
        return $out;
364
    }
365
366
    /**
367
     * Returns the last used page box
368
     *
369
     * @return string
370
     */
371
    public function getLastUsedPageBox() {
372
        return $this->lastUsedPageBox;
373
    }
374
375
376
    public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
377
        if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
378
            $size = $this->getTemplateSize($tplidx, $_w, $_h);
379
            $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
380
            $size = array($size['w'], $size['h']);
381
382
            $this->setPageFormat($size, $orientation);
383
        }
384
385
        $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
386
        $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
387
        $this->_out('Q');
388
389
        return $s;
390
    }
391
392
    /**
393
     * Private method, that rebuilds all needed objects of source files
394
     */
395
    public function _putimportedobjects() {
396
        if (is_array($this->parsers) && count($this->parsers) > 0) {
397
            foreach($this->parsers AS $filename => $p) {
398
                $this->current_parser =& $this->parsers[$filename];
399
                if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
400
                    while(($n = key($this->_obj_stack[$filename])) !== null) {
401
                        $nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]);
402
403
                        $this->_newobj($this->_obj_stack[$filename][$n][0]);
404
405
                        if ($nObj[0] == PDF_TYPE_STREAM) {
406
                            $this->pdf_write_value($nObj);
407
                        } else {
408
                            $this->pdf_write_value($nObj[1]);
409
                        }
410
411
                        $this->_out('endobj');
412
                        $this->_obj_stack[$filename][$n] = null; // free memory
413
                        unset($this->_obj_stack[$filename][$n]);
414
                        reset($this->_obj_stack[$filename]);
415
                    }
416
                }
417
418
                // We're done with this parser.  Clean it up to free a bit of RAM.
419
                $this->current_parser->cleanUp();
420
                unset($this->parsers[$filename]);
421
            }
422
        }
423
    }
424
425
426
    /**
427
     * Private Method that writes the form xobjects
428
     */
429
    public function _putformxobjects() {
430
        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
431
        reset($this->tpls);
432
        foreach($this->tpls AS $tplidx => $tpl) {
433
            $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
434
            $this->_newobj();
435
            $cN = $this->n; // TCPDF/Protection: rem current "n"
436
437
            $this->tpls[$tplidx]['n'] = $this->n;
438
            $this->_out('<<' . $filter . '/Type /XObject');
439
            $this->_out('/Subtype /Form');
440
            $this->_out('/FormType 1');
441
442
            $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
443
                (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
444
                (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
445
                (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
446
                (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
447
            ));
448
449
            $c = 1;
450
            $s = 0;
451
            $tx = 0;
452
            $ty = 0;
453
454
            if (isset($tpl['box'])) {
455
                $tx = -$tpl['box']['llx'];
456
                $ty = -$tpl['box']['lly'];
457
458
                if ($tpl['_rotationAngle'] <> 0) {
459
                    $angle = $tpl['_rotationAngle'] * M_PI/180;
460
                    $c=cos($angle);
461
                    $s=sin($angle);
462
463
                    switch($tpl['_rotationAngle']) {
464 View Code Duplication
                        case -90:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
465
                            $tx = -$tpl['box']['lly'];
466
                            $ty = $tpl['box']['urx'];
467
                            break;
468 View Code Duplication
                        case -180:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
469
                            $tx = $tpl['box']['urx'];
470
                            $ty = $tpl['box']['ury'];
471
                            break;
472 View Code Duplication
                        case -270:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
473
                            $tx = $tpl['box']['ury'];
474
                            $ty = -$tpl['box']['llx'];
475
                            break;
476
                    }
477
                }
478
            } elseif ($tpl['x'] != 0 || $tpl['y'] != 0) {
479
                $tx = -$tpl['x'] * 2;
480
                $ty = $tpl['y'] * 2;
481
            }
482
483
            $tx *= $this->k;
484
            $ty *= $this->k;
485
486
            if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
487
                $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
488
                    $c, $s, -$s, $c, $tx, $ty
489
                ));
490
            }
491
492
            $this->_out('/Resources ');
493
494
            if (isset($tpl['resources'])) {
495
                $this->current_parser =& $tpl['parser'];
496
                $this->pdf_write_value($tpl['resources']); // "n" will be changed
497
            } else {
498
                $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
499 View Code Duplication
                if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
                    $this->_out('/Font <<');
501
                    foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
502
                        $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
503
                    $this->_out('>>');
504
                }
505 View Code Duplication
                if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
506
                    isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
507
                {
508
                    $this->_out('/XObject <<');
509
                    if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
510
                        foreach($this->_res['tpl'][$tplidx]['images'] as $image)
511
                            $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
512
                    }
513
                    if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
514
                        foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
515
                            $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
516
                    }
517
                    $this->_out('>>');
518
                }
519
                $this->_out('>>');
520
            }
521
522
            $this->_out('/Group <</Type/Group/S/Transparency>>');
523
524
            $nN = $this->n; // TCPDF: rem new "n"
525
            $this->n = $cN; // TCPDF: reset to current "n"
526
527
            $p = $this->_getrawstream($p);
528
            $this->_out('/Length ' . strlen($p) . ' >>');
529
            $this->_out("stream\n" . $p . "\nendstream");
530
531
            $this->_out('endobj');
532
            $this->n = $nN; // TCPDF: reset to new "n"
533
        }
534
535
        $this->_putimportedobjects();
536
    }
537
538
    /**
539
     * Rewritten to handle existing own defined objects
540
     */
541
    protected function _newobj($obj_id = false, $onlynewobj = false) {
542
        if (!$obj_id) {
543
            $obj_id = ++$this->n;
544
        }
545
546
        //Begin a new object
547
        if (!$onlynewobj) {
548
            $this->offsets[$obj_id] = $this->bufferlen;
549
            $this->_out($obj_id . ' 0 obj');
550
            $this->_current_obj_id = $obj_id; // for later use with encryption
0 ignored issues
show
Documentation Bug introduced by
It seems like $obj_id can also be of type double or boolean. However, the property $_current_obj_id is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
551
        }
552
553
        return $obj_id;
554
    }
555
556
    /**
557
     * Writes a value
558
     * Needed to rebuild the source document
559
     *
560
     * @param mixed $value A PDF-Value. Structure of values see cases in this method
561
     */
562
    public function pdf_write_value(&$value)
563
    {
564
        switch ($value[0]) {
565 View Code Duplication
            case PDF_TYPE_STRING:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
566
                if ($this->encrypted) {
567
                    $value[1] = $this->_unescape($value[1]);
568
                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
569
                    $value[1] = TCPDF_STATIC::_escape($value[1]);
570
                }
571
                break;
572
573
            case PDF_TYPE_STREAM:
574
                if ($this->encrypted) {
575
                    $value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
576
                    $value[1][1]['/Length'] = array(
577
                        PDF_TYPE_NUMERIC,
578
                        strlen($value[2][1])
579
                    );
580
                }
581
                break;
582
583 View Code Duplication
            case PDF_TYPE_HEX:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
584
                if ($this->encrypted) {
585
                    $value[1] = $this->hex2str($value[1]);
586
                    $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
587
588
                    // remake hexstring of encrypted string
589
                    $value[1] = $this->str2hex($value[1]);
590
                }
591
                break;
592
        }
593
594
        switch ($value[0]) {
595
596
            case PDF_TYPE_TOKEN:
597
                $this->_straightOut('/'.$value[1] . ' ');
598
                break;
599
            case PDF_TYPE_NUMERIC:
600
            case PDF_TYPE_REAL:
601
                if (is_float($value[1]) && $value[1] != 0) {
602
                    $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
603
                } else {
604
                    $this->_straightOut($value[1] . ' ');
605
                }
606
                break;
607
608
            case PDF_TYPE_ARRAY:
609
610
                // An array. Output the proper
611
                // structure and move on.
612
613
                $this->_straightOut('[');
614
                for ($i = 0; $i < count($value[1]); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
615
                    $this->pdf_write_value($value[1][$i]);
616
                }
617
618
                $this->_out(']');
619
                break;
620
621
            case PDF_TYPE_DICTIONARY:
622
623
                // A dictionary.
624
                $this->_straightOut('<<');
625
626
                foreach ($value[1] as $k => $v) {
627
                    $this->_straightOut($k . ' ');
628
                    $this->pdf_write_value($v);
629
                }
630
631
                $this->_straightOut('>>');
632
                break;
633
634
            case PDF_TYPE_OBJREF:
635
636
                // An indirect object reference
637
                // Fill the object stack if needed
638
                $cpfn =& $this->current_parser->uniqueid;
639
640
                if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
641
                    $this->_newobj(false, true);
642
                    $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
643
                    $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
644
                }
645
                $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
646
647
                $this->_out($objid . ' 0 R');
648
                break;
649
650
            case PDF_TYPE_STRING:
651
652
                // A string.
653
                $this->_straightOut('(' . $value[1] . ')');
654
655
                break;
656
657
            case PDF_TYPE_STREAM:
658
659
                // A stream. First, output the
660
                // stream dictionary, then the
661
                // stream data itself.
662
                $this->pdf_write_value($value[1]);
663
                $this->_out('stream');
664
                $this->_out($value[2][1]);
665
                $this->_out('endstream');
666
                break;
667
668
            case PDF_TYPE_HEX:
669
                $this->_straightOut('<' . $value[1] . '>');
670
                break;
671
672
            case PDF_TYPE_BOOLEAN:
673
                $this->_straightOut($value[1] ? 'true ' : 'false ');
674
                break;
675
676
            case PDF_TYPE_NULL:
677
                // The null object.
678
679
                $this->_straightOut('null ');
680
                break;
681
        }
682
    }
683
684
    /**
685
     * Modified so not each call will add a newline to the output.
686
     */
687
    protected function _straightOut($s) {
688
        if ($this->state == 2) {
689
            if ($this->inxobj) {
690
                // we are inside an XObject template
691
                $this->xobjects[$this->xobjid]['outdata'] .= $s;
692
            } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
693
                // puts data before page footer
694
                $pagebuff = $this->getPageBuffer($this->page);
695
                $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
696
                $footer = substr($pagebuff, -$this->footerlen[$this->page]);
697
                $this->setPageBuffer($this->page, $page.$s.$footer);
698
                // update footer position
699
                $this->footerpos[$this->page] += strlen($s);
700
            } else {
701
                // set page data
702
                $this->setPageBuffer($this->page, $s, true);
703
            }
704
        } elseif ($this->state > 0) {
705
            // set general data
706
            $this->setBuffer($s);
707
        }
708
    }
709
710
    /**
711
     * rewritten to close opened parsers
712
     *
713
     */
714
    protected function _enddoc() {
715
        parent::_enddoc();
716
        $this->_closeParsers();
717
    }
718
719
    /**
720
     * close all files opened by parsers
721
     */
722
    protected function _closeParsers() {
723
        if ($this->state > 2 && count($this->parsers) > 0) {
724
            $this->cleanUp();
725
            return true;
726
        }
727
        return false;
728
    }
729
730
    /**
731
     * Removes cylced references and closes the file handles of the parser objects
732
     */
733
    public function cleanUp() {
734
        foreach ($this->parsers as $k => $_){
735
            $this->parsers[$k]->cleanUp();
736
            $this->parsers[$k] = null;
737
            unset($this->parsers[$k]);
738
        }
739
    }
740
741
    // Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it
742
    protected function _putstream($s, $n=0) {
743
        $this->_out($this->_getstream($s, $n));
744
    }
745
746
    protected function _getxobjectdict() {
747
        $out = parent::_getxobjectdict();
748 View Code Duplication
        if (count($this->tpls)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
749
            foreach($this->tpls as $tplidx => $tpl) {
750
                $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
751
            }
752
        }
753
754
        return $out;
755
    }
756
757
    /**
758
     * Unescapes a PDF string
759
     *
760
     * @param string $s
761
     * @return string
762
     */
763
    protected function _unescape($s) {
764
        $out = '';
765
        for ($count = 0, $n = strlen($s); $count < $n; $count++) {
766
            if ($s[$count] != '\\' || $count == $n-1) {
767
                $out .= $s[$count];
768
            } else {
769
                switch ($s[++$count]) {
770
                    case ')':
771
                    case '(':
772
                    case '\\':
773
                        $out .= $s[$count];
774
                        break;
775
                    case 'f':
776
                        $out .= chr(0x0C);
777
                        break;
778
                    case 'b':
779
                        $out .= chr(0x08);
780
                        break;
781
                    case 't':
782
                        $out .= chr(0x09);
783
                        break;
784
                    case 'r':
785
                        $out .= chr(0x0D);
786
                        break;
787
                    case 'n':
788
                        $out .= chr(0x0A);
789
                        break;
790
                    case "\r":
791
                        if ($count != $n-1 && $s[$count+1] == "\n")
792
                            $count++;
793
                        break;
794
                    case "\n":
795
                        break;
796
                    default:
797
                        // Octal-Values
798
                        if (ord($s[$count]) >= ord('0') &&
799
                            ord($s[$count]) <= ord('9')) {
800
                            $oct = ''. $s[$count];
801
802
                            if (ord($s[$count+1]) >= ord('0') &&
803
                                ord($s[$count+1]) <= ord('9')) {
804
                                $oct .= $s[++$count];
805
806
                                if (ord($s[$count+1]) >= ord('0') &&
807
                                    ord($s[$count+1]) <= ord('9')) {
808
                                    $oct .= $s[++$count];
809
                                }
810
                            }
811
812
                            $out .= chr(octdec($oct));
813
                        } else {
814
                            $out .= $s[$count];
815
                        }
816
                }
817
            }
818
        }
819
        return $out;
820
    }
821
822
    /**
823
     * Hexadecimal to string
824
     *
825
     * @param string $hex
826
     * @return string
827
     */
828
    public function hex2str($hex) {
829
        return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
830
    }
831
832
    /**
833
     * String to hexadecimal
834
     *
835
     * @param string $str
836
     * @return string
837
     */
838
    public function str2hex($str) {
839
        return current(unpack('H*', $str));
840
    }
841
}
842