Passed
Push — master ( 93737b...9ea852 )
by
unknown
04:17
created

Cpdf::o_toUnicode()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 52
Code Lines 25

Duplication

Lines 5
Ratio 9.62 %

Importance

Changes 0
Metric Value
dl 5
loc 52
rs 8.6868
c 0
b 0
f 0
cc 5
eloc 25
nc 5
nop 2

How to fix   Long Method   

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
 * A PHP class to provide the basic functionality to create a pdf document without
4
 * any requirement for additional modules.
5
 *
6
 * Extended by Orion Richardson to support Unicode / UTF-8 characters using
7
 * TCPDF and others as a guide.
8
 *
9
 * @author  Wayne Munro <[email protected]>
10
 * @author  Orion Richardson <[email protected]>
11
 * @author  Helmut Tischer <[email protected]>
12
 * @author  Ryan H. Masten <[email protected]>
13
 * @author  Brian Sweeney <[email protected]>
14
 * @author  Fabien Ménager <[email protected]>
15
 * @license Public Domain http://creativecommons.org/licenses/publicdomain/
16
 * @package Cpdf
17
 */
18
use FontLib\Font;
19
use FontLib\BinaryStream;
20
21
class Cpdf
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
22
{
23
24
    /**
25
     * @var integer The current number of pdf objects in the document
26
     */
27
    public $numObj = 0;
28
29
    /**
30
     * @var array This array contains all of the pdf objects, ready for final assembly
31
     */
32
    public $objects = array();
33
34
    /**
35
     * @var integer The objectId (number within the objects array) of the document catalog
36
     */
37
    public $catalogId;
38
39
    /**
40
     * @var array Array carrying information about the fonts that the system currently knows about
41
     * Used to ensure that a font is not loaded twice, among other things
42
     */
43
    public $fonts = array();
44
45
    /**
46
     * @var string The default font metrics file to use if no other font has been loaded.
47
     * The path to the directory containing the font metrics should be included
48
     */
49
    public $defaultFont = './fonts/Helvetica.afm';
50
51
    /**
52
     * @string A record of the current font
53
     */
54
    public $currentFont = '';
55
56
    /**
57
     * @var string The current base font
58
     */
59
    public $currentBaseFont = '';
60
61
    /**
62
     * @var integer The number of the current font within the font array
63
     */
64
    public $currentFontNum = 0;
65
66
    /**
67
     * @var integer
68
     */
69
    public $currentNode;
70
71
    /**
72
     * @var integer Object number of the current page
73
     */
74
    public $currentPage;
75
76
    /**
77
     * @var integer Object number of the currently active contents block
78
     */
79
    public $currentContents;
80
81
    /**
82
     * @var integer Number of fonts within the system
83
     */
84
    public $numFonts = 0;
85
86
    /**
87
     * @var integer Number of graphic state resources used
88
     */
89
    private $numStates = 0;
90
91
    /**
92
     * @var array Number of graphic state resources used
93
     */
94
    private $gstates = array();
95
96
    /**
97
     * @var array Current color for fill operations, defaults to inactive value,
98
     * all three components should be between 0 and 1 inclusive when active
99
     */
100
    public $currentColor = null;
101
102
    /**
103
     * @var array Current color for stroke operations (lines etc.)
104
     */
105
    public $currentStrokeColor = null;
106
107
    /**
108
     * @var string Fill rule (nonzero or evenodd)
109
     */
110
    public $fillRule = "nonzero";
111
112
    /**
113
     * @var string Current style that lines are drawn in
114
     */
115
    public $currentLineStyle = '';
116
117
    /**
118
     * @var array Current line transparency (partial graphics state)
119
     */
120
    public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
121
122
    /**
123
     * array Current fill transparency (partial graphics state)
124
     */
125
    public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
126
127
    /**
128
     * @var array An array which is used to save the state of the document, mainly the colors and styles
129
     * it is used to temporarily change to another state, then change back to what it was before
130
     */
131
    public $stateStack = array();
132
133
    /**
134
     * @var integer Number of elements within the state stack
135
     */
136
    public $nStateStack = 0;
137
138
    /**
139
     * @var integer Number of page objects within the document
140
     */
141
    public $numPages = 0;
142
143
    /**
144
     * @var array Object Id storage stack
145
     */
146
    public $stack = array();
147
148
    /**
149
     * @var integer Number of elements within the object Id storage stack
150
     */
151
    public $nStack = 0;
152
153
    /**
154
     * an array which contains information about the objects which are not firmly attached to pages
155
     * these have been added with the addObject function
156
     */
157
    public $looseObjects = array();
158
159
    /**
160
     * array contains information about how the loose objects are to be added to the document
161
     */
162
    public $addLooseObjects = array();
163
164
    /**
165
     * @var integer The objectId of the information object for the document
166
     * this contains authorship, title etc.
167
     */
168
    public $infoObject = 0;
169
170
    /**
171
     * @var integer Number of images being tracked within the document
172
     */
173
    public $numImages = 0;
174
175
    /**
176
     * @var array An array containing options about the document
177
     * it defaults to turning on the compression of the objects
178
     */
179
    public $options = array('compression' => true);
180
181
    /**
182
     * @var integer The objectId of the first page of the document
183
     */
184
    public $firstPageId;
185
186
    /**
187
     * @var integer The object Id of the procset object
188
     */
189
    public $procsetObjectId;
190
191
    /**
192
     * @var array Store the information about the relationship between font families
193
     * this used so that the code knows which font is the bold version of another font, etc.
194
     * the value of this array is initialised in the constructor function.
195
     */
196
    public $fontFamilies = array();
197
198
    /**
199
     * @var string Folder for php serialized formats of font metrics files.
200
     * If empty string, use same folder as original metrics files.
201
     * This can be passed in from class creator.
202
     * If this folder does not exist or is not writable, Cpdf will be **much** slower.
203
     * Because of potential trouble with php safe mode, folder cannot be created at runtime.
204
     */
205
    public $fontcache = '';
206
207
    /**
208
     * @var integer The version of the font metrics cache file.
209
     * This value must be manually incremented whenever the internal font data structure is modified.
210
     */
211
    public $fontcacheVersion = 6;
212
213
    /**
214
     * @var string Temporary folder.
215
     * If empty string, will attempt system tmp folder.
216
     * This can be passed in from class creator.
217
     */
218
    public $tmp = '';
219
220
    /**
221
     * @var string Track if the current font is bolded or italicised
222
     */
223
    public $currentTextState = '';
224
225
    /**
226
     * @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
227
     */
228
    public $messages = '';
229
230
    /**
231
     * @var string The encryption array for the document encryption is stored here
232
     */
233
    public $arc4 = '';
234
235
    /**
236
     * @var integer The object Id of the encryption information
237
     */
238
    public $arc4_objnum = 0;
239
240
    /**
241
     * @var string The file identifier, used to uniquely identify a pdf document
242
     */
243
    public $fileIdentifier = '';
244
245
    /**
246
     * @var boolean A flag to say if a document is to be encrypted or not
247
     */
248
    public $encrypted = false;
249
250
    /**
251
     * @var string The encryption key for the encryption of all the document content (structure is not encrypted)
252
     */
253
    public $encryptionKey = '';
254
255
    /**
256
     * @var array Array which forms a stack to keep track of nested callback functions
257
     */
258
    public $callback = array();
259
260
    /**
261
     * @var integer The number of callback functions in the callback array
262
     */
263
    public $nCallback = 0;
264
265
    /**
266
     * @var array Store label->id pairs for named destinations, these will be used to replace internal links
267
     * done this way so that destinations can be defined after the location that links to them
268
     */
269
    public $destinations = array();
270
271
    /**
272
     * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
273
     * publiciables within the class, so that the user can rollback at will (from each 'start' command)
274
     * note that this includes the objects array, so these can be large.
275
     */
276
    public $checkpoint = '';
277
278
    /**
279
     * @var array Table of Image origin filenames and image labels which were already added with o_image().
280
     * Allows to merge identical images
281
     */
282
    public $imagelist = array();
283
284
    /**
285
     * @var boolean Whether the text passed in should be treated as Unicode or just local character set.
286
     */
287
    public $isUnicode = false;
288
289
    /**
290
     * @var string the JavaScript code of the document
291
     */
292
    public $javascript = '';
293
294
    /**
295
     * @var boolean whether the compression is possible
296
     */
297
    protected $compressionReady = false;
298
299
    /**
300
     * @var array Current page size
301
     */
302
    protected $currentPageSize = array("width" => 0, "height" => 0);
303
304
    /**
305
     * @var array All the chars that will be required in the font subsets
306
     */
307
    protected $stringSubsets = array();
308
309
    /**
310
     * @var string The target internal encoding
311
     */
312
    static protected $targetEncoding = 'Windows-1252';
313
314
    /**
315
     * @var array The list of the core fonts
316
     */
317
    static protected $coreFonts = array(
318
        'courier',
319
        'courier-bold',
320
        'courier-oblique',
321
        'courier-boldoblique',
322
        'helvetica',
323
        'helvetica-bold',
324
        'helvetica-oblique',
325
        'helvetica-boldoblique',
326
        'times-roman',
327
        'times-bold',
328
        'times-italic',
329
        'times-bolditalic',
330
        'symbol',
331
        'zapfdingbats'
332
    );
333
334
    /**
335
     * Class constructor
336
     * This will start a new document
337
     *
338
     * @param array   $pageSize  Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
339
     * @param boolean $isUnicode Whether text will be treated as Unicode or not.
340
     * @param string  $fontcache The font cache folder
341
     * @param string  $tmp       The temporary folder
342
     */
343
    function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
344
    {
345
        $this->isUnicode = $isUnicode;
346
        $this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\");
347
        $this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir());
348
        $this->newDocument($pageSize);
349
350
        $this->compressionReady = function_exists('gzcompress');
351
352
        if (in_array('Windows-1252', mb_list_encodings())) {
353
            self::$targetEncoding = 'Windows-1252';
354
        }
355
356
        // also initialize the font families that are known about already
357
        $this->setFontFamily('init');
358
    }
359
360
    /**
361
     * Document object methods (internal use only)
362
     *
363
     * There is about one object method for each type of object in the pdf document
364
     * Each function has the same call list ($id,$action,$options).
365
     * $id = the object ID of the object, or what it is to be if it is being created
366
     * $action = a string specifying the action to be performed, though ALL must support:
367
     *           'new' - create the object with the id $id
368
     *           'out' - produce the output for the pdf object
369
     * $options = optional, a string or array containing the various parameters for the object
370
     *
371
     * These, in conjunction with the output function are the ONLY way for output to be produced
372
     * within the pdf 'file'.
373
     */
374
375
    /**
376
     * Destination object, used to specify the location for the user to jump to, presently on opening
377
     *
378
     * @param $id
379
     * @param $action
380
     * @param string $options
381
     * @return string|null
382
     */
383
    protected function o_destination($id, $action, $options = '')
384
    {
385
        switch ($action) {
386
            case 'new':
387
                $this->objects[$id] = array('t' => 'destination', 'info' => array());
388
                $tmp = '';
389
                switch ($options['type']) {
390
                    case 'XYZ':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
391
                    /** @noinspection PhpMissingBreakStatementInspection */
392
                    case 'FitR':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
393
                        $tmp = ' ' . $options['p3'] . $tmp;
394
                    case 'FitH':
395
                    case 'FitV':
396
                    case 'FitBH':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
397
                    /** @noinspection PhpMissingBreakStatementInspection */
398
                    case 'FitBV':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
399
                        $tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp;
400
                    case 'Fit':
401
                    case 'FitB':
402
                        $tmp = $options['type'] . $tmp;
403
                        $this->objects[$id]['info']['string'] = $tmp;
404
                        $this->objects[$id]['info']['page'] = $options['page'];
405
                }
406
                break;
407
408
            case 'out':
409
                $o = &$this->objects[$id];
410
411
                $tmp = $o['info'];
412
                $res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj";
413
414
                return $res;
415
        }
416
417
        return null;
418
    }
419
420
    /**
421
     * set the viewer preferences
422
     *
423
     * @param $id
424
     * @param $action
425
     * @param string|array $options
426
     * @return string|null
427
     */
428
    protected function o_viewerPreferences($id, $action, $options = '')
429
    {
430
        switch ($action) {
431
            case 'new':
432
                $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array());
433
                break;
434
435
            case 'add':
436
                $o = &$this->objects[$id];
437
438
                foreach ($options as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $options of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
439
                    switch ($k) {
440
                        // Boolean keys
441
                        case 'HideToolbar':
442
                        case 'HideMenubar':
443
                        case 'HideWindowUI':
444
                        case 'FitWindow':
445
                        case 'CenterWindow':
446
                        case 'DisplayDocTitle':
447
                        case 'PickTrayByPDFSize':
448
                            $o['info'][$k] = (bool)$v;
449
                            break;
450
451
                        // Integer keys
452
                        case 'NumCopies':
453
                            $o['info'][$k] = (int)$v;
454
                            break;
455
456
                        // Name keys
457
                        case 'ViewArea':
458
                        case 'ViewClip':
459
                        case 'PrintClip':
460
                        case 'PrintArea':
461
                            $o['info'][$k] = (string)$v;
462
                            break;
463
464
                        // Named with limited valid values
465
                        case 'NonFullScreenPageMode':
466
                            if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) {
467
                                continue;
468
                            }
469
                            $o['info'][$k] = $v;
470
                            break;
471
472 View Code Duplication
                        case 'Direction':
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
                            if (!in_array($v, array('L2R', 'R2L'))) {
474
                                continue;
475
                            }
476
                            $o['info'][$k] = $v;
477
                            break;
478
479 View Code Duplication
                        case 'PrintScaling':
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...
480
                            if (!in_array($v, array('None', 'AppDefault'))) {
481
                                continue;
482
                            }
483
                            $o['info'][$k] = $v;
484
                            break;
485
486 View Code Duplication
                        case 'Duplex':
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...
487
                            if (!in_array($v, array('None', 'AppDefault'))) {
488
                                continue;
489
                            }
490
                            $o['info'][$k] = $v;
491
                            break;
492
493
                        // Integer array
494
                        case 'PrintPageRange':
495
                            // Cast to integer array
496
                            foreach ($v as $vK => $vV) {
497
                                $v[$vK] = (int)$vV;
498
                            }
499
                            $o['info'][$k] = array_values($v);
500
                            break;
501
                    }
502
                }
503
                break;
504
505
            case 'out':
506
                $o = &$this->objects[$id];
507
                $res = "\n$id 0 obj\n<< ";
508
509
                foreach ($o['info'] as $k => $v) {
510
                    if (is_string($v)) {
511
                        $v = '/' . $v;
512
                    } elseif (is_int($v)) {
513
                        $v = (string) $v;
514
                    } elseif (is_bool($v)) {
515
                        $v = ($v ? 'true' : 'false');
516
                    } elseif (is_array($v)) {
517
                        $v = '[' . implode(' ', $v) . ']';
518
                    }
519
                    $res .= "\n/$k $v";
520
                }
521
                $res .= "\n>>\n";
522
523
                return $res;
524
        }
525
526
        return null;
527
    }
528
529
    /**
530
     * define the document catalog, the overall controller for the document
531
     *
532
     * @param $id
533
     * @param $action
534
     * @param string|array $options
535
     * @return string|null
536
     */
537
    protected function o_catalog($id, $action, $options = '')
538
    {
539
        if ($action !== 'new') {
540
            $o = &$this->objects[$id];
541
        }
542
543
        switch ($action) {
544
            case 'new':
545
                $this->objects[$id] = array('t' => 'catalog', 'info' => array());
546
                $this->catalogId = $id;
547
                break;
548
549
            case 'outlines':
550
            case 'pages':
551
            case 'openHere':
552
            case 'javascript':
553
                $o['info'][$action] = $options;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
554
                break;
555
556
            case 'viewerPreferences':
557
                if (!isset($o['info']['viewerPreferences'])) {
558
                    $this->numObj++;
559
                    $this->o_viewerPreferences($this->numObj, 'new');
560
                    $o['info']['viewerPreferences'] = $this->numObj;
561
                }
562
563
                $vp = $o['info']['viewerPreferences'];
564
                $this->o_viewerPreferences($vp, 'add', $options);
565
566
                break;
567
568
            case 'out':
569
                $res = "\n$id 0 obj\n<< /Type /Catalog";
570
571
                foreach ($o['info'] as $k => $v) {
572
                    switch ($k) {
573
                        case 'outlines':
574
                            $res .= "\n/Outlines $v 0 R";
575
                            break;
576
577
                        case 'pages':
578
                            $res .= "\n/Pages $v 0 R";
579
                            break;
580
581
                        case 'viewerPreferences':
582
                            $res .= "\n/ViewerPreferences $v 0 R";
583
                            break;
584
585
                        case 'openHere':
586
                            $res .= "\n/OpenAction $v 0 R";
587
                            break;
588
589
                        case 'javascript':
590
                            $res .= "\n/Names <</JavaScript $v 0 R>>";
591
                            break;
592
                    }
593
                }
594
595
                $res .= " >>\nendobj";
596
597
                return $res;
598
        }
599
600
        return null;
601
    }
602
603
    /**
604
     * object which is a parent to the pages in the document
605
     *
606
     * @param $id
607
     * @param $action
608
     * @param string $options
609
     * @return string|null
610
     */
611
    protected function o_pages($id, $action, $options = '')
612
    {
613
        if ($action !== 'new') {
614
            $o = &$this->objects[$id];
615
        }
616
617
        switch ($action) {
618 View Code Duplication
            case 'new':
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...
619
                $this->objects[$id] = array('t' => 'pages', 'info' => array());
620
                $this->o_catalog($this->catalogId, 'pages', $id);
621
                break;
622
623
            case 'page':
624
                if (!is_array($options)) {
625
                    // then it will just be the id of the new page
626
                    $o['info']['pages'][] = $options;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
627
                } else {
628
                    // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
629
                    // and pos is either 'before' or 'after', saying where this page will fit.
630
                    if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
631
                        $i = array_search($options['rid'], $o['info']['pages']);
632
                        if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
633
634
                            // then there is a match
635
                            // make a space
636
                            switch ($options['pos']) {
637
                                case 'before':
638
                                    $k = $i;
639
                                    break;
640
641
                                case 'after':
642
                                    $k = $i + 1;
643
                                    break;
644
645
                                default:
646
                                    $k = -1;
647
                                    break;
648
                            }
649
650
                            if ($k >= 0) {
651
                                for ($j = count($o['info']['pages']) - 1; $j >= $k; $j--) {
652
                                    $o['info']['pages'][$j + 1] = $o['info']['pages'][$j];
653
                                }
654
655
                                $o['info']['pages'][$k] = $options['id'];
656
                            }
657
                        }
658
                    }
659
                }
660
                break;
661
662
            case 'procset':
663
                $o['info']['procset'] = $options;
664
                break;
665
666
            case 'mediaBox':
667
                $o['info']['mediaBox'] = $options;
668
                // which should be an array of 4 numbers
669
                $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
670
                break;
671
672 View Code Duplication
            case 'font':
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...
673
                $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']);
674
                break;
675
676
            case 'extGState':
677
                $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
678
                break;
679
680 View Code Duplication
            case 'xObject':
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...
681
                $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']);
682
                break;
683
684
            case 'out':
685
                if (count($o['info']['pages'])) {
686
                    $res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
687
                    foreach ($o['info']['pages'] as $v) {
688
                        $res .= "$v 0 R\n";
689
                    }
690
691
                    $res .= "]\n/Count " . count($this->objects[$id]['info']['pages']);
692
693
                    if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
694
                        isset($o['info']['procset']) ||
695
                        (isset($o['info']['extGStates']) && count($o['info']['extGStates']))
696
                    ) {
697
                        $res .= "\n/Resources <<";
698
699
                        if (isset($o['info']['procset'])) {
700
                            $res .= "\n/ProcSet " . $o['info']['procset'] . " 0 R";
701
                        }
702
703 View Code Duplication
                        if (isset($o['info']['fonts']) && count($o['info']['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...
704
                            $res .= "\n/Font << ";
705
                            foreach ($o['info']['fonts'] as $finfo) {
706
                                $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R";
707
                            }
708
                            $res .= "\n>>";
709
                        }
710
711 View Code Duplication
                        if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
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...
712
                            $res .= "\n/XObject << ";
713
                            foreach ($o['info']['xObjects'] as $finfo) {
714
                                $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R";
715
                            }
716
                            $res .= "\n>>";
717
                        }
718
719 View Code Duplication
                        if (isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
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...
720
                            $res .= "\n/ExtGState << ";
721
                            foreach ($o['info']['extGStates'] as $gstate) {
722
                                $res .= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
723
                            }
724
                            $res .= "\n>>";
725
                        }
726
727
                        $res .= "\n>>";
728 View Code Duplication
                        if (isset($o['info']['mediaBox'])) {
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...
729
                            $tmp = $o['info']['mediaBox'];
730
                            $res .= "\n/MediaBox [" . sprintf(
731
                                    '%.3F %.3F %.3F %.3F',
732
                                    $tmp[0],
733
                                    $tmp[1],
734
                                    $tmp[2],
735
                                    $tmp[3]
736
                                ) . ']';
737
                        }
738
                    }
739
740
                    $res .= "\n >>\nendobj";
741
                } else {
742
                    $res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
743
                }
744
745
                return $res;
746
        }
747
748
        return null;
749
    }
750
751
    /**
752
     * define the outlines in the doc, empty for now
753
     *
754
     * @param $id
755
     * @param $action
756
     * @param string $options
757
     * @return string|null
758
     */
759
    protected function o_outlines($id, $action, $options = '')
760
    {
761
        if ($action !== 'new') {
762
            $o = &$this->objects[$id];
763
        }
764
765
        switch ($action) {
766 View Code Duplication
            case 'new':
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...
767
                $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array()));
768
                $this->o_catalog($this->catalogId, 'outlines', $id);
769
                break;
770
771
            case 'outline':
772
                $o['info']['outlines'][] = $options;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
773
                break;
774
775
            case 'out':
776
                if (count($o['info']['outlines'])) {
777
                    $res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
778
                    foreach ($o['info']['outlines'] as $v) {
779
                        $res .= "$v 0 R ";
780
                    }
781
782
                    $res .= "] /Count " . count($o['info']['outlines']) . " >>\nendobj";
783
                } else {
784
                    $res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
785
                }
786
787
                return $res;
788
        }
789
790
        return null;
791
    }
792
793
    /**
794
     * an object to hold the font description
795
     *
796
     * @param $id
797
     * @param $action
798
     * @param string|array $options
799
     * @return string|null
800
     */
801
    protected function o_font($id, $action, $options = '')
802
    {
803
        if ($action !== 'new') {
804
            $o = &$this->objects[$id];
805
        }
806
807
        switch ($action) {
808
            case 'new':
809
                $this->objects[$id] = array(
810
                    't'    => 'font',
811
                    'info' => array(
812
                        'name'         => $options['name'],
813
                        'fontFileName' => $options['fontFileName'],
814
                        'SubType'      => 'Type1'
815
                    )
816
                );
817
                $fontNum = $this->numFonts;
818
                $this->objects[$id]['info']['fontNum'] = $fontNum;
819
820
                // deal with the encoding and the differences
821
                if (isset($options['differences'])) {
822
                    // then we'll need an encoding dictionary
823
                    $this->numObj++;
824
                    $this->o_fontEncoding($this->numObj, 'new', $options);
0 ignored issues
show
Bug introduced by
It seems like $options defined by parameter $options on line 801 can also be of type array; however, Cpdf::o_fontEncoding() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
825
                    $this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
826
                } else {
827
                    if (isset($options['encoding'])) {
828
                        // we can specify encoding here
829
                        switch ($options['encoding']) {
830
                            case 'WinAnsiEncoding':
831
                            case 'MacRomanEncoding':
832
                            case 'MacExpertEncoding':
833
                                $this->objects[$id]['info']['encoding'] = $options['encoding'];
834
                                break;
835
836
                            case 'none':
837
                                break;
838
839
                            default:
840
                                $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
841
                                break;
842
                        }
843
                    } else {
844
                        $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
845
                    }
846
                }
847
848
                if ($this->fonts[$options['fontFileName']]['isUnicode']) {
849
                    // For Unicode fonts, we need to incorporate font data into
850
                    // sub-sections that are linked from the primary font section.
851
                    // Look at o_fontGIDtoCID and o_fontDescendentCID functions
852
                    // for more information.
853
                    //
854
                    // All of this code is adapted from the excellent changes made to
855
                    // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
856
857
                    $toUnicodeId = ++$this->numObj;
858
                    $this->o_toUnicode($toUnicodeId, 'new');
859
                    $this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
860
861
                    $cidFontId = ++$this->numObj;
862
                    $this->o_fontDescendentCID($cidFontId, 'new', $options);
863
                    $this->objects[$id]['info']['cidFont'] = $cidFontId;
864
                }
865
866
                // also tell the pages node about the new font
867
                $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
0 ignored issues
show
Documentation introduced by
array('fontNum' => $fontNum, 'objNum' => $id) is of type array<string,?,{"fontNum...integer","objNum":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
868
                break;
869
870
            case 'add':
871
                foreach ($options as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $options of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
872
                    switch ($k) {
873
                        case 'BaseFont':
874
                            $o['info']['name'] = $v;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
875
                            break;
876
                        case 'FirstChar':
877
                        case 'LastChar':
878
                        case 'Widths':
879
                        case 'FontDescriptor':
880
                        case 'SubType':
881
                            $this->addMessage('o_font ' . $k . " : " . $v);
882
                            $o['info'][$k] = $v;
883
                            break;
884
                    }
885
                }
886
887
                // pass values down to descendent font
888
                if (isset($o['info']['cidFont'])) {
889
                    $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
890
                }
891
                break;
892
893
            case 'out':
894
                if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
895
                    // For Unicode fonts, we need to incorporate font data into
896
                    // sub-sections that are linked from the primary font section.
897
                    // Look at o_fontGIDtoCID and o_fontDescendentCID functions
898
                    // for more information.
899
                    //
900
                    // All of this code is adapted from the excellent changes made to
901
                    // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
902
903
                    $res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
904
                    $res .= "/BaseFont /" . $o['info']['name'] . "\n";
905
906
                    // The horizontal identity mapping for 2-byte CIDs; may be used
907
                    // with CIDFonts using any Registry, Ordering, and Supplement values.
908
                    $res .= "/Encoding /Identity-H\n";
909
                    $res .= "/DescendantFonts [" . $o['info']['cidFont'] . " 0 R]\n";
910
                    $res .= "/ToUnicode " . $o['info']['toUnicode'] . " 0 R\n";
911
                    $res .= ">>\n";
912
                    $res .= "endobj";
913
                } else {
914
                    $res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /" . $o['info']['SubType'] . "\n";
915
                    $res .= "/Name /F" . $o['info']['fontNum'] . "\n";
916
                    $res .= "/BaseFont /" . $o['info']['name'] . "\n";
917
918
                    if (isset($o['info']['encodingDictionary'])) {
919
                        // then place a reference to the dictionary
920
                        $res .= "/Encoding " . $o['info']['encodingDictionary'] . " 0 R\n";
921 View Code Duplication
                    } else {
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...
922
                        if (isset($o['info']['encoding'])) {
923
                            // use the specified encoding
924
                            $res .= "/Encoding /" . $o['info']['encoding'] . "\n";
925
                        }
926
                    }
927
928
                    if (isset($o['info']['FirstChar'])) {
929
                        $res .= "/FirstChar " . $o['info']['FirstChar'] . "\n";
930
                    }
931
932
                    if (isset($o['info']['LastChar'])) {
933
                        $res .= "/LastChar " . $o['info']['LastChar'] . "\n";
934
                    }
935
936
                    if (isset($o['info']['Widths'])) {
937
                        $res .= "/Widths " . $o['info']['Widths'] . " 0 R\n";
938
                    }
939
940 View Code Duplication
                    if (isset($o['info']['FontDescriptor'])) {
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...
941
                        $res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
942
                    }
943
944
                    $res .= ">>\n";
945
                    $res .= "endobj";
946
                }
947
948
                return $res;
949
        }
950
951
        return null;
952
    }
953
954
    /**
955
     * A toUnicode section, needed for unicode fonts
956
     *
957
     * @param $id
958
     * @param $action
959
     * @return null|string
960
     */
961
    protected function o_toUnicode($id, $action)
962
    {
963
        switch ($action) {
964
            case 'new':
965
                $this->objects[$id] = array(
966
                    't'    => 'toUnicode'
967
                );
968
                break;
969
            case 'add':
970
                break;
971
            case 'out':
972
                $ordering = '(UCS)';
973
                $registry = '(Adobe)';
974
975 View Code Duplication
                if ($this->encrypted) {
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...
976
                    $this->encryptInit($id);
977
                    $ordering = $this->ARC4($ordering);
978
                    $registry = $this->ARC4($registry);
979
                }
980
981
                $stream = <<<EOT
982
/CIDInit /ProcSet findresource begin
983
12 dict begin
984
begincmap
985
/CIDSystemInfo
986
<</Registry $registry
987
/Ordering $ordering
988
/Supplement 0
989
>> def
990
/CMapName /Adobe-Identity-UCS def
991
/CMapType 2 def
992
1 begincodespacerange
993
<0000> <FFFF>
994
endcodespacerange
995
1 beginbfrange
996
<0000> <FFFF> <0000>
997
endbfrange
998
endcmap
999
CMapName currentdict /CMap defineresource pop
1000
end
1001
end
1002
EOT;
1003
1004
                $res = "\n$id 0 obj\n";
1005
                $res .= "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
1006
                $res .= "stream\n" . $stream . "\nendstream" . "\nendobj";;
1007
1008
                return $res;
1009
        }
1010
1011
        return null;
1012
    }
1013
1014
    /**
1015
     * a font descriptor, needed for including additional fonts
1016
     *
1017
     * @param $id
1018
     * @param $action
1019
     * @param string $options
1020
     * @return null|string
1021
     */
1022
    protected function o_fontDescriptor($id, $action, $options = '')
1023
    {
1024
        if ($action !== 'new') {
1025
            $o = &$this->objects[$id];
1026
        }
1027
1028
        switch ($action) {
1029
            case 'new':
1030
                $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options);
1031
                break;
1032
1033
            case 'out':
1034
                $res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
1035
                foreach ($o['info'] as $label => $value) {
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1036
                    switch ($label) {
1037
                        case 'Ascent':
1038
                        case 'CapHeight':
1039
                        case 'Descent':
1040
                        case 'Flags':
1041
                        case 'ItalicAngle':
1042
                        case 'StemV':
1043
                        case 'AvgWidth':
1044
                        case 'Leading':
1045
                        case 'MaxWidth':
1046
                        case 'MissingWidth':
1047
                        case 'StemH':
1048
                        case 'XHeight':
1049
                        case 'CharSet':
1050
                            if (mb_strlen($value, '8bit')) {
1051
                                $res .= "/$label $value\n";
1052
                            }
1053
1054
                            break;
1055
                        case 'FontFile':
1056
                        case 'FontFile2':
1057
                        case 'FontFile3':
1058
                            $res .= "/$label $value 0 R\n";
1059
                            break;
1060
1061
                        case 'FontBBox':
1062
                            $res .= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
1063
                            break;
1064
1065
                        case 'FontName':
1066
                            $res .= "/$label /$value\n";
1067
                            break;
1068
                    }
1069
                }
1070
1071
                $res .= ">>\nendobj";
1072
1073
                return $res;
1074
        }
1075
1076
        return null;
1077
    }
1078
1079
    /**
1080
     * the font encoding
1081
     *
1082
     * @param $id
1083
     * @param $action
1084
     * @param string $options
1085
     * @return null|string
1086
     */
1087
    protected function o_fontEncoding($id, $action, $options = '')
1088
    {
1089
        if ($action !== 'new') {
1090
            $o = &$this->objects[$id];
1091
        }
1092
1093
        switch ($action) {
1094
            case 'new':
1095
                // the options array should contain 'differences' and maybe 'encoding'
1096
                $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options);
1097
                break;
1098
1099
            case 'out':
1100
                $res = "\n$id 0 obj\n<< /Type /Encoding\n";
1101
                if (!isset($o['info']['encoding'])) {
1102
                    $o['info']['encoding'] = 'WinAnsiEncoding';
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1103
                }
1104
1105 View Code Duplication
                if ($o['info']['encoding'] !== 'none') {
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...
1106
                    $res .= "/BaseEncoding /" . $o['info']['encoding'] . "\n";
1107
                }
1108
1109
                $res .= "/Differences \n[";
1110
1111
                $onum = -100;
1112
1113
                foreach ($o['info']['differences'] as $num => $label) {
1114
                    if ($num != $onum + 1) {
1115
                        // we cannot make use of consecutive numbering
1116
                        $res .= "\n$num /$label";
1117
                    } else {
1118
                        $res .= " /$label";
1119
                    }
1120
1121
                    $onum = $num;
1122
                }
1123
1124
                $res .= "\n]\n>>\nendobj";
1125
1126
                return $res;
1127
        }
1128
1129
        return null;
1130
    }
1131
1132
    /**
1133
     * a descendent cid font, needed for unicode fonts
1134
     *
1135
     * @param $id
1136
     * @param $action
1137
     * @param string|array $options
1138
     * @return null|string
1139
     */
1140
    protected function o_fontDescendentCID($id, $action, $options = '')
1141
    {
1142
        if ($action !== 'new') {
1143
            $o = &$this->objects[$id];
1144
        }
1145
1146
        switch ($action) {
1147
            case 'new':
1148
                $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
1149
1150
                // we need a CID system info section
1151
                $cidSystemInfoId = ++$this->numObj;
1152
                $this->o_cidSystemInfo($cidSystemInfoId, 'new');
1153
                $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
1154
1155
                // and a CID to GID map
1156
                $cidToGidMapId = ++$this->numObj;
1157
                $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
0 ignored issues
show
Bug introduced by
It seems like $options defined by parameter $options on line 1140 can also be of type array; however, Cpdf::o_fontGIDtoCIDMap() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1158
                $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
1159
                break;
1160
1161
            case 'add':
1162
                foreach ($options as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $options of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1163
                    switch ($k) {
1164
                        case 'BaseFont':
1165
                            $o['info']['name'] = $v;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1166
                            break;
1167
1168
                        case 'FirstChar':
1169
                        case 'LastChar':
1170
                        case 'MissingWidth':
1171
                        case 'FontDescriptor':
1172
                        case 'SubType':
1173
                            $this->addMessage("o_fontDescendentCID $k : $v");
1174
                            $o['info'][$k] = $v;
1175
                            break;
1176
                    }
1177
                }
1178
1179
                // pass values down to cid to gid map
1180
                $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
0 ignored issues
show
Bug introduced by
It seems like $options defined by parameter $options on line 1140 can also be of type array; however, Cpdf::o_fontGIDtoCIDMap() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1181
                break;
1182
1183
            case 'out':
1184
                $res = "\n$id 0 obj\n";
1185
                $res .= "<</Type /Font\n";
1186
                $res .= "/Subtype /CIDFontType2\n";
1187
                $res .= "/BaseFont /" . $o['info']['name'] . "\n";
1188
                $res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n";
1189
                //      if (isset($o['info']['FirstChar'])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
83% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1190
                //        $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1191
                //      }
1192
1193
                //      if (isset($o['info']['LastChar'])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
83% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1194
                //        $res.= "/LastChar ".$o['info']['LastChar']."\n";
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1195
                //      }
1196 View Code Duplication
                if (isset($o['info']['FontDescriptor'])) {
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...
1197
                    $res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n";
1198
                }
1199
1200
                if (isset($o['info']['MissingWidth'])) {
1201
                    $res .= "/DW " . $o['info']['MissingWidth'] . "\n";
1202
                }
1203
1204
                if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
1205
                    $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
1206
                    $w = '';
1207
                    foreach ($cid_widths as $cid => $width) {
1208
                        $w .= "$cid [$width] ";
1209
                    }
1210
                    $res .= "/W [$w]\n";
1211
                }
1212
1213
                $res .= "/CIDToGIDMap " . $o['info']['cidToGidMap'] . " 0 R\n";
1214
                $res .= ">>\n";
1215
                $res .= "endobj";
1216
1217
                return $res;
1218
        }
1219
1220
        return null;
1221
    }
1222
1223
    /**
1224
     * CID system info section, needed for unicode fonts
1225
     *
1226
     * @param $id
1227
     * @param $action
1228
     * @return null|string
1229
     */
1230
    protected function o_cidSystemInfo($id, $action)
1231
    {
1232
        switch ($action) {
1233
            case 'new':
1234
                $this->objects[$id] = array(
1235
                    't' => 'cidSystemInfo'
1236
                );
1237
                break;
1238
            case 'add':
1239
                break;
1240
            case 'out':
1241
                $ordering = '(UCS)';
1242
                $registry = '(Adobe)';
1243
1244 View Code Duplication
                if ($this->encrypted) {
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...
1245
                    $this->encryptInit($id);
1246
                    $ordering = $this->ARC4($ordering);
1247
                    $registry = $this->ARC4($registry);
1248
                }
1249
1250
1251
                $res = "\n$id 0 obj\n";
1252
1253
                $res .= '<</Registry ' . $registry . "\n"; // A string identifying an issuer of character collections
1254
                $res .= '/Ordering ' . $ordering . "\n"; // A string that uniquely names a character collection issued by a specific registry
1255
                $res .= "/Supplement 0\n"; // The supplement number of the character collection.
1256
                $res .= ">>";
1257
1258
                $res .= "\nendobj";;
1259
1260
                return $res;
1261
        }
1262
1263
        return null;
1264
    }
1265
1266
    /**
1267
     * a font glyph to character map, needed for unicode fonts
1268
     *
1269
     * @param $id
1270
     * @param $action
1271
     * @param string $options
1272
     * @return null|string
1273
     */
1274
    protected function o_fontGIDtoCIDMap($id, $action, $options = '')
1275
    {
1276
        if ($action !== 'new') {
1277
            $o = &$this->objects[$id];
1278
        }
1279
1280
        switch ($action) {
1281
            case 'new':
1282
                $this->objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options);
1283
                break;
1284
1285
            case 'out':
1286
                $res = "\n$id 0 obj\n";
1287
                $fontFileName = $o['info']['fontFileName'];
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1288
                $tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
1289
1290
                $compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
1291
                    $this->fonts[$fontFileName]['CIDtoGID_Compressed'];
1292
1293
                if (!$compressed && isset($o['raw'])) {
1294
                    $res .= $tmp;
1295
                } else {
1296
                    $res .= "<<";
1297
1298 View Code Duplication
                    if (!$compressed && $this->compressionReady && $this->options['compression']) {
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...
1299
                        // then implement ZLIB based compression on this content stream
1300
                        $compressed = true;
1301
                        $tmp = gzcompress($tmp, 6);
1302
                    }
1303
                    if ($compressed) {
1304
                        $res .= "\n/Filter /FlateDecode";
1305
                    }
1306
1307
                    if ($this->encrypted) {
1308
                        $this->encryptInit($id);
1309
                        $tmp = $this->ARC4($tmp);
1310
                    }
1311
1312
                    $res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream";
1313
                }
1314
1315
                $res .= "\nendobj";
1316
1317
                return $res;
1318
        }
1319
1320
        return null;
1321
    }
1322
1323
    /**
1324
     * the document procset, solves some problems with printing to old PS printers
1325
     *
1326
     * @param $id
1327
     * @param $action
1328
     * @param string $options
1329
     * @return null|string
1330
     */
1331
    protected function o_procset($id, $action, $options = '')
1332
    {
1333
        if ($action !== 'new') {
1334
            $o = &$this->objects[$id];
1335
        }
1336
1337
        switch ($action) {
1338 View Code Duplication
            case 'new':
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...
1339
                $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1));
1340
                $this->o_pages($this->currentNode, 'procset', $id);
1341
                $this->procsetObjectId = $id;
1342
                break;
1343
1344
            case 'add':
1345
                // this is to add new items to the procset list, despite the fact that this is considered
1346
                // obsolete, the items are required for printing to some postscript printers
1347
                switch ($options) {
1348
                    case 'ImageB':
1349
                    case 'ImageC':
1350
                    case 'ImageI':
1351
                        $o['info'][$options] = 1;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1352
                        break;
1353
                }
1354
                break;
1355
1356
            case 'out':
1357
                $res = "\n$id 0 obj\n[";
1358
                foreach ($o['info'] as $label => $val) {
1359
                    $res .= "/$label ";
1360
                }
1361
                $res .= "]\nendobj";
1362
1363
                return $res;
1364
        }
1365
1366
        return null;
1367
    }
1368
1369
    /**
1370
     * define the document information
1371
     *
1372
     * @param $id
1373
     * @param $action
1374
     * @param string $options
1375
     * @return null|string
1376
     */
1377
    protected function o_info($id, $action, $options = '')
1378
    {
1379
        switch ($action) {
1380
            case 'new':
1381
                $this->infoObject = $id;
1382
                $date = 'D:' . @date('Ymd');
1383
                $this->objects[$id] = array(
1384
                    't'    => 'info',
1385
                    'info' => array(
1386
                        'Producer'      => 'CPDF (dompdf)',
1387
                        'CreationDate' => $date
1388
                    )
1389
                );
1390
                break;
1391
            case 'Title':
1392
            case 'Author':
1393
            case 'Subject':
1394
            case 'Keywords':
1395
            case 'Creator':
1396
            case 'Producer':
1397
            case 'CreationDate':
1398
            case 'ModDate':
1399
            case 'Trapped':
1400
                $this->objects[$id]['info'][$action] = $options;
1401
                break;
1402
1403
            case 'out':
1404
                $encrypted = $this->encrypted;
1405
                if ($encrypted) {
1406
                    $this->encryptInit($id);
1407
                }
1408
1409
                $res = "\n$id 0 obj\n<<\n";
1410
                $o = &$this->objects[$id];
1411
                foreach ($o['info'] as $k => $v) {
1412
                    $res .= "/$k (";
1413
1414
                    // dates must be outputted as-is, without Unicode transformations
1415
                    if ($k !== 'CreationDate' && $k !== 'ModDate') {
1416
                        $v = $this->filterText($v, true, false);
1417
                    }
1418
1419
                    if ($encrypted) {
1420
                        $v = $this->ARC4($v);
1421
                    }
1422
1423
                    $res .= $v;
1424
                    $res .= ")\n";
1425
                }
1426
1427
                $res .= ">>\nendobj";
1428
1429
                return $res;
1430
        }
1431
1432
        return null;
1433
    }
1434
1435
    /**
1436
     * an action object, used to link to URLS initially
1437
     *
1438
     * @param $id
1439
     * @param $action
1440
     * @param string $options
1441
     * @return null|string
1442
     */
1443
    protected function o_action($id, $action, $options = '')
1444
    {
1445
        if ($action !== 'new') {
1446
            $o = &$this->objects[$id];
1447
        }
1448
1449
        switch ($action) {
1450
            case 'new':
1451
                if (is_array($options)) {
1452
                    $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']);
1453
                } else {
1454
                    // then assume a URI action
1455
                    $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI');
1456
                }
1457
                break;
1458
1459
            case 'out':
1460
                if ($this->encrypted) {
1461
                    $this->encryptInit($id);
1462
                }
1463
1464
                $res = "\n$id 0 obj\n<< /Type /Action";
1465
                switch ($o['type']) {
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1466
                    case 'ilink':
1467
                        if (!isset($this->destinations[(string)$o['info']['label']])) {
1468
                            break;
1469
                        }
1470
1471
                        // there will be an 'label' setting, this is the name of the destination
1472
                        $res .= "\n/S /GoTo\n/D " . $this->destinations[(string)$o['info']['label']] . " 0 R";
1473
                        break;
1474
1475
                    case 'URI':
1476
                        $res .= "\n/S /URI\n/URI (";
1477
                        if ($this->encrypted) {
1478
                            $res .= $this->filterText($this->ARC4($o['info']), true, false);
1479
                        } else {
1480
                            $res .= $this->filterText($o['info'], true, false);
1481
                        }
1482
1483
                        $res .= ")";
1484
                        break;
1485
                }
1486
1487
                $res .= "\n>>\nendobj";
1488
1489
                return $res;
1490
        }
1491
1492
        return null;
1493
    }
1494
1495
    /**
1496
     * an annotation object, this will add an annotation to the current page.
1497
     * initially will support just link annotations
1498
     *
1499
     * @param $id
1500
     * @param $action
1501
     * @param string $options
1502
     * @return null|string
1503
     */
1504
    protected function o_annotation($id, $action, $options = '')
1505
    {
1506
        if ($action !== 'new') {
1507
            $o = &$this->objects[$id];
1508
        }
1509
1510
        switch ($action) {
1511
            case 'new':
1512
                // add the annotation to the current page
1513
                $pageId = $this->currentPage;
1514
                $this->o_page($pageId, 'annot', $id);
1515
1516
                // and add the action object which is going to be required
1517
                switch ($options['type']) {
1518
                    case 'link':
1519
                        $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
1520
                        $this->numObj++;
1521
                        $this->o_action($this->numObj, 'new', $options['url']);
1522
                        $this->objects[$id]['info']['actionId'] = $this->numObj;
1523
                        break;
1524
1525
                    case 'ilink':
1526
                        // this is to a named internal link
1527
                        $label = $options['label'];
1528
                        $this->objects[$id] = array('t' => 'annotation', 'info' => $options);
1529
                        $this->numObj++;
1530
                        $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label));
0 ignored issues
show
Documentation introduced by
array('type' => 'ilink', 'label' => $label) is of type array<string,string,{"ty...ing","label":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1531
                        $this->objects[$id]['info']['actionId'] = $this->numObj;
1532
                        break;
1533
                }
1534
                break;
1535
1536
            case 'out':
1537
                $res = "\n$id 0 obj\n<< /Type /Annot";
1538
                switch ($o['info']['type']) {
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1539
                    case 'link':
1540
                    case 'ilink':
1541
                        $res .= "\n/Subtype /Link";
1542
                        break;
1543
                }
1544
                $res .= "\n/A " . $o['info']['actionId'] . " 0 R";
1545
                $res .= "\n/Border [0 0 0]";
1546
                $res .= "\n/H /I";
1547
                $res .= "\n/Rect [ ";
1548
1549
                foreach ($o['info']['rect'] as $v) {
1550
                    $res .= sprintf("%.4F ", $v);
1551
                }
1552
1553
                $res .= "]";
1554
                $res .= "\n>>\nendobj";
1555
1556
                return $res;
1557
        }
1558
1559
        return null;
1560
    }
1561
1562
    /**
1563
     * a page object, it also creates a contents object to hold its contents
1564
     *
1565
     * @param $id
1566
     * @param $action
1567
     * @param string $options
1568
     * @return null|string
1569
     */
1570
    protected function o_page($id, $action, $options = '')
1571
    {
1572
        if ($action !== 'new') {
1573
            $o = &$this->objects[$id];
1574
        }
1575
1576
        switch ($action) {
1577
            case 'new':
1578
                $this->numPages++;
1579
                $this->objects[$id] = array(
1580
                    't'    => 'page',
1581
                    'info' => array(
1582
                        'parent'  => $this->currentNode,
1583
                        'pageNum' => $this->numPages,
1584
                        'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox']
1585
                    )
1586
                );
1587
1588
                if (is_array($options)) {
1589
                    // then this must be a page insertion, array should contain 'rid','pos'=[before|after]
1590
                    $options['id'] = $id;
1591
                    $this->o_pages($this->currentNode, 'page', $options);
0 ignored issues
show
Documentation introduced by
$options is of type array<string,?,{"id":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1592
                } else {
1593
                    $this->o_pages($this->currentNode, 'page', $id);
1594
                }
1595
1596
                $this->currentPage = $id;
1597
                //make a contents object to go with this page
1598
                $this->numObj++;
1599
                $this->o_contents($this->numObj, 'new', $id);
1600
                $this->currentContents = $this->numObj;
1601
                $this->objects[$id]['info']['contents'] = array();
1602
                $this->objects[$id]['info']['contents'][] = $this->numObj;
1603
1604
                $match = ($this->numPages % 2 ? 'odd' : 'even');
1605
                foreach ($this->addLooseObjects as $oId => $target) {
1606
                    if ($target === 'all' || $match === $target) {
1607
                        $this->objects[$id]['info']['contents'][] = $oId;
1608
                    }
1609
                }
1610
                break;
1611
1612
            case 'content':
1613
                $o['info']['contents'][] = $options;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1614
                break;
1615
1616
            case 'annot':
1617
                // add an annotation to this page
1618
                if (!isset($o['info']['annot'])) {
1619
                    $o['info']['annot'] = array();
1620
                }
1621
1622
                // $options should contain the id of the annotation dictionary
1623
                $o['info']['annot'][] = $options;
1624
                break;
1625
1626
            case 'out':
1627
                $res = "\n$id 0 obj\n<< /Type /Page";
1628 View Code Duplication
                if (isset($o['info']['mediaBox'])) {
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...
1629
                    $tmp = $o['info']['mediaBox'];
1630
                    $res .= "\n/MediaBox [" . sprintf(
1631
                            '%.3F %.3F %.3F %.3F',
1632
                            $tmp[0],
1633
                            $tmp[1],
1634
                            $tmp[2],
1635
                            $tmp[3]
1636
                        ) . ']';
1637
                }
1638
                $res .= "\n/Parent " . $o['info']['parent'] . " 0 R";
1639
1640
                if (isset($o['info']['annot'])) {
1641
                    $res .= "\n/Annots [";
1642
                    foreach ($o['info']['annot'] as $aId) {
1643
                        $res .= " $aId 0 R";
1644
                    }
1645
                    $res .= " ]";
1646
                }
1647
1648
                $count = count($o['info']['contents']);
1649
                if ($count == 1) {
1650
                    $res .= "\n/Contents " . $o['info']['contents'][0] . " 0 R";
1651
                } else {
1652
                    if ($count > 1) {
1653
                        $res .= "\n/Contents [\n";
1654
1655
                        // reverse the page contents so added objects are below normal content
1656
                        //foreach (array_reverse($o['info']['contents']) as $cId) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1657
                        // Back to normal now that I've got transparency working --Benj
1658
                        foreach ($o['info']['contents'] as $cId) {
1659
                            $res .= "$cId 0 R\n";
1660
                        }
1661
                        $res .= "]";
1662
                    }
1663
                }
1664
1665
                $res .= "\n>>\nendobj";
1666
1667
                return $res;
1668
        }
1669
1670
        return null;
1671
    }
1672
1673
    /**
1674
     * the contents objects hold all of the content which appears on pages
1675
     *
1676
     * @param $id
1677
     * @param $action
1678
     * @param string|array $options
1679
     * @return null|string
1680
     */
1681
    protected function o_contents($id, $action, $options = '')
1682
    {
1683
        if ($action !== 'new') {
1684
            $o = &$this->objects[$id];
1685
        }
1686
1687
        switch ($action) {
1688
            case 'new':
1689
                $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array());
1690
                if (mb_strlen($options, '8bit') && intval($options)) {
1691
                    // then this contents is the primary for a page
1692
                    $this->objects[$id]['onPage'] = $options;
1693
                } else {
1694
                    if ($options === 'raw') {
1695
                        // then this page contains some other type of system object
1696
                        $this->objects[$id]['raw'] = 1;
1697
                    }
1698
                }
1699
                break;
1700
1701
            case 'add':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
1702
                // add more options to the declaration
1703
                foreach ($options as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $options of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1704
                    $o['info'][$k] = $v;
0 ignored issues
show
Bug introduced by
The variable $o does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1705
                }
1706
1707
            case 'out':
1708
                $tmp = $o['c'];
1709
                $res = "\n$id 0 obj\n";
1710
1711
                if (isset($this->objects[$id]['raw'])) {
1712
                    $res .= $tmp;
1713
                } else {
1714
                    $res .= "<<";
1715 View Code Duplication
                    if ($this->compressionReady && $this->options['compression']) {
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...
1716
                        // then implement ZLIB based compression on this content stream
1717
                        $res .= " /Filter /FlateDecode";
1718
                        $tmp = gzcompress($tmp, 6);
1719
                    }
1720
1721
                    if ($this->encrypted) {
1722
                        $this->encryptInit($id);
1723
                        $tmp = $this->ARC4($tmp);
1724
                    }
1725
1726
                    foreach ($o['info'] as $k => $v) {
1727
                        $res .= "\n/$k $v";
1728
                    }
1729
1730
                    $res .= "\n/Length " . mb_strlen($tmp, '8bit') . " >>\nstream\n$tmp\nendstream";
1731
                }
1732
1733
                $res .= "\nendobj";
1734
1735
                return $res;
1736
        }
1737
1738
        return null;
1739
    }
1740
1741
    /**
1742
     * @param $id
1743
     * @param $action
1744
     * @return string|null
1745
     */
1746
    protected function o_embedjs($id, $action)
1747
    {
1748
        switch ($action) {
1749
            case 'new':
1750
                $this->objects[$id] = array(
1751
                    't'    => 'embedjs',
1752
                    'info' => array(
1753
                        'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]'
1754
                    )
1755
                );
1756
                break;
1757
1758 View Code Duplication
            case 'out':
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...
1759
                $o = &$this->objects[$id];
1760
                $res = "\n$id 0 obj\n<< ";
1761
                foreach ($o['info'] as $k => $v) {
1762
                    $res .= "\n/$k $v";
1763
                }
1764
                $res .= "\n>>\nendobj";
1765
1766
                return $res;
1767
        }
1768
1769
        return null;
1770
    }
1771
1772
    /**
1773
     * @param $id
1774
     * @param $action
1775
     * @param string $code
1776
     * @return null|string
1777
     */
1778
    protected function o_javascript($id, $action, $code = '')
1779
    {
1780
        switch ($action) {
1781
            case 'new':
1782
                $this->objects[$id] = array(
1783
                    't'    => 'javascript',
1784
                    'info' => array(
1785
                        'S'  => '/JavaScript',
1786
                        'JS' => '(' . $this->filterText($code, true, false) . ')',
1787
                    )
1788
                );
1789
                break;
1790
1791 View Code Duplication
            case 'out':
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...
1792
                $o = &$this->objects[$id];
1793
                $res = "\n$id 0 obj\n<< ";
1794
1795
                foreach ($o['info'] as $k => $v) {
1796
                    $res .= "\n/$k $v";
1797
                }
1798
                $res .= "\n>>\nendobj";
1799
1800
                return $res;
1801
        }
1802
1803
        return null;
1804
    }
1805
1806
    /**
1807
     * an image object, will be an XObject in the document, includes description and data
1808
     *
1809
     * @param $id
1810
     * @param $action
1811
     * @param string $options
1812
     * @return null|string
1813
     */
1814
    protected function o_image($id, $action, $options = '')
1815
    {
1816
        switch ($action) {
1817
            case 'new':
1818
                // make the new object
1819
                $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array());
1820
1821
                $info =& $this->objects[$id]['info'];
1822
1823
                $info['Type'] = '/XObject';
1824
                $info['Subtype'] = '/Image';
1825
                $info['Width'] = $options['iw'];
1826
                $info['Height'] = $options['ih'];
1827
1828
                if (isset($options['masked']) && $options['masked']) {
1829
                    $info['SMask'] = ($this->numObj - 1) . ' 0 R';
1830
                }
1831
1832
                if (!isset($options['type']) || $options['type'] === 'jpg') {
1833
                    if (!isset($options['channels'])) {
1834
                        $options['channels'] = 3;
1835
                    }
1836
1837
                    switch ($options['channels']) {
1838
                        case  1:
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1839
                            $info['ColorSpace'] = '/DeviceGray';
1840
                            break;
1841
                        case  4:
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1842
                            $info['ColorSpace'] = '/DeviceCMYK';
1843
                            break;
1844
                        default:
1845
                            $info['ColorSpace'] = '/DeviceRGB';
1846
                            break;
1847
                    }
1848
1849
                    if ($info['ColorSpace'] === '/DeviceCMYK') {
1850
                        $info['Decode'] = '[1 0 1 0 1 0 1 0]';
1851
                    }
1852
1853
                    $info['Filter'] = '/DCTDecode';
1854
                    $info['BitsPerComponent'] = 8;
1855
                } else {
1856
                    if ($options['type'] === 'png') {
1857
                        $info['Filter'] = '/FlateDecode';
1858
                        $info['DecodeParms'] = '<< /Predictor 15 /Colors ' . $options['ncolor'] . ' /Columns ' . $options['iw'] . ' /BitsPerComponent ' . $options['bitsPerComponent'] . '>>';
1859
1860
                        if ($options['isMask']) {
1861
                            $info['ColorSpace'] = '/DeviceGray';
1862
                        } else {
1863
                            if (mb_strlen($options['pdata'], '8bit')) {
1864
                                $tmp = ' [ /Indexed /DeviceRGB ' . (mb_strlen($options['pdata'], '8bit') / 3 - 1) . ' ';
1865
                                $this->numObj++;
1866
                                $this->o_contents($this->numObj, 'new');
1867
                                $this->objects[$this->numObj]['c'] = $options['pdata'];
1868
                                $tmp .= $this->numObj . ' 0 R';
1869
                                $tmp .= ' ]';
1870
                                $info['ColorSpace'] = $tmp;
1871
1872 View Code Duplication
                                if (isset($options['transparency'])) {
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...
1873
                                    $transparency = $options['transparency'];
1874
                                    switch ($transparency['type']) {
1875
                                        case 'indexed':
1876
                                            $tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
1877
                                            $info['Mask'] = $tmp;
1878
                                            break;
1879
1880
                                        case 'color-key':
1881
                                            $tmp = ' [ ' .
1882
                                                $transparency['r'] . ' ' . $transparency['r'] .
1883
                                                $transparency['g'] . ' ' . $transparency['g'] .
1884
                                                $transparency['b'] . ' ' . $transparency['b'] .
1885
                                                ' ] ';
1886
                                            $info['Mask'] = $tmp;
1887
                                            break;
1888
                                    }
1889
                                }
1890
                            } else {
1891 View Code Duplication
                                if (isset($options['transparency'])) {
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...
1892
                                    $transparency = $options['transparency'];
1893
1894
                                    switch ($transparency['type']) {
1895
                                        case 'indexed':
1896
                                            $tmp = ' [ ' . $transparency['data'] . ' ' . $transparency['data'] . '] ';
1897
                                            $info['Mask'] = $tmp;
1898
                                            break;
1899
1900
                                        case 'color-key':
1901
                                            $tmp = ' [ ' .
1902
                                                $transparency['r'] . ' ' . $transparency['r'] . ' ' .
1903
                                                $transparency['g'] . ' ' . $transparency['g'] . ' ' .
1904
                                                $transparency['b'] . ' ' . $transparency['b'] .
1905
                                                ' ] ';
1906
                                            $info['Mask'] = $tmp;
1907
                                            break;
1908
                                    }
1909
                                }
1910
                                $info['ColorSpace'] = '/' . $options['color'];
1911
                            }
1912
                        }
1913
1914
                        $info['BitsPerComponent'] = $options['bitsPerComponent'];
1915
                    }
1916
                }
1917
1918
                // assign it a place in the named resource dictionary as an external object, according to
1919
                // the label passed in with it.
1920
                $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id));
0 ignored issues
show
Documentation introduced by
array('label' => $option...bel'], 'objNum' => $id) is of type array<string,?,{"label":"string","objNum":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1921
1922
                // also make sure that we have the right procset object for it.
1923
                $this->o_procset($this->procsetObjectId, 'add', 'ImageC');
1924
                break;
1925
1926
            case 'out':
1927
                $o = &$this->objects[$id];
1928
                $tmp = &$o['data'];
1929
                $res = "\n$id 0 obj\n<<";
1930
1931
                foreach ($o['info'] as $k => $v) {
1932
                    $res .= "\n/$k $v";
1933
                }
1934
1935
                if ($this->encrypted) {
1936
                    $this->encryptInit($id);
1937
                    $tmp = $this->ARC4($tmp);
1938
                }
1939
1940
                $res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream\nendobj";
1941
1942
                return $res;
1943
        }
1944
1945
        return null;
1946
    }
1947
1948
    /**
1949
     * graphics state object
1950
     *
1951
     * @param $id
1952
     * @param $action
1953
     * @param string $options
1954
     * @return null|string
1955
     */
1956
    protected function o_extGState($id, $action, $options = "")
1957
    {
1958
        static $valid_params = array(
1959
            "LW",
1960
            "LC",
1961
            "LC",
1962
            "LJ",
1963
            "ML",
1964
            "D",
1965
            "RI",
1966
            "OP",
1967
            "op",
1968
            "OPM",
1969
            "Font",
1970
            "BG",
1971
            "BG2",
1972
            "UCR",
1973
            "TR",
1974
            "TR2",
1975
            "HT",
1976
            "FL",
1977
            "SM",
1978
            "SA",
1979
            "BM",
1980
            "SMask",
1981
            "CA",
1982
            "ca",
1983
            "AIS",
1984
            "TK"
1985
        );
1986
1987
        switch ($action) {
1988 View Code Duplication
            case "new":
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...
1989
                $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
1990
1991
                // Tell the pages about the new resource
1992
                $this->numStates++;
1993
                $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
0 ignored issues
show
Documentation introduced by
array('objNum' => $id, '...m' => $this->numStates) is of type array<string,?,{"objNum"...,"stateNum":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1994
                break;
1995
1996 View Code Duplication
            case "out":
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...
1997
                $o = &$this->objects[$id];
1998
                $res = "\n$id 0 obj\n<< /Type /ExtGState\n";
1999
2000
                foreach ($o["info"] as $k => $v) {
2001
                    if (!in_array($k, $valid_params)) {
2002
                        continue;
2003
                    }
2004
                    $res .= "/$k $v\n";
2005
                }
2006
2007
                $res .= ">>\nendobj";
2008
2009
                return $res;
2010
        }
2011
2012
        return null;
2013
    }
2014
2015
    /**
2016
     * encryption object.
2017
     *
2018
     * @param $id
2019
     * @param $action
2020
     * @param string $options
2021
     * @return string|null
2022
     */
2023
    protected function o_encryption($id, $action, $options = '')
2024
    {
2025
        switch ($action) {
2026
            case 'new':
2027
                // make the new object
2028
                $this->objects[$id] = array('t' => 'encryption', 'info' => $options);
2029
                $this->arc4_objnum = $id;
2030
                break;
2031
2032
            case 'keys':
2033
                // figure out the additional parameters required
2034
                $pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41)
2035
                    . chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08)
2036
                    . chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80)
2037
                    . chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A);
2038
2039
                $info = $this->objects[$id]['info'];
2040
2041
                $len = mb_strlen($info['owner'], '8bit');
2042
2043 View Code Duplication
                if ($len > 32) {
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...
2044
                    $owner = substr($info['owner'], 0, 32);
2045
                } else {
2046
                    if ($len < 32) {
2047
                        $owner = $info['owner'] . substr($pad, 0, 32 - $len);
2048
                    } else {
2049
                        $owner = $info['owner'];
2050
                    }
2051
                }
2052
2053
                $len = mb_strlen($info['user'], '8bit');
2054 View Code Duplication
                if ($len > 32) {
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...
2055
                    $user = substr($info['user'], 0, 32);
2056
                } else {
2057
                    if ($len < 32) {
2058
                        $user = $info['user'] . substr($pad, 0, 32 - $len);
2059
                    } else {
2060
                        $user = $info['user'];
2061
                    }
2062
                }
2063
2064
                $tmp = $this->md5_16($owner);
2065
                $okey = substr($tmp, 0, 5);
2066
                $this->ARC4_init($okey);
2067
                $ovalue = $this->ARC4($user);
2068
                $this->objects[$id]['info']['O'] = $ovalue;
2069
2070
                // now make the u value, phew.
2071
                $tmp = $this->md5_16(
2072
                    $user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier)
2073
                );
2074
2075
                $ukey = substr($tmp, 0, 5);
2076
                $this->ARC4_init($ukey);
2077
                $this->encryptionKey = $ukey;
2078
                $this->encrypted = true;
2079
                $uvalue = $this->ARC4($pad);
2080
                $this->objects[$id]['info']['U'] = $uvalue;
2081
                // initialize the arc4 array
2082
                break;
2083
2084
            case 'out':
2085
                $o = &$this->objects[$id];
2086
2087
                $res = "\n$id 0 obj\n<<";
2088
                $res .= "\n/Filter /Standard";
2089
                $res .= "\n/V 1";
2090
                $res .= "\n/R 2";
2091
                $res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')';
2092
                $res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')';
2093
                // and the p-value needs to be converted to account for the twos-complement approach
2094
                $o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1;
2095
                $res .= "\n/P " . ($o['info']['p']);
2096
                $res .= "\n>>\nendobj";
2097
2098
                return $res;
2099
        }
2100
2101
        return null;
2102
    }
2103
2104
    /**
2105
     * ARC4 functions
2106
     * A series of function to implement ARC4 encoding in PHP
2107
     */
2108
2109
    /**
2110
     * calculate the 16 byte version of the 128 bit md5 digest of the string
2111
     *
2112
     * @param $string
2113
     * @return string
2114
     */
2115
    function md5_16($string)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2116
    {
2117
        $tmp = md5($string);
2118
        $out = '';
2119
        for ($i = 0; $i <= 30; $i = $i + 2) {
2120
            $out .= chr(hexdec(substr($tmp, $i, 2)));
2121
        }
2122
2123
        return $out;
2124
    }
2125
2126
    /**
2127
     * initialize the encryption for processing a particular object
2128
     *
2129
     * @param $id
2130
     */
2131
    function encryptInit($id)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2132
    {
2133
        $tmp = $this->encryptionKey;
2134
        $hex = dechex($id);
2135
        if (mb_strlen($hex, '8bit') < 6) {
2136
            $hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex;
2137
        }
2138
        $tmp .= chr(hexdec(substr($hex, 4, 2)))
2139
            . chr(hexdec(substr($hex, 2, 2)))
2140
            . chr(hexdec(substr($hex, 0, 2)))
2141
            . chr(0)
2142
            . chr(0)
2143
        ;
2144
        $key = $this->md5_16($tmp);
2145
        $this->ARC4_init(substr($key, 0, 10));
2146
    }
2147
2148
    /**
2149
     * initialize the ARC4 encryption
2150
     *
2151
     * @param string $key
2152
     */
2153
    function ARC4_init($key = '')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2154
    {
2155
        $this->arc4 = '';
2156
2157
        // setup the control array
2158
        if (mb_strlen($key, '8bit') == 0) {
2159
            return;
2160
        }
2161
2162
        $k = '';
2163
        while (mb_strlen($k, '8bit') < 256) {
2164
            $k .= $key;
2165
        }
2166
2167
        $k = substr($k, 0, 256);
2168
        for ($i = 0; $i < 256; $i++) {
2169
            $this->arc4 .= chr($i);
2170
        }
2171
2172
        $j = 0;
2173
2174
        for ($i = 0; $i < 256; $i++) {
2175
            $t = $this->arc4[$i];
2176
            $j = ($j + ord($t) + ord($k[$i])) % 256;
2177
            $this->arc4[$i] = $this->arc4[$j];
2178
            $this->arc4[$j] = $t;
2179
        }
2180
    }
2181
2182
    /**
2183
     * ARC4 encrypt a text string
2184
     *
2185
     * @param $text
2186
     * @return string
2187
     */
2188
    function ARC4($text)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2189
    {
2190
        $len = mb_strlen($text, '8bit');
2191
        $a = 0;
2192
        $b = 0;
2193
        $c = $this->arc4;
2194
        $out = '';
2195
        for ($i = 0; $i < $len; $i++) {
2196
            $a = ($a + 1) % 256;
2197
            $t = $c[$a];
2198
            $b = ($b + ord($t)) % 256;
2199
            $c[$a] = $c[$b];
2200
            $c[$b] = $t;
2201
            $k = ord($c[(ord($c[$a]) + ord($c[$b])) % 256]);
2202
            $out .= chr(ord($text[$i]) ^ $k);
2203
        }
2204
2205
        return $out;
2206
    }
2207
2208
    /**
2209
     * functions which can be called to adjust or add to the document
2210
     */
2211
2212
    /**
2213
     * add a link in the document to an external URL
2214
     *
2215
     * @param $url
2216
     * @param $x0
2217
     * @param $y0
2218
     * @param $x1
2219
     * @param $y1
2220
     */
2221
    function addLink($url, $x0, $y0, $x1, $y1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2222
    {
2223
        $this->numObj++;
2224
        $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1));
2225
        $this->o_annotation($this->numObj, 'new', $info);
0 ignored issues
show
Documentation introduced by
$info is of type array<string,?,{"type":"...:\"?\",\"3\":\"?\"}>"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2226
    }
2227
2228
    /**
2229
     * add a link in the document to an internal destination (ie. within the document)
2230
     *
2231
     * @param $label
2232
     * @param $x0
2233
     * @param $y0
2234
     * @param $x1
2235
     * @param $y1
2236
     */
2237
    function addInternalLink($label, $x0, $y0, $x1, $y1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2238
    {
2239
        $this->numObj++;
2240
        $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1));
2241
        $this->o_annotation($this->numObj, 'new', $info);
0 ignored issues
show
Documentation introduced by
$info is of type array<string,?,{"type":"...:\"?\",\"3\":\"?\"}>"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2242
    }
2243
2244
    /**
2245
     * set the encryption of the document
2246
     * can be used to turn it on and/or set the passwords which it will have.
2247
     * also the functions that the user will have are set here, such as print, modify, add
2248
     *
2249
     * @param string $userPass
2250
     * @param string $ownerPass
2251
     * @param array $pc
2252
     */
2253
    function setEncryption($userPass = '', $ownerPass = '', $pc = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2254
    {
2255
        $p = bindec("11000000");
2256
2257
        $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32);
2258
2259
        foreach ($pc as $k => $v) {
2260
            if ($v && isset($options[$k])) {
2261
                $p += $options[$k];
2262
            } else {
2263
                if (isset($options[$v])) {
2264
                    $p += $options[$v];
2265
                }
2266
            }
2267
        }
2268
2269
        // implement encryption on the document
2270
        if ($this->arc4_objnum == 0) {
2271
            // then the block does not exist already, add it.
2272
            $this->numObj++;
2273
            if (mb_strlen($ownerPass) == 0) {
2274
                $ownerPass = $userPass;
2275
            }
2276
2277
            $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p));
0 ignored issues
show
Documentation introduced by
array('user' => $userPas... $ownerPass, 'p' => $p) is of type array<string,string|inte...,"p":"integer|double"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2278
        }
2279
    }
2280
2281
    /**
2282
     * should be used for internal checks, not implemented as yet
2283
     */
2284
    function checkAllHere()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2285
    {
2286
    }
2287
2288
    /**
2289
     * return the pdf stream as a string returned from the function
2290
     *
2291
     * @param bool $debug
2292
     * @return string
2293
     */
2294
    function output($debug = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2295
    {
2296
        if ($debug) {
2297
            // turn compression off
2298
            $this->options['compression'] = false;
2299
        }
2300
2301
        if ($this->javascript) {
2302
            $this->numObj++;
2303
2304
            $js_id = $this->numObj;
2305
            $this->o_embedjs($js_id, 'new');
2306
            $this->o_javascript(++$this->numObj, 'new', $this->javascript);
2307
2308
            $id = $this->catalogId;
2309
2310
            $this->o_catalog($id, 'javascript', $js_id);
2311
        }
2312
2313
        if ($this->fileIdentifier === '') {
2314
            $tmp = implode('',  $this->objects[$this->infoObject]['info']);
2315
            $this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand());
2316
        }
2317
2318
        if ($this->arc4_objnum) {
2319
            $this->o_encryption($this->arc4_objnum, 'keys');
2320
            $this->ARC4_init($this->encryptionKey);
2321
        }
2322
2323
        $this->checkAllHere();
2324
2325
        $xref = array();
2326
        $content = '%PDF-1.3';
2327
        $pos = mb_strlen($content, '8bit');
2328
2329
        foreach ($this->objects as $k => $v) {
2330
            $tmp = 'o_' . $v['t'];
2331
            $cont = $this->$tmp($k, 'out');
2332
            $content .= $cont;
2333
            $xref[] = $pos + 1; //+1 to account for \n at the start of each object
2334
            $pos += mb_strlen($cont, '8bit');
2335
        }
2336
2337
        $content .= "\nxref\n0 " . (count($xref) + 1) . "\n0000000000 65535 f \n";
2338
2339
        foreach ($xref as $p) {
2340
            $content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
2341
        }
2342
2343
        $content .= "trailer\n<<\n" .
2344
            '/Size ' . (count($xref) + 1) . "\n" .
2345
            '/Root 1 0 R' . "\n" .
2346
            '/Info ' . $this->infoObject . " 0 R\n"
2347
        ;
2348
2349
        // if encryption has been applied to this document then add the marker for this dictionary
2350
        if ($this->arc4_objnum > 0) {
2351
            $content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n";
2352
        }
2353
2354
        $content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n";
2355
2356
        // account for \n added at start of xref table
2357
        $pos++;
2358
2359
        $content .= ">>\nstartxref\n$pos\n%%EOF\n";
2360
2361
        return $content;
2362
    }
2363
2364
    /**
2365
     * initialize a new document
2366
     * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
2367
     * this function is called automatically by the constructor function
2368
     *
2369
     * @param array $pageSize
2370
     */
2371
    private function newDocument($pageSize = array(0, 0, 612, 792))
2372
    {
2373
        $this->numObj = 0;
2374
        $this->objects = array();
2375
2376
        $this->numObj++;
2377
        $this->o_catalog($this->numObj, 'new');
2378
2379
        $this->numObj++;
2380
        $this->o_outlines($this->numObj, 'new');
2381
2382
        $this->numObj++;
2383
        $this->o_pages($this->numObj, 'new');
2384
2385
        $this->o_pages($this->numObj, 'mediaBox', $pageSize);
0 ignored issues
show
Documentation introduced by
$pageSize is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2386
        $this->currentNode = 3;
2387
2388
        $this->numObj++;
2389
        $this->o_procset($this->numObj, 'new');
2390
2391
        $this->numObj++;
2392
        $this->o_info($this->numObj, 'new');
2393
2394
        $this->numObj++;
2395
        $this->o_page($this->numObj, 'new');
2396
2397
        // need to store the first page id as there is no way to get it to the user during
2398
        // startup
2399
        $this->firstPageId = $this->currentContents;
2400
    }
2401
2402
    /**
2403
     * open the font file and return a php structure containing it.
2404
     * first check if this one has been done before and saved in a form more suited to php
2405
     * note that if a php serialized version does not exist it will try and make one, but will
2406
     * require write access to the directory to do it... it is MUCH faster to have these serialized
2407
     * files.
2408
     *
2409
     * @param $font
2410
     */
2411
    private function openFont($font)
2412
    {
2413
        // assume that $font contains the path and file but not the extension
2414
        $name = basename($font);
2415
        $dir = dirname($font) . '/';
2416
2417
        $fontcache = $this->fontcache;
2418
        if ($fontcache == '') {
2419
            $fontcache = rtrim($dir, DIRECTORY_SEPARATOR."/\\");
2420
        }
2421
2422
        //$name       filename without folder and extension of font metrics
2423
        //$dir      folder of font metrics
2424
        //$fontcache  folder of runtime created php serialized version of font metrics.
2425
        //            If this is not given, the same folder as the font metrics will be used.
2426
        //            Storing and reusing serialized versions improves speed much
2427
2428
        $this->addMessage("openFont: $font - $name");
2429
2430
        if (!$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts)) {
2431
            $metrics_name = "$name.afm";
2432
        } else {
2433
            $metrics_name = "$name.ufm";
2434
        }
2435
2436
        $cache_name = "$metrics_name.php";
2437
        $this->addMessage("metrics: $metrics_name, cache: $cache_name");
2438
2439
        if (file_exists($fontcache . '/' . $cache_name)) {
2440
            $this->addMessage("openFont: php file exists $fontcache/$cache_name");
2441
            $this->fonts[$font] = require($fontcache . '/' . $cache_name);
2442
2443
            if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
2444
                // if the font file is old, then clear it out and prepare for re-creation
2445
                $this->addMessage('openFont: clear out, make way for new version.');
2446
                $this->fonts[$font] = null;
2447
                unset($this->fonts[$font]);
2448
            }
2449
        } else {
2450
            $old_cache_name = "php_$metrics_name";
2451
            if (file_exists($fontcache . '/' . $old_cache_name)) {
2452
                $this->addMessage(
2453
                    "openFont: php file doesn't exist $fontcache/$cache_name, creating it from the old format"
2454
                );
2455
                $old_cache = file_get_contents($fontcache . '/' . $old_cache_name);
2456
                file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . $old_cache . ';');
2457
2458
                $this->openFont($font);
2459
                return;
2460
            }
2461
        }
2462
2463
        if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
2464
            // then rebuild the php_<font>.afm file from the <font>.afm file
2465
            $this->addMessage("openFont: build php file from $dir$metrics_name");
2466
            $data = array();
2467
2468
            // 20 => 'space'
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2469
            $data['codeToName'] = array();
2470
2471
            // Since we're not going to enable Unicode for the core fonts we need to use a font-based
2472
            // setting for Unicode support rather than a global setting.
2473
            $data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
2474
2475
            $cidtogid = '';
2476
            if ($data['isUnicode']) {
2477
                $cidtogid = str_pad('', 256 * 256 * 2, "\x00");
2478
            }
2479
2480
            $file = file($dir . $metrics_name);
2481
2482
            foreach ($file as $rowA) {
2483
                $row = trim($rowA);
2484
                $pos = strpos($row, ' ');
2485
2486
                if ($pos) {
2487
                    // then there must be some keyword
2488
                    $key = substr($row, 0, $pos);
2489
                    switch ($key) {
2490
                        case 'FontName':
2491
                        case 'FullName':
2492
                        case 'FamilyName':
2493
                        case 'PostScriptName':
2494
                        case 'Weight':
2495
                        case 'ItalicAngle':
2496
                        case 'IsFixedPitch':
2497
                        case 'CharacterSet':
2498
                        case 'UnderlinePosition':
2499
                        case 'UnderlineThickness':
2500
                        case 'Version':
2501
                        case 'EncodingScheme':
2502
                        case 'CapHeight':
2503
                        case 'XHeight':
2504
                        case 'Ascender':
2505
                        case 'Descender':
2506
                        case 'StdHW':
2507
                        case 'StdVW':
2508
                        case 'StartCharMetrics':
2509
                        case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font.  Otherwise it's too big.
2510
                            $data[$key] = trim(substr($row, $pos));
2511
                            break;
2512
2513
                        case 'FontBBox':
2514
                            $data[$key] = explode(' ', trim(substr($row, $pos)));
2515
                            break;
2516
2517
                        //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
2518
                        case 'C': // Found in AFM files
2519
                            $bits = explode(';', trim($row));
2520
                            $dtmp = array();
2521
2522 View Code Duplication
                            foreach ($bits as $bit) {
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...
2523
                                $bits2 = explode(' ', trim($bit));
2524
                                if (mb_strlen($bits2[0], '8bit') == 0) {
2525
                                    continue;
2526
                                }
2527
2528
                                if (count($bits2) > 2) {
2529
                                    $dtmp[$bits2[0]] = array();
2530
                                    for ($i = 1; $i < count($bits2); $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...
2531
                                        $dtmp[$bits2[0]][] = $bits2[$i];
2532
                                    }
2533
                                } else {
2534
                                    if (count($bits2) == 2) {
2535
                                        $dtmp[$bits2[0]] = $bits2[1];
2536
                                    }
2537
                                }
2538
                            }
2539
2540
                            $c = (int)$dtmp['C'];
2541
                            $n = $dtmp['N'];
2542
                            $width = floatval($dtmp['WX']);
2543
2544
                            if ($c >= 0) {
2545
                                if ($c != hexdec($n)) {
2546
                                    $data['codeToName'][$c] = $n;
2547
                                }
2548
                                $data['C'][$c] = $width;
2549
                            } else {
2550
                                $data['C'][$n] = $width;
2551
                            }
2552
2553 View Code Duplication
                            if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
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...
2554
                                $data['MissingWidth'] = $width;
2555
                            }
2556
2557
                            break;
2558
2559
                        // U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
2560
                        case 'U': // Found in UFM files
2561
                            if (!$data['isUnicode']) {
2562
                                break;
2563
                            }
2564
2565
                            $bits = explode(';', trim($row));
2566
                            $dtmp = array();
2567
2568 View Code Duplication
                            foreach ($bits as $bit) {
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...
2569
                                $bits2 = explode(' ', trim($bit));
2570
                                if (mb_strlen($bits2[0], '8bit') === 0) {
2571
                                    continue;
2572
                                }
2573
2574
                                if (count($bits2) > 2) {
2575
                                    $dtmp[$bits2[0]] = array();
2576
                                    for ($i = 1; $i < count($bits2); $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...
2577
                                        $dtmp[$bits2[0]][] = $bits2[$i];
2578
                                    }
2579
                                } else {
2580
                                    if (count($bits2) == 2) {
2581
                                        $dtmp[$bits2[0]] = $bits2[1];
2582
                                    }
2583
                                }
2584
                            }
2585
2586
                            $c = (int)$dtmp['U'];
2587
                            $n = $dtmp['N'];
2588
                            $glyph = $dtmp['G'];
2589
                            $width = floatval($dtmp['WX']);
2590
2591
                            if ($c >= 0) {
2592
                                // Set values in CID to GID map
2593 View Code Duplication
                                if ($c >= 0 && $c < 0xFFFF && $glyph) {
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...
2594
                                    $cidtogid[$c * 2] = chr($glyph >> 8);
2595
                                    $cidtogid[$c * 2 + 1] = chr($glyph & 0xFF);
2596
                                }
2597
2598
                                if ($c != hexdec($n)) {
2599
                                    $data['codeToName'][$c] = $n;
2600
                                }
2601
                                $data['C'][$c] = $width;
2602
                            } else {
2603
                                $data['C'][$n] = $width;
2604
                            }
2605
2606 View Code Duplication
                            if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
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...
2607
                                $data['MissingWidth'] = $width;
2608
                            }
2609
2610
                            break;
2611
2612
                        case 'KPX':
2613
                            break; // don't include them as they are not used yet
2614
                            //KPX Adieresis yacute -40
2615
                            /*$bits = explode(' ', trim($row));
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2616
                            $data['KPX'][$bits[1]][$bits[2]] = $bits[3];
2617
                            break;*/
2618
                    }
2619
                }
2620
            }
2621
2622 View Code Duplication
            if ($this->compressionReady && $this->options['compression']) {
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...
2623
                // then implement ZLIB based compression on CIDtoGID string
2624
                $data['CIDtoGID_Compressed'] = true;
2625
                $cidtogid = gzcompress($cidtogid, 6);
2626
            }
2627
            $data['CIDtoGID'] = base64_encode($cidtogid);
2628
            $data['_version_'] = $this->fontcacheVersion;
2629
            $this->fonts[$font] = $data;
2630
2631
            //Because of potential trouble with php safe mode, expect that the folder already exists.
2632
            //If not existing, this will hit performance because of missing cached results.
2633
            if (is_dir($fontcache) && is_writable($fontcache)) {
2634
                file_put_contents($fontcache . '/' . $cache_name, '<?php return ' . var_export($data, true) . ';');
2635
            }
2636
            $data = null;
0 ignored issues
show
Unused Code introduced by
$data is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2637
        }
2638
2639
        if (!isset($this->fonts[$font])) {
2640
            $this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
2641
        }
2642
2643
        //pre_r($this->messages);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2644
    }
2645
2646
    /**
2647
     * if the font is not loaded then load it and make the required object
2648
     * else just make it the current font
2649
     * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
2650
     * note that encoding='none' will need to be used for symbolic fonts
2651
     * and 'differences' => an array of mappings between numbers 0->255 and character names.
2652
     *
2653
     * @param $fontName
2654
     * @param string $encoding
2655
     * @param bool $set
2656
     * @return int
2657
     */
2658
    function selectFont($fontName, $encoding = '', $set = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2659
    {
2660
        $ext = substr($fontName, -4);
2661
        if ($ext === '.afm' || $ext === '.ufm') {
2662
            $fontName = substr($fontName, 0, mb_strlen($fontName) - 4);
2663
        }
2664
2665
        if (!isset($this->fonts[$fontName])) {
2666
            $this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
2667
2668
            // load the file
2669
            $this->openFont($fontName);
2670
2671
            if (isset($this->fonts[$fontName])) {
2672
                $this->numObj++;
2673
                $this->numFonts++;
2674
2675
                $font = &$this->fonts[$fontName];
2676
2677
                $name = basename($fontName);
2678
                $dir = dirname($fontName) . '/';
0 ignored issues
show
Unused Code introduced by
$dir is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2679
                $options = array('name' => $name, 'fontFileName' => $fontName);
2680
2681
                if (is_array($encoding)) {
2682
                    // then encoding and differences might be set
2683
                    if (isset($encoding['encoding'])) {
2684
                        $options['encoding'] = $encoding['encoding'];
2685
                    }
2686
2687
                    if (isset($encoding['differences'])) {
2688
                        $options['differences'] = $encoding['differences'];
2689
                    }
2690
                } else {
2691
                    if (mb_strlen($encoding, '8bit')) {
2692
                        // then perhaps only the encoding has been set
2693
                        $options['encoding'] = $encoding;
2694
                    }
2695
                }
2696
2697
                $fontObj = $this->numObj;
2698
                $this->o_font($this->numObj, 'new', $options);
2699
                $font['fontNum'] = $this->numFonts;
2700
2701
                // if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
2702
                // should be for all non-basic fonts), then load it into an object and put the
2703
                // references into the font object
2704
                $basefile = $fontName;
2705
2706
                $fbtype = '';
2707
                if (file_exists("$basefile.ttf")) {
2708
                    $fbtype = 'ttf';
2709
                } elseif (file_exists("$basefile.TTF")) {
2710
                    $fbtype = 'TTF';
2711
                } elseif (file_exists("$basefile.pfb")) {
2712
                    $fbtype = 'pfb';
2713
                } elseif (file_exists("$basefile.PFB")) {
2714
                    $fbtype = 'PFB';
2715
                }
2716
2717
                $fbfile = "$basefile.$fbtype";
2718
2719
                //      $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2720
                //      $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2721
                $this->addMessage('selectFont: checking for - ' . $fbfile);
2722
2723
                // OAR - I don't understand this old check
2724
                // if (substr($fontName, -4) ===  '.afm' &&  strlen($fbtype)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2725
                if ($fbtype) {
2726
                    $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
2727
                    //        $fontObj = $this->numObj;
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2728
                    $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
2729
2730
                    // find the array of font widths, and put that into an object.
2731
                    $firstChar = -1;
2732
                    $lastChar = 0;
2733
                    $widths = array();
2734
                    $cid_widths = array();
2735
2736
                    foreach ($font['C'] as $num => $d) {
2737
                        if (intval($num) > 0 || $num == '0') {
2738
                            if (!$font['isUnicode']) {
2739
                                // With Unicode, widths array isn't used
2740
                                if ($lastChar > 0 && $num > $lastChar + 1) {
2741
                                    for ($i = $lastChar + 1; $i < $num; $i++) {
2742
                                        $widths[] = 0;
2743
                                    }
2744
                                }
2745
                            }
2746
2747
                            $widths[] = $d;
2748
2749
                            if ($font['isUnicode']) {
2750
                                $cid_widths[$num] = $d;
2751
                            }
2752
2753
                            if ($firstChar == -1) {
2754
                                $firstChar = $num;
2755
                            }
2756
2757
                            $lastChar = $num;
2758
                        }
2759
                    }
2760
2761
                    // also need to adjust the widths for the differences array
2762
                    if (isset($options['differences'])) {
2763
                        foreach ($options['differences'] as $charNum => $charName) {
2764
                            if ($charNum > $lastChar) {
2765
                                if (!$font['isUnicode']) {
2766
                                    // With Unicode, widths array isn't used
2767
                                    for ($i = $lastChar + 1; $i <= $charNum; $i++) {
2768
                                        $widths[] = 0;
2769
                                    }
2770
                                }
2771
2772
                                $lastChar = $charNum;
2773
                            }
2774
2775
                            if (isset($font['C'][$charName])) {
2776
                                $widths[$charNum - $firstChar] = $font['C'][$charName];
2777
                                if ($font['isUnicode']) {
2778
                                    $cid_widths[$charName] = $font['C'][$charName];
2779
                                }
2780
                            }
2781
                        }
2782
                    }
2783
2784
                    if ($font['isUnicode']) {
2785
                        $font['CIDWidths'] = $cid_widths;
2786
                    }
2787
2788
                    $this->addMessage('selectFont: FirstChar = ' . $firstChar);
2789
                    $this->addMessage('selectFont: LastChar = ' . $lastChar);
2790
2791
                    $widthid = -1;
2792
2793
                    if (!$font['isUnicode']) {
2794
                        // With Unicode, widths array isn't used
2795
2796
                        $this->numObj++;
2797
                        $this->o_contents($this->numObj, 'new', 'raw');
2798
                        $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']';
2799
                        $widthid = $this->numObj;
2800
                    }
2801
2802
                    $missing_width = 500;
2803
                    $stemV = 70;
2804
2805
                    if (isset($font['MissingWidth'])) {
2806
                        $missing_width = $font['MissingWidth'];
2807
                    }
2808
                    if (isset($font['StdVW'])) {
2809
                        $stemV = $font['StdVW'];
2810
                    } else {
2811
                        if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
2812
                            $stemV = 120;
2813
                        }
2814
                    }
2815
2816
                    // load the pfb file, and put that into an object too.
2817
                    // note that pdf supports only binary format type 1 font files, though there is a
2818
                    // simple utility to convert them from pfa to pfb.
2819
                    // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750.
2820
                    if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) {
2821
                        $data = file_get_contents($fbfile);
2822
                    } else {
2823
                        $this->stringSubsets[$fontName][] = 32; // Force space if not in yet
2824
2825
                        $subset = $this->stringSubsets[$fontName];
2826
                        sort($subset);
2827
2828
                        // Load font
2829
                        $font_obj = Font::load($fbfile);
2830
                        $font_obj->parse();
2831
2832
                        // Define subset
2833
                        $font_obj->setSubset($subset);
2834
                        $font_obj->reduce();
2835
2836
                        // Write new font
2837
                        $tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid();
2838
                        $font_obj->open($tmp_name, BinaryStream::modeWrite);
2839
                        $font_obj->encode(array("OS/2"));
2840
                        $font_obj->close();
2841
2842
                        // Parse the new font to get cid2gid and widths
2843
                        $font_obj = Font::load($tmp_name);
2844
2845
                        // Find Unicode char map table
2846
                        $subtable = null;
2847
                        foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
2848
                            if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
2849
                                $subtable = $_subtable;
2850
                                break;
2851
                            }
2852
                        }
2853
2854
                        if ($subtable) {
2855
                            $glyphIndexArray = $subtable["glyphIndexArray"];
2856
                            $hmtx = $font_obj->getData("hmtx");
2857
2858
                            unset($glyphIndexArray[0xFFFF]);
2859
2860
                            $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00");
2861
                            $font['CIDWidths'] = array();
2862
                            foreach ($glyphIndexArray as $cid => $gid) {
2863 View Code Duplication
                                if ($cid >= 0 && $cid < 0xFFFF && $gid) {
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...
2864
                                    $cidtogid[$cid * 2] = chr($gid >> 8);
2865
                                    $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF);
2866
                                }
2867
2868
                                $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
2869
                                $font['CIDWidths'][$cid] = $width;
2870
                            }
2871
2872
                            $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
2873
                            $font['CIDtoGID_Compressed'] = true;
2874
2875
                            $data = file_get_contents($tmp_name);
2876
                        } else {
2877
                            $data = file_get_contents($fbfile);
2878
                        }
2879
2880
                        $font_obj->close();
2881
                        unlink($tmp_name);
2882
                    }
2883
2884
                    // create the font descriptor
2885
                    $this->numObj++;
2886
                    $fontDescriptorId = $this->numObj;
2887
2888
                    $this->numObj++;
2889
                    $pfbid = $this->numObj;
2890
2891
                    // determine flags (more than a little flakey, hopefully will not matter much)
2892
                    $flags = 0;
2893
2894
                    if ($font['ItalicAngle'] != 0) {
2895
                        $flags += pow(2, 6);
2896
                    }
2897
2898
                    if ($font['IsFixedPitch'] === 'true') {
2899
                        $flags += 1;
2900
                    }
2901
2902
                    $flags += pow(2, 5); // assume non-sybolic
2903
                    $list = array(
2904
                        'Ascent'       => 'Ascender',
2905
                        'CapHeight'    => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight'
2906
                        'MissingWidth' => 'MissingWidth',
2907
                        'Descent'      => 'Descender',
2908
                        'FontBBox'     => 'FontBBox',
2909
                        'ItalicAngle'  => 'ItalicAngle'
2910
                    );
2911
                    $fdopt = array(
2912
                        'Flags'    => $flags,
2913
                        'FontName' => $adobeFontName,
2914
                        'StemV'    => $stemV
2915
                    );
2916
2917
                    foreach ($list as $k => $v) {
2918
                        if (isset($font[$v])) {
2919
                            $fdopt[$k] = $font[$v];
2920
                        }
2921
                    }
2922
2923
                    if (strtolower($fbtype) === 'pfb') {
2924
                        $fdopt['FontFile'] = $pfbid;
2925
                    } elseif (strtolower($fbtype) === 'ttf') {
2926
                        $fdopt['FontFile2'] = $pfbid;
2927
                    }
2928
2929
                    $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
0 ignored issues
show
Documentation introduced by
$fdopt is of type array<string,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2930
2931
                    // embed the font program
2932
                    $this->o_contents($this->numObj, 'new');
2933
                    $this->objects[$pfbid]['c'] .= $data;
2934
2935
                    // determine the cruicial lengths within this file
2936
                    if (strtolower($fbtype) === 'pfb') {
2937
                        $l1 = strpos($data, 'eexec') + 6;
2938
                        $l2 = strpos($data, '00000000') - $l1;
2939
                        $l3 = mb_strlen($data, '8bit') - $l2 - $l1;
2940
                        $this->o_contents(
2941
                            $this->numObj,
2942
                            'add',
2943
                            array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)
2944
                        );
2945
                    } elseif (strtolower($fbtype) == 'ttf') {
2946
                        $l1 = mb_strlen($data, '8bit');
2947
                        $this->o_contents($this->numObj, 'add', array('Length1' => $l1));
2948
                    }
2949
2950
                    // tell the font object about all this new stuff
2951
                    $tmp = array(
2952
                        'BaseFont'       => $adobeFontName,
2953
                        'MissingWidth'   => $missing_width,
2954
                        'Widths'         => $widthid,
2955
                        'FirstChar'      => $firstChar,
2956
                        'LastChar'       => $lastChar,
2957
                        'FontDescriptor' => $fontDescriptorId
2958
                    );
2959
2960
                    if (strtolower($fbtype) === 'ttf') {
2961
                        $tmp['SubType'] = 'TrueType';
2962
                    }
2963
2964
                    $this->addMessage("adding extra info to font.($fontObj)");
2965
2966
                    foreach ($tmp as $fk => $fv) {
2967
                        $this->addMessage("$fk : $fv");
2968
                    }
2969
2970
                    $this->o_font($fontObj, 'add', $tmp);
2971
                } else {
2972
                    $this->addMessage(
2973
                        'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'
2974
                    );
2975
                }
2976
2977
                // also set the differences here, note that this means that these will take effect only the
2978
                //first time that a font is selected, else they are ignored
2979
                if (isset($options['differences'])) {
2980
                    $font['differences'] = $options['differences'];
2981
                }
2982
            }
2983
        }
2984
2985
        if ($set && isset($this->fonts[$fontName])) {
2986
            // so if for some reason the font was not set in the last one then it will not be selected
2987
            $this->currentBaseFont = $fontName;
2988
2989
            // the next lines mean that if a new font is selected, then the current text state will be
2990
            // applied to it as well.
2991
            $this->currentFont = $this->currentBaseFont;
2992
            $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2993
2994
            //$this->setCurrentFont();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2995
        }
2996
2997
        return $this->currentFontNum;
2998
        //return $this->numObj;
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2999
    }
3000
3001
    /**
3002
     * sets up the current font, based on the font families, and the current text state
3003
     * note that this system is quite flexible, a bold-italic font can be completely different to a
3004
     * italic-bold font, and even bold-bold will have to be defined within the family to have meaning
3005
     * This function is to be called whenever the currentTextState is changed, it will update
3006
     * the currentFont setting to whatever the appropriate family one is.
3007
     * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
3008
     * This function will change the currentFont to whatever it should be, but will not change the
3009
     * currentBaseFont.
3010
     */
3011
    private function setCurrentFont()
3012
    {
3013
        //   if (strlen($this->currentBaseFont) == 0){
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3014
        //     // then assume an initial font
3015
        //     $this->selectFont($this->defaultFont);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3016
        //   }
3017
        //   $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3018
        //   if (strlen($this->currentTextState)
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3019
        //     && isset($this->fontFamilies[$cf])
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3020
        //       && isset($this->fontFamilies[$cf][$this->currentTextState])){
0 ignored issues
show
Unused Code Comprehensibility introduced by
79% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3021
        //     // then we are in some state or another
3022
        //     // and this font has a family, and the current setting exists within it
3023
        //     // select the font, then return it
3024
        //     $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
0 ignored issues
show
Unused Code Comprehensibility introduced by
68% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3025
        //     $this->selectFont($nf,'',0);
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3026
        //     $this->currentFont = $nf;
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3027
        //     $this->currentFontNum = $this->fonts[$nf]['fontNum'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3028
        //   } else {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3029
        //     // the this font must not have the right family member for the current state
3030
        //     // simply assume the base font
3031
        $this->currentFont = $this->currentBaseFont;
3032
        $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
3033
        //  }
3034
    }
3035
3036
    /**
3037
     * function for the user to find out what the ID is of the first page that was created during
3038
     * startup - useful if they wish to add something to it later.
3039
     *
3040
     * @return int
3041
     */
3042
    function getFirstPageId()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3043
    {
3044
        return $this->firstPageId;
3045
    }
3046
3047
    /**
3048
     * add content to the currently active object
3049
     *
3050
     * @param $content
3051
     */
3052
    private function addContent($content)
3053
    {
3054
        $this->objects[$this->currentContents]['c'] .= $content;
3055
    }
3056
3057
    /**
3058
     * sets the color for fill operations
3059
     *
3060
     * @param $color
3061
     * @param bool $force
3062
     */
3063 View Code Duplication
    function setColor($color, $force = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
3064
    {
3065
        $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
3066
3067
        if (!$force && $this->currentColor == $new_color) {
3068
            return;
3069
        }
3070
3071
        if (isset($new_color[3])) {
3072
            $this->currentColor = $new_color;
3073
            $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor));
3074
        } else {
3075
            if (isset($new_color[2])) {
3076
                $this->currentColor = $new_color;
3077
                $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor));
3078
            }
3079
        }
3080
    }
3081
3082
    /**
3083
     * sets the color for fill operations
3084
     *
3085
     * @param $fillRule
3086
     */
3087
    function setFillRule($fillRule)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3088
    {
3089
        if (!in_array($fillRule, array("nonzero", "evenodd"))) {
3090
            return;
3091
        }
3092
3093
        $this->fillRule = $fillRule;
3094
    }
3095
3096
    /**
3097
     * sets the color for stroke operations
3098
     *
3099
     * @param $color
3100
     * @param bool $force
3101
     */
3102 View Code Duplication
    function setStrokeColor($color, $force = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
3103
    {
3104
        $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
3105
3106
        if (!$force && $this->currentStrokeColor == $new_color) {
3107
            return;
3108
        }
3109
3110
        if (isset($new_color[3])) {
3111
            $this->currentStrokeColor = $new_color;
3112
            $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor));
3113
        } else {
3114
            if (isset($new_color[2])) {
3115
                $this->currentStrokeColor = $new_color;
3116
                $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor));
3117
            }
3118
        }
3119
    }
3120
3121
    /**
3122
     * Set the graphics state for compositions
3123
     *
3124
     * @param $parameters
3125
     */
3126
    function setGraphicsState($parameters)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3127
    {
3128
        // Create a new graphics state object if necessary
3129
        if (($gstate = array_search($parameters, $this->gstates)) === false) {
3130
            $this->numObj++;
3131
            $this->o_extGState($this->numObj, 'new', $parameters);
3132
            $gstate = $this->numStates;
3133
            $this->gstates[$gstate] = $parameters;
3134
        }
3135
        $this->addContent("\n/GS$gstate gs");
3136
    }
3137
3138
    /**
3139
     * Set current blend mode & opacity for lines.
3140
     *
3141
     * Valid blend modes are:
3142
     *
3143
     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
3144
     * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
3145
     * Exclusion
3146
     *
3147
     * @param string $mode    the blend mode to use
3148
     * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
3149
     */
3150 View Code Duplication
    function setLineTransparency($mode, $opacity)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
3151
    {
3152
        static $blend_modes = array(
3153
            "Normal",
3154
            "Multiply",
3155
            "Screen",
3156
            "Overlay",
3157
            "Darken",
3158
            "Lighten",
3159
            "ColorDogde",
3160
            "ColorBurn",
3161
            "HardLight",
3162
            "SoftLight",
3163
            "Difference",
3164
            "Exclusion"
3165
        );
3166
3167
        if (!in_array($mode, $blend_modes)) {
3168
            $mode = "Normal";
3169
        }
3170
3171
        // Only create a new graphics state if required
3172
        if ($mode === $this->currentLineTransparency["mode"] &&
3173
            $opacity == $this->currentLineTransparency["opacity"]
3174
        ) {
3175
            return;
3176
        }
3177
3178
        $this->currentLineTransparency["mode"] = $mode;
3179
        $this->currentLineTransparency["opacity"] = $opacity;
3180
3181
        $options = array(
3182
            "BM" => "/$mode",
3183
            "CA" => (float)$opacity
3184
        );
3185
3186
        $this->setGraphicsState($options);
3187
    }
3188
3189
    /**
3190
     * Set current blend mode & opacity for filled objects.
3191
     *
3192
     * Valid blend modes are:
3193
     *
3194
     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
3195
     * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
3196
     * Exclusion
3197
     *
3198
     * @param string $mode    the blend mode to use
3199
     * @param float  $opacity 0.0 fully transparent, 1.0 fully opaque
3200
     */
3201 View Code Duplication
    function setFillTransparency($mode, $opacity)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
3202
    {
3203
        static $blend_modes = array(
3204
            "Normal",
3205
            "Multiply",
3206
            "Screen",
3207
            "Overlay",
3208
            "Darken",
3209
            "Lighten",
3210
            "ColorDogde",
3211
            "ColorBurn",
3212
            "HardLight",
3213
            "SoftLight",
3214
            "Difference",
3215
            "Exclusion"
3216
        );
3217
3218
        if (!in_array($mode, $blend_modes)) {
3219
            $mode = "Normal";
3220
        }
3221
3222
        if ($mode === $this->currentFillTransparency["mode"] &&
3223
            $opacity == $this->currentFillTransparency["opacity"]
3224
        ) {
3225
            return;
3226
        }
3227
3228
        $this->currentFillTransparency["mode"] = $mode;
3229
        $this->currentFillTransparency["opacity"] = $opacity;
3230
3231
        $options = array(
3232
            "BM" => "/$mode",
3233
            "ca" => (float)$opacity,
3234
        );
3235
3236
        $this->setGraphicsState($options);
3237
    }
3238
3239
    /**
3240
     * draw a line from one set of coordinates to another
3241
     *
3242
     * @param $x1
3243
     * @param $y1
3244
     * @param $x2
3245
     * @param $y2
3246
     * @param bool $stroke
3247
     */
3248
    function line($x1, $y1, $x2, $y2, $stroke = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3249
    {
3250
        $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
3251
3252
        if ($stroke) {
3253
            $this->addContent(' S');
3254
        }
3255
    }
3256
3257
    /**
3258
     * draw a bezier curve based on 4 control points
3259
     *
3260
     * @param $x0
3261
     * @param $y0
3262
     * @param $x1
3263
     * @param $y1
3264
     * @param $x2
3265
     * @param $y2
3266
     * @param $x3
3267
     * @param $y3
3268
     */
3269
    function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3270
    {
3271
        // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
3272
        // as the control points for the curve.
3273
        $this->addContent(
3274
            sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
3275
        );
3276
    }
3277
3278
    /**
3279
     * draw a part of an ellipse
3280
     *
3281
     * @param $x0
3282
     * @param $y0
3283
     * @param $astart
3284
     * @param $afinish
3285
     * @param $r1
3286
     * @param int $r2
3287
     * @param int $angle
3288
     * @param int $nSeg
3289
     */
3290
    function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3291
    {
3292
        $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
3293
    }
3294
3295
    /**
3296
     * draw a filled ellipse
3297
     *
3298
     * @param $x0
3299
     * @param $y0
3300
     * @param $r1
3301
     * @param int $r2
3302
     * @param int $angle
3303
     * @param int $nSeg
3304
     * @param int $astart
3305
     * @param int $afinish
3306
     */
3307
    function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3308
    {
3309
        $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
3310
    }
3311
3312
    /**
3313
     * @param $x
3314
     * @param $y
3315
     */
3316
    function lineTo($x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3317
    {
3318
        $this->addContent(sprintf("\n%.3F %.3F l", $x, $y));
3319
    }
3320
3321
    /**
3322
     * @param $x
3323
     * @param $y
3324
     */
3325
    function moveTo($x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3326
    {
3327
        $this->addContent(sprintf("\n%.3F %.3F m", $x, $y));
3328
    }
3329
3330
    /**
3331
     * draw a bezier curve based on 4 control points
3332
     *
3333
     * @param $x1
3334
     * @param $y1
3335
     * @param $x2
3336
     * @param $y2
3337
     * @param $x3
3338
     * @param $y3
3339
     */
3340
    function curveTo($x1, $y1, $x2, $y2, $x3, $y3)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3341
    {
3342
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", $x1, $y1, $x2, $y2, $x3, $y3));
3343
    }
3344
 
3345
    /**
3346
     * draw a bezier curve based on 4 control points
3347
     */    function quadTo($cpx, $cpy, $x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3348
    {
3349
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y));
3350
    }
3351
    
3352
    function closePath()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3353
    {
3354
        $this->addContent(' h');
3355
    }
3356
3357
    function endPath()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3358
    {
3359
        $this->addContent(' n');
3360
    }
3361
3362
    /**
3363
     * draw an ellipse
3364
     * note that the part and filled ellipse are just special cases of this function
3365
     *
3366
     * draws an ellipse in the current line style
3367
     * centered at $x0,$y0, radii $r1,$r2
3368
     * if $r2 is not set, then a circle is drawn
3369
     * from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
3370
     * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
3371
     * pretty crappy shape at 2, as we are approximating with bezier curves.
3372
     *
3373
     * @param $x0
3374
     * @param $y0
3375
     * @param $r1
3376
     * @param int $r2
3377
     * @param int $angle
3378
     * @param int $nSeg
3379
     * @param int $astart
3380
     * @param int $afinish
3381
     * @param bool $close
3382
     * @param bool $fill
3383
     * @param bool $stroke
3384
     * @param bool $incomplete
3385
     */
3386
    function ellipse(
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3387
        $x0,
3388
        $y0,
3389
        $r1,
3390
        $r2 = 0,
3391
        $angle = 0,
3392
        $nSeg = 8,
3393
        $astart = 0,
3394
        $afinish = 360,
3395
        $close = true,
3396
        $fill = false,
3397
        $stroke = true,
3398
        $incomplete = false
3399
    ) {
3400
        if ($r1 == 0) {
3401
            return;
3402
        }
3403
3404
        if ($r2 == 0) {
3405
            $r2 = $r1;
3406
        }
3407
3408
        if ($nSeg < 2) {
3409
            $nSeg = 2;
3410
        }
3411
3412
        $astart = deg2rad((float)$astart);
3413
        $afinish = deg2rad((float)$afinish);
3414
        $totalAngle = $afinish - $astart;
3415
3416
        $dt = $totalAngle / $nSeg;
3417
        $dtm = $dt / 3;
3418
3419
        if ($angle != 0) {
3420
            $a = -1 * deg2rad((float)$angle);
3421
3422
            $this->addContent(
3423
                sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0)
3424
            );
3425
3426
            $x0 = 0;
3427
            $y0 = 0;
3428
        }
3429
3430
        $t1 = $astart;
3431
        $a0 = $x0 + $r1 * cos($t1);
3432
        $b0 = $y0 + $r2 * sin($t1);
3433
        $c0 = -$r1 * sin($t1);
3434
        $d0 = $r2 * cos($t1);
3435
3436
        if (!$incomplete) {
3437
            $this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
3438
        }
3439
3440
        for ($i = 1; $i <= $nSeg; $i++) {
3441
            // draw this bit of the total curve
3442
            $t1 = $i * $dt + $astart;
3443
            $a1 = $x0 + $r1 * cos($t1);
3444
            $b1 = $y0 + $r2 * sin($t1);
3445
            $c1 = -$r1 * sin($t1);
3446
            $d1 = $r2 * cos($t1);
3447
3448
            $this->addContent(
3449
                sprintf(
3450
                    "\n%.3F %.3F %.3F %.3F %.3F %.3F c",
3451
                    ($a0 + $c0 * $dtm),
3452
                    ($b0 + $d0 * $dtm),
3453
                    ($a1 - $c1 * $dtm),
3454
                    ($b1 - $d1 * $dtm),
3455
                    $a1,
3456
                    $b1
3457
                )
3458
            );
3459
3460
            $a0 = $a1;
3461
            $b0 = $b1;
3462
            $c0 = $c1;
3463
            $d0 = $d1;
3464
        }
3465
3466
        if (!$incomplete) {
3467
            if ($fill) {
3468
                $this->addContent(' f');
3469
            }
3470
3471
            if ($stroke) {
3472
                if ($close) {
3473
                    $this->addContent(' s'); // small 's' signifies closing the path as well
3474
                } else {
3475
                    $this->addContent(' S');
3476
                }
3477
            }
3478
        }
3479
3480
        if ($angle != 0) {
3481
            $this->addContent(' Q');
3482
        }
3483
    }
3484
3485
    /**
3486
     * this sets the line drawing style.
3487
     * width, is the thickness of the line in user units
3488
     * cap is the type of cap to put on the line, values can be 'butt','round','square'
3489
     *    where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
3490
     *    end of the line.
3491
     * join can be 'miter', 'round', 'bevel'
3492
     * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
3493
     *   on and off dashes.
3494
     *   (2) represents 2 on, 2 off, 2 on , 2 off ...
3495
     *   (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
3496
     * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
3497
     *
3498
     * @param int $width
3499
     * @param string $cap
3500
     * @param string $join
3501
     * @param string $dash
3502
     * @param int $phase
3503
     */
3504
    function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3505
    {
3506
        // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
3507
        $string = '';
3508
3509
        if ($width > 0) {
3510
            $string .= "$width w";
3511
        }
3512
3513
        $ca = array('butt' => 0, 'round' => 1, 'square' => 2);
3514
3515
        if (isset($ca[$cap])) {
3516
            $string .= " $ca[$cap] J";
3517
        }
3518
3519
        $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
3520
3521
        if (isset($ja[$join])) {
3522
            $string .= " $ja[$join] j";
3523
        }
3524
3525
        if (is_array($dash)) {
3526
            $string .= ' [ ' . implode(' ', $dash) . " ] $phase d";
3527
        }
3528
3529
        $this->currentLineStyle = $string;
3530
        $this->addContent("\n$string");
3531
    }
3532
3533
    /**
3534
     * draw a polygon, the syntax for this is similar to the GD polygon command
3535
     *
3536
     * @param $p
3537
     * @param $np
3538
     * @param bool $f
3539
     */
3540
    function polygon($p, $np, $f = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3541
    {
3542
        $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
3543
3544
        for ($i = 2; $i < $np * 2; $i = $i + 2) {
3545
            $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1]));
3546
        }
3547
3548
        if ($f) {
3549
            $this->addContent(' f');
3550
        } else {
3551
            $this->addContent(' S');
3552
        }
3553
    }
3554
3555
    /**
3556
     * a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
3557
     * the coordinates of the upper-right corner
3558
     *
3559
     * @param $x1
3560
     * @param $y1
3561
     * @param $width
3562
     * @param $height
3563
     */
3564
    function filledRectangle($x1, $y1, $width, $height)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3565
    {
3566
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
3567
    }
3568
3569
    /**
3570
     * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
3571
     * the coordinates of the upper-right corner
3572
     *
3573
     * @param $x1
3574
     * @param $y1
3575
     * @param $width
3576
     * @param $height
3577
     */
3578
    function rectangle($x1, $y1, $width, $height)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3579
    {
3580
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
3581
    }
3582
3583
    /**
3584
     * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not
3585
     * the coordinates of the upper-right corner
3586
     *
3587
     * @param $x1
3588
     * @param $y1
3589
     * @param $width
3590
     * @param $height
3591
     */
3592
    function rect($x1, $y1, $width, $height)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3593
    {
3594
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height));
3595
    }
3596
3597
    function stroke()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3598
    {
3599
        $this->addContent("\nS");
3600
    }
3601
3602
    function fill()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3603
    {
3604
        $this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : ""));
3605
    }
3606
3607
    function fillStroke()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3608
    {
3609
        $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : ""));
3610
    }
3611
3612
    /**
3613
     * save the current graphic state
3614
     */
3615
    function save()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3616
    {
3617
        // we must reset the color cache or it will keep bad colors after clipping
3618
        $this->currentColor = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $currentColor.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3619
        $this->currentStrokeColor = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $currentStrokeColor.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3620
        $this->addContent("\nq");
3621
    }
3622
3623
    /**
3624
     * restore the last graphic state
3625
     */
3626
    function restore()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3627
    {
3628
        // we must reset the color cache or it will keep bad colors after clipping
3629
        $this->currentColor = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $currentColor.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3630
        $this->currentStrokeColor = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $currentStrokeColor.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3631
        $this->addContent("\nQ");
3632
    }
3633
3634
    /**
3635
     * draw a clipping rectangle, all the elements added after this will be clipped
3636
     *
3637
     * @param $x1
3638
     * @param $y1
3639
     * @param $width
3640
     * @param $height
3641
     */
3642
    function clippingRectangle($x1, $y1, $width, $height)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3643
    {
3644
        $this->save();
3645
        $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
3646
    }
3647
3648
    /**
3649
     * draw a clipping rounded rectangle, all the elements added after this will be clipped
3650
     *
3651
     * @param $x1
3652
     * @param $y1
3653
     * @param $w
3654
     * @param $h
3655
     * @param $rTL
3656
     * @param $rTR
3657
     * @param $rBR
3658
     * @param $rBL
3659
     */
3660
    function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3661
    {
3662
        $this->save();
3663
3664
        // start: top edge, left end
3665
        $this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
3666
3667
        // line: bottom edge, left end
3668
        $this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL));
3669
3670
        // curve: bottom-left corner
3671
        $this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
3672
3673
        // line: right edge, bottom end
3674
        $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
3675
3676
        // curve: bottom-right corner
3677
        $this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
3678
3679
        // line: right edge, top end
3680
        $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
3681
3682
        // curve: bottom-right corner
3683
        $this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
3684
3685
        // line: bottom edge, right end
3686
        $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
3687
3688
        // curve: top-right corner
3689
        $this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
3690
3691
        // line: top edge, left end
3692
        $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
3693
3694
        // Close & clip
3695
        $this->addContent(" W n");
3696
    }
3697
3698
    /**
3699
     * ends the last clipping shape
3700
     */
3701
    function clippingEnd()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3702
    {
3703
        $this->restore();
3704
    }
3705
3706
    /**
3707
     * scale
3708
     *
3709
     * @param float $s_x scaling factor for width as percent
3710
     * @param float $s_y scaling factor for height as percent
3711
     * @param float $x   Origin abscissa
3712
     * @param float $y   Origin ordinate
3713
     */
3714
    function scale($s_x, $s_y, $x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3715
    {
3716
        $y = $this->currentPageSize["height"] - $y;
3717
3718
        $tm = array(
3719
            $s_x,
3720
            0,
3721
            0,
3722
            $s_y,
3723
            $x * (1 - $s_x),
3724
            $y * (1 - $s_y)
3725
        );
3726
3727
        $this->transform($tm);
3728
    }
3729
3730
    /**
3731
     * translate
3732
     *
3733
     * @param float $t_x movement to the right
3734
     * @param float $t_y movement to the bottom
3735
     */
3736
    function translate($t_x, $t_y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3737
    {
3738
        $tm = array(
3739
            1,
3740
            0,
3741
            0,
3742
            1,
3743
            $t_x,
3744
            -$t_y
3745
        );
3746
3747
        $this->transform($tm);
3748
    }
3749
3750
    /**
3751
     * rotate
3752
     *
3753
     * @param float $angle angle in degrees for counter-clockwise rotation
3754
     * @param float $x     Origin abscissa
3755
     * @param float $y     Origin ordinate
3756
     */
3757
    function rotate($angle, $x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3758
    {
3759
        $y = $this->currentPageSize["height"] - $y;
3760
3761
        $a = deg2rad($angle);
3762
        $cos_a = cos($a);
3763
        $sin_a = sin($a);
3764
3765
        $tm = array(
3766
            $cos_a,
3767
            -$sin_a,
3768
            $sin_a,
3769
            $cos_a,
3770
            $x - $sin_a * $y - $cos_a * $x,
3771
            $y - $cos_a * $y + $sin_a * $x,
3772
        );
3773
3774
        $this->transform($tm);
3775
    }
3776
3777
    /**
3778
     * skew
3779
     *
3780
     * @param float $angle_x
3781
     * @param float $angle_y
3782
     * @param float $x Origin abscissa
3783
     * @param float $y Origin ordinate
3784
     */
3785
    function skew($angle_x, $angle_y, $x, $y)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3786
    {
3787
        $y = $this->currentPageSize["height"] - $y;
3788
3789
        $tan_x = tan(deg2rad($angle_x));
3790
        $tan_y = tan(deg2rad($angle_y));
3791
3792
        $tm = array(
3793
            1,
3794
            -$tan_y,
3795
            -$tan_x,
3796
            1,
3797
            $tan_x * $y,
3798
            $tan_y * $x,
3799
        );
3800
3801
        $this->transform($tm);
3802
    }
3803
3804
    /**
3805
     * apply graphic transformations
3806
     *
3807
     * @param array $tm transformation matrix
3808
     */
3809
    function transform($tm)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3810
    {
3811
        $this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
3812
    }
3813
3814
    /**
3815
     * add a new page to the document
3816
     * this also makes the new page the current active object
3817
     *
3818
     * @param int $insert
3819
     * @param int $id
3820
     * @param string $pos
3821
     * @return int
3822
     */
3823
    function newPage($insert = 0, $id = 0, $pos = 'after')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3824
    {
3825
        // if there is a state saved, then go up the stack closing them
3826
        // then on the new page, re-open them with the right setings
3827
3828
        if ($this->nStateStack) {
3829
            for ($i = $this->nStateStack; $i >= 1; $i--) {
3830
                $this->restoreState($i);
3831
            }
3832
        }
3833
3834
        $this->numObj++;
3835
3836
        if ($insert) {
3837
            // the id from the ezPdf class is the id of the contents of the page, not the page object itself
3838
            // query that object to find the parent
3839
            $rid = $this->objects[$id]['onPage'];
3840
            $opt = array('rid' => $rid, 'pos' => $pos);
3841
            $this->o_page($this->numObj, 'new', $opt);
0 ignored issues
show
Documentation introduced by
$opt is of type array<string,?,{"rid":"?","pos":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3842
        } else {
3843
            $this->o_page($this->numObj, 'new');
3844
        }
3845
3846
        // if there is a stack saved, then put that onto the page
3847
        if ($this->nStateStack) {
3848
            for ($i = 1; $i <= $this->nStateStack; $i++) {
3849
                $this->saveState($i);
3850
            }
3851
        }
3852
3853
        // and if there has been a stroke or fill color set, then transfer them
3854
        if (isset($this->currentColor)) {
3855
            $this->setColor($this->currentColor, true);
3856
        }
3857
3858
        if (isset($this->currentStrokeColor)) {
3859
            $this->setStrokeColor($this->currentStrokeColor, true);
3860
        }
3861
3862
        // if there is a line style set, then put this in too
3863
        if (mb_strlen($this->currentLineStyle, '8bit')) {
3864
            $this->addContent("\n$this->currentLineStyle");
3865
        }
3866
3867
        // the call to the o_page object set currentContents to the present page, so this can be returned as the page id
3868
        return $this->currentContents;
3869
    }
3870
3871
    /**
3872
     * Streams the PDF to the client.
3873
     *
3874
     * @param string $filename The filename to present to the client.
3875
     * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
3876
     */
3877
    function stream($filename = "document.pdf", $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3878
    {
3879
        if (headers_sent()) {
3880
            die("Unable to stream pdf: headers already sent");
0 ignored issues
show
Coding Style Compatibility introduced by
The method stream() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3881
        }
3882
3883
        if (!isset($options["compress"])) $options["compress"] = true;
3884
        if (!isset($options["Attachment"])) $options["Attachment"] = true;
3885
3886
        $debug = !$options['compress'];
3887
        $tmp = ltrim($this->output($debug));
3888
3889
        header("Cache-Control: private");
3890
        header("Content-Type: application/pdf");
3891
        header("Content-Length: " . mb_strlen($tmp, "8bit"));
3892
3893
        $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
3894
        $attachment = $options["Attachment"] ? "attachment" : "inline";
3895
3896
        $encoding = mb_detect_encoding($filename);
3897
        $fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding);
3898
        $fallbackfilename = str_replace("\"", "", $fallbackfilename);
3899
        $encodedfilename = rawurlencode($filename);
3900
3901
        $contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\"";
3902
        if ($fallbackfilename !== $filename) {
3903
            $contentDisposition .= "; filename*=UTF-8''$encodedfilename";
3904
        }
3905
        header($contentDisposition);
3906
3907
        echo $tmp;
3908
        flush();
3909
    }
3910
3911
    /**
3912
     * return the height in units of the current font in the given size
3913
     *
3914
     * @param $size
3915
     * @return float|int
3916
     */
3917
    function getFontHeight($size)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3918
    {
3919
        if (!$this->numFonts) {
3920
            $this->selectFont($this->defaultFont);
3921
        }
3922
3923
        $font = $this->fonts[$this->currentFont];
3924
3925
        // for the current font, and the given size, what is the height of the font in user units
3926
        if (isset($font['Ascender']) && isset($font['Descender'])) {
3927
            $h = $font['Ascender'] - $font['Descender'];
3928
        } else {
3929
            $h = $font['FontBBox'][3] - $font['FontBBox'][1];
3930
        }
3931
3932
        // have to adjust by a font offset for Windows fonts.  unfortunately it looks like
3933
        // the bounding box calculations are wrong and I don't know why.
3934
        if (isset($font['FontHeightOffset'])) {
3935
            // For CourierNew from Windows this needs to be -646 to match the
3936
            // Adobe native Courier font.
3937
            //
3938
            // For FreeMono from GNU this needs to be -337 to match the
3939
            // Courier font.
3940
            //
3941
            // Both have been added manually to the .afm and .ufm files.
3942
            $h += (int)$font['FontHeightOffset'];
3943
        }
3944
3945
        return $size * $h / 1000;
3946
    }
3947
3948
    /**
3949
     * @param $size
3950
     * @return float|int
3951
     */
3952
    function getFontXHeight($size)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3953
    {
3954
        if (!$this->numFonts) {
3955
            $this->selectFont($this->defaultFont);
3956
        }
3957
3958
        $font = $this->fonts[$this->currentFont];
3959
3960
        // for the current font, and the given size, what is the height of the font in user units
3961
        if (isset($font['XHeight'])) {
3962
            $xh = $font['Ascender'] - $font['Descender'];
3963
        } else {
3964
            $xh = $this->getFontHeight($size) / 2;
3965
        }
3966
3967
        return $size * $xh / 1000;
3968
    }
3969
3970
    /**
3971
     * return the font descender, this will normally return a negative number
3972
     * if you add this number to the baseline, you get the level of the bottom of the font
3973
     * it is in the pdf user units
3974
     *
3975
     * @param $size
3976
     * @return float|int
3977
     */
3978
    function getFontDescender($size)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3979
    {
3980
        // note that this will most likely return a negative value
3981
        if (!$this->numFonts) {
3982
            $this->selectFont($this->defaultFont);
3983
        }
3984
3985
        //$h = $this->fonts[$this->currentFont]['FontBBox'][1];
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3986
        $h = $this->fonts[$this->currentFont]['Descender'];
3987
3988
        return $size * $h / 1000;
3989
    }
3990
3991
    /**
3992
     * filter the text, this is applied to all text just before being inserted into the pdf document
3993
     * it escapes the various things that need to be escaped, and so on
3994
     *
3995
     * @access private
3996
     *
3997
     * @param $text
3998
     * @param bool $bom
3999
     * @param bool $convert_encoding
4000
     * @return string
4001
     */
4002
    function filterText($text, $bom = true, $convert_encoding = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4003
    {
4004
        if (!$this->numFonts) {
4005
            $this->selectFont($this->defaultFont);
4006
        }
4007
4008
        if ($convert_encoding) {
4009
            $cf = $this->currentFont;
4010
            if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
4011
                $text = $this->utf8toUtf16BE($text, $bom);
4012
            } else {
4013
                //$text = html_entity_decode($text, ENT_QUOTES);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4014
                $text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
4015
            }
4016
        } else if ($bom) {
4017
            $text = $this->utf8toUtf16BE($text, $bom);
4018
        }
4019
4020
        // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
4021
        return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
4022
    }
4023
4024
    /**
4025
     * return array containing codepoints (UTF-8 character values) for the
4026
     * string passed in.
4027
     *
4028
     * based on the excellent TCPDF code by Nicola Asuni and the
4029
     * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
4030
     *
4031
     * @access private
4032
     * @author Orion Richardson
4033
     * @since  January 5, 2008
4034
     *
4035
     * @param string $text UTF-8 string to process
4036
     *
4037
     * @return array UTF-8 codepoints array for the string
4038
     */
4039
    function utf8toCodePointsArray(&$text)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4040
    {
4041
        $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
4042
        $unicode = array(); // array containing unicode values
4043
        $bytes = array(); // array containing single character byte sequences
4044
        $numbytes = 1; // number of octets needed to represent the UTF-8 character
4045
4046
        for ($i = 0; $i < $length; $i++) {
4047
            $c = ord($text[$i]); // get one string character at time
4048
            if (count($bytes) === 0) { // get starting octect
4049
                if ($c <= 0x7F) {
4050
                    $unicode[] = $c; // use the character "as is" because is ASCII
4051
                    $numbytes = 1;
4052 View Code Duplication
                } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
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...
4053
                    $bytes[] = ($c - 0xC0) << 0x06;
4054
                    $numbytes = 2;
4055
                } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
4056
                    $bytes[] = ($c - 0xE0) << 0x0C;
4057
                    $numbytes = 3;
4058 View Code Duplication
                } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
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...
4059
                    $bytes[] = ($c - 0xF0) << 0x12;
4060
                    $numbytes = 4;
4061
                } else {
4062
                    // use replacement character for other invalid sequences
4063
                    $unicode[] = 0xFFFD;
4064
                    $bytes = array();
4065
                    $numbytes = 1;
4066
                }
4067
            } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
4068
                $bytes[] = $c - 0x80;
4069
                if (count($bytes) === $numbytes) {
4070
                    // compose UTF-8 bytes to a single unicode value
4071
                    $c = $bytes[0];
4072
                    for ($j = 1; $j < $numbytes; $j++) {
4073
                        $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
4074
                    }
4075
                    if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
4076
                        // The definition of UTF-8 prohibits encoding character numbers between
4077
                        // U+D800 and U+DFFF, which are reserved for use with the UTF-16
4078
                        // encoding form (as surrogate pairs) and do not directly represent
4079
                        // characters.
4080
                        $unicode[] = 0xFFFD; // use replacement character
4081
                    } else {
4082
                        $unicode[] = $c; // add char to array
4083
                    }
4084
                    // reset data for next char
4085
                    $bytes = array();
4086
                    $numbytes = 1;
4087
                }
4088
            } else {
4089
                // use replacement character for other invalid sequences
4090
                $unicode[] = 0xFFFD;
4091
                $bytes = array();
4092
                $numbytes = 1;
4093
            }
4094
        }
4095
4096
        return $unicode;
4097
    }
4098
4099
    /**
4100
     * convert UTF-8 to UTF-16 with an additional byte order marker
4101
     * at the front if required.
4102
     *
4103
     * based on the excellent TCPDF code by Nicola Asuni and the
4104
     * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
4105
     *
4106
     * @access private
4107
     * @author Orion Richardson
4108
     * @since  January 5, 2008
4109
     *
4110
     * @param string  $text UTF-8 string to process
4111
     * @param boolean $bom  whether to add the byte order marker
4112
     *
4113
     * @return string UTF-16 result string
4114
     */
4115
    function utf8toUtf16BE(&$text, $bom = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4116
    {
4117
        $out = $bom ? "\xFE\xFF" : '';
4118
4119
        $unicode = $this->utf8toCodePointsArray($text);
4120
        foreach ($unicode as $c) {
4121
            if ($c === 0xFFFD) {
4122
                $out .= "\xFF\xFD"; // replacement character
4123
            } elseif ($c < 0x10000) {
4124
                $out .= chr($c >> 0x08) . chr($c & 0xFF);
4125
            } else {
4126
                $c -= 0x10000;
4127
                $w1 = 0xD800 | ($c >> 0x10);
4128
                $w2 = 0xDC00 | ($c & 0x3FF);
4129
                $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
4130
            }
4131
        }
4132
4133
        return $out;
4134
    }
4135
4136
    /**
4137
     * given a start position and information about how text is to be laid out, calculate where
4138
     * on the page the text will end
4139
     *
4140
     * @param $x
4141
     * @param $y
4142
     * @param $angle
4143
     * @param $size
4144
     * @param $wa
4145
     * @param $text
4146
     * @return array
4147
     */
4148
    private function getTextPosition($x, $y, $angle, $size, $wa, $text)
4149
    {
4150
        // given this information return an array containing x and y for the end position as elements 0 and 1
4151
        $w = $this->getTextWidth($size, $text);
4152
4153
        // need to adjust for the number of spaces in this text
4154
        $words = explode(' ', $text);
4155
        $nspaces = count($words) - 1;
4156
        $w += $wa * $nspaces;
4157
        $a = deg2rad((float)$angle);
4158
4159
        return array(cos($a) * $w + $x, -sin($a) * $w + $y);
4160
    }
4161
4162
    /**
4163
     * Callback method used by smallCaps
4164
     *
4165
     * @param array $matches
4166
     *
4167
     * @return string
4168
     */
4169
    function toUpper($matches)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4170
    {
4171
        return mb_strtoupper($matches[0]);
4172
    }
4173
4174
    function concatMatches($matches)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4175
    {
4176
        $str = "";
4177
        foreach ($matches as $match) {
4178
            $str .= $match[0];
4179
        }
4180
4181
        return $str;
4182
    }
4183
4184
    /**
4185
     * register text for font subsetting
4186
     *
4187
     * @param $font
4188
     * @param $text
4189
     */
4190
    function registerText($font, $text)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4191
    {
4192
        if (!$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts)) {
4193
            return;
4194
        }
4195
4196
        if (!isset($this->stringSubsets[$font])) {
4197
            $this->stringSubsets[$font] = array();
4198
        }
4199
4200
        $this->stringSubsets[$font] = array_unique(
4201
            array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text))
4202
        );
4203
    }
4204
4205
    /**
4206
     * add text to the document, at a specified location, size and angle on the page
4207
     *
4208
     * @param $x
4209
     * @param $y
4210
     * @param $size
4211
     * @param $text
4212
     * @param int $angle
4213
     * @param int $wordSpaceAdjust
4214
     * @param int $charSpaceAdjust
4215
     * @param bool $smallCaps
4216
     */
4217
    function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4218
    {
4219
        if (!$this->numFonts) {
4220
            $this->selectFont($this->defaultFont);
4221
        }
4222
4223
        $text = str_replace(array("\r", "\n"), "", $text);
4224
4225
        if ($smallCaps) {
4226
            preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
4227
            $lower = $this->concatMatches($matches);
4228
            d($lower);
4229
4230
            preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
4231
            $other = $this->concatMatches($matches);
4232
            d($other);
4233
4234
            //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4235
        }
4236
4237
        // if there are any open callbacks, then they should be called, to show the start of the line
4238
        if ($this->nCallback > 0) {
4239
            for ($i = $this->nCallback; $i > 0; $i--) {
4240
                // call each function
4241
                $info = array(
4242
                    'x'         => $x,
4243
                    'y'         => $y,
4244
                    'angle'     => $angle,
4245
                    'status'    => 'sol',
4246
                    'p'         => $this->callback[$i]['p'],
4247
                    'nCallback' => $this->callback[$i]['nCallback'],
4248
                    'height'    => $this->callback[$i]['height'],
4249
                    'descender' => $this->callback[$i]['descender']
4250
                );
4251
4252
                $func = $this->callback[$i]['f'];
4253
                $this->$func($info);
4254
            }
4255
        }
4256
4257
        if ($angle == 0) {
4258
            $this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
4259
        } else {
4260
            $a = deg2rad((float)$angle);
4261
            $this->addContent(
4262
                sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y)
4263
            );
4264
        }
4265
4266
        if ($wordSpaceAdjust != 0) {
4267
            $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
4268
        }
4269
4270
        if ($charSpaceAdjust != 0) {
4271
            $this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
4272
        }
4273
4274
        $len = mb_strlen($text);
4275
        $start = 0;
4276
4277
        if ($start < $len) {
4278
            $part = $text; // OAR - Don't need this anymore, given that $start always equals zero.  substr($text, $start);
4279
            $place_text = $this->filterText($part, false);
4280
            // modify unicode text so that extra word spacing is manually implemented (bug #)
4281
            $cf = $this->currentFont;
4282
            if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
4283
                $space_scale = 1000 / $size;
4284
                $place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text);
4285
            }
4286
            $this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size));
4287
            $this->addContent(" [($place_text)] TJ");
4288
        }
4289
4290
        if ($wordSpaceAdjust != 0) {
4291
            $this->addContent(sprintf(" %.3F Tw", 0));
4292
        }
4293
4294
        if ($charSpaceAdjust != 0) {
4295
            $this->addContent(sprintf(" %.3F Tc", 0));
4296
        }
4297
4298
        $this->addContent(' ET');
4299
4300
        // if there are any open callbacks, then they should be called, to show the end of the line
4301
        if ($this->nCallback > 0) {
4302
            for ($i = $this->nCallback; $i > 0; $i--) {
4303
                // call each function
4304
                $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
4305
                $info = array(
4306
                    'x'         => $tmp[0],
4307
                    'y'         => $tmp[1],
4308
                    'angle'     => $angle,
4309
                    'status'    => 'eol',
4310
                    'p'         => $this->callback[$i]['p'],
4311
                    'nCallback' => $this->callback[$i]['nCallback'],
4312
                    'height'    => $this->callback[$i]['height'],
4313
                    'descender' => $this->callback[$i]['descender']
4314
                );
4315
                $func = $this->callback[$i]['f'];
4316
                $this->$func($info);
4317
            }
4318
        }
4319
    }
4320
4321
    /**
4322
     * calculate how wide a given text string will be on a page, at a given size.
4323
     * this can be called externally, but is also used by the other class functions
4324
     *
4325
     * @param $size
4326
     * @param $text
4327
     * @param int $word_spacing
4328
     * @param int $char_spacing
4329
     * @return float|int
4330
     */
4331
    function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4332
    {
4333
        static $ord_cache = array();
4334
4335
        // this function should not change any of the settings, though it will need to
4336
        // track any directives which change during calculation, so copy them at the start
4337
        // and put them back at the end.
4338
        $store_currentTextState = $this->currentTextState;
4339
4340
        if (!$this->numFonts) {
4341
            $this->selectFont($this->defaultFont);
4342
        }
4343
4344
        $text = str_replace(array("\r", "\n"), "", $text);
4345
4346
        // converts a number or a float to a string so it can get the width
4347
        $text = "$text";
4348
4349
        // hmm, this is where it all starts to get tricky - use the font information to
4350
        // calculate the width of each character, add them up and convert to user units
4351
        $w = 0;
4352
        $cf = $this->currentFont;
4353
        $current_font = $this->fonts[$cf];
4354
        $space_scale = 1000 / ($size > 0 ? $size : 1);
4355
        $n_spaces = 0;
4356
4357
        if ($current_font['isUnicode']) {
4358
            // for Unicode, use the code points array to calculate width rather
4359
            // than just the string itself
4360
            $unicode = $this->utf8toCodePointsArray($text);
4361
4362
            foreach ($unicode as $char) {
4363
                // check if we have to replace character
4364
                if (isset($current_font['differences'][$char])) {
4365
                    $char = $current_font['differences'][$char];
4366
                }
4367
4368 View Code Duplication
                if (isset($current_font['C'][$char])) {
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...
4369
                    $char_width = $current_font['C'][$char];
4370
4371
                    // add the character width
4372
                    $w += $char_width;
4373
4374
                    // add additional padding for space
4375
                    if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') {  // Space
4376
                        $w += $word_spacing * $space_scale;
4377
                        $n_spaces++;
4378
                    }
4379
                }
4380
            }
4381
4382
            // add additional char spacing
4383
            if ($char_spacing != 0) {
4384
                $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
4385
            }
4386
4387
        } else {
4388
            // If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
4389
            if ($this->isUnicode) {
4390
                $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
4391
            }
4392
4393
            $len = mb_strlen($text, 'Windows-1252');
4394
4395
            for ($i = 0; $i < $len; $i++) {
4396
                $c = $text[$i];
4397
                $char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
4398
4399
                // check if we have to replace character
4400
                if (isset($current_font['differences'][$char])) {
4401
                    $char = $current_font['differences'][$char];
4402
                }
4403
4404 View Code Duplication
                if (isset($current_font['C'][$char])) {
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...
4405
                    $char_width = $current_font['C'][$char];
4406
4407
                    // add the character width
4408
                    $w += $char_width;
4409
4410
                    // add additional padding for space
4411
                    if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') {  // Space
4412
                        $w += $word_spacing * $space_scale;
4413
                        $n_spaces++;
4414
                    }
4415
                }
4416
            }
4417
4418
            // add additional char spacing
4419
            if ($char_spacing != 0) {
4420
                $w += $char_spacing * $space_scale * ($len + $n_spaces);
4421
            }
4422
        }
4423
4424
        $this->currentTextState = $store_currentTextState;
4425
        $this->setCurrentFont();
4426
4427
        return $w * $size / 1000;
4428
    }
4429
4430
    /**
4431
     * this will be called at a new page to return the state to what it was on the
4432
     * end of the previous page, before the stack was closed down
4433
     * This is to get around not being able to have open 'q' across pages
4434
     *
4435
     * @param int $pageEnd
4436
     */
4437
    function saveState($pageEnd = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4438
    {
4439
        if ($pageEnd) {
4440
            // this will be called at a new page to return the state to what it was on the
4441
            // end of the previous page, before the stack was closed down
4442
            // This is to get around not being able to have open 'q' across pages
4443
            $opt = $this->stateStack[$pageEnd];
4444
            // ok to use this as stack starts numbering at 1
4445
            $this->setColor($opt['col'], true);
4446
            $this->setStrokeColor($opt['str'], true);
4447
            $this->addContent("\n" . $opt['lin']);
4448
            //    $this->currentLineStyle = $opt['lin'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4449
        } else {
4450
            $this->nStateStack++;
4451
            $this->stateStack[$this->nStateStack] = array(
4452
                'col' => $this->currentColor,
4453
                'str' => $this->currentStrokeColor,
4454
                'lin' => $this->currentLineStyle
4455
            );
4456
        }
4457
4458
        $this->save();
4459
    }
4460
4461
    /**
4462
     * restore a previously saved state
4463
     *
4464
     * @param int $pageEnd
4465
     */
4466
    function restoreState($pageEnd = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4467
    {
4468
        if (!$pageEnd) {
4469
            $n = $this->nStateStack;
4470
            $this->currentColor = $this->stateStack[$n]['col'];
4471
            $this->currentStrokeColor = $this->stateStack[$n]['str'];
4472
            $this->addContent("\n" . $this->stateStack[$n]['lin']);
4473
            $this->currentLineStyle = $this->stateStack[$n]['lin'];
4474
            $this->stateStack[$n] = null;
4475
            unset($this->stateStack[$n]);
4476
            $this->nStateStack--;
4477
        }
4478
4479
        $this->restore();
4480
    }
4481
4482
    /**
4483
     * make a loose object, the output will go into this object, until it is closed, then will revert to
4484
     * the current one.
4485
     * this object will not appear until it is included within a page.
4486
     * the function will return the object number
4487
     *
4488
     * @return int
4489
     */
4490
    function openObject()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4491
    {
4492
        $this->nStack++;
4493
        $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
4494
        // add a new object of the content type, to hold the data flow
4495
        $this->numObj++;
4496
        $this->o_contents($this->numObj, 'new');
4497
        $this->currentContents = $this->numObj;
4498
        $this->looseObjects[$this->numObj] = 1;
4499
4500
        return $this->numObj;
4501
    }
4502
4503
    /**
4504
     * open an existing object for editing
4505
     *
4506
     * @param $id
4507
     */
4508
    function reopenObject($id)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4509
    {
4510
        $this->nStack++;
4511
        $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
4512
        $this->currentContents = $id;
4513
4514
        // also if this object is the primary contents for a page, then set the current page to its parent
4515
        if (isset($this->objects[$id]['onPage'])) {
4516
            $this->currentPage = $this->objects[$id]['onPage'];
4517
        }
4518
    }
4519
4520
    /**
4521
     * close an object
4522
     */
4523
    function closeObject()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4524
    {
4525
        // close the object, as long as there was one open in the first place, which will be indicated by
4526
        // an objectId on the stack.
4527
        if ($this->nStack > 0) {
4528
            $this->currentContents = $this->stack[$this->nStack]['c'];
4529
            $this->currentPage = $this->stack[$this->nStack]['p'];
4530
            $this->nStack--;
4531
            // easier to probably not worry about removing the old entries, they will be overwritten
4532
            // if there are new ones.
4533
        }
4534
    }
4535
4536
    /**
4537
     * stop an object from appearing on pages from this point on
4538
     *
4539
     * @param $id
4540
     */
4541
    function stopObject($id)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4542
    {
4543
        // if an object has been appearing on pages up to now, then stop it, this page will
4544
        // be the last one that could contain it.
4545
        if (isset($this->addLooseObjects[$id])) {
4546
            $this->addLooseObjects[$id] = '';
4547
        }
4548
    }
4549
4550
    /**
4551
     * after an object has been created, it wil only show if it has been added, using this function.
4552
     *
4553
     * @param $id
4554
     * @param string $options
4555
     */
4556
    function addObject($id, $options = 'add')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4557
    {
4558
        // add the specified object to the page
4559
        if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
4560
            // then it is a valid object, and it is not being added to itself
4561
            switch ($options) {
4562
                case 'all':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
4563
                    // then this object is to be added to this page (done in the next block) and
4564
                    // all future new pages.
4565
                    $this->addLooseObjects[$id] = 'all';
4566
4567
                case 'add':
4568
                    if (isset($this->objects[$this->currentContents]['onPage'])) {
4569
                        // then the destination contents is the primary for the page
4570
                        // (though this object is actually added to that page)
4571
                        $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
4572
                    }
4573
                    break;
4574
4575 View Code Duplication
                case 'even':
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...
4576
                    $this->addLooseObjects[$id] = 'even';
4577
                    $pageObjectId = $this->objects[$this->currentContents]['onPage'];
4578
                    if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 0) {
4579
                        $this->addObject($id);
4580
                        // hacky huh :)
4581
                    }
4582
                    break;
4583
4584 View Code Duplication
                case 'odd':
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...
4585
                    $this->addLooseObjects[$id] = 'odd';
4586
                    $pageObjectId = $this->objects[$this->currentContents]['onPage'];
4587
                    if ($this->objects[$pageObjectId]['info']['pageNum'] % 2 == 1) {
4588
                        $this->addObject($id);
4589
                        // hacky huh :)
4590
                    }
4591
                    break;
4592
4593
                case 'next':
4594
                    $this->addLooseObjects[$id] = 'all';
4595
                    break;
4596
4597
                case 'nexteven':
4598
                    $this->addLooseObjects[$id] = 'even';
4599
                    break;
4600
4601
                case 'nextodd':
4602
                    $this->addLooseObjects[$id] = 'odd';
4603
                    break;
4604
            }
4605
        }
4606
    }
4607
4608
    /**
4609
     * return a storable representation of a specific object
4610
     *
4611
     * @param $id
4612
     * @return string|null
4613
     */
4614
    function serializeObject($id)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4615
    {
4616
        if (array_key_exists($id, $this->objects)) {
4617
            return serialize($this->objects[$id]);
4618
        }
4619
4620
        return null;
4621
    }
4622
4623
    /**
4624
     * restore an object from its stored representation.  returns its new object id.
4625
     *
4626
     * @param $obj
4627
     * @return int
4628
     */
4629
    function restoreSerializedObject($obj)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4630
    {
4631
        $obj_id = $this->openObject();
4632
        $this->objects[$obj_id] = unserialize($obj);
4633
        $this->closeObject();
4634
4635
        return $obj_id;
4636
    }
4637
4638
    /**
4639
     * add content to the documents info object
4640
     *
4641
     * @param $label
4642
     * @param int $value
4643
     */
4644
    function addInfo($label, $value = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4645
    {
4646
        // this will only work if the label is one of the valid ones.
4647
        // modify this so that arrays can be passed as well.
4648
        // if $label is an array then assume that it is key => value pairs
4649
        // else assume that they are both scalar, anything else will probably error
4650
        if (is_array($label)) {
4651
            foreach ($label as $l => $v) {
4652
                $this->o_info($this->infoObject, $l, $v);
4653
            }
4654
        } else {
4655
            $this->o_info($this->infoObject, $label, $value);
4656
        }
4657
    }
4658
4659
    /**
4660
     * set the viewer preferences of the document, it is up to the browser to obey these.
4661
     *
4662
     * @param $label
4663
     * @param int $value
4664
     */
4665
    function setPreferences($label, $value = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4666
    {
4667
        // this will only work if the label is one of the valid ones.
4668
        if (is_array($label)) {
4669
            foreach ($label as $l => $v) {
4670
                $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
4671
            }
4672
        } else {
4673
            $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
4674
        }
4675
    }
4676
4677
    /**
4678
     * extract an integer from a position in a byte stream
4679
     *
4680
     * @param $data
4681
     * @param $pos
4682
     * @param $num
4683
     * @return int
4684
     */
4685
    private function getBytes(&$data, $pos, $num)
4686
    {
4687
        // return the integer represented by $num bytes from $pos within $data
4688
        $ret = 0;
4689
        for ($i = 0; $i < $num; $i++) {
4690
            $ret *= 256;
4691
            $ret += ord($data[$pos + $i]);
4692
        }
4693
4694
        return $ret;
4695
    }
4696
4697
    /**
4698
     * Check if image already added to pdf image directory.
4699
     * If yes, need not to create again (pass empty data)
4700
     *
4701
     * @param $imgname
4702
     * @return bool
4703
     */
4704
    function image_iscached($imgname)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4705
    {
4706
        return isset($this->imagelist[$imgname]);
4707
    }
4708
4709
    /**
4710
     * add a PNG image into the document, from a GD object
4711
     * this should work with remote files
4712
     *
4713
     * @param string $file The PNG file
4714
     * @param float $x X position
4715
     * @param float $y Y position
4716
     * @param float $w Width
4717
     * @param float $h Height
4718
     * @param resource $img A GD resource
4719
     * @param bool $is_mask true if the image is a mask
4720
     * @param bool $mask true if the image is masked
4721
     * @throws Exception
4722
     */
4723
    function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4724
    {
4725
        if (!function_exists("imagepng")) {
4726
            throw new \Exception("The PHP GD extension is required, but is not installed.");
4727
        }
4728
4729
        //if already cached, need not to read again
4730
        if (isset($this->imagelist[$file])) {
4731
            $data = null;
4732
        } else {
4733
            // Example for transparency handling on new image. Retain for current image
4734
            // $tIndex = imagecolortransparent($img);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4735
            // if ($tIndex > 0) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4736
            //   $tColor    = imagecolorsforindex($img, $tIndex);
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4737
            //   $new_tIndex    = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4738
            //   imagefill($new_img, 0, 0, $new_tIndex);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4739
            //   imagecolortransparent($new_img, $new_tIndex);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4740
            // }
4741
            // blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
4742
            //imagealphablending($img, true);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4743
4744
            //default, but explicitely set to ensure pdf compatibility
4745
            imagesavealpha($img, false/*!$is_mask && !$mask*/);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4746
4747
            $error = 0;
4748
            //DEBUG_IMG_TEMP
4749
            //debugpng
4750
            if (defined("DEBUGPNG") && DEBUGPNG) {
4751
                print '[addImagePng ' . $file . ']';
4752
            }
4753
4754
            ob_start();
4755
            @imagepng($img);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4756
            $data = ob_get_clean();
4757
4758 View Code Duplication
            if ($data == '') {
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...
4759
                $error = 1;
4760
                $errormsg = 'trouble writing file from GD';
4761
                //DEBUG_IMG_TEMP
4762
                //debugpng
4763
                if (defined("DEBUGPNG") && DEBUGPNG) {
4764
                    print 'trouble writing file from GD';
4765
                }
4766
            }
4767
4768
            if ($error) {
4769
                $this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
0 ignored issues
show
Bug introduced by
The variable $errormsg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4770
4771
                return;
4772
            }
4773
        }  //End isset($this->imagelist[$file]) (png Duplicate removal)
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4774
4775
        $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
0 ignored issues
show
Bug introduced by
It seems like $mask defined by parameter $mask on line 4723 can also be of type boolean; however, Cpdf::addPngFromBuf() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4776
    }
4777
4778
    /**
4779
     * @param $file
4780
     * @param $x
4781
     * @param $y
4782
     * @param $w
4783
     * @param $h
4784
     * @param $byte
4785
     */
4786
    protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte)
4787
    {
4788
        // generate images
4789
        $img = imagecreatefrompng($file);
4790
4791
        if ($img === false) {
4792
            return;
4793
        }
4794
4795
        // FIXME The pixel transformation doesn't work well with 8bit PNGs
4796
        $eight_bit = ($byte & 4) !== 4;
4797
4798
        $wpx = imagesx($img);
4799
        $hpx = imagesy($img);
4800
4801
        imagesavealpha($img, false);
4802
4803
        // create temp alpha file
4804
        $tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
4805
        @unlink($tempfile_alpha);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4806
        $tempfile_alpha = "$tempfile_alpha.png";
4807
4808
        // create temp plain file
4809
        $tempfile_plain = tempnam($this->tmp, "cpdf_img_");
4810
        @unlink($tempfile_plain);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4811
        $tempfile_plain = "$tempfile_plain.png";
4812
4813
        $imgalpha = imagecreate($wpx, $hpx);
4814
        imagesavealpha($imgalpha, false);
4815
4816
        // generate gray scale palette (0 -> 255)
4817
        for ($c = 0; $c < 256; ++$c) {
4818
            imagecolorallocate($imgalpha, $c, $c, $c);
4819
        }
4820
4821
        // Use PECL gmagick + Graphics Magic to process transparent PNG images
4822
        if (extension_loaded("gmagick")) {
4823
            $gmagick = new \Gmagick($file);
4824
            $gmagick->setimageformat('png');
4825
4826
            // Get opacity channel (negative of alpha channel)
4827
            $alpha_channel_neg = clone $gmagick;
4828
            $alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY);
4829
4830
            // Negate opacity channel
4831
            $alpha_channel = new \Gmagick();
4832
            $alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
4833
            $alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
4834
            $alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED);
4835
            $alpha_channel->writeimage($tempfile_alpha);
4836
4837
            // Cast to 8bit+palette
4838
            $imgalpha_ = imagecreatefrompng($tempfile_alpha);
4839
            imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
4840
            imagedestroy($imgalpha_);
4841
            imagepng($imgalpha, $tempfile_alpha);
4842
4843
            // Make opaque image
4844
            $color_channels = new \Gmagick();
4845
            $color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
4846
            $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0);
4847
            $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0);
4848
            $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0);
4849
            $color_channels->writeimage($tempfile_plain);
4850
4851
            $imgplain = imagecreatefrompng($tempfile_plain);
4852
        }
4853
        // Use PECL imagick + ImageMagic to process transparent PNG images
4854
        elseif (extension_loaded("imagick")) {
4855
            // Native cloning was added to pecl-imagick in svn commit 263814
4856
            // the first version containing it was 3.0.1RC1
4857
            static $imagickClonable = null;
4858
            if ($imagickClonable === null) {
4859
                $imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0;
4860
            }
4861
4862
            $imagick = new \Imagick($file);
4863
            $imagick->setFormat('png');
4864
4865
            // Get opacity channel (negative of alpha channel)
4866
            $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone();
4867
            $alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA);
4868
            $alpha_channel->negateImage(true);
4869
            $alpha_channel->writeImage($tempfile_alpha);
4870
4871
            // Cast to 8bit+palette
4872
            $imgalpha_ = imagecreatefrompng($tempfile_alpha);
4873
            imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
4874
            imagedestroy($imgalpha_);
4875
            imagepng($imgalpha, $tempfile_alpha);
4876
4877
            // Make opaque image
4878
            $color_channels = new \Imagick();
4879
            $color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
4880
            $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0);
4881
            $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0);
4882
            $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0);
4883
            $color_channels->writeImage($tempfile_plain);
4884
4885
            $imgplain = imagecreatefrompng($tempfile_plain);
4886
        } else {
4887
            // allocated colors cache
4888
            $allocated_colors = array();
4889
4890
            // extract alpha channel
4891
            for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4892
                for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4893
                    $color = imagecolorat($img, $xpx, $ypx);
4894
                    $col = imagecolorsforindex($img, $color);
4895
                    $alpha = $col['alpha'];
4896
4897
                    if ($eight_bit) {
4898
                        // with gamma correction
4899
                        $gammacorr = 2.2;
4900
                        $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
4901
                    } else {
4902
                        // without gamma correction
4903
                        $pixel = (127 - $alpha) * 2;
4904
4905
                        $key = $col['red'] . $col['green'] . $col['blue'];
4906
4907
                        if (!isset($allocated_colors[$key])) {
4908
                            $pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
4909
                            $allocated_colors[$key] = $pixel_img;
4910
                        } else {
4911
                            $pixel_img = $allocated_colors[$key];
4912
                        }
4913
4914
                        imagesetpixel($img, $xpx, $ypx, $pixel_img);
4915
                    }
4916
4917
                    imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
4918
                }
4919
            }
4920
4921
            // extract image without alpha channel
4922
            $imgplain = imagecreatetruecolor($wpx, $hpx);
4923
            imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4924
            imagedestroy($img);
4925
4926
            imagepng($imgalpha, $tempfile_alpha);
4927
            imagepng($imgplain, $tempfile_plain);
4928
        }
4929
4930
        // embed mask image
4931
        $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
4932
        imagedestroy($imgalpha);
4933
4934
        // embed image, masked with previously embedded mask
4935
        $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, true);
4936
        imagedestroy($imgplain);
4937
4938
        // remove temp files
4939
        unlink($tempfile_alpha);
4940
        unlink($tempfile_plain);
4941
    }
4942
4943
    /**
4944
     * add a PNG image into the document, from a file
4945
     * this should work with remote files
4946
     *
4947
     * @param $file
4948
     * @param $x
4949
     * @param $y
4950
     * @param int $w
4951
     * @param int $h
4952
     * @throws Exception
4953
     */
4954
    function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
4955
    {
4956
        if (!function_exists("imagecreatefrompng")) {
4957
            throw new \Exception("The PHP GD extension is required, but is not installed.");
4958
        }
4959
4960
        //if already cached, need not to read again
4961
        if (isset($this->imagelist[$file])) {
4962
            $img = null;
4963
        } else {
4964
            $info = file_get_contents($file, false, null, 24, 5);
4965
            $meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
4966
            $bit_depth = $meta["bitDepth"];
4967
            $color_type = $meta["colorType"];
4968
4969
            // http://www.w3.org/TR/PNG/#11IHDR
4970
            // 3 => indexed
4971
            // 4 => greyscale with alpha
4972
            // 6 => fullcolor with alpha
4973
            $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
4974
4975
            if ($is_alpha) { // exclude grayscale alpha
4976
                $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
4977
                return;
4978
            }
4979
4980
            //png files typically contain an alpha channel.
4981
            //pdf file format or class.pdf does not support alpha blending.
4982
            //on alpha blended images, more transparent areas have a color near black.
4983
            //This appears in the result on not storing the alpha channel.
4984
            //Correct would be the box background image or its parent when transparent.
4985
            //But this would make the image dependent on the background.
4986
            //Therefore create an image with white background and copy in
4987
            //A more natural background than black is white.
4988
            //Therefore create an empty image with white background and merge the
4989
            //image in with alpha blending.
4990
            $imgtmp = @imagecreatefrompng($file);
4991
            if (!$imgtmp) {
4992
                return;
4993
            }
4994
            $sx = imagesx($imgtmp);
4995
            $sy = imagesy($imgtmp);
4996
            $img = imagecreatetruecolor($sx, $sy);
4997
            imagealphablending($img, true);
4998
4999
            // @todo is it still needed ??
5000
            $ti = imagecolortransparent($imgtmp);
5001
            if ($ti >= 0) {
5002
                $tc = imagecolorsforindex($imgtmp, $ti);
5003
                $ti = imagecolorallocate($img, $tc['red'], $tc['green'], $tc['blue']);
5004
                imagefill($img, 0, 0, $ti);
5005
                imagecolortransparent($img, $ti);
5006
            } else {
5007
                imagefill($img, 1, 1, imagecolorallocate($img, 255, 255, 255));
5008
            }
5009
5010
            imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy);
5011
            imagedestroy($imgtmp);
5012
        }
5013
        $this->addImagePng($file, $x, $y, $w, $h, $img);
0 ignored issues
show
Bug introduced by
It seems like $img defined by null on line 4962 can also be of type null; however, Cpdf::addImagePng() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
5014
5015
        if ($img) {
5016
            imagedestroy($img);
5017
        }
5018
    }
5019
5020
    /**
5021
     * add a PNG image into the document, from a file
5022
     * this should work with remote files
5023
     *
5024
     * @param $file
5025
     * @param $x
5026
     * @param $y
5027
     * @param int $w
5028
     * @param int $h
5029
     */
5030
    function addSvgFromFile($file, $x, $y, $w = 0, $h = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5031
    {
5032
        $doc = new \Svg\Document();
5033
        $doc->loadFile($file);
5034
        $dimensions = $doc->getDimensions();
5035
5036
        $this->save();
5037
5038
        $this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y));
5039
5040
        $surface = new \Svg\Surface\SurfaceCpdf($doc, $this);
5041
        $doc->render($surface);
5042
5043
        $this->restore();
5044
    }
5045
5046
    /**
5047
     * add a PNG image into the document, from a memory buffer of the file
5048
     *
5049
     * @param $file
5050
     * @param $x
5051
     * @param $y
5052
     * @param float $w
5053
     * @param float $h
5054
     * @param $data
5055
     * @param bool $is_mask
5056
     * @param null $mask
5057
     */
5058
    function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5059
    {
5060
        if (isset($this->imagelist[$file])) {
5061
            $data = null;
5062
            $info['width'] = $this->imagelist[$file]['w'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$info was never initialized. Although not strictly required by PHP, it is generally a good practice to add $info = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
5063
            $info['height'] = $this->imagelist[$file]['h'];
5064
            $label = $this->imagelist[$file]['label'];
5065
        } else {
5066
            if ($data == null) {
5067
                $this->addMessage('addPngFromBuf error - data not present!');
5068
5069
                return;
5070
            }
5071
5072
            $error = 0;
5073
5074
            if (!$error) {
5075
                $header = chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10);
5076
5077
                if (mb_substr($data, 0, 8, '8bit') != $header) {
5078
                    $error = 1;
5079
5080
                    if (defined("DEBUGPNG") && DEBUGPNG) {
5081
                        print '[addPngFromFile this file does not have a valid header ' . $file . ']';
5082
                    }
5083
5084
                    $errormsg = 'this file does not have a valid header';
5085
                }
5086
            }
5087
5088
            if (!$error) {
5089
                // set pointer
5090
                $p = 8;
5091
                $len = mb_strlen($data, '8bit');
5092
5093
                // cycle through the file, identifying chunks
5094
                $haveHeader = 0;
5095
                $info = array();
5096
                $idata = '';
5097
                $pdata = '';
5098
5099
                while ($p < $len) {
5100
                    $chunkLen = $this->getBytes($data, $p, 4);
5101
                    $chunkType = mb_substr($data, $p + 4, 4, '8bit');
5102
5103
                    switch ($chunkType) {
5104
                        case 'IHDR':
5105
                            // this is where all the file information comes from
5106
                            $info['width'] = $this->getBytes($data, $p + 8, 4);
5107
                            $info['height'] = $this->getBytes($data, $p + 12, 4);
5108
                            $info['bitDepth'] = ord($data[$p + 16]);
5109
                            $info['colorType'] = ord($data[$p + 17]);
5110
                            $info['compressionMethod'] = ord($data[$p + 18]);
5111
                            $info['filterMethod'] = ord($data[$p + 19]);
5112
                            $info['interlaceMethod'] = ord($data[$p + 20]);
5113
5114
                            //print_r($info);
5115
                            $haveHeader = 1;
5116 View Code Duplication
                            if ($info['compressionMethod'] != 0) {
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...
5117
                                $error = 1;
5118
5119
                                //debugpng
5120
                                if (defined("DEBUGPNG") && DEBUGPNG) {
5121
                                    print '[addPngFromFile unsupported compression method ' . $file . ']';
5122
                                }
5123
5124
                                $errormsg = 'unsupported compression method';
5125
                            }
5126
5127 View Code Duplication
                            if ($info['filterMethod'] != 0) {
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...
5128
                                $error = 1;
5129
5130
                                //debugpng
5131
                                if (defined("DEBUGPNG") && DEBUGPNG) {
5132
                                    print '[addPngFromFile unsupported filter method ' . $file . ']';
5133
                                }
5134
5135
                                $errormsg = 'unsupported filter method';
5136
                            }
5137
                            break;
5138
5139
                        case 'PLTE':
5140
                            $pdata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
5141
                            break;
5142
5143
                        case 'IDAT':
5144
                            $idata .= mb_substr($data, $p + 8, $chunkLen, '8bit');
5145
                            break;
5146
5147
                        case 'tRNS':
5148
                            //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
5149
                            //print "tRNS found, color type = ".$info['colorType']."\n";
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5150
                            $transparency = array();
5151
5152
                            switch ($info['colorType']) {
5153
                                // indexed color, rbg
5154
                                case 3:
5155
                                    /* corresponding to entries in the plte chunk
5156
                                     Alpha for palette index 0: 1 byte
5157
                                     Alpha for palette index 1: 1 byte
5158
                                     ...etc...
5159
                                    */
5160
                                    // there will be one entry for each palette entry. up until the last non-opaque entry.
5161
                                    // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
5162
                                    $transparency['type'] = 'indexed';
5163
                                    $trans = 0;
5164
5165
                                    for ($i = $chunkLen; $i >= 0; $i--) {
5166
                                        if (ord($data[$p + 8 + $i]) == 0) {
5167
                                            $trans = $i;
5168
                                        }
5169
                                    }
5170
5171
                                    $transparency['data'] = $trans;
5172
                                    break;
5173
5174
                                // grayscale
5175
                                case 0:
5176
                                    /* corresponding to entries in the plte chunk
5177
                                     Gray: 2 bytes, range 0 .. (2^bitdepth)-1
5178
                                    */
5179
                                    //            $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5180
                                    $transparency['type'] = 'indexed';
5181
                                    $transparency['data'] = ord($data[$p + 8 + 1]);
5182
                                    break;
5183
5184
                                // truecolor
5185
                                case 2:
5186
                                    /* corresponding to entries in the plte chunk
5187
                                     Red: 2 bytes, range 0 .. (2^bitdepth)-1
5188
                                     Green: 2 bytes, range 0 .. (2^bitdepth)-1
5189
                                     Blue: 2 bytes, range 0 .. (2^bitdepth)-1
5190
                                    */
5191
                                    $transparency['r'] = $this->getBytes($data, $p + 8, 2);
5192
                                    // r from truecolor
5193
                                    $transparency['g'] = $this->getBytes($data, $p + 10, 2);
5194
                                    // g from truecolor
5195
                                    $transparency['b'] = $this->getBytes($data, $p + 12, 2);
5196
                                    // b from truecolor
5197
5198
                                    $transparency['type'] = 'color-key';
5199
                                    break;
5200
5201
                                //unsupported transparency type
5202
                                default:
5203
                                    if (defined("DEBUGPNG") && DEBUGPNG) {
5204
                                        print '[addPngFromFile unsupported transparency type ' . $file . ']';
5205
                                    }
5206
                                    break;
5207
                            }
5208
5209
                            // KS End new code
5210
                            break;
5211
5212
                        default:
5213
                            break;
5214
                    }
5215
5216
                    $p += $chunkLen + 12;
5217
                }
5218
5219 View Code Duplication
                if (!$haveHeader) {
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...
5220
                    $error = 1;
5221
5222
                    //debugpng
5223
                    if (defined("DEBUGPNG") && DEBUGPNG) {
5224
                        print '[addPngFromFile information header is missing ' . $file . ']';
5225
                    }
5226
5227
                    $errormsg = 'information header is missing';
5228
                }
5229
5230 View Code Duplication
                if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
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...
5231
                    $error = 1;
5232
5233
                    //debugpng
5234
                    if (defined("DEBUGPNG") && DEBUGPNG) {
5235
                        print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
5236
                    }
5237
5238
                    $errormsg = 'There appears to be no support for interlaced images in pdf.';
5239
                }
5240
            }
5241
5242 View Code Duplication
            if (!$error && $info['bitDepth'] > 8) {
0 ignored issues
show
Bug introduced by
The variable $info does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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...
5243
                $error = 1;
5244
5245
                //debugpng
5246
                if (defined("DEBUGPNG") && DEBUGPNG) {
5247
                    print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
5248
                }
5249
5250
                $errormsg = 'only bit depth of 8 or less is supported';
5251
            }
5252
5253
            if (!$error) {
5254
                switch ($info['colorType']) {
5255
                    case 3:
5256
                        $color = 'DeviceRGB';
5257
                        $ncolor = 1;
5258
                        break;
5259
5260
                    case 2:
5261
                        $color = 'DeviceRGB';
5262
                        $ncolor = 3;
5263
                        break;
5264
5265
                    case 0:
5266
                        $color = 'DeviceGray';
5267
                        $ncolor = 1;
5268
                        break;
5269
5270
                    default:
5271
                        $error = 1;
5272
5273
                        //debugpng
5274
                        if (defined("DEBUGPNG") && DEBUGPNG) {
5275
                            print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
5276
                        }
5277
5278
                        $errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
5279
                }
5280
            }
5281
5282
            if ($error) {
5283
                $this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
0 ignored issues
show
Bug introduced by
The variable $errormsg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5284
5285
                return;
5286
            }
5287
5288
            //print_r($info);
5289
            // so this image is ok... add it in.
5290
            $this->numImages++;
5291
            $im = $this->numImages;
5292
            $label = "I$im";
5293
            $this->numObj++;
5294
5295
            //  $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5296
            $options = array(
5297
                'label'            => $label,
5298
                'data'             => $idata,
0 ignored issues
show
Bug introduced by
The variable $idata does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5299
                'bitsPerComponent' => $info['bitDepth'],
5300
                'pdata'            => $pdata,
0 ignored issues
show
Bug introduced by
The variable $pdata does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5301
                'iw'               => $info['width'],
5302
                'ih'               => $info['height'],
5303
                'type'             => 'png',
5304
                'color'            => $color,
0 ignored issues
show
Bug introduced by
The variable $color does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5305
                'ncolor'           => $ncolor,
0 ignored issues
show
Bug introduced by
The variable $ncolor does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5306
                'masked'           => $mask,
5307
                'isMask'           => $is_mask
5308
            );
5309
5310
            if (isset($transparency)) {
5311
                $options['transparency'] = $transparency;
5312
            }
5313
5314
            $this->o_image($this->numObj, 'new', $options);
0 ignored issues
show
Documentation introduced by
$options is of type array<string,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5315
            $this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']);
5316
        }
5317
5318
        if ($is_mask) {
5319
            return;
5320
        }
5321
5322
        if ($w <= 0 && $h <= 0) {
5323
            $w = $info['width'];
5324
            $h = $info['height'];
5325
        }
5326
5327
        if ($w <= 0) {
5328
            $w = $h / $info['height'] * $info['width'];
5329
        }
5330
5331
        if ($h <= 0) {
5332
            $h = $w * $info['height'] / $info['width'];
5333
        }
5334
5335
        $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
5336
    }
5337
5338
    /**
5339
     * add a JPEG image into the document, from a file
5340
     *
5341
     * @param $img
5342
     * @param $x
5343
     * @param $y
5344
     * @param int $w
5345
     * @param int $h
5346
     */
5347
    function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5348
    {
5349
        // attempt to add a jpeg image straight from a file, using no GD commands
5350
        // note that this function is unable to operate on a remote file.
5351
5352
        if (!file_exists($img)) {
5353
            return;
5354
        }
5355
5356
        if ($this->image_iscached($img)) {
5357
            $data = null;
5358
            $imageWidth = $this->imagelist[$img]['w'];
5359
            $imageHeight = $this->imagelist[$img]['h'];
5360
            $channels = $this->imagelist[$img]['c'];
5361
        } else {
5362
            $tmp = getimagesize($img);
5363
            $imageWidth = $tmp[0];
5364
            $imageHeight = $tmp[1];
5365
5366
            if (isset($tmp['channels'])) {
5367
                $channels = $tmp['channels'];
5368
            } else {
5369
                $channels = 3;
5370
            }
5371
5372
            $data = file_get_contents($img);
5373
        }
5374
5375
        if ($w <= 0 && $h <= 0) {
5376
            $w = $imageWidth;
5377
        }
5378
5379
        if ($w == 0) {
5380
            $w = $h / $imageHeight * $imageWidth;
5381
        }
5382
5383
        if ($h == 0) {
5384
            $h = $w * $imageHeight / $imageWidth;
5385
        }
5386
5387
        $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
5388
    }
5389
5390
    /**
5391
     * common code used by the two JPEG adding functions
5392
     * @param $data
5393
     * @param $x
5394
     * @param $y
5395
     * @param int $w
5396
     * @param int $h
5397
     * @param $imageWidth
5398
     * @param $imageHeight
5399
     * @param int $channels
5400
     * @param $imgname
5401
     */
5402
    private function addJpegImage_common(
5403
        &$data,
5404
        $x,
5405
        $y,
5406
        $w = 0,
5407
        $h = 0,
5408
        $imageWidth,
5409
        $imageHeight,
5410
        $channels = 3,
5411
        $imgname
5412
    ) {
5413
        if ($this->image_iscached($imgname)) {
5414
            $label = $this->imagelist[$imgname]['label'];
5415
            //debugpng
5416
            //if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5417
5418
        } else {
5419
            if ($data == null) {
5420
                $this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
5421
5422
                return;
5423
            }
5424
5425
            // note that this function is not to be called externally
5426
            // it is just the common code between the GD and the file options
5427
            $this->numImages++;
5428
            $im = $this->numImages;
5429
            $label = "I$im";
5430
            $this->numObj++;
5431
5432
            $this->o_image(
5433
                $this->numObj,
5434
                'new',
5435
                array(
0 ignored issues
show
Documentation introduced by
array('label' => $label,...channels' => $channels) is of type array<string,?,{"label":...,"channels":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5436
                    'label'    => $label,
5437
                    'data'     => &$data,
5438
                    'iw'       => $imageWidth,
5439
                    'ih'       => $imageHeight,
5440
                    'channels' => $channels
5441
                )
5442
            );
5443
5444
            $this->imagelist[$imgname] = array(
5445
                'label' => $label,
5446
                'w'     => $imageWidth,
5447
                'h'     => $imageHeight,
5448
                'c'     => $channels
5449
            );
5450
        }
5451
5452
        $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
5453
    }
5454
5455
    /**
5456
     * specify where the document should open when it first starts
5457
     *
5458
     * @param $style
5459
     * @param int $a
5460
     * @param int $b
5461
     * @param int $c
5462
     */
5463 View Code Duplication
    function openHere($style, $a = 0, $b = 0, $c = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
5464
    {
5465
        // this function will open the document at a specified page, in a specified style
5466
        // the values for style, and the required parameters are:
5467
        // 'XYZ'  left, top, zoom
5468
        // 'Fit'
5469
        // 'FitH' top
5470
        // 'FitV' left
5471
        // 'FitR' left,bottom,right
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5472
        // 'FitB'
5473
        // 'FitBH' top
5474
        // 'FitBV' left
5475
        $this->numObj++;
5476
        $this->o_destination(
5477
            $this->numObj,
5478
            'new',
5479
            array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
0 ignored issues
show
Documentation introduced by
array('page' => $this->c...'p2' => $b, 'p3' => $c) is of type array<string,?,{"page":"...teger","p3":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5480
        );
5481
        $id = $this->catalogId;
5482
        $this->o_catalog($id, 'openHere', $this->numObj);
5483
    }
5484
5485
    /**
5486
     * Add JavaScript code to the PDF document
5487
     *
5488
     * @param string $code
5489
     */
5490
    function addJavascript($code)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5491
    {
5492
        $this->javascript .= $code;
5493
    }
5494
5495
    /**
5496
     * create a labelled destination within the document
5497
     *
5498
     * @param $label
5499
     * @param $style
5500
     * @param int $a
5501
     * @param int $b
5502
     * @param int $c
5503
     */
5504 View Code Duplication
    function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
5505
    {
5506
        // associates the given label with the destination, it is done this way so that a destination can be specified after
5507
        // it has been linked to
5508
        // styles are the same as the 'openHere' function
5509
        $this->numObj++;
5510
        $this->o_destination(
5511
            $this->numObj,
5512
            'new',
5513
            array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c)
0 ignored issues
show
Documentation introduced by
array('page' => $this->c...'p2' => $b, 'p3' => $c) is of type array<string,?,{"page":"...teger","p3":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
5514
        );
5515
        $id = $this->numObj;
5516
5517
        // store the label->idf relationship, note that this means that labels can be used only once
5518
        $this->destinations["$label"] = $id;
5519
    }
5520
5521
    /**
5522
     * define font families, this is used to initialize the font families for the default fonts
5523
     * and for the user to add new ones for their fonts. The default bahavious can be overridden should
5524
     * that be desired.
5525
     *
5526
     * @param $family
5527
     * @param string $options
5528
     */
5529
    function setFontFamily($family, $options = '')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5530
    {
5531
        if (!is_array($options)) {
5532
            if ($family === 'init') {
5533
                // set the known family groups
5534
                // these font families will be used to enable bold and italic markers to be included
5535
                // within text streams. html forms will be used... <b></b> <i></i>
5536
                $this->fontFamilies['Helvetica.afm'] =
5537
                    array(
5538
                        'b'  => 'Helvetica-Bold.afm',
5539
                        'i'  => 'Helvetica-Oblique.afm',
5540
                        'bi' => 'Helvetica-BoldOblique.afm',
5541
                        'ib' => 'Helvetica-BoldOblique.afm'
5542
                    );
5543
5544
                $this->fontFamilies['Courier.afm'] =
5545
                    array(
5546
                        'b'  => 'Courier-Bold.afm',
5547
                        'i'  => 'Courier-Oblique.afm',
5548
                        'bi' => 'Courier-BoldOblique.afm',
5549
                        'ib' => 'Courier-BoldOblique.afm'
5550
                    );
5551
5552
                $this->fontFamilies['Times-Roman.afm'] =
5553
                    array(
5554
                        'b'  => 'Times-Bold.afm',
5555
                        'i'  => 'Times-Italic.afm',
5556
                        'bi' => 'Times-BoldItalic.afm',
5557
                        'ib' => 'Times-BoldItalic.afm'
5558
                    );
5559
            }
5560
        } else {
5561
5562
            // the user is trying to set a font family
5563
            // note that this can also be used to set the base ones to something else
5564
            if (mb_strlen($family)) {
5565
                $this->fontFamilies[$family] = $options;
5566
            }
5567
        }
5568
    }
5569
5570
    /**
5571
     * used to add messages for use in debugging
5572
     *
5573
     * @param $message
5574
     */
5575
    function addMessage($message)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5576
    {
5577
        $this->messages .= $message . "\n";
5578
    }
5579
5580
    /**
5581
     * a few functions which should allow the document to be treated transactionally.
5582
     *
5583
     * @param $action
5584
     */
5585
    function transaction($action)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
5586
    {
5587
        switch ($action) {
5588
            case 'start':
5589
                // store all the data away into the checkpoint variable
5590
                $data = get_object_vars($this);
5591
                $this->checkpoint = $data;
5592
                unset($data);
5593
                break;
5594
5595
            case 'commit':
5596
                if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
5597
                    $tmp = $this->checkpoint['checkpoint'];
5598
                    $this->checkpoint = $tmp;
5599
                    unset($tmp);
5600
                } else {
5601
                    $this->checkpoint = '';
0 ignored issues
show
Documentation Bug introduced by
It seems like '' of type string is incompatible with the declared type array of property $checkpoint.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
5602
                }
5603
                break;
5604
5605 View Code Duplication
            case 'rewind':
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...
5606
                // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
5607
                if (is_array($this->checkpoint)) {
5608
                    // can only abort if were inside a checkpoint
5609
                    $tmp = $this->checkpoint;
5610
5611
                    foreach ($tmp as $k => $v) {
5612
                        if ($k !== 'checkpoint') {
5613
                            $this->$k = $v;
5614
                        }
5615
                    }
5616
                    unset($tmp);
5617
                }
5618
                break;
5619
5620 View Code Duplication
            case 'abort':
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...
5621
                if (is_array($this->checkpoint)) {
5622
                    // can only abort if were inside a checkpoint
5623
                    $tmp = $this->checkpoint;
5624
                    foreach ($tmp as $k => $v) {
5625
                        $this->$k = $v;
5626
                    }
5627
                    unset($tmp);
5628
                }
5629
                break;
5630
        }
5631
    }
5632
}
5633