Issues (4714)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Intraface/LegacyCpdf.php (112 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Create pdf documents without additional modules
4
 *
5
 * Note that the companion class Document_CezPdf can be used to extend this class and
6
 * simplify the creation of documents.
7
 *
8
 * @category Documents
9
 * @package  Document_Cpdf
10
 * @author   Wayne Munro (inactive) <[email protected]>
11
 * @author   Lars Olesen <[email protected]>
12
 * @author   Sune Jensen <[email protected]>
13
 * @author   Ole K <[email protected]>
14
 * @copyright 2007 - 2013 The authors
15
 * @license   GPL http://www.opensource.org/licenses/gpl-license.php
16
 * @version  0.11.8
17
 * @link     http://pdf-php.sf.net
18
 */
19
20
class Intraface_LegacyCpdf
21
{
22
    /**
23
     * allow the programmer to output debug messages on serveral places
24
     * 'none' = no debug output at all
25
     * 'error_log' = use error_log
26
     * 'variable' = store in a variable called $this->messages
27
     *
28
     * @default 'error_log'
29
     */
30
    public $DEBUG = 'error_log';
31
    
32
    /**
33
     * Set the debug level
34
     * E_USER_ERROR = only errors
35
     * E_USER_WARNING = errors and warning
36
     * E_USER_NOTICE =  nearly everything
37
     *
38
     * @default E_USER_WARNING
39
     */
40
    public $DEBUGLEVEL = E_USER_WARNING;
41
    /**
42
     * Reversed char string to allow arabic or Hebrew
43
     */
44
    public $rtl = false;
45
    
46
    /**
47
     * flag to validate the output and if output method has be executed
48
     */
49
    protected $valid = false;
50
    
51
    /**
52
     * global defined temporary path used on several places
53
     */
54
    public $tempPath = '/tmp';
55
    /**
56
     * the current number of pdf objects in the document
57
     *
58
     * @var integer
59
     */
60
    private $numObj=0;
61
62
    /**
63
      * this array contains all of the pdf objects, ready for final assembly
64
      *
65
      * @var array
66
      */
67
    private $objects = array();
68
69
    /**
70
     * allows object being hashed (affect images only)
71
     */
72
    public $hashed = true;
73
    /**
74
     * Object hash used to free pdf from redundacies (primary images)
75
     */
76
    private $objectHashes = array();
0 ignored issues
show
The property $objectHashes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
77
    
78
    /**
79
      * the objectId (number within the objects array) of the document catalog
80
      *
81
      * @var integer
82
      */
83
    private $catalogId;
84
85
86
    public $targetEncoding = 'iso-8859-1';
87
    /**
88
     * @var boolean Whether the text passed in should be treated as Unicode or just local character set.
89
     */
90
    public $isUnicode = false;
91
92
    /**
93
     * @var boolean used to either embed or not embed ttf/pfb fonts.
94
     */
95
    protected $embedFont = true;
96
97
    /**
98
     * store the information about the relationship between font families
99
     * this used so that the code knows which font is the bold version of another font, etc.
100
     * the value of this array is initialised in the constuctor function.
101
     *
102
     * @var array
103
     */
104
    private $fontFamilies = array(
105
            'Helvetica' => array(
106
                    'b'=>'Helvetica-Bold',
107
                    'i'=>'Helvetica-Oblique',
108
                    'bi'=>'Helvetica-BoldOblique',
109
                    'ib'=>'Helvetica-BoldOblique',
110
                ),
111
            'Courier' => array(
112
                    'b'=>'Courier-Bold',
113
                    'i'=>'Courier-Oblique',
114
                    'bi'=>'Courier-BoldOblique',
115
                    'ib'=>'Courier-BoldOblique',
116
                ),
117
            'Times-Roman' => array(
118
                    'b'=>'Times-Bold',
119
                    'i'=>'Times-Italic',
120
                    'bi'=>'Times-BoldItalic',
121
                    'ib'=>'Times-BoldItalic',
122
                )
123
    );
124
125
    /**
126
     * the core fonts to ignore them from unicode
127
     */
128
    private $coreFonts = array('courier', 'courier-bold', 'courier-oblique', 'courier-boldoblique',
129
    'helvetica', 'helvetica-bold', 'helvetica-oblique', 'helvetica-boldoblique',
130
    'times-roman', 'times-bold', 'times-italic', 'times-bolditalic',
131
    'symbol', 'zapfdingbats');
132
133
    /**
134
     * array carrying information about the fonts that the system currently knows about
135
     * used to ensure that a font is not loaded twice, among other things
136
     *
137
     * @var array
138
     */
139
    private $fonts = array();
140
141
    /**
142
      * a record of the current font
143
      *
144
      * @var string
145
      */
146
    private $currentFont='';
147
148
    /**
149
     * the current base font
150
     *
151
     * @var string
152
     */
153
    private $currentBaseFont='';
154
155
    /**
156
      * the number of the current font within the font array
157
      *
158
      * @var integer
159
      */
160
    private $currentFontNum=0;
161
162
    /**
163
     * @var integer
164
     */
165
    private $currentNode;
166
167
    /**
168
      * object number of the current page
169
      *
170
      * @var integer
171
      */
172
    private $currentPage;
173
174
    /**
175
      * object number of the currently active contents block
176
      */
177
    private $currentContents;
178
179
    /**
180
      * number of fonts within the system
181
      */
182
    private $numFonts = 0;
183
184
    /**
185
     * current colour for fill operations, defaults to inactive value, all three components should be between 0 and 1 inclusive when active
186
     */
187
    private $currentColour = array('r' => -1, 'g' => -1, 'b' => -1);
188
189
    /**
190
     * current colour for stroke operations (lines etc.)
191
     */
192
    private $currentStrokeColour = array('r' => -1, 'g' => -1, 'b' => -1);
193
194
    /**
195
      * current style that lines are drawn in
196
      */
197
    private $currentLineStyle='';
198
199
    /**
200
      * an array which is used to save the state of the document, mainly the colours and styles
201
      * it is used to temporarily change to another state, the change back to what it was before
202
      */
203
    private $stateStack = array();
204
205
    /**
206
     * number of elements within the state stack
207
     */
208
    private $nStateStack = 0;
209
210
    /**
211
     * number of page objects within the document
212
     */
213
    private $numPages=0;
214
215
    /**
216
     * object Id storage stack
217
     */
218
    private $stack=array();
219
220
    /**
221
     * number of elements within the object Id storage stack
222
     */
223
    private $nStack=0;
224
225
    /**
226
     * an array which contains information about the objects which are not firmly attached to pages
227
     * these have been added with the addObject function
228
     */
229
    private $looseObjects=array();
230
231
    /**
232
     * array contains infomation about how the loose objects are to be added to the document
233
     */
234
    private $addLooseObjects=array();
235
236
    /**
237
      * the objectId of the information object for the document
238
      * this contains authorship, title etc.
239
      */
240
    private $infoObject=0;
241
242
    /**
243
      * number of images being tracked within the document
244
      */
245
    private $numImages=0;
246
247
    /**
248
      * some additional options while generation
249
      * currently used for compression only
250
      * Default: 'compression' => -1 which will set gzcompress to the default level of 6
251
      */
252
    public $options=array('compression'=>-1);
253
254
    /**
255
      * the objectId of the first page of the document
256
      */
257
    private $firstPageId;
258
259
    /**
260
      * used to track the last used value of the inter-word spacing, this is so that it is known
261
      * when the spacing is changed.
262
      */
263
    private $wordSpaceAdjust=0;
264
265
    /**
266
      * track if the current font is bolded or italicised
267
      */
268
    private $currentTextState = '';
269
270
    /**
271
     * messages are stored here during processing, these can be selected afterwards to give some useful debug information
272
     */
273
    public $messages='';
274
275
    /**
276
     * the ancryption array for the document encryption is stored here
277
     */
278
    private $arc4='';
279
280
    /**
281
     * the object Id of the encryption information
282
     */
283
    private $arc4_objnum=0;
284
285
    /**
286
     * the file identifier, used to uniquely identify a pdf document
287
     */
288
    public $fileIdentifier='';
289
290
    /**
291
     * a flag to say if a document is to be encrypted or not
292
     *
293
     * @var boolean
294
     */
295
    private $encrypted=0;
296
297
    /**
298
     * Set the encryption mode
299
     * 1 = RC40bit
300
     * 2 = RC128bit (since PDF Version 1.4)
301
     */
302
    private $encryptionMode = 1;
303
    /**
304
     * the encryption key for the encryption of all the document content (structure is not encrypted)
305
     *
306
     * @var string
307
     */
308
    private $encryptionKey='';
309
    
310
    /*
311
     * encryption padding fetched from the Adobe PDF reference
312
     */
313
    private $encryptionPad;
314
    
315
    /**
316
     * array which forms a stack to keep track of nested callback functions
317
     *
318
     * @var array
319
     */
320
    private $callback = array();
321
322
    /**
323
     * the number of callback functions in the callback array
324
     *
325
     * @var integer
326
     */
327
    private $nCallback = 0;
328
329
    /**
330
     * store label->id pairs for named destinations, these will be used to replace internal links
331
     * done this way so that destinations can be defined after the location that links to them
332
     *
333
     * @var array
334
     */
335
    private $destinations = array();
336
337
    /**
338
     * store the stack for the transaction commands, each item in here is a record of the values of all the
339
     * variables within the class, so that the user can rollback at will (from each 'start' command)
340
     * note that this includes the objects array, so these can be large.
341
     *
342
     * @var string
343
     */
344
    private $checkpoint = '';
345
346
    /**
347
     * Constructor - starts a new document
348
     *
349
     * @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
350
     *
351
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
352
     */
353 3
    public function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false)
354
    {
355 3
        $this->isUnicode = $isUnicode;
356
        // set the hardcoded encryption pad
357 3
        $this->encryptionPad = chr(0x28).chr(0xBF).chr(0x4E).chr(0x5E).chr(0x4E).chr(0x75).chr(0x8A).chr(0x41).chr(0x64).chr(0x00).chr(0x4E).chr(0x56).chr(0xFF).chr(0xFA).chr(0x01).chr(0x08).chr(0x2E).chr(0x2E).chr(0x00).chr(0xB6).chr(0xD0).chr(0x68).chr(0x3E).chr(0x80).chr(0x2F).chr(0x0C).chr(0xA9).chr(0xFE).chr(0x64).chr(0x53).chr(0x69).chr(0x7A);
358
        
359 3
        $this->newDocument($pageSize);
360
361 3
        if (in_array('Windows-1252', mb_list_encodings())) {
362 3
              $this->targetEncoding = 'Windows-1252';
363 3
        }
364
    
365
        // font familys are already known in $this->fontFamilies
366 3
        $this->fileIdentifier = md5('ROSPDF');
367 3
    }
368
369
    /**
370
     * Document object methods (internal use only)
371
     *
372
     * There is about one object method for each type of object in the pdf document
373
     * Each function has the same call list ($id,$action,$options).
374
     * $id = the object ID of the object, or what it is to be if it is being created
375
     * $action = a string specifying the action to be performed, though ALL must support:
376
     *           'new' - create the object with the id $id
377
     *           'out' - produce the output for the pdf object
378
     * $options = optional, a string or array containing the various parameters for the object
379
     *
380
     * These, in conjunction with the output function are the ONLY way for output to be produced
381
     * within the pdf 'file'.
382
     */
383
384
    /**
385
     * destination object, used to specify the location for the user to jump to, presently on opening
386
     * @access private
387
     */
388
    private function o_destination($id, $action, $options = '')
389
    {
390
        if ($action!='new') {
391
            $o =& $this->objects[$id];
392
        }
393
        switch ($action) {
394
            case 'new':
395
                 $this->objects[$id]=array('t'=>'destination','info'=>array());
396
                 $tmp = '';
397
                switch ($options['type']) {
398
                    case 'XYZ':
399
                    case 'FitR':
400
                        $tmp =  ' '.$options['p3'].$tmp;
401
                    case 'FitH':
402
                    case 'FitV':
403
                    case 'FitBH':
404
                    case 'FitBV':
405
                        $tmp =  ' '.$options['p1'].' '.$options['p2'].$tmp;
406
                    case 'Fit':
407
                    case 'FitB':
408
                        $tmp =  $options['type'].$tmp;
409
                        $this->objects[$id]['info']['string']=$tmp;
410
                        $this->objects[$id]['info']['page']=$options['page'];
411
                }
412
                break;
413
            case 'out':
414
                $tmp = $o['info'];
0 ignored issues
show
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...
415
                $res="\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj";
416
                return $res;
417
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
418
        }
419
    }
420
421
    /**
422
     * sets the viewer preferences
423
     * @access private
424
     */
425
    private function o_viewerPreferences($id, $action, $options = '')
426
    {
427
        if ($action!='new') {
428
            $o =& $this->objects[$id];
429
        }
430
        switch ($action) {
431
            case 'new':
432
                $this->objects[$id]=array('t'=>'viewerPreferences','info'=>array());
433
                break;
434
            case 'add':
435
                foreach ($options as $k => $v) {
0 ignored issues
show
The expression $options of type string is not traversable.
Loading history...
436
                    switch ($k) {
437
                        case 'HideToolbar':
438
                        case 'HideMenubar':
439
                        case 'HideWindowUI':
440
                        case 'FitWindow':
441
                        case 'CenterWindow':
442
                        case 'DisplayDocTitle':
443
                        case 'NonFullScreenPageMode':
444
                        case 'Direction':
445
                            $o['info'][$k]=$v;
0 ignored issues
show
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...
446
                            break;
447
                    }
448
                }
449
                break;
450
            case 'out':
451
                $res="\n".$id." 0 obj\n".'<< ';
452
                foreach ($o['info'] as $k => $v) {
453
                    $res.="\n/".$k.' '.$v;
454
                }
455
                $res.="\n>>\n";
456
                return $res;
457
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
458
        }
459
    }
460
461
    /**
462
     * define the document catalog, the overall controller for the document
463
     * @access private
464
     */
465 3
    private function o_catalog($id, $action, $options = '')
466
    {
467 3
        if ($action!='new') {
468 3
            $o =& $this->objects[$id];
469 3
        }
470
        switch ($action) {
471 3
            case 'new':
472 3
                $this->objects[$id]=array('t'=>'catalog','info'=>array());
473 3
                $this->catalogId=$id;
474 3
                break;
475 3
            case 'outlines':
476 3
            case 'pages':
477 3
            case 'openHere':
478 3
                $o['info'][$action]=$options;
0 ignored issues
show
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...
479 3
                break;
480
            case 'viewerPreferences':
481
                if (!isset($o['info']['viewerPreferences'])) {
482
                    $this->numObj++;
483
                    $this->o_viewerPreferences($this->numObj, 'new');
484
                    $o['info']['viewerPreferences']=$this->numObj;
485
                }
486
                $vp = $o['info']['viewerPreferences'];
487
                $this->o_viewerPreferences($vp, 'add', $options);
488
                break;
489
            case 'out':
490
                $res="\n".$id." 0 obj\n".'<< /Type /Catalog';
491
                foreach ($o['info'] as $k => $v) {
492
                    switch ($k) {
493
                        case 'outlines':
494
                            $res.=' /Outlines '.$v.' 0 R';
495
                            break;
496
                        case 'pages':
497
                            $res.=' /Pages '.$v.' 0 R';
498
                            break;
499
                        case 'viewerPreferences':
500
                            $res.=' /ViewerPreferences '.$o['info']['viewerPreferences'].' 0 R';
501
                            break;
502
                        case 'openHere':
503
                            $res.=' /OpenAction '.$o['info']['openHere'].' 0 R';
504
                            break;
505
                    }
506
                }
507
                $res.=" >>\nendobj";
508
                return $res;
509
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
510
        }
511 3
    }
512
513
    /**
514
     * object which is a parent to the pages in the document
515
     * @access private
516
     */
517 3
    private function o_pages($id, $action, $options = '')
518
    {
519 3
        if ($action!='new') {
520 3
            $o =& $this->objects[$id];
521 3
        }
522
        switch ($action) {
523 3 View Code Duplication
            case 'new':
524 3
                $this->objects[$id]=array('t'=>'pages','info'=>array());
525 3
                $this->o_catalog($this->catalogId, 'pages', $id);
526 3
                break;
527 3
            case 'page':
528 3
                if (!is_array($options)) {
529
                    // then it will just be the id of the new page
530 3
                    $o['info']['pages'][]=$options;
0 ignored issues
show
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...
531 3
                } else {
532
                    // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
533
                    // and pos is either 'before' or 'after', saying where this page will fit.
534
                    if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
535
                        $i = array_search($options['rid'], $o['info']['pages']);
536
                        if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i]==$options['rid']) {
537
                            // then there is a match make a space
538
                            switch ($options['pos']) {
539
                                case 'before':
540
                                    $k = $i;
541
                                    break;
542
                                case 'after':
543
                                    $k=$i+1;
544
                                    break;
545
                                default:
546
                                    $k=-1;
547
                                    break;
548
                            }
549
                            if ($k>=0) {
550
                                for ($j=count($o['info']['pages'])-1; $j>=$k; $j--) {
551
                                    $o['info']['pages'][$j+1]=$o['info']['pages'][$j];
552
                                }
553
                                $o['info']['pages'][$k]=$options['id'];
554
                            }
555
                        }
556
                    }
557
                }
558 3
                break;
559 3
            case 'procset':
560 3
                $o['info']['procset']=$options;
561 3
                break;
562 3
            case 'mediaBox':
563 3
                $o['info']['mediaBox']=$options; // which should be an array of 4 numbers
564 3
                break;
565 3 View Code Duplication
            case 'font':
566 3
                $o['info']['fonts'][]=array('objNum'=>$options['objNum'],'fontNum'=>$options['fontNum']);
567 3
                break;
568 View Code Duplication
            case 'xObject':
569
                $o['info']['xObjects'][]=array('objNum'=>$options['objNum'],'label'=>$options['label']);
570
                break;
571
            case 'out':
572
                if (count($o['info']['pages'])) {
573
                    $res="\n".$id." 0 obj\n<< /Type /Pages /Kids [";
574
                    foreach ($o['info']['pages'] as $k => $v) {
575
                        $res.=$v." 0 R ";
576
                    }
577
                    $res.="] /Count ".count($this->objects[$id]['info']['pages']);
578
                    if ((isset($o['info']['fonts']) && count($o['info']['fonts'])) || isset($o['info']['procset'])) {
579
                        $res.=" /Resources <<";
580
                        if (isset($o['info']['procset'])) {
581
                            $res.=" /ProcSet ".$o['info']['procset'];
582
                        }
583 View Code Duplication
                        if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
584
                            $res.=" /Font << ";
585
                            foreach ($o['info']['fonts'] as $finfo) {
586
                                $res.=" /F".$finfo['fontNum']." ".$finfo['objNum']." 0 R";
587
                            }
588
                            $res.=" >>";
589
                        }
590 View Code Duplication
                        if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
591
                            $res.=" /XObject << ";
592
                            foreach ($o['info']['xObjects'] as $finfo) {
593
                                $res.=" /".$finfo['label']." ".$finfo['objNum']." 0 R";
594
                            }
595
                            $res.=" >>";
596
                        }
597
                        $res.=" >>";
598
                        if (isset($o['info']['mediaBox'])) {
599
                            $tmp=$o['info']['mediaBox'];
600
                            $res.=" /MediaBox [".sprintf('%.3F', $tmp[0]).' '.sprintf('%.3F', $tmp[1]).' '.sprintf('%.3F', $tmp[2]).' '.sprintf('%.3F', $tmp[3]).']';
601
                        }
602
                    }
603
                    $res.=" >>\nendobj";
604
                } else {
605
                    $res="\n".$id." 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
606
                }
607
                return $res;
608
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
609
        }
610 3
    }
611
612
    /**
613
     * Beta Redirection function
614
     * @access private
615
     */
616
    private function o_redirect($id, $action, $options = '')
617
    {
618
        switch ($action) {
619
            case 'new':
620
                $this->objects[$id]=array('t'=>'redirect','data'=>$options['data'],'info'=>array());
621
                $this->o_pages($this->currentNode, 'xObject', array('label'=>$options['label'],'objNum'=>$id));
0 ignored issues
show
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...
622
                break;
623
            case 'out':
624
                $o =& $this->objects[$id];
625
                $tmp=$o['data'];
0 ignored issues
show
$tmp 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...
626
                $res= "\n".$id." 0 obj\n<<";
627
                $res.="/R".$o['data']." ".$o['data']." 0 R>>\nendobj";
628
                return $res;
629
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
630
        }
631
    }
632
633
    /**
634
     * defines the outlines in the doc, empty for now
635
     * @access private
636
     */
637 3
    private function o_outlines($id, $action, $options = '')
638
    {
639 3
        if ($action!='new') {
640
            $o =& $this->objects[$id];
641
        }
642
        switch ($action) {
643 3 View Code Duplication
            case 'new':
644 3
                $this->objects[$id]=array('t'=>'outlines','info'=>array('outlines'=>array()));
645 3
                $this->o_catalog($this->catalogId, 'outlines', $id);
646 3
                break;
647
            case 'outline':
648
                $o['info']['outlines'][]=$options;
0 ignored issues
show
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...
649
                break;
650
            case 'out':
651
                if (count($o['info']['outlines'])) {
652
                    $res="\n".$id." 0 obj\n<< /Type /Outlines /Kids [";
653
                    foreach ($o['info']['outlines'] as $k => $v) {
654
                        $res.=$v." 0 R ";
655
                    }
656
                    $res.="] /Count ".count($o['info']['outlines'])." >>\nendobj";
657
                } else {
658
                    $res="\n".$id." 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
659
                }
660
                return $res;
661
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
662
        }
663 3
    }
664
665
    /**
666
     * an object to hold the font description
667
     * @access private
668
     */
669 3
    private function o_font($id, $action, $options = '')
670
    {
671 3
        if ($action!='new') {
672
            $o =& $this->objects[$id];
673
        }
674
        switch ($action) {
675 3
            case 'new':
676 3
                $this->objects[$id]=array('t'=>'font','info'=>array('name'=>$options['name'], 'fontFileName' => $options['fontFileName'],'SubType'=>'Type1'));
677 3
                $fontNum=$this->numFonts;
678 3
                $this->objects[$id]['info']['fontNum']=$fontNum;
679
                // deal with the encoding and the differences
680 3
                if (isset($options['differences'])) {
681
                    // then we'll need an encoding dictionary
682 3
                    $this->numObj++;
683 3
                    $this->o_fontEncoding($this->numObj, 'new', $options);
684 3
                    $this->objects[$id]['info']['encodingDictionary']=$this->numObj;
685 3
                } elseif (isset($options['encoding'])) {
686
                    // we can specify encoding here
687
                    switch ($options['encoding']) {
688
                        case 'WinAnsiEncoding':
689
                        case 'MacRomanEncoding':
690
                        case 'MacExpertEncoding':
691
                            $this->objects[$id]['info']['encoding']=$options['encoding'];
692
                            break;
693
                        case 'none':
694
                            break;
695
                        default:
696
                            $this->objects[$id]['info']['encoding']='WinAnsiEncoding';
697
                            break;
698
                    }
699
                } else {
700
                    $this->objects[$id]['info']['encoding']='WinAnsiEncoding';
701
                }
702
                
703 3
                if ($this->fonts[$options['fontFileName']]['isUnicode']) {
704
                    // For Unicode fonts, we need to incorporate font data into
705
                    // sub-sections that are linked from the primary font section.
706
                    // Look at o_fontGIDtoCID and o_fontDescendentCID functions
707
                    // for more informaiton.
708
                    //
709
                    // All of this code is adapted from the excellent changes made to
710
                    // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
711
                    $toUnicodeId = ++$this->numObj;
712
                    $this->o_contents($toUnicodeId, 'new', 'raw');
713
                    $this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
714
715
                    $stream = "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo <</Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n1 beginbfrange\n<0000> <FFFF> <0000>\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n";
716
717
                    $res = "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
718
                    $res .= "stream\n" . $stream . "\nendstream";
719
720
                    $this->objects[$toUnicodeId]['c'] = $res;
721
722
                    $cidFontId = ++$this->numObj;
723
                    $this->o_fontDescendentCID($cidFontId, 'new', $options);
724
                    $this->objects[$id]['info']['cidFont'] = $cidFontId;
725
                }
726
                // also tell the pages node about the new font
727 3
                $this->o_pages($this->currentNode, 'font', array('fontNum'=>$fontNum,'objNum'=>$id));
0 ignored issues
show
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...
728 3
                break;
729
            case 'add':
730
                foreach ($options as $k => $v) {
0 ignored issues
show
The expression $options of type string is not traversable.
Loading history...
731
                    switch ($k) {
732
                        case 'BaseFont':
733
                            $o['info']['name'] = $v;
0 ignored issues
show
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...
734
                            break;
735
                        case 'FirstChar':
736
                        case 'LastChar':
737
                        case 'Widths':
738
                        case 'FontDescriptor':
739
                        case 'SubType':
740
                            $this->debug('o_font '.$k." : ".$v, E_USER_NOTICE);
741
                            $o['info'][$k] = $v;
742
                            break;
743
                    }
744
                }
745
                
746
                // pass values down to descendent font
747
                if (isset($o['info']['cidFont'])) {
748
                    $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
749
                }
750
                break;
751
            case 'out':
752
                // when font program is embedded and its not a coreFont, attach the font either as subset or completely
753
                if ($this->embedFont && !in_array(strtolower($o['info']['name']), $this->coreFonts)) {
754
                    // when TrueType font is used
755
                    if (isset($this->objects[$o['info']['FontDescriptor']]['info']['FontFile2'])) {
756
                        // find font program id for TTF fonts (FontFile2)
757
                        $pfbid = $this->objects[$o['info']['FontDescriptor']]['info']['FontFile2'];
758
                        // if subsetting is set
759
                        if ($this->fonts[$o['info']['fontFileName']]['isSubset'] && !empty($this->fonts[$o['info']['fontFileName']]['subset'])) {
760
                            $this->debug('subset font for ' . $o['info']['fontFileName'], E_USER_NOTICE);
761
                            $subsetFontName = "AAAAAD+" . $o['info']['name'];
762
                            $o['info']['name'] = $subsetFontName;
763
                            // find descendant font
764
                            $this->objects[$o['info']['cidFont']]['info']['name'] = $subsetFontName;
765
                            // find font descriptor
766
                            $this->objects[$o['info']['FontDescriptor']]['info']['FontName'] = $subsetFontName;
767
                            
768
                            // use TTF subset script from http://www.4real.gr/technical-documents-ttf-subset.html
769
                            $t = new TTFsubset();
770
                            // combine all used characters as string
771
                            $s = implode('', array_keys($this->fonts[$o['info']['fontFileName']]['subset']));
772
                            // submit the string to TTFsubset class to return the subset (as binary)
773
                            $data = $t->doSubset($o['info']['fontFileName'] . '.ttf', $s, null);
774
                            // $data is the new (subset) of the font font
775
                            //file_put_contents($o['info']['name'] . '.ttf', $data);
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...
776
                            
777
                            $newcidwidth = array();
778
                            $cidwidth = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
779
                            foreach ($t->TTFchars as $TTFchar) {
780
                                if (!empty($TTFchar->charCode) && isset($cidwidth[$TTFchar->charCode])) {
781
                                    $newcidwidth[$TTFchar->charCode] = $cidwidth[$TTFchar->charCode];
782
                                }
783
                            }
784
                            $cidwidth = $newcidwidth;
785
                        } else {
786
                            $data = file_get_contents($o['info']['fontFileName']. '.ttf');
787
                        }
788
                    
789
                        // TODO: cache the subset
790
                        
791
                        $l1 = strlen($data);
792
                        $this->objects[$pfbid]['c'].= $data;
793
                        $this->o_contents($pfbid, 'add', array('Length1'=>$l1));
0 ignored issues
show
array('Length1' => $l1) is of type array<string,integer,{"Length1":"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...
794
                    } elseif (isset($this->objects[$o['info']['FontDescriptor']]['info']['FontFile'])) {
795
                        // find FontFile id - used for PFB fonts
796
                        $pfbid = $this->objects[$o['info']['FontDescriptor']]['info']['FontFile'];
797
                        $data = file_get_contents($o['info']['fontFileName']. '.pfb');
798
                        $l1 = strpos($data, 'eexec')+6;
799
                        $l2 = strpos($data, '00000000')-$l1;
800
                        $l3 = strlen($data)-$l2-$l1;
801
                        $this->o_contents($pfbid, 'add', array('Length1'=>$l1,'Length2'=>$l2,'Length3'=>$l3));
0 ignored issues
show
array('Length1' => $l1, ... $l2, 'Length3' => $l3) is of type array<string,integer,{"L...","Length3":"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...
802
                    } else {
803
                        $this->debug("Failed to select the correct font program", E_USER_WARNING);
804
                    }
805
                }
806
                
807
                if ($this->fonts[$o['info']['fontFileName']]['isUnicode']) {
808
                    // For Unicode fonts, we need to incorporate font data into
809
                    // sub-sections that are linked from the primary font section.
810
                    // Look at o_fontGIDtoCID and o_fontDescendentCID functions
811
                    // for more informaiton.
812
                    //
813
                    // All of this code is adapted from the excellent changes made to
814
                    // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
815
816
                    $res = "\n$id 0 obj\n<</Type /Font /Subtype /Type0 /BaseFont /".$o['info']['name']."";
817
                    // The horizontal identity mapping for 2-byte CIDs; may be used
818
                    // with CIDFonts using any Registry, Ordering, and Supplement values.
819
                       $res.= " /Encoding /Identity-H /DescendantFonts [".$o['info']['cidFont']." 0 R] /ToUnicode ".$o['info']['toUnicode']." 0 R >>\n";
820
                    $res.= "endobj";
821
                } else {
822
                    $res="\n".$id." 0 obj\n<< /Type /Font /Subtype /".$o['info']['SubType']." ";
823
                    $res.="/Name /F".$o['info']['fontNum']." ";
824
                    $res.="/BaseFont /".$o['info']['name']." ";
825
                    if (isset($o['info']['encodingDictionary'])) {
826
                        // then place a reference to the dictionary
827
                        $res.="/Encoding ".$o['info']['encodingDictionary']." 0 R ";
828 View Code Duplication
                    } elseif (isset($o['info']['encoding'])) {
829
                        // use the specified encoding
830
                        $res.="/Encoding /".$o['info']['encoding']." ";
831
                    }
832
                    if (isset($o['info']['FirstChar'])) {
833
                        $res.="/FirstChar ".$o['info']['FirstChar']." ";
834
                    }
835
                    if (isset($o['info']['LastChar'])) {
836
                        $res.="/LastChar ".$o['info']['LastChar']." ";
837
                    }
838
                    if (isset($o['info']['Widths'])) {
839
                        $res.="/Widths ".$o['info']['Widths']." 0 R ";
840
                    }
841 View Code Duplication
                    if (isset($o['info']['FontDescriptor'])) {
842
                        $res.="/FontDescriptor ".$o['info']['FontDescriptor']." 0 R ";
843
                    }
844
                    $res.=">>\nendobj";
845
                }
846
                return $res;
847
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
848
        }
849 3
    }
850
851
    /**
852
     * a font descriptor, needed for including additional fonts
853
     * @access private
854
     */
855
    private function o_fontDescriptor($id, $action, $options = '')
856
    {
857
        if ($action!='new') {
858
            $o =& $this->objects[$id];
859
        }
860
        switch ($action) {
861
            case 'new':
862
                $this->objects[$id]=array('t'=>'fontDescriptor','info'=>$options);
863
                break;
864
            case 'out':
865
                $res="\n".$id." 0 obj\n<< /Type /FontDescriptor ";
866
                foreach ($o['info'] as $label => $value) {
0 ignored issues
show
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...
867
                    switch ($label) {
868
                        case 'Ascent':
869
                        case 'CapHeight':
870
                        case 'Descent':
871
                        case 'Flags':
872
                        case 'ItalicAngle':
873
                        case 'StemV':
874
                        case 'AvgWidth':
875
                        case 'Leading':
876
                        case 'MaxWidth':
877
                        case 'MissingWidth':
878
                        case 'StemH':
879
                        case 'XHeight':
880
                        case 'CharSet':
881
                            if (strlen($value)) {
882
                                $res.='/'.$label.' '.$value." ";
883
                            }
884
                            break;
885
                        case 'FontFile':
886
                        case 'FontFile2':
887
                        case 'FontFile3':
888
                            $res.='/'.$label.' '.$value." 0 R ";
889
                            break;
890
                        case 'FontBBox':
891
                            $res.='/'.$label.' ['.$value[0].' '.$value[1].' '.$value[2].' '.$value[3]."] ";
892
                            break;
893
                        case 'FontName':
894
                            $res.='/'.$label.' /'.$value." ";
895
                            break;
896
                    }
897
                }
898
                $res.=">>\nendobj";
899
                return $res;
900
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
901
        }
902
    }
903
904
    /**
905
     * the font encoding
906
     * @access private
907
     */
908 3
    private function o_fontEncoding($id, $action, $options = '')
909
    {
910 3
        if ($action!='new') {
911
            $o =& $this->objects[$id];
912
        }
913
        switch ($action) {
914 3
            case 'new':
915
                // the options array should contain 'differences' and maybe 'encoding'
916 3
                $this->objects[$id]=array('t'=>'fontEncoding','info'=>$options);
917 3
                break;
918
            case 'out':
919
                $res="\n".$id." 0 obj\n<< /Type /Encoding ";
920
                if (!isset($o['info']['encoding'])) {
921
                    $o['info']['encoding']='WinAnsiEncoding';
0 ignored issues
show
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...
922
                }
923 View Code Duplication
                if ($o['info']['encoding']!='none') {
924
                    $res.="/BaseEncoding /".$o['info']['encoding']." ";
925
                }
926
                $res.="/Differences [";
927
                $onum=-100;
928
                foreach ($o['info']['differences'] as $num => $label) {
929
                    if ($num!=$onum+1) {
930
                        // we cannot make use of consecutive numbering
931
                        $res.= " ".$num." /".$label;
932
                    } else {
933
                        $res.= " /".$label;
934
                    }
935
                    $onum=$num;
936
                }
937
                $res.="] >>\nendobj";
938
                return $res;
939
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
940
        }
941 3
    }
942
943
    /**
944
     * a descendent cid font, needed for unicode fonts
945
     * @access private
946
     */
947
    private function o_fontDescendentCID($id, $action, $options = '')
948
    {
949
        if ($action !== 'new') {
950
            $o = & $this->objects[$id];
951
        }
952
953
        switch ($action) {
954
            case 'new':
955
                  $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options);
956
                  // and a CID to GID map
957
                if ($this->embedFont) {
958
                    $cidToGidMapId = ++$this->numObj;
959
                    $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
960
                    $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
961
                }
962
                break;
963
964
            case 'add':
965
                foreach ($options as $k => $v) {
0 ignored issues
show
The expression $options of type string is not traversable.
Loading history...
966
                    switch ($k) {
967
                        case 'BaseFont':
968
                              $o['info']['name'] = $v;
0 ignored issues
show
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...
969
                            break;
970
971
                        case 'FirstChar':
972
                        case 'LastChar':
973
                        case 'MissingWidth':
974
                        case 'FontDescriptor':
975
                        case 'SubType':
976
                              $this->debug("o_fontDescendentCID $k : $v", E_USER_NOTICE);
977
                              $o['info'][$k] = $v;
978
                            break;
979
                    }
980
                }
981
982
                  // pass values down to cid to gid map
983
                if ($this->embedFont) {
984
                      $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
985
                }
986
                break;
987
988
            case 'out':
989
                  $res = "\n$id 0 obj\n";
990
                  $res.= "<</Type /Font /Subtype /CIDFontType2 /BaseFont /".$o['info']['name']." /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>";
991 View Code Duplication
                if (isset($o['info']['FontDescriptor'])) {
992
                    $res.= " /FontDescriptor ".$o['info']['FontDescriptor']." 0 R";
993
                }
994
995
                if (isset($o['info']['MissingWidth'])) {
996
                    $res.= " /DW ".$o['info']['MissingWidth']."";
997
                }
998
999
                if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
1000
                    $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
1001
                    $w = '';
1002
                    foreach ($cid_widths as $cid => $width) {
1003
                        $w .= "$cid [$width] ";
1004
                    }
1005
                    $res.= " /W [$w]";
1006
                }
1007
          
1008
                if ($this->embedFont) {
1009
                      $res.= " /CIDToGIDMap ".$o['info']['cidToGidMap']." 0 R";
1010
                }
1011
                  $res.= "  >>\n";
1012
                  $res.= "endobj";
1013
1014
                return $res;
1015
        }
1016
    }
1017
1018
    /**
1019
      * a font glyph to character map, needed for unicode fonts
1020
      * @access private
1021
      */
1022
    private function o_fontGIDtoCIDMap($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' => 'fontGIDtoCIDMap', 'info' => $options);
1031
                break;
1032
1033
            case 'out':
1034
                  $res = "\n$id 0 obj\n";
1035
                  $fontFileName = $o['info']['fontFileName'];
0 ignored issues
show
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
                  $tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
1037
          
1038
                if (isset($o['raw'])) {
1039
                    $res.= $tmp;
1040
                } else {
1041
                    $res.= "<<";
1042 View Code Duplication
                    if (function_exists('gzcompress') && $this->options['compression']) {
1043
                        // then implement ZLIB based compression on this content stream
1044
                        $tmp = gzcompress($tmp, $this->options['compression']);
1045
                        $res.= " /Filter /FlateDecode";
1046
                    }
1047
            
1048
                    $res.= " /Length ".mb_strlen($tmp, '8bit') .">>\nstream\n$tmp\nendstream";
1049
                }
1050
1051
                  $res.= "\nendobj";
1052
                return $res;
1053
        }
1054
    }
1055
1056
    /**
1057
     * define the document information
1058
     * @access private
1059
     */
1060 3
    private function o_info($id, $action, $options = '')
1061
    {
1062 3
        if ($action!='new') {
1063
            $o =& $this->objects[$id];
1064
        }
1065
        switch ($action) {
1066 3
            case 'new':
1067 3
                $this->infoObject=$id;
1068 3
                $date='D:'.date('Ymd');
1069 3
                $this->objects[$id]=array('t'=>'info','info'=>array('Creator'=>'R and OS php pdf writer, http://www.ros.co.nz','CreationDate'=>$date));
1070 3
                break;
1071
            case 'Title':
1072
            case 'Author':
1073
            case 'Subject':
1074
            case 'Keywords':
1075
            case 'Creator':
1076
            case 'Producer':
1077
            case 'CreationDate':
1078
            case 'ModDate':
1079
            case 'Trapped':
1080
                $o['info'][$action]=$options;
0 ignored issues
show
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...
1081
                break;
1082
            case 'out':
1083
                if ($this->encrypted) {
1084
                    $this->encryptInit($id);
1085
                }
1086
                $res="\n".$id." 0 obj\n<< ";
1087
                foreach ($o['info'] as $k => $v) {
1088
                    $res.='/'.$k.' (';
1089 View Code Duplication
                    if ($this->encrypted) {
1090
                        $res.=$this->filterText($this->ARC4($v), true, false);
1091
                    } else {
1092
                        $res.=$this->filterText($v, true, false);
1093
                    }
1094
                    $res.=") ";
1095
                }
1096
                $res.=">>\nendobj";
1097
                return $res;
1098
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1099
        }
1100 3
    }
1101
1102
    /**
1103
     * an action object, used to link to URLS initially
1104
     * @access private
1105
     */
1106
    private function o_action($id, $action, $options = '')
1107
    {
1108
        if ($action!='new') {
1109
            $o =& $this->objects[$id];
1110
        }
1111
        switch ($action) {
1112
            case 'new':
1113
                if (is_array($options)) {
1114
                    $this->objects[$id]=array('t'=>'action','info'=>$options,'type'=>$options['type']);
1115
                } else {
1116
                    // then assume a URI action
1117
                    $this->objects[$id]=array('t'=>'action','info'=>$options,'type'=>'URI');
1118
                }
1119
                break;
1120
            case 'out':
1121
                if ($this->encrypted) {
1122
                    $this->encryptInit($id);
1123
                }
1124
                $res="\n".$id." 0 obj\n<< /Type /Action";
1125
                switch ($o['type']) {
0 ignored issues
show
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...
1126
                    case 'ilink':
1127
                        // there will be an 'label' setting, this is the name of the destination
1128
                        $res.=" /S /GoTo /D ".$this->destinations[(string)$o['info']['label']]." 0 R";
1129
                        break;
1130
                    case 'URI':
1131
                        $res.=" /S /URI /URI (";
1132 View Code Duplication
                        if ($this->encrypted) {
1133
                            $res.=$this->filterText($this->ARC4($o['info']), true, false);
1134
                        } else {
1135
                            $res.=$this->filterText($o['info'], true, false);
1136
                        }
1137
                            $res.=")";
1138
                        break;
1139
                }
1140
                $res.=" >>\nendobj";
1141
                return $res;
1142
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1143
        }
1144
    }
1145
1146
    /**
1147
     * an annotation object, this will add an annotation to the current page.
1148
     * initially will support just link annotations
1149
     * @access private
1150
     */
1151
    private function o_annotation($id, $action, $options = '')
1152
    {
1153
        if ($action!='new') {
1154
            $o =& $this->objects[$id];
1155
        }
1156
        switch ($action) {
1157
            case 'new':
1158
                // add the annotation to the current page
1159
                $pageId = $this->currentPage;
1160
                $this->o_page($pageId, 'annot', $id);
1161
                // and add the action object which is going to be required
1162
                switch ($options['type']) {
1163
                    case 'link':
1164
                        $this->objects[$id]=array('t'=>'annotation','info'=>$options);
1165
                        $this->numObj++;
1166
                        $this->o_action($this->numObj, 'new', $options['url']);
1167
                        $this->objects[$id]['info']['actionId']=$this->numObj;
1168
                        break;
1169
                    case 'ilink':
1170
                        // this is to a named internal link
1171
                        $label = $options['label'];
1172
                        $this->objects[$id]=array('t'=>'annotation','info'=>$options);
1173
                        $this->numObj++;
1174
                        $this->o_action($this->numObj, 'new', array('type'=>'ilink','label'=>$label));
0 ignored issues
show
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...
1175
                        $this->objects[$id]['info']['actionId']=$this->numObj;
1176
                        break;
1177
                }
1178
                break;
1179
            case 'out':
1180
                $res="\n".$id." 0 obj << /Type /Annot";
1181
                switch ($o['info']['type']) {
0 ignored issues
show
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...
1182
                    case 'link':
1183
                    case 'ilink':
1184
                        $res.= " /Subtype /Link";
1185
                        break;
1186
                }
1187
                $res.=" /A ".$o['info']['actionId']." 0 R";
1188
                $res.=" /Border [0 0 0]";
1189
                $res.=" /H /I";
1190
                $res.=" /Rect [ ";
1191
                foreach ($o['info']['rect'] as $v) {
1192
                    $res.= sprintf("%.4f ", $v);
1193
                }
1194
                $res.="]";
1195
                $res.=" >>\nendobj";
1196
                return $res;
1197
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1198
        }
1199
    }
1200
1201
    /**
1202
     * a page object, it also creates a contents object to hold its contents
1203
     * @access private
1204
     */
1205 3
    private function o_page($id, $action, $options = '')
1206
    {
1207 3
        if ($action!='new') {
1208
            $o =& $this->objects[$id];
1209
        }
1210
        switch ($action) {
1211 3
            case 'new':
1212 3
                $this->numPages++;
1213 3
                $this->objects[$id]=array('t'=>'page','info'=>array('parent'=>$this->currentNode,'pageNum'=>$this->numPages));
1214 3
                if (is_array($options)) {
1215
                    // then this must be a page insertion, array shoudl contain 'rid','pos'=[before|after]
1216
                    $options['id']=$id;
1217
                    $this->o_pages($this->currentNode, 'page', $options);
0 ignored issues
show
$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...
1218
                } else {
1219 3
                    $this->o_pages($this->currentNode, 'page', $id);
1220
                }
1221 3
                    $this->currentPage=$id;
1222
                    // make a contents object to go with this page
1223 3
                    $this->numObj++;
1224 3
                    $this->o_contents($this->numObj, 'new', $id);
1225 3
                    $this->currentContents=$this->numObj;
1226 3
                    $this->objects[$id]['info']['contents']=array();
1227 3
                    $this->objects[$id]['info']['contents'][]=$this->numObj;
1228 3
                    $match = ($this->numPages%2 ? 'odd' : 'even');
1229 3
                foreach ($this->addLooseObjects as $oId => $target) {
1230
                    if ($target=='all' || $match==$target) {
1231
                        $this->objects[$id]['info']['contents'][]=$oId;
1232
                    }
1233 3
                }
1234 3
                break;
1235
            case 'content':
1236
                $o['info']['contents'][]=$options;
0 ignored issues
show
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...
1237
                break;
1238
            case 'annot':
1239
                // add an annotation to this page
1240
                if (!isset($o['info']['annot'])) {
1241
                    $o['info']['annot']=array();
1242
                }
1243
                // $options should contain the id of the annotation dictionary
1244
                $o['info']['annot'][]=$options;
1245
                break;
1246
            case 'out':
1247
                $res="\n".$id." 0 obj\n<< /Type /Page";
1248
                $res.=" /Parent ".$o['info']['parent']." 0 R";
1249
                if (isset($o['info']['annot'])) {
1250
                    $res.=" /Annots [";
1251
                    foreach ($o['info']['annot'] as $aId) {
1252
                        $res.=" ".$aId." 0 R";
1253
                    }
1254
                    $res.=" ]";
1255
                }
1256
                $count = count($o['info']['contents']);
1257
                if ($count==1) {
1258
                    $res.=" /Contents ".$o['info']['contents'][0]." 0 R";
1259
                } elseif ($count>1) {
1260
                    $res.=" /Contents [ ";
1261
                    foreach ($o['info']['contents'] as $cId) {
1262
                        $res.=$cId." 0 R ";
1263
                    }
1264
                    $res.="]";
1265
                }
1266
                    $res.=" >>\nendobj";
1267
                return $res;
1268
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1269
        }
1270 3
    }
1271
1272
    /**
1273
     * the contents objects hold all of the content which appears on pages
1274
     * @access private
1275
     */
1276 3
    private function o_contents($id, $action, $options = '')
1277
    {
1278 3
        if ($action!='new') {
1279
            $o =& $this->objects[$id];
1280
        }
1281
        switch ($action) {
1282 3
            case 'new':
1283 3
                $this->objects[$id]=array('t'=>'contents','c'=>'','info'=>array());
1284 3
                if (strlen($options) && intval($options)) {
1285
                    // then this contents is the primary for a page
1286 3
                    $this->objects[$id]['onPage']=$options;
1287 3
                } elseif ($options=='raw') {
1288
                    // then this page contains some other type of system object
1289
                    $this->objects[$id]['raw']=1;
1290
                }
1291 3
                break;
1292
            case 'add':
1293
                // add more options to the decleration
1294
                foreach ($options as $k => $v) {
0 ignored issues
show
The expression $options of type string is not traversable.
Loading history...
1295
                    $o['info'][$k]=$v;
0 ignored issues
show
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...
1296
                }
1297
            case 'out':
1298
                $tmp=$o['c'];
1299
                $res= "\n".$id." 0 obj\n";
1300
                if (isset($this->objects[$id]['raw'])) {
1301
                    $res.=$tmp;
1302
                } else {
1303
                    $res.= "<<";
1304 View Code Duplication
                    if (function_exists('gzcompress') && $this->options['compression']) {
1305
                        // then implement ZLIB based compression on this content stream
1306
                        $res.=" /Filter /FlateDecode";
1307
                        $tmp = gzcompress($tmp, $this->options['compression']);
1308
                    }
1309
                    if ($this->encrypted) {
1310
                        $this->encryptInit($id);
1311
                        $tmp = $this->ARC4($tmp);
1312
                    }
1313
                    foreach ($o['info'] as $k => $v) {
1314
                        $res .= " /".$k.' '.$v;
1315
                    }
1316
                    $res.=" /Length ".strlen($tmp)." >> stream\n".$tmp."\nendstream";
1317
                }
1318
                    $res.="\nendobj";
1319
                return $res;
1320
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1321
        }
1322 3
    }
1323
1324
    /**
1325
     * an image object, will be an XObject in the document, includes description and data
1326
     * @access private
1327
     */
1328
    private function o_image($id, $action, $options = '')
1329
    {
1330
        if ($action!='new') {
1331
            $o =& $this->objects[$id];
1332
        }
1333
        switch ($action) {
1334
            case 'new':
1335
                // make the new object
1336
                $this->objects[$id]=array('t'=>'image','data'=>$options['data'],'info'=>array());
1337
                $this->objects[$id]['info']['Type']='/XObject';
1338
                $this->objects[$id]['info']['Subtype']='/Image';
1339
                $this->objects[$id]['info']['Width']=$options['iw'];
1340
                $this->objects[$id]['info']['Height']=$options['ih'];
1341
                if (!isset($options['type']) || $options['type']=='jpg') {
1342
                    if (!isset($options['channels'])) {
1343
                        $options['channels']=3;
1344
                    }
1345
                    switch ($options['channels']) {
1346
                        case 1:
1347
                            $this->objects[$id]['info']['ColorSpace']='/DeviceGray';
1348
                            break;
1349
                        default:
1350
                            $this->objects[$id]['info']['ColorSpace']='/DeviceRGB';
1351
                            break;
1352
                    }
1353
                    $this->objects[$id]['info']['Filter']='/DCTDecode';
1354
                    $this->objects[$id]['info']['BitsPerComponent']=8;
1355
                } elseif ($options['type']=='png') {
1356
                    if (strlen($options['pdata'])) {
1357
                        $this->numObj++;
1358
                        $this->objects[$this->numObj]=array('t'=>'image','c'=>'','info'=>array());
1359
                        $this->objects[$this->numObj]['info'] = array('Type'=>'/XObject', 'Subtype'=>'/Image', 'Width'=> $options['iw'], 'Height'=> $options['ih'], 'Filter'=>'/FlateDecode', 'ColorSpace'=>'/DeviceGray', 'BitsPerComponent'=>'8', 'DecodeParms'=>'<< /Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$options['iw'].' >>');
1360
                        $this->objects[$this->numObj]['data']=$options['pdata'];
1361
                        if (isset($options['transparency'])) {
1362
                            switch ($options['transparency']['type']) {
1363
                                case 'indexed':
1364
                                    $tmp=' [ '.$options['transparency']['data'].' '.$options['transparency']['data'].'] ';
1365
                                    $this->objects[$id]['info']['Mask'] = $tmp;
1366
                                    $this->objects[$id]['info']['ColorSpace'] = ' [ /Indexed /DeviceRGB '.(strlen($options['pdata'])/3-1).' '.$this->numObj.' 0 R ]';
1367
                                    break;
1368
                                case 'alpha':
1369
                                    $this->objects[$id]['info']['SMask'] = $this->numObj.' 0 R';
1370
                                    $this->objects[$id]['info']['ColorSpace'] = '/'.$options['color'];
1371
                                    break;
1372
                            }
1373
                        }
1374
                    } else {
1375
                        $this->objects[$id]['info']['ColorSpace']='/'.$options['color'];
1376
                    }
1377
                    $this->objects[$id]['info']['BitsPerComponent']=$options['bitsPerComponent'];
1378
                    $this->objects[$id]['info']['Filter']='/FlateDecode';
1379
                    $this->objects[$id]['data'] = $options['data'];
1380
                    $this->objects[$id]['info']['DecodeParms']='<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
1381
                }
1382
                    // assign it a place in the named resource dictionary as an external object, according to
1383
                    // the label passed in with it.
1384
                    $this->o_pages($this->currentNode, 'xObject', array('label'=>$options['label'],'objNum'=>$id));
0 ignored issues
show
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...
1385
                break;
1386
            case 'out':
1387
                $tmp=$o['data'];
0 ignored issues
show
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...
1388
                $res= "\n".$id." 0 obj\n<<";
1389
                foreach ($o['info'] as $k => $v) {
1390
                    $res.=" /".$k.' '.$v;
1391
                }
1392
                if ($this->encrypted) {
1393
                    $this->encryptInit($id);
1394
                    $tmp = $this->ARC4($tmp);
1395
                }
1396
                $res.=" /Length ".strlen($tmp)." >> stream\n".$tmp."\nendstream\nendobj";
1397
                return $res;
1398
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1399
        }
1400
    }
1401
1402
    /**
1403
     * encryption object.
1404
     * @access private
1405
     */
1406
    private function o_encryption($id, $action, $options = '')
1407
    {
1408
        if ($action!='new') {
1409
            $o =& $this->objects[$id];
1410
        }
1411
        switch ($action) {
1412
            case 'new':
1413
                // make the new object
1414
                $this->objects[$id]=array('t'=>'encryption','info'=>$options);
1415
                $this->arc4_objnum=$id;
1416
            
1417
                // Pad or truncate the owner password
1418
                $owner = substr($options['owner'].$this->encryptionPad, 0, 32);
1419
                $user = substr($options['user'].$this->encryptionPad, 0, 32);
1420
            
1421
                $this->debug("o_encryption: user password (".$options['user'].") / owner password (".$options['owner'].")");
1422
            
1423
                // convert permission set into binary string
1424
                $permissions = sprintf("%c%c%c%c", ($options['p'] & 255), (($options['p'] >> 8) & 255), (($options['p'] >> 16) & 255), (($options['p'] >> 24) & 255));
1425
            
1426
                // Algo 3.3 Owner Password being set into /O Dictionary
1427
                $this->objects[$id]['info']['O'] = $this->encryptOwner($owner, $user);
1428
            
1429
                // Algo 3.5 User Password - START
1430
                $this->objects[$id]['info']['U'] = $this->encryptUser($user, $this->objects[$id]['info']['O'], $permissions);
1431
                // encryption key is set in encryptUser function
1432
                //$this->encryptionKey = $encryptionKey;
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...
1433
            
1434
                $this->encrypted=1;
0 ignored issues
show
Documentation Bug introduced by
The property $encrypted was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1435
                break;
1436
            case 'out':
1437
                $res= "\n".$id." 0 obj\n<<";
1438
                $res.=' /Filter /Standard';
1439
                if ($this->encryptionMode > 1) { // RC4 128bit encryption
1440
                    $res.=' /V 2';
1441
                    $res.=' /R 3';
1442
                    $res.=' /Length 128';
1443
                } else { // RC4 40bit encryption
1444
                    $res.=' /V 1';
1445
                    $res.=' /R 2';
1446
                }
1447
                    // use hex string instead of char code - char codes can make troubles (E.g. CR or LF)
1448
                    $res.=' /O <'.$this->strToHex($o['info']['O']).'>';
0 ignored issues
show
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...
1449
                    $res.=' /U <'.$this->strToHex($o['info']['U']).'>';
1450
                    // and the p-value needs to be converted to account for the twos-complement approach
1451
                    //$o['info']['p'] = (($o['info']['p'] ^ 0xFFFFFFFF)+1)*-1;
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% 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...
1452
                    $res.=' /P '.($o['info']['p']);
1453
                    $res.=" >>\nendobj";
1454
                return $res;
1455
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1456
        }
1457
    }
1458
    
1459
    /**
1460
     * owner part of the encryption
1461
     * @param $owner - owner password plus padding
1462
     * @param $user - user password plus padding
1463
     * @access private
1464
     */
1465
    private function encryptOwner($owner, $user)
1466
    {
1467
        $keylength = 5;
1468
        if ($this->encryptionMode > 1) {
1469
            $keylength = 16;
1470
        }
1471
        
1472
        $ownerHash = $this->md5_16($owner); // PDF 1.4 - repeat this 50 times in revision 3
1473
        if ($this->encryptionMode > 1) { // if it is the RC4 128bit encryption
1474
            for ($i = 0; $i < 50; $i++) {
1475
                $ownerHash = $this->md5_16($ownerHash);
1476
            }
1477
        }
1478
        
1479
        $ownerKey = substr($ownerHash, 0, $keylength); // PDF 1.4 - Create the encryption key (IMPORTANT: need to check Length)
1480
        
1481
        $this->ARC4_init($ownerKey); // 5 bytes of the encryption key (hashed 50 times)
1482
        $ovalue=$this->ARC4($user); // PDF 1.4 - Encrypt the padded user password using RC4
1483
        
1484
        if ($this->encryptionMode > 1) {
1485
            $len = strlen($ownerKey);
1486 View Code Duplication
            for ($i = 1; $i<=19; ++$i) {
1487
                $ek = '';
1488
                for ($j=0; $j < $len; $j++) {
1489
                    $ek .= chr(ord($ownerKey[$j]) ^ $i);
1490
                }
1491
                $this->ARC4_init($ek);
1492
                $ovalue = $this->ARC4($ovalue);
1493
            }
1494
        }
1495
        return $ovalue;
1496
    }
1497
    
1498
    /**
1499
     *
1500
     * user part of the encryption
1501
     * @param $user - user password plus padding
1502
     * @param $ownerDict - encrypted owner entry
1503
     * @param $permissions - permission set (print, copy, modify, ...)
1504
     */
1505
    function encryptUser($user, $ownerDict, $permissions)
1506
    {
1507
        $keylength = 5;
1508
        if ($this->encryptionMode > 1) {
1509
            $keylength = 16;
1510
        }
1511
        // make hash with user, encrypted owner, permission set and fileIdentifier
1512
        $hash = $this->md5_16($user.$ownerDict.$permissions.$this->hexToStr($this->fileIdentifier));
1513
        
1514
        // loop thru the hash process when it is revision 3 of encryption routine (usually RC4 128bit)
1515
        if ($this->encryptionMode > 1) {
1516
            for ($i = 0; $i < 50; ++$i) {
1517
                $hash = $this->md5_16(substr($hash, 0, $keylength)); // use only length of encryption key from the previous hash
1518
            }
1519
        }
1520
        
1521
        $this->encryptionKey = substr($hash, 0, $keylength); // PDF 1.4 - Create the encryption key (IMPORTANT: need to check Length)
1522
        
1523
        if ($this->encryptionMode > 1) { // if it is the RC4 128bit encryption
1524
            // make a md5 hash from padding string (hardcoded by Adobe) and the fileIdenfier
1525
            $userHash = $this->md5_16($this->encryptionPad.$this->hexToStr($this->fileIdentifier));
1526
            
1527
            // encrypt the hash from the previous method by using the encryptionKey
1528
            $this->ARC4_init($this->encryptionKey);
1529
            $uvalue=$this->ARC4($userHash);
1530
            
1531
            $len = strlen($this->encryptionKey);
1532 View Code Duplication
            for ($i = 1; $i<=19; ++$i) {
1533
                $ek = '';
1534
                for ($j=0; $j< $len; $j++) {
1535
                    $ek .= chr(ord($this->encryptionKey[$j]) ^ $i);
1536
                }
1537
                $this->ARC4_init($ek);
1538
                $uvalue = $this->ARC4($uvalue);
1539
            }
1540
            $uvalue .= substr($this->encryptionPad, 0, 16);
1541
            
1542
            //$this->encryptionKey = $encryptionKey;
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...
1543
        } else { // if it is the RC4 40bit encryption
1544
            $this->ARC4_init($this->encryptionKey);
1545
            //$this->encryptionKey = $encryptionKey;
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...
1546
            //$this->encrypted=1;
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...
1547
            $uvalue=$this->ARC4($this->encryptionPad);
1548
        }
1549
        return $uvalue;
1550
    }
1551
    
1552
    /**
1553
     * internal method to convert string to hexstring (used for owner and user dictionary)
1554
     * @param $string - any string value
1555
     * @access protected
1556
     */
1557
    protected function strToHex($string)
1558
    {
1559
        $hex = '';
1560
        for ($i=0; $i < strlen($string); $i++) {
1561
            $hex .= sprintf("%02x", ord($string[$i]));
1562
        }
1563
        return $hex;
1564
    }
1565
    
1566
    protected function hexToStr($hex)
1567
    {
1568
        $str = '';
1569
        for ($i=0; $i<strlen($hex); $i+=2) {
1570
            $str .= chr(hexdec(substr($hex, $i, 2)));
1571
        }
1572
        return $str;
1573
    }
1574
1575
    /**
1576
     * calculate the 16 byte version of the 128 bit md5 digest of the string
1577
     * @access private
1578
     */
1579
    private function md5_16($string)
1580
    {
1581
        $tmp = md5($string);
1582
        $out = pack("H*", $tmp);
1583
        return $out;
1584
    }
1585
1586
    /**
1587
     * initialize the encryption for processing a particular object
1588
     * @access private
1589
     */
1590
    private function encryptInit($id)
1591
    {
1592
        $tmp = $this->encryptionKey;
1593
        $hex = dechex($id);
1594
        if (strlen($hex)<6) {
1595
            $hex = substr('000000', 0, 6-strlen($hex)).$hex;
1596
        }
1597
        $tmp.= chr(hexdec(substr($hex, 4, 2))).chr(hexdec(substr($hex, 2, 2))).chr(hexdec(substr($hex, 0, 2))).chr(0).chr(0);
1598
        $key = $this->md5_16($tmp);
1599
        if ($this->encryptionMode > 1) {
1600
            $this->ARC4_init(substr($key, 0, 16)); // use max 16 bytes for RC4 128bit encryption key
1601
        } else {
1602
            $this->ARC4_init(substr($key, 0, 10)); // use (n + 5 bytes) for RC4 40bit encryption key
1603
        }
1604
    }
1605
1606
    /**
1607
     * initialize the ARC4 encryption
1608
     * @access private
1609
     */
1610
    private function ARC4_init($key = '')
1611
    {
1612
        $this->arc4 = '';
1613
        // setup the control array
1614
        if (strlen($key)==0) {
1615
            return;
1616
        }
1617
        $k = '';
1618
        while (strlen($k)<256) {
1619
            $k.=$key;
1620
        }
1621
        $k=substr($k, 0, 256);
1622
        for ($i=0; $i<256; $i++) {
1623
            $this->arc4 .= chr($i);
1624
        }
1625
        $j=0;
1626
        for ($i=0; $i<256; $i++) {
1627
            $t = $this->arc4[$i];
1628
            $j = ($j + ord($t) + ord($k[$i]))%256;
1629
            $this->arc4[$i]=$this->arc4[$j];
1630
            $this->arc4[$j]=$t;
1631
        }
1632
    }
1633
1634
    /**
1635
     * ARC4 encrypt a text string
1636
     * @access private
1637
     */
1638
    private function ARC4($text)
1639
    {
1640
        $len=strlen($text);
1641
        $a=0;
1642
        $b=0;
1643
        $c = $this->arc4;
1644
        $out='';
1645
        for ($i=0; $i<$len; $i++) {
1646
            $a = ($a+1)%256;
1647
            $t= $c[$a];
1648
            $b = ($b+ord($t))%256;
1649
            $c[$a]=$c[$b];
1650
            $c[$b]=$t;
1651
            $k = ord($c[(ord($c[$a])+ord($c[$b]))%256]);
1652
            $out.=chr(ord($text[$i]) ^ $k);
1653
        }
1654
        return $out;
1655
    }
1656
1657
    /**
1658
     * add a link in the document to an external URL
1659
     * @access public
1660
     */
1661
    public function addLink($url, $x0, $y0, $x1, $y1)
1662
    {
1663
        $this->numObj++;
1664
        $info = array('type'=>'link','url'=>$url,'rect'=>array($x0,$y0,$x1,$y1));
1665
        $this->o_annotation($this->numObj, 'new', $info);
0 ignored issues
show
$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...
1666
    }
1667
1668
    /**
1669
     * add a link in the document to an internal destination (ie. within the document)
1670
     * @access public
1671
     */
1672
    public function addInternalLink($label, $x0, $y0, $x1, $y1)
1673
    {
1674
        $this->numObj++;
1675
        $info = array('type'=>'ilink','label'=>$label,'rect'=>array($x0,$y0,$x1,$y1));
1676
        $this->o_annotation($this->numObj, 'new', $info);
0 ignored issues
show
$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...
1677
    }
1678
1679
    /**
1680
     * set the encryption of the document
1681
     * can be used to turn it on and/or set the passwords which it will have.
1682
     * also the functions that the user will have are set here, such as print, modify, add
1683
     * @access public
1684
     */
1685
    public function setEncryption($userPass = '', $ownerPass = '', $pc = array(), $mode = 1)
1686
    {
1687
        if ($mode > 1) {
1688
            $p=bindec('01111111111111111111000011000000'); // revision 3 is using bit 3 - 6 AND 9 - 12
1689
        } else {
1690
            $p=bindec('01111111111111111111111111000000'); // while revision 2 is using bit 3 - 6 only
1691
        }
1692
        
1693
        $options = array(
1694
            'print'=>4
1695
            ,'modify'=>8
1696
            ,'copy'=>16
1697
            ,'add'=>32
1698
            ,'fill'=>256
1699
            ,'extract'=>512
1700
            ,'assemble'=>1024
1701
            ,'represent'=>2048
1702
        );
1703
        foreach ($pc as $k => $v) {
1704
            if ($v && isset($options[$k])) {
1705
                $p+=$options[$k];
1706
            } elseif (isset($options[$v])) {
1707
                $p+=$options[$v];
1708
            }
1709
        }
1710
        
1711
        // set the encryption mode to either RC4 40bit or RC4 128bit
1712
        $this->encryptionMode = $mode;
1713
        
1714
        // implement encryption on the document
1715
        if ($this->arc4_objnum == 0) {
1716
            // then the block does not exist already, add it.
1717
            $this->numObj++;
1718
            if (strlen($ownerPass)==0) {
1719
                $ownerPass=$userPass;
1720
            }
1721
            $this->o_encryption($this->numObj, 'new', array('user'=>$userPass,'owner'=>$ownerPass,'p'=>$p));
0 ignored issues
show
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...
1722
        }
1723
    }
1724
1725
    /**
1726
     * should be used for internal checks, not implemented as yet
1727
     * @access public
1728
     */
1729
    function checkAllHere()
1730
    {
1731
        // set the validation flag to true when everything is ok.
1732
        // currently it only checks if output function has been called
1733
        $this->valid = true;
1734
    }
1735
1736
    /**
1737
     * return the pdf stream as a string returned from the function
1738
     * This method is protect to force user to use ezOutput from Cezpdf.php
1739
     * @access protected
1740
     */
1741
    function output($debug = 0)
1742
    {
1743
1744
        if ($debug) {
1745
            // turn compression off
1746
            $this->options['compression']=0;
1747
        }
1748
1749
        if ($this->arc4_objnum) {
1750
            $this->ARC4_init($this->encryptionKey);
1751
        }
1752
        
1753
        if ($this->valid) {
1754
            $this->debug('The output method has been executed again', E_USER_WARNING);
1755
        }
1756
1757
        $this->checkAllHere();
1758
1759
        $xref=array();
1760
        $content="%PDF-1.4\n%����";
1761
        //  $content="%PDF-1.3\n";
1762
        $pos=strlen($content);
1763
        foreach ($this->objects as $k => $v) {
1764
            $tmp='o_'.$v['t'];
1765
            $cont=$this->$tmp($k, 'out');
1766
            $content.=$cont;
1767
            $xref[]=$pos;
1768
            $pos+=strlen($cont);
1769
        }
1770
        ++$pos;
1771
        $content.="\nxref\n0 ".(count($xref)+1)."\n0000000000 65535 f \n";
1772
        foreach ($xref as $p) {
1773
            $content.=substr('0000000000', 0, 10-strlen($p+1)).($p+1)." 00000 n \n";
1774
        }
1775
        $content.="trailer\n<< /Size ".(count($xref)+1)." /Root 1 0 R /Info ".$this->infoObject." 0 R";
1776
        // if encryption has been applied to this document then add the marker for this dictionary
1777
        if ($this->arc4_objnum > 0) {
1778
            $content .= " /Encrypt ".$this->arc4_objnum." 0 R";
1779
        }
1780
        if (strlen($this->fileIdentifier)) {
1781
            $content .= " /ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]";
1782
        }
1783
        $content .= " >>\nstartxref\n".$pos."\n%%EOF\n";
1784
        return $content;
1785
    }
1786
1787
    /**
1788
     * intialize a new document
1789
     * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
1790
     * this function is called automatically by the constructor function
1791
     *
1792
     * @access protected
1793
     */
1794 3
    protected function newDocument($pageSize = array(0,0,612,792))
1795
    {
1796 3
        $this->numObj=0;
1797 3
        $this->objects = array();
1798
1799 3
        $this->numObj++;
1800 3
        $this->o_catalog($this->numObj, 'new');
1801
1802 3
        $this->numObj++;
1803 3
        $this->o_outlines($this->numObj, 'new');
1804
1805 3
        $this->numObj++;
1806 3
        $this->o_pages($this->numObj, 'new');
1807
1808 3
        $this->o_pages($this->numObj, 'mediaBox', $pageSize);
0 ignored issues
show
$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...
1809 3
        $this->currentNode = 3;
1810
1811 3
        $this->o_pages($this->numObj, 'procset', '[/PDF/TEXT/ImageB/ImageC/ImageI]');
1812
1813 3
        $this->numObj++;
1814 3
        $this->o_info($this->numObj, 'new');
1815
1816 3
        $this->numObj++;
1817 3
        $this->o_page($this->numObj, 'new');
1818
1819
        // need to store the first page id as there is no way to get it to the user during
1820
        // startup
1821 3
        $this->firstPageId = $this->currentContents;
1822 3
    }
1823
1824
    /**
1825
     * open the font file and return a php structure containing it.
1826
     * first check if this one has been done before and saved in a form more suited to php
1827
     * note that if a php serialized version does not exist it will try and make one, but will
1828
     * require write access to the directory to do it... it is MUCH faster to have these serialized
1829
     * files.
1830
     *
1831
     * @param string $font Font name (can contain both path and extension)
1832
     *
1833
     * @return void
1834
     */
1835 3
    protected function openFont($font)
1836
    {
1837
        // assume that $font contains both the path and perhaps the extension to the file, split them
1838 3
        $pos = strrpos($font, '/');
1839 3
        if ($pos === false) {
1840
            // $dir  = './';
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
1841 3
            $dir  = dirname(__FILE__) . '/fonts/';
1842 3
            $name = $font;
1843 3
        } else {
1844
            $dir  = substr($font, 0, $pos + 1);
1845
            $name = substr($font, $pos + 1);
1846
        }
1847
       
1848 3
        if (!$this->isUnicode) {
1849 3
            $metrics_name = "$name.afm";
1850 3
        } else {
1851
            $metrics_name = "$name.ufm";
1852
        }
1853
        
1854 3
        $this->debug('openFont executed: '.$font.' - '.$name.' / IsUnicode: '.$this->isUnicode);
1855 3
        $cachedFile = 'cached'.$metrics_name.'.php';
1856
        
1857
        // use the temp folder to read/write cached font data
1858 3
        if (file_exists($this->tempPath.'/'.$cachedFile)) {
1859 2
            $cacheDate = filemtime($this->tempPath.'/'.$cachedFile);
1860
            
1861 2
            if (file_exists($dir.$metrics_name) && filemtime($dir.$metrics_name) > $cacheDate) {
1862
                unset($this->fonts[$font]);
1863
                $this->debug('openFont: update required for cached file '.$this->tempPath.'/'.$cachedFile);
1864
            } else {
1865 2
                $this->debug('openFont: cached file found in '.$this->tempPath.'/'.$cachedFile);
1866 2
                $this->fonts[$font] = require($this->tempPath.'/'.$cachedFile);
1867 2
                if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_']<2) {
1868
                    // if the font file is old, then clear it out and prepare for re-creation
1869
                    $this->debug('openFont: version conflict with cached font file. Force recreation');
1870
                    unset($this->fonts[$font]);
1871
                }
1872
            }
1873 2
        }
1874 3
        if (!isset($this->fonts[$font]) && file_exists($dir.$metrics_name)) {
1875
            // (re)build the php_<font>.afm file from the <font>.afm file
1876 1
            $this->debug('openFont: cached file '.$cachedFile.' (re)created');
1877 1
            $data = array();
1878
            // set unicode to true ufm file is used
1879 1
            $data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
1880
            
1881 1
            $cidtogid = '';
1882 1
            if ($data['isUnicode']) {
1883
                $cidtogid = str_pad('', 256*256*2, "\x00");
1884
            }
1885
            
1886 1
            $file = file($dir.$metrics_name);
1887 1
            foreach ($file as $row) {
1888 1
                $row=trim($row);
1889 1
                $pos=strpos($row, ' ');
1890 1
                if ($pos) {
1891
                    // then there must be some keyword
1892 1
                    $key = substr($row, 0, $pos);
1893
                    switch ($key) {
1894 1
                        case 'FontName':
1895 1
                        case 'FullName':
1896 1
                        case 'FamilyName':
1897 1
                        case 'Weight':
1898 1
                        case 'ItalicAngle':
1899 1
                        case 'IsFixedPitch':
1900 1
                        case 'CharacterSet':
1901 1
                        case 'UnderlinePosition':
1902 1
                        case 'UnderlineThickness':
1903 1
                        case 'Version':
1904 1
                        case 'EncodingScheme':
1905 1
                        case 'CapHeight':
1906 1
                        case 'XHeight':
1907 1
                        case 'Ascender':
1908 1
                        case 'Descender':
1909 1
                        case 'StdHW':
1910 1
                        case 'StdVW':
1911 1
                        case 'StartCharMetrics':
1912 1
                            $data[$key]=trim(substr($row, $pos));
1913 1
                            break;
1914 1
                        case 'FontBBox':
1915 1
                            $data[$key]=explode(' ', trim(substr($row, $pos)));
1916 1
                            break;
1917 1
                        case 'C':
1918
                            // C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
1919
                            // use preg_match instead to improve performace
1920
                            // IMPORTANT: if "L i fi ; L l fl ;" is required preg_match must be amended
1921 1
                            $r = preg_match('/C (-?\d+) ; WX (-?\d+) ; N (\w+) ; B (-?\d+) (-?\d+) (-?\d+) (-?\d+) ;/', $row, $m);
1922 1
                            if ($r == 1) {
1923
                                //$dtmp = array('C'=> $m[1],'WX'=> $m[2], 'N' => $m[3], 'B' => array($m[4], $m[5], $m[6], $m[7]));
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% 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...
1924 1
                                $c = (int)$m[1];
1925 1
                                $n = $m[3];
1926 1
                                $width = floatval($m[2]);
1927
            
1928 1
                                if ($c >= 0) {
1929 1
                                    if ($c != hexdec($n)) {
1930 1
                                        $data['codeToName'][$c] = $n;
1931 1
                                    }
1932 1
                                    $data['C'][$c] = $width;
1933 1
                                    $data['C'][$n] = $width;
1934 1
                                } else {
1935 1
                                    $data['C'][$n] = $width;
1936
                                }
1937
                            
1938 1 View Code Duplication
                                if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
1939
                                      $data['MissingWidth'] = $width;
1940
                                }
1941 1
                            }
1942 1
                            break;
1943
                    // U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
1944 1
                        case 'U': // Found in UFM files
1945
                            if (!$data['isUnicode']) {
1946
                                break;
1947
                            }
1948
                        
1949
                            $r = preg_match('/U (-?\d+) ; WX (-?\d+) ; N (\w+) ; G (-?\d+) ;/', $row, $m);
1950
                        
1951
                            if ($r == 1) {
1952
                                //$dtmp = array('U'=> $m[1],'WX'=> $m[2], 'N' => $m[3], 'G' => $m[4]);
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...
1953
                                $c = (int)$m[1];
1954
                                $n = $m[3];
1955
                                $glyph = $m[4];
1956
                                $width = floatval($m[2]);
1957
                            
1958
                                if ($c >= 0) {
1959
                                    if ($c >= 0 && $c < 0xFFFF && $glyph) {
1960
                                           $cidtogid[$c*2] = chr($glyph >> 8);
1961
                                        $cidtogid[$c*2 + 1] = chr($glyph & 0xFF);
1962
                                    }
1963
                                    if ($c != hexdec($n)) {
1964
                                        $data['codeToName'][$c] = $n;
1965
                                    }
1966
                                    $data['C'][$c] = $width;
1967
                                } else {
1968
                                    $data['C'][$n] = $width;
1969
                                }
1970
                            
1971 View Code Duplication
                                if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
1972
                                      $data['MissingWidth'] = $width;
1973
                                }
1974
                            }
1975
                            break;
1976 1
                        case 'KPX':
1977 1
                            break;
1978
                        // KPX Adieresis yacute -40
1979
                        $bits=explode(' ', $row);
0 ignored issues
show
// KPX Adieresis yacute ...s = explode(' ', $row); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1980
                        $data['KPX'][$bits[1]][$bits[2]]=$bits[3];
1981
                        break;
1982
                    }
1983 1
                }
1984 1
            }
1985 1
            $data['CIDtoGID'] = base64_encode($cidtogid);
1986 1
            $data['_version_']=2;
1987
            
1988 1
            $this->fonts[$font]=$data;
1989 1
            $fp = fopen($this->tempPath.'/'.$cachedFile, 'w'); // use the temp folder to write cached font data
1990 1
            fwrite($fp, '<?php /* R&OS php pdf class font cache file */ return '.var_export($data, true).'; ?>');
1991 1
            fclose($fp);
1992 3
        } elseif (!isset($this->fonts[$font])) {
1993
            $this->debug(sprintf('openFont: no font file found for "'.$font.'" IsUnicode: %b', $font, $this->isUnicode), E_USER_WARNING);
1994
        }
1995 3
    }
1996
1997
    /**
1998
     * if the font is not loaded then load it and make the required object
1999
     * else just make it the current font
2000
     * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
2001
     * note that encoding='none' will need to be used for symbolic fonts
2002
     * and 'differences' => an array of mappings between numbers 0->255 and character names.
2003
     *
2004
     * @param string  $fontName Name of the font
2005
     * @param string  $encoding Which encoding to use
2006
     * @param integer $set      What is this
2007
     *
2008
     * @return void
2009
     * @access public
2010
     */
2011 3
    public function selectFont($fontName, $encoding = '', $set = 1, $subsetFont = false)
2012
    {
2013 3
        if ($subsetFont && !class_exists('TTFsubset')) {
2014
            $this->debug("TTFsubset class not found. Falling back to complete font program", E_USER_WARNING);
2015
            $subsetFont = false;
2016
        }
2017
    
2018 3
        $ext = substr($fontName, -4);
2019 3
        if ($ext === '.afm' || $ext === '.ufm') {
2020 3
            $fontName = substr($fontName, 0, strlen($fontName)-4);
2021 3
        }
2022
        
2023 3
        $pos = strrpos($fontName, '/');
2024 3
        if ($pos !== false) {
2025
            $name = substr($fontName, $pos + 1);
2026
        } else {
2027 3
            $name = $fontName;
2028
        }
2029
        
2030 3
        if (!isset($this->fonts[$fontName])) {
2031
            // load the file
2032 3
            $this->openFont($fontName);
2033 3
            if (isset($this->fonts[$fontName])) {
2034 3
                $this->numObj++;
2035 3
                $this->numFonts++;
2036
                
2037 3
                $font = &$this->fonts[$fontName];
2038 3
                $options = array('name' => $name, 'fontFileName' => $fontName);
2039
                
2040 3
                if (is_array($encoding)) {
2041
                    // then encoding and differences might be set
2042 3
                    if (isset($encoding['encoding'])) {
2043
                        $options['encoding'] = $encoding['encoding'];
2044
                    }
2045 3
                    if (isset($encoding['differences'])) {
2046 3
                        $options['differences'] = $encoding['differences'];
2047 3
                    }
2048 3
                } elseif (strlen($encoding)) {
2049
                    // then perhaps only the encoding has been set
2050
                    $options['encoding'] = $encoding;
2051
                }
2052 3
                $fontObj = $this->numObj;
2053 3
                $this->o_font($fontObj, 'new', $options);
0 ignored issues
show
$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...
2054 3
                $font['fontNum'] = $this->numFonts;
2055
                // if this is a '.afm' font, and there is a '.pfa' file to go with it (as there
2056
                // should be for all non-basic fonts), then load it into an object and put the
2057
                // references into the font object
2058
                
2059 3
                $fbtype = '';
2060 3
                if (file_exists($fontName.'.pfb')) {
2061
                    $fbtype = 'pfb';
2062 3
                } elseif (file_exists($fontName.'.ttf')) {
2063
                    $fbtype = 'ttf';
2064
                }
2065
                
2066 3
                $fbfile = $fontName.'.'.$fbtype;
2067
                
2068 3
                if ($fbtype) {
2069
                    $adobeFontName = $font['FontName'];
2070
                    $this->debug('selectFont: adding font file "'.$fbfile.'" to pdf');
2071
                    // find the array of fond widths, and put that into an object.
2072
                    $firstChar = -1;
2073
                    $lastChar = 0;
2074
                    $widths = array();
2075
                    $cid_widths = array();
2076
                    
2077
                    foreach ($font['C'] as $num => $d) {
2078
                        if (intval($num) > 0 || $num == '0') {
2079
                            if (!$font['isUnicode']) {
2080
                                if ($lastChar > 0 && $num > $lastChar + 1) {
2081
                                    for ($i = $lastChar + 1; $i < $num; $i++) {
2082
                                        $widths[] = 0;
2083
                                    }
2084
                                }
2085
                            }
2086
                            $widths[] = $d;
2087
                            
2088
                            if ($font['isUnicode']) {
2089
                                $cid_widths[$num] = $d;
2090
                            }
2091
                            
2092
                            if ($firstChar == -1) {
2093
                                $firstChar = $num;
2094
                            }
2095
                            $lastChar = $num;
2096
                        }
2097
                    }
2098
                    // also need to adjust the widths for the differences array
2099
                    if (isset($options['differences'])) {
2100
                        foreach ($options['differences'] as $charNum => $charName) {
2101
                            if ($charNum>$lastChar) {
2102
                                for ($i = $lastChar + 1; $i <= $charNum; $i++) {
2103
                                    $widths[]=0;
2104
                                }
2105
                                $lastChar = $charNum;
2106
                            }
2107
                            if (isset($font['C'][$charName])) {
2108
                                $widths[$charNum-$firstChar]=$font['C'][$charName];
2109
                                if ($font['isUnicode']) {
2110
                                    $cid_widths[$charName] = $font['C'][$charName];
2111
                                }
2112
                            }
2113
                        }
2114
                    }
2115
                    
2116
                    if ($font['isUnicode']) {
2117
                        $font['CIDWidths'] = $cid_widths;
2118
                    }
2119
                    $this->debug('selectFont: FirstChar='.$firstChar);
2120
                    $this->debug('selectFont: LastChar='.$lastChar);
2121
                    
2122
                    $widthid = -1;
2123
                    
2124
                    if (!$font['isUnicode']) {
2125
                        $this->numObj++;
2126
                        $this->o_contents($this->numObj, 'new', 'raw');
2127
                        $this->objects[$this->numObj]['c'].='['.implode(' ', $widths).']';
2128
                        $widthid = $this->numObj;
2129
                    }
2130
                    
2131
                    $missing_width = 500;
0 ignored issues
show
$missing_width 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...
2132
                    $stemV = 70;
2133
                    
2134
                    if (isset($font['MissingWidth'])) {
2135
                        $missing_width = $font['MissingWidth'];
0 ignored issues
show
$missing_width 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...
2136
                    }
2137
                    if (isset($font['StdVW'])) {
2138
                        $stemV = $font['StdVW'];
2139
                    } elseif (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
2140
                        $stemV = 120;
2141
                    }
2142
2143
                    // create the font descriptor
2144
                    $fontDescriptorId = ++$this->numObj;
2145
                    
2146
                    // determine flags (more than a little flakey, hopefully will not matter much)
2147
                    $flags=0;
2148
                    if ($font['ItalicAngle']!=0) {
0 ignored issues
show
It seems like you are loosely comparing $font['ItalicAngle'] of type integer|array|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
2149
                        $flags+=pow(2, 6);
2150
                    }
2151
                    if ($font['IsFixedPitch']=='true') {
2152
                        $flags+=1;
2153
                    }
2154
                    $flags+=pow(2, 5); // assume non-sybolic
2155
2156
                    $list = array('Ascent'=>'Ascender','CapHeight'=>'CapHeight','Descent'=>'Descender','FontBBox'=>'FontBBox','ItalicAngle'=>'ItalicAngle');
2157
                    $fdopt = array(
2158
                        'Flags' => $flags,
2159
                        'FontName' => $adobeFontName,
2160
                        'StemV' => $stemV
2161
                    );
2162
                    foreach ($list as $k => $v) {
2163
                        if (isset($font[$v])) {
2164
                            $fdopt[$k]=$font[$v];
2165
                        }
2166
                    }
2167
                    
2168
                    // setup the basic properties for o_font output
2169
                    $tmp = array('BaseFont'=>$adobeFontName,'Widths'=>$widthid
2170
                                      ,'FirstChar'=>$firstChar,'LastChar'=>$lastChar
2171
                                      ,'FontDescriptor'=>$fontDescriptorId);
2172
                    
2173
                    // binary content of pfb or ttf file
2174
                    $pfbid = ++$this->numObj;
2175
                    
2176
                    // embed the font program
2177
                    // to allow font subsets embedding fonts is proceed in o_font 'output'
2178
                    if ($this->embedFont) {
2179
                        if ($fbtype=='pfb') {
2180
                            $fdopt['FontFile']=$pfbid;
2181
                        } elseif ($fbtype=='ttf') {
2182
                            $fdopt['FontFile2']=$pfbid;
2183
                            $tmp['SubType']='TrueType'; // Declare basic font as TrueType
2184
                        }
2185
                        $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
0 ignored issues
show
$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...
2186
                        $this->o_contents($pfbid, 'new');
2187
                    }
2188
                    
2189
                    $this->debug('selectFont: adding extra info to font.('.$fontObj.')');
2190
                    foreach ($tmp as $fk => $fv) {
2191
                        $this->debug($fk." : ".$fv);
2192
                    }
2193
                    $this->o_font($fontObj, 'add', $tmp);
0 ignored issues
show
$tmp 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...
2194 3
                } elseif (!in_array(strtolower($name), $this->coreFonts)) {
2195
                    $this->debug('selectFont: No pfb/ttf file found for "'.$name.'"', E_USER_WARNING);
2196
                }
2197
2198
                // also set the differences here, note that this means that these will take effect only the
2199
                // first time that a font is selected, else they are ignored
2200 3
                if (isset($options['differences'])) {
2201 3
                    $font['differences']=$options['differences'];
2202 3
                }
2203 3
            }
2204 3
        }
2205
        
2206 3
        $this->fonts[$fontName]['isSubset'] = $subsetFont;
2207 3
        if (!isset($this->fonts[$fontName]['subset'])) {
2208 3
            $this->fonts[$fontName]['subset'] = array();
2209 3
        }
2210
        
2211 3
        if ($set && isset($this->fonts[$fontName])) {
2212
            // so if for some reason the font was not set in the last one then it will not be selected
2213 3
            $this->currentBaseFont=$fontName;
2214
            // the next line means that if a new font is selected, then the current text state will be
2215
            // applied to it as well.
2216 3
            $this->setCurrentFont();
2217 3
        }
2218 3
        return $this->currentFontNum;
2219
    }
2220
2221
    /**
2222
     * sets up the current font, based on the font families, and the current text state
2223
     * note that this system is quite flexible, a <b><i> font can be completely different to a
2224
     * <i><b> font, and even <b><b> will have to be defined within the family to have meaning
2225
     * This function is to be called whenever the currentTextState is changed, it will update
2226
     * the currentFont setting to whatever the appropriatte family one is.
2227
     * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
2228
     * This function will change the currentFont to whatever it should be, but will not change the
2229
     * currentBaseFont.
2230
     *
2231
     * @access protected
2232
     */
2233 3
    protected function setCurrentFont()
2234
    {
2235 3
        if (strlen($this->currentBaseFont)==0) {
2236
            // then assume an initial font
2237
            $this->selectFont(dirname(__FILE__) . '/fonts/Helvetica');
2238
        }
2239 3
        $pos = strrpos($this->currentBaseFont, '/');
2240 3
        if ($pos !== false) {
2241
            $cf = substr($this->currentBaseFont, $pos + 1);
2242
        } else {
2243 3
            $cf = $this->currentBaseFont;
2244
        }
2245 3
        if (strlen($this->currentTextState)
2246 3
            && isset($this->fontFamilies[$cf])
2247 3
            && isset($this->fontFamilies[$cf][$this->currentTextState])) {
2248
            // then we are in some state or another
2249
            // and this font has a family, and the current setting exists within it
2250
            // select the font, then return it
2251
            if ($pos !== false) {
2252
                $nf = substr($this->currentBaseFont, 0, strrpos($this->currentBaseFont, '/') + 1).$this->fontFamilies[$cf][$this->currentTextState];
2253
            } else {
2254
                $nf = $this->fontFamilies[$cf][$this->currentTextState];
2255
            }
2256
            $this->selectFont($nf, '', 0);
2257
            $this->currentFont = $nf;
2258
            $this->currentFontNum = $this->fonts[$nf]['fontNum'];
2259
        } else {
2260
            // the this font must not have the right family member for the current state
2261
            // simply assume the base font
2262 3
            $this->currentFont = $this->currentBaseFont;
2263 3
            $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2264
        }
2265 3
    }
2266
2267
    /**
2268
     * function for the user to find out what the ID is of the first page that was created during
2269
     * startup - useful if they wish to add something to it later.
2270
     * @access protected
2271
     */
2272
    protected function getFirstPageId()
2273
    {
2274
        return $this->firstPageId;
2275
    }
2276
2277
    /**
2278
     * add content to the currently active object
2279
     * @access protected
2280
     */
2281
    protected function addContent($content)
2282
    {
2283
        $this->objects[$this->currentContents]['c'].=$content;
2284
    }
2285
2286
    /**
2287
     * sets the colour for fill operations
2288
     * @access public
2289
     */
2290 View Code Duplication
    public function setColor($r, $g, $b, $force = 0)
0 ignored issues
show
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...
2291
    {
2292
        if ($r>=0 && ($force || $r!=$this->currentColour['r'] || $g!=$this->currentColour['g'] || $b!=$this->currentColour['b'])) {
2293
            $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $r).' '.sprintf('%.3F', $g).' '.sprintf('%.3F', $b).' rg';
2294
            $this->currentColour=array('r'=>$r,'g'=>$g,'b'=>$b);
2295
        }
2296
    }
2297
2298
    /**
2299
     * sets the colour for stroke operations
2300
     * @access public
2301
     */
2302 View Code Duplication
    public function setStrokeColor($r, $g, $b, $force = 0)
0 ignored issues
show
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...
2303
    {
2304
        if ($r>=0 && ($force || $r!=$this->currentStrokeColour['r'] || $g!=$this->currentStrokeColour['g'] || $b!=$this->currentStrokeColour['b'])) {
2305
            $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $r).' '.sprintf('%.3F', $g).' '.sprintf('%.3F', $b).' RG';
2306
            $this->currentStrokeColour=array('r'=>$r,'g'=>$g,'b'=>$b);
2307
        }
2308
    }
2309
2310
    /**
2311
     * draw a line from one set of coordinates to another
2312
     * @access public
2313
     */
2314
    public function line($x1, $y1, $x2, $y2)
2315
    {
2316
        $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $x1).' '.sprintf('%.3F', $y1).' m '.sprintf('%.3F', $x2).' '.sprintf('%.3F', $y2).' l S';
2317
    }
2318
2319
    /**
2320
     * draw a bezier curve based on 4 control points
2321
     * @access public
2322
     */
2323
    public function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
2324
    {
2325
        // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
2326
        // as the control points for the curve.
2327
        $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $x0).' '.sprintf('%.3F', $y0).' m '.sprintf('%.3F', $x1).' '.sprintf('%.3F', $y1);
2328
        $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3F', $x2).' '.sprintf('%.3F', $y2).' '.sprintf('%.3F', $x3).' '.sprintf('%.3F', $y3).' c S';
2329
    }
2330
2331
    /**
2332
     * draw a part of an ellipse
2333
     * @access public
2334
     */
2335
    public function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8)
2336
    {
2337
        $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, 0);
2338
    }
2339
2340
    /**
2341
     * draw a filled ellipse
2342
     * @access public
2343
     */
2344
    public function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360)
0 ignored issues
show
The parameter $r2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2345
    {
2346
        return $this->ellipse($x0, $y0, $r1, $r2 = 0, $angle, $nSeg, $astart, $afinish, 1, 1);
2347
    }
2348
2349
    /**
2350
     * draw an ellipse
2351
     * note that the part and filled ellipse are just special cases of this function
2352
     *
2353
     * draws an ellipse in the current line style
2354
     * centered at $x0,$y0, radii $r1,$r2
2355
     * if $r2 is not set, then a circle is drawn
2356
     * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
2357
     * pretty crappy shape at 2, as we are approximating with bezier curves.
2358
     * @access public
2359
     */
2360
    public function ellipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = 1, $fill = 0)
2361
    {
2362
        if ($r1==0) {
2363
            return;
2364
        }
2365
        if ($r2==0) {
2366
            $r2=$r1;
2367
        }
2368
        if ($nSeg<2) {
2369
            $nSeg=2;
2370
        }
2371
2372
        $astart = deg2rad((float)$astart);
2373
        $afinish = deg2rad((float)$afinish);
2374
        $totalAngle =$afinish-$astart;
2375
2376
        $dt = $totalAngle/$nSeg;
2377
        $dtm = $dt/3;
2378
2379
        if ($angle != 0) {
2380
            $a = -1*deg2rad((float)$angle);
2381
            $tmp = "\n q ";
2382
            $tmp .= sprintf('%.3F', cos($a)).' '.sprintf('%.3F', (-1.0*sin($a))).' '.sprintf('%.3F', sin($a)).' '.sprintf('%.3F', cos($a)).' ';
2383
            $tmp .= sprintf('%.3F', $x0).' '.sprintf('%.3F', $y0).' cm';
2384
            $this->objects[$this->currentContents]['c'].= $tmp;
2385
            $x0=0;
2386
            $y0=0;
2387
        }
2388
2389
        $t1 = $astart;
2390
        $a0 = $x0+$r1*cos($t1);
2391
        $b0 = $y0+$r2*sin($t1);
2392
        $c0 = -$r1*sin($t1);
2393
        $d0 = $r2*cos($t1);
2394
2395
        $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $a0).' '.sprintf('%.3F', $b0).' m ';
2396
        for ($i=1; $i<=$nSeg; $i++) {
2397
            // draw this bit of the total curve
2398
            $t1 = $i*$dt+$astart;
2399
            $a1 = $x0+$r1*cos($t1);
2400
            $b1 = $y0+$r2*sin($t1);
2401
            $c1 = -$r1*sin($t1);
2402
            $d1 = $r2*cos($t1);
2403
            $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', ($a0+$c0*$dtm)).' '.sprintf('%.3F', ($b0+$d0*$dtm));
2404
            $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3F', ($a1-$c1*$dtm)).' '.sprintf('%.3F', ($b1-$d1*$dtm)).' '.sprintf('%.3F', $a1).' '.sprintf('%.3F', $b1).' c';
2405
            $a0=$a1;
2406
            $b0=$b1;
2407
            $c0=$c1;
2408
            $d0=$d1;
2409
        }
2410
        if ($fill) {
2411
            $this->objects[$this->currentContents]['c'].=' f';
2412 View Code Duplication
        } else {
2413
            if ($close) {
2414
                $this->objects[$this->currentContents]['c'].=' s'; // small 's' signifies closing the path as well
2415
            } else {
2416
                $this->objects[$this->currentContents]['c'].=' S';
2417
            }
2418
        }
2419
        if ($angle !=0) {
2420
            $this->objects[$this->currentContents]['c'].=' Q';
2421
        }
2422
    }
2423
2424
    /**
2425
     * this sets the line drawing style.
2426
     * width, is the thickness of the line in user units
2427
     * cap is the type of cap to put on the line, values can be 'butt','round','square'
2428
     *    where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
2429
     *    end of the line.
2430
     * join can be 'miter', 'round', 'bevel'
2431
     * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
2432
     *   on and off dashes.
2433
     *   (2) represents 2 on, 2 off, 2 on , 2 off ...
2434
     *   (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
2435
     * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
2436
     * @access public
2437
     */
2438
    public function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0)
2439
    {
2440
2441
        // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
2442
        $string = '';
2443
        if ($width>0) {
2444
            $string.= $width.' w';
2445
        }
2446
        $ca = array('butt'=>0,'round'=>1,'square'=>2);
2447
        if (isset($ca[$cap])) {
2448
            $string.= ' '.$ca[$cap].' J';
2449
        }
2450
        $ja = array('miter'=>0,'round'=>1,'bevel'=>2);
2451
        if (isset($ja[$join])) {
2452
            $string.= ' '.$ja[$join].' j';
2453
        }
2454
        if (is_array($dash)) {
2455
            $string.= ' [';
2456
            foreach ($dash as $len) {
2457
                $string.=' '.$len;
2458
            }
2459
            $string.= ' ] '.$phase.' d';
2460
        }
2461
        $this->currentLineStyle = $string;
2462
        $this->objects[$this->currentContents]['c'].="\n".$string;
2463
    }
2464
2465
    /**
2466
     * draw a polygon, the syntax for this is similar to the GD polygon command
2467
     * @access public
2468
     */
2469
    public function polygon($p, $np, $f = 0)
2470
    {
2471
        $this->objects[$this->currentContents]['c'].="\n";
2472
        $this->objects[$this->currentContents]['c'].=sprintf('%.3F', $p[0]).' '.sprintf('%.3F', $p[1]).' m ';
2473
        for ($i=2; $i<$np*2; $i=$i+2) {
2474
            $this->objects[$this->currentContents]['c'].= sprintf('%.3F', $p[$i]).' '.sprintf('%.3F', $p[$i+1]).' l ';
2475
        }
2476 View Code Duplication
        if ($f==1) {
2477
            $this->objects[$this->currentContents]['c'].=' f';
2478
        } else {
2479
            $this->objects[$this->currentContents]['c'].=' S';
2480
        }
2481
    }
2482
2483
    /**
2484
     * a filled rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2485
     * the coordinates of the upper-right corner
2486
     * @access public
2487
     */
2488
    public function filledRectangle($x1, $y1, $width, $height)
2489
    {
2490
        $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $x1).' '.sprintf('%.3F', $y1).' '.sprintf('%.3F', $width).' '.sprintf('%.3F', $height).' re f';
2491
    }
2492
2493
    /**
2494
     * draw a rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2495
     * the coordinates of the upper-right corner
2496
     * @access public
2497
     */
2498
    public function rectangle($x1, $y1, $width, $height)
2499
    {
2500
        $this->objects[$this->currentContents]['c'].="\n".sprintf('%.3F', $x1).' '.sprintf('%.3F', $y1).' '.sprintf('%.3F', $width).' '.sprintf('%.3F', $height).' re S';
2501
    }
2502
2503
    /**
2504
     * add a new page to the document
2505
     * this also makes the new page the current active object
2506
     * @access public
2507
     */
2508
    public function newPage($insert = 0, $id = 0, $pos = 'after')
2509
    {
2510
2511
        // if there is a state saved, then go up the stack closing them
2512
        // then on the new page, re-open them with the right setings
2513
2514
        if ($this->nStateStack) {
2515
            for ($i=$this->nStateStack; $i>=1; $i--) {
2516
                $this->restoreState($i);
2517
            }
2518
        }
2519
2520
        $this->numObj++;
2521
        if ($insert) {
2522
            // the id from the ezPdf class is the od of the contents of the page, not the page object itself
2523
            // query that object to find the parent
2524
            $rid = $this->objects[$id]['onPage'];
2525
            $opt= array('rid'=>$rid,'pos'=>$pos);
2526
            $this->o_page($this->numObj, 'new', $opt);
0 ignored issues
show
$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...
2527
        } else {
2528
            $this->o_page($this->numObj, 'new');
2529
        }
2530
        // if there is a stack saved, then put that onto the page
2531
        if ($this->nStateStack) {
2532
            for ($i=1; $i<=$this->nStateStack; $i++) {
2533
                $this->saveState($i);
2534
            }
2535
        }
2536
        // and if there has been a stroke or fill colour set, then transfer them
2537 View Code Duplication
        if ($this->currentColour['r']>=0) {
2538
            $this->setColor($this->currentColour['r'], $this->currentColour['g'], $this->currentColour['b'], 1);
2539
        }
2540 View Code Duplication
        if ($this->currentStrokeColour['r']>=0) {
2541
            $this->setStrokeColor($this->currentStrokeColour['r'], $this->currentStrokeColour['g'], $this->currentStrokeColour['b'], 1);
2542
        }
2543
2544
        // if there is a line style set, then put this in too
2545
        if (strlen($this->currentLineStyle)) {
2546
            $this->objects[$this->currentContents]['c'].="\n".$this->currentLineStyle;
2547
        }
2548
2549
        // the call to the o_page object set currentContents to the present page, so this can be returned as the page id
2550
        return $this->currentContents;
2551
    }
2552
2553
    /**
2554
     * output the pdf code, streaming it to the browser
2555
     * the relevant headers are set so that hopefully the browser will recognise it
2556
     * this method is protected to force user to use ezStream method from Cezpdf.php
2557
     * @access protected
2558
     */
2559
    protected function stream($options = '')
2560
    {
2561
        // setting the options allows the adjustment of the headers
2562
        // values at the moment are:
2563
        // 'Content-Disposition'=>'filename'  - sets the filename, though not too sure how well this will
2564
        //        work as in my trial the browser seems to use the filename of the php file with .pdf on the end
2565
        // 'Accept-Ranges'=>1 or 0 - if this is not set to 1, then this header is not included, off by default
2566
        //    this header seems to have caused some problems despite tha fact that it is supposed to solve
2567
        //    them, so I am leaving it off by default.
2568
        // 'compress'=> 1 or 0 - apply content stream compression, this is on (1) by default
2569
        // 'download'=> 1 or 0 - provide download dialog
2570
        if (!is_array($options)) {
2571
            $options=array();
2572
        }
2573
        if (isset($options['compress']) && $options['compress']==0) {
2574
            $tmp = $this->output(1);
2575
        } else {
2576
            $tmp = $this->output();
2577
        }
2578
        header("Content-Type: application/pdf");
2579
        header("Content-Length: ".strlen(ltrim($tmp)));
2580
        $fileName = (isset($options['Content-Disposition'])?$options['Content-Disposition']:'file.pdf');
2581
        if (isset($options['download']) && $options['download'] == 1) {
2582
            $attached = 'attachment';
2583
        } else {
2584
            $attached = 'inline';
2585
        }
2586
        header("Content-Disposition: $attached; filename=".$fileName);
2587
        if (isset($options['Accept-Ranges']) && $options['Accept-Ranges']==1) {
2588
            header("Accept-Ranges: ".strlen(ltrim($tmp)));
2589
        }
2590
        echo ltrim($tmp);
2591
    }
2592
2593
    /**
2594
     * return the height in units of the current font in the given size
2595
     * @access public
2596
     */
2597
    public function getFontHeight($size)
2598
    {
2599
        if (!$this->numFonts) {
2600
            $this->selectFont('./fonts/Helvetica');
2601
        }
2602
        
2603
        $font = &$this->fonts[$this->currentFont];
2604
        // for the current font, and the given size, what is the height of the font in user units
2605
        $h = $font['FontBBox'][3] - $font['FontBBox'][1];
2606
        return $size*$h/1000;
2607
    }
2608
2609
    /**
2610
     * return the font decender, this will normally return a negative number
2611
     * if you add this number to the baseline, you get the level of the bottom of the font
2612
     * it is in the pdf user units
2613
     * @access public
2614
     */
2615
    public function getFontDecender($size)
2616
    {
2617
        // note that this will most likely return a negative value
2618
        if (!$this->numFonts) {
2619
            $this->selectFont('./fonts/Helvetica');
2620
        }
2621
        $h = $this->fonts[$this->currentFont]['Descender'];
2622
        return $size*$h/1000;
2623
    }
2624
   
2625
    /**
2626
     * filter the text, this is applied to all text just before being inserted into the pdf document
2627
     * it escapes the various things that need to be escaped, and so on
2628
     *
2629
     * @access protected
2630
     */
2631
    protected function filterText($text, $bom = true, $convert_encoding = true)
2632
    {
2633
        
2634
        if ($convert_encoding) {
2635
            $cf = $this->currentFont;
2636
            if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
2637
                $text = $this->utf8toUtf16BE($text, $bom); // convert utf8 chars into utf16
2638
                // store all used characters if subset font is set to true
2639 View Code Duplication
                if ($this->fonts[$cf]['isSubset']) {
2640
                    for ($i = 0; $i < mb_strlen($text, 'UTF-16BE'); $i++) {
2641
                        $this->fonts[$cf]['subset'][mb_substr($text, $i, 1, 'UTF-16BE')] = true;
2642
                    }
2643
                }
2644
            } else {
2645
                $text = mb_convert_encoding($text, $this->targetEncoding);
2646
                $hexStr = $this->strToHex($text);
2647
                // store all used characters if subset font is set to true
2648 View Code Duplication
                if ($this->fonts[$cf]['isSubset']) {
2649
                    for ($i = 0; $i < (strlen($hexStr) / 2); $i++) {
2650
                        array_push($this->fonts[$cf]['subset'], substr($hexStr, ($i * 2), 2));
2651
                    }
2652
                }
2653
            }
2654
        }
2655
        $text = strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(8) => '\\b', chr(9) => '\\t', chr(10) => '\\n', chr(12) => '\\f' ,chr(13) => '\\r'));
2656
2657
        if ($this->rtl) {
2658
            $text = strrev($text);
2659
        }
2660
2661
        return $text;
2662
    }
2663
2664
    /**
2665
       * return array containing codepoints (UTF-8 character values) for the
2666
       * string passed in.
2667
       *
2668
       * based on the excellent TCPDF code by Nicola Asuni and the
2669
       * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
2670
       *
2671
       * @access private
2672
       * @author Orion Richardson
2673
       * @since January 5, 2008
2674
       * @param string $text UTF-8 string to process
2675
       * @return array UTF-8 codepoints array for the string
2676
       */
2677
    private function utf8toCodePointsArray(&$text)
2678
    {
2679
        $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
2680
        $unicode = array(); // array containing unicode values
2681
        $bytes = array(); // array containing single character byte sequences
2682
        $numbytes = 1; // number of octetc needed to represent the UTF-8 character
2683
2684
        for ($i = 0; $i < $length; $i++) {
2685
            $c = ord($text[$i]); // get one string character at time
2686
            if (count($bytes) === 0) { // get starting octect
2687
                if ($c <= 0x7F) {
2688
                    $unicode[] = $c; // use the character "as is" because is ASCII
2689
                    $numbytes = 1;
2690 View Code Duplication
                } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
2691
                    $bytes[] = ($c - 0xC0) << 0x06;
2692
                    $numbytes = 2;
2693
                } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
2694
                    $bytes[] = ($c - 0xE0) << 0x0C;
2695
                    $numbytes = 3;
2696 View Code Duplication
                } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
2697
                    $bytes[] = ($c - 0xF0) << 0x12;
2698
                    $numbytes = 4;
2699
                } else {
2700
                    // use replacement character for other invalid sequences
2701
                    $unicode[] = 0xFFFD;
2702
                    $bytes = array();
2703
                    $numbytes = 1;
2704
                }
2705
            } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
2706
                $bytes[] = $c - 0x80;
2707
                if (count($bytes) === $numbytes) {
2708
                    // compose UTF-8 bytes to a single unicode value
2709
                    $c = $bytes[0];
2710
                    for ($j = 1; $j < $numbytes; $j++) {
2711
                        $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
2712
                    }
2713
                    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...
2714
                        // The definition of UTF-8 prohibits encoding character numbers between
2715
                        // U+D800 and U+DFFF, which are reserved for use with the UTF-16
2716
                        // encoding form (as surrogate pairs) and do not directly represent
2717
                        // characters.
2718
                        $unicode[] = 0xFFFD; // use replacement character
2719
                    } else {
2720
                        $unicode[] = $c; // add char to array
2721
                    }
2722
                    // reset data for next char
2723
                    $bytes = array();
2724
                    $numbytes = 1;
2725
                }
2726
            } else {
2727
                // use replacement character for other invalid sequences
2728
                $unicode[] = 0xFFFD;
2729
                $bytes = array();
2730
                $numbytes = 1;
2731
            }
2732
        }
2733
        return $unicode;
2734
    }
2735
2736
      /**
2737
       * convert UTF-8 to UTF-16 with an additional byte order marker
2738
       * at the front if required.
2739
       *
2740
       * based on the excellent TCPDF code by Nicola Asuni and the
2741
       * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
2742
       *
2743
       * @access private
2744
       * @author Orion Richardson
2745
       * @since January 5, 2008
2746
       * @param string $text UTF-8 string to process
2747
       * @param boolean $bom whether to add the byte order marker
2748
       * @return string UTF-16 result string
2749
       */
2750
    private function utf8toUtf16BE(&$text, $bom = true)
2751
    {
2752
        $cf = $this->currentFont;
2753
        if (!$this->fonts[$cf]['isUnicode']) {
2754
            return $text;
2755
        }
2756
        $out = $bom ? "\xFE\xFF" : '';
2757
        $unicode = $this->utf8toCodePointsArray($text);
2758
        foreach ($unicode as $c) {
2759
            if ($c === 0xFFFD) {
2760
                $out .= "\xFF\xFD"; // replacement character
2761
            } elseif ($c < 0x10000) {
2762
                $out .= chr($c >> 0x08) . chr($c & 0xFF);
2763
            } else {
2764
                $c -= 0x10000;
2765
                $w1 = 0xD800 | ($c >> 0x10);
2766
                $w2 = 0xDC00 | ($c & 0x3FF);
2767
                $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
2768
            }
2769
        }
2770
        return $out;
2771
    }
2772
2773
    /**
2774
     * given a start position and information about how text is to be laid out, calculate where
2775
     * on the page the text will end
2776
     *
2777
     * @access protected
2778
     */
2779
    protected function getTextPosition($x, $y, $angle, $size, $wa, $text)
2780
    {
2781
        // given this information return an array containing x and y for the end position as elements 0 and 1
2782
        $w = $this->getTextWidth($size, $text);
2783
        // need to adjust for the number of spaces in this text
2784
        $words = explode(' ', $text);
2785
        $nspaces=count($words)-1;
2786
        $w += $wa*$nspaces;
2787
        $a = deg2rad((float)$angle);
2788
        return array(cos($a)*$w+$x,-sin($a)*$w+$y);
2789
    }
2790
2791
    /**
2792
     * wrapper function for checkTextDirective1
2793
     *
2794
     * @access private
2795
     */
2796
    private function checkTextDirective(&$text, $i, &$f)
2797
    {
2798
        $x=0;
2799
        $y=0;
2800
        return $this->checkTextDirective1($text, $i, $f, 0, $x, $y);
2801
    }
2802
2803
    /**
2804
     * checks if the text stream contains a control directive
2805
     * if so then makes some changes and returns the number of characters involved in the directive
2806
     * this has been re-worked to include everything neccesary to fins the current writing point, so that
2807
     * the location can be sent to the callback function if required
2808
     * if the directive does not require a font change, then $f should be set to 0
2809
     *
2810
     * @access private
2811
     */
2812
    private function checkTextDirective1(&$text, $i, &$f, $final, &$x, &$y, $size = 0, $angle = 0, $wordSpaceAdjust = 0)
2813
    {
2814
        $directive = 0;
2815
        $j=$i;
2816
        if ($text[$j]=='<') {
2817
            $j++;
2818
            switch ($text[$j]) {
2819
                case '/':
2820
                    $j++;
2821
                    if (strlen($text) <= $j) {
2822
                        return $directive;
2823
                    }
2824
                    switch ($text[$j]) {
2825
                        case 'b':
2826
                        case 'i':
2827
                            $j++;
2828
                            if ($text[$j]=='>') {
2829
                                $p = strrpos($this->currentTextState, $text[$j-1]);
2830
                                if ($p !== false) {
2831
                                    // then there is one to remove
2832
                                    $this->currentTextState = substr($this->currentTextState, 0, $p).substr($this->currentTextState, $p+1);
2833
                                }
2834
                                $directive=$j-$i+1;
2835
                            }
2836
                            break;
2837
                        case 'c':
2838
                            // this this might be a callback function
2839
                            $j++;
2840
                            $k = strpos($text, '>', $j);
2841
                            if ($k!==false && $text[$j]==':') {
2842
                                // then this will be treated as a callback directive
2843
                                $directive = $k-$i+1;
2844
                                $f=0;
2845
                                // split the remainder on colons to get the function name and the paramater
2846
                                $tmp = substr($text, $j+1, $k-$j-1);
2847
                                $b1 = strpos($tmp, ':');
2848 View Code Duplication
                                if ($b1!==false) {
2849
                                    $func = substr($tmp, 0, $b1);
2850
                                    $parm = substr($tmp, $b1+1);
2851
                                } else {
2852
                                    $func=$tmp;
2853
                                    $parm='';
2854
                                }
2855
                                if (!isset($func) || !strlen(trim($func))) {
2856
                                    $directive=0;
2857
                                } else {
2858
                                    // only call the function if this is the final call
2859
                                    if ($final) {
2860
                                        // need to assess the text position, calculate the text width to this point
2861
                                        // can use getTextWidth to find the text width I think
2862
                                        $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i));
2863
                                        $info = array('x'=>$tmp[0],'y'=>$tmp[1],'angle'=>$angle,'status'=>'end','p'=>$parm,'nCallback'=>$this->nCallback);
2864
                                        $x=$tmp[0];
2865
                                        $y=$tmp[1];
2866
                                        $ret = $this->$func($info);
2867 View Code Duplication
                                        if (is_array($ret)) {
2868
                                            // then the return from the callback function could set the position, to start with, later will do font colour, and font
2869
                                            foreach ($ret as $rk => $rv) {
2870
                                                switch ($rk) {
2871
                                                    case 'x':
2872
                                                    case 'y':
2873
                                                        $$rk=$rv;
2874
                                                        break;
2875
                                                }
2876
                                            }
2877
                                        }
2878
                                        // also remove from to the stack
2879
                                        // for simplicity, just take from the end, fix this another day
2880
                                        $this->nCallback--;
2881
                                        if ($this->nCallback<0) {
2882
                                            $this->nCallBack=0;
0 ignored issues
show
The property nCallBack does not seem to exist. Did you mean callback?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2883
                                        }
2884
                                    }
2885
                                }
2886
                            }
2887
                            break;
2888
                    }
2889
                    break;
2890
                case 'b':
2891
                case 'i':
2892
                    $j++;
2893
                    if ($text[$j]=='>') {
2894
                        $this->currentTextState.=$text[$j-1];
2895
                        $directive=$j-$i+1;
2896
                    }
2897
                    break;
2898
                case 'C':
2899
                    $noClose=1;
2900
                case 'c':
2901
                    // this this might be a callback function
2902
                    $j++;
2903
                    $k = strpos($text, '>', $j);
2904
                    if ($k!==false && $text[$j]==':') {
2905
                        // then this will be treated as a callback directive
2906
                        $directive = $k-$i+1;
2907
                        $f=0;
2908
                        // split the remainder on colons to get the function name and the paramater
2909
                        // $bits = explode(':',substr($text,$j+1,$k-$j-1));
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
2910
                        $tmp = substr($text, $j+1, $k-$j-1);
2911
                        $b1 = strpos($tmp, ':');
2912 View Code Duplication
                        if ($b1!==false) {
2913
                            $func = substr($tmp, 0, $b1);
2914
                            $parm = substr($tmp, $b1+1);
2915
                        } else {
2916
                            $func=$tmp;
2917
                            $parm='';
2918
                        }
2919
                        if (!isset($func) || !strlen(trim($func))) {
2920
                            $directive=0;
2921
                        } else {
2922
                        // only call the function if this is the final call, ie, the one actually doing printing, not measurement
2923
                            if ($final) {
2924
                                // need to assess the text position, calculate the text width to this point
2925
                                // can use getTextWidth to find the text width I think
2926
                                // also add the text height and decender
2927
                                $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i));
2928
                                $info = array('x'=>$tmp[0],'y'=>$tmp[1],'angle'=>$angle,'status'=>'start','p'=>$parm,'f'=>$func,'height'=>$this->getFontHeight($size),'decender'=>$this->getFontDecender($size));
2929
                                $x=$tmp[0];
2930
                                $y=$tmp[1];
2931
                                if (!isset($noClose) || !$noClose) {
2932
                                    // only add to the stack if this is a small 'c', therefore is a start-stop pair
2933
                                    $this->nCallback++;
2934
                                    $info['nCallback']=$this->nCallback;
2935
                                    $this->callback[$this->nCallback]=$info;
2936
                                }
2937
                                $ret = $this->$func($info);
2938 View Code Duplication
                                if (is_array($ret)) {
2939
                                    // then the return from the callback function could set the position, to start with, later will do font colour, and font
2940
                                    foreach ($ret as $rk => $rv) {
2941
                                        switch ($rk) {
2942
                                            case 'x':
2943
                                            case 'y':
2944
                                                $$rk=$rv;
2945
                                                break;
2946
                                        }
2947
                                    }
2948
                                }
2949
                            }
2950
                        }
2951
                    }
2952
                    break;
2953
            }
2954
        }
2955
        return $directive;
2956
    }
2957
2958
    /**
2959
     * add text to the document, at a specified location, size and angle on the page
2960
     * @access public
2961
     */
2962
    public function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0)
2963
    {
2964
        if (!$this->numFonts) {
2965
            $this->selectFont(dirname(__FILE__) . '/fonts/Helvetica');
2966
        }
2967
        // if there are any open callbacks, then they should be called, to show the start of the line
2968
        if ($this->nCallback > 0) {
2969
            for ($i = $this->nCallback; $i > 0; $i--) {
2970
                // call each function
2971
                $info = array('x'=>$x,'y'=>$y,'angle'=>$angle,'status'=>'sol','p'=>$this->callback[$i]['p'],'nCallback'=>$this->callback[$i]['nCallback'],'height'=>$this->callback[$i]['height'],'decender'=>$this->callback[$i]['decender']);
2972
                $func = $this->callback[$i]['f'];
2973
                $this->$func($info);
2974
            }
2975
        }
2976
        if ($angle == 0) {
2977
            $this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
2978
        } else {
2979
            $a = deg2rad((float)$angle);
2980
            $this->addContent(sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y));
2981
        }
2982
2983 View Code Duplication
        if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) {
2984
            $this->wordSpaceAdjust = $wordSpaceAdjust;
2985
            $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
2986
        }
2987
        
2988
        $len = strlen($text);
2989
        $start=0;
2990
        for ($i=0; $i<$len; $i++) {
2991
            $f=1;
2992
            $directive = $this->checkTextDirective($text, $i, $f);
2993
            if ($directive) {
2994
                // then we should write what we need to
2995
                if ($i>$start) {
2996
                    $part = substr($text, $start, $i-$start);
2997
                    $this->addContent(' /F'.$this->currentFontNum.' '.sprintf('%.1f', $size).' Tf ');
2998
                    $this->addContent(' ('.$this->filterText($part, false).') Tj');
2999
                }
3000
                if ($f) {
3001
                    // then there was nothing drastic done here, restore the contents
3002
                    $this->setCurrentFont();
3003
                } else {
3004
                    $this->addContent(' ET');
3005
                    $f=1;
3006
                    $xp=$x;
3007
                    $yp=$y;
3008
                    $directive = $this->checkTextDirective1($text, $i, $f, 1, $xp, $yp, $size, $angle, $wordSpaceAdjust);
3009
3010
                    // restart the text object
3011
                    if ($angle==0) {
3012
                        $this->addContent("\n".'BT '.sprintf('%.3F', $xp).' '.sprintf('%.3F', $yp).' Td');
3013
                    } else {
3014
                        $a = deg2rad((float)$angle);
3015
                        $tmp = "\n".'BT ';
3016
                        $tmp .= sprintf('%.3F', cos($a)).' '.sprintf('%.3F', (-1.0*sin($a))).' '.sprintf('%.3F', sin($a)).' '.sprintf('%.3F', cos($a)).' ';
3017
                        $tmp .= sprintf('%.3F', $xp).' '.sprintf('%.3F', $yp).' Tm';
3018
                        $this->addContent($tmp);
3019
                    }
3020 View Code Duplication
                    if ($wordSpaceAdjust!=0 || $wordSpaceAdjust != $this->wordSpaceAdjust) {
3021
                        $this->wordSpaceAdjust=$wordSpaceAdjust;
3022
                        $this->addContent(' '.sprintf('%.3F', $wordSpaceAdjust).' Tw');
3023
                    }
3024
                }
3025
                // and move the writing point to the next piece of text
3026
                $i=$i+$directive-1;
3027
                $start=$i+1;
3028
            }
3029
        }
3030
3031
        if ($start < $len) {
3032
            $part = substr($text, $start);
3033
            $place_text = $this->filterText($part, false);
3034
          // modify unicode text so that extra word spacing is manually implemented (bug #)
3035
            $cf = $this->currentFont;
3036
            if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
3037
                $space_scale = 1000 / $size;
3038
                $place_text = str_replace(' ', ' ) '.(-round($space_scale*$wordSpaceAdjust)).' (', $place_text);
3039
            }
3040
            $this->addContent(" /F$this->currentFontNum ".sprintf('%.1F Tf ', $size));
3041
            $this->addContent(" [($place_text)] TJ");
3042
        }
3043
3044
        $this->addContent(' ET');
3045
3046
        // if there are any open callbacks, then they should be called, to show the end of the line
3047
        if ($this->nCallback > 0) {
3048
            for ($i = $this->nCallback; $i > 0; $i--) {
3049
                // call each function
3050
                $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
3051
                $info = array(
3052
                'x' => $tmp[0],
3053
                'y' => $tmp[1],
3054
                'angle' => $angle,
3055
                'status' => 'eol',
3056
                'p'         => $this->callback[$i]['p'],
3057
                'nCallback' => $this->callback[$i]['nCallback'],
3058
                'height'    => $this->callback[$i]['height'],
3059
                'descender' => $this->callback[$i]['descender']
3060
                );
3061
                $func = $this->callback[$i]['f'];
3062
                $this->$func($info);
3063
            }
3064
        }
3065
    }
3066
3067
    /**
3068
     * calculate how wide a given text string will be on a page, at a given size.
3069
     * this can be called externally, but is alse used by the other class functions
3070
     * @access public
3071
     */
3072
    public function getTextWidth($size, $text)
3073
    {
3074
        // this function should not change any of the settings, though it will need to
3075
        // track any directives which change during calculation, so copy them at the start
3076
        // and put them back at the end.
3077
        $store_currentTextState = $this->currentTextState;
3078
3079
        if (!$this->numFonts) {
3080
            $this->selectFont('./fonts/Helvetica');
3081
        }
3082
3083
        // converts a number or a float to a string so it can get the width
3084
        $text = "$text";
3085
3086
        // hmm, this is where it all starts to get tricky - use the font information to
3087
        // calculate the width of each character, add them up and convert to user units
3088
        $w=0;
3089
        $len = strlen($text);
0 ignored issues
show
$len 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...
3090
        $cf = $this->currentFont;
3091
        // font is set to unicode mode
3092
        $unicode = $this->fonts[$cf]['isUnicode'];
3093
        
3094
        if ($unicode) {
3095
            $out = $this->utf8toUtf16BE($text);
3096
        }
3097
        
3098
        for ($i=0; $i< strlen($text); $i++) {
3099
            $f=1;
3100
            $directive = $this->checkTextDirective($text, $i, $f);
3101
            if ($directive) {
3102
                if ($f) {
3103
                    $this->setCurrentFont();
3104
                    $cf = $this->currentFont;
3105
                }
3106
                $i=$i+$directive-1;
3107
            } else {
3108
                if ($unicode) {
3109
                    // use the previous converted text (utf8toUtf16BE) and get the correct character index by using below workaround
3110
                    $char = hexdec(bin2hex(mb_substr($out, $i + 1, 1, 'UTF-16BE')));
0 ignored issues
show
The variable $out 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...
3111
                } else {
3112
                    // normal ANSI text characters
3113
                    $char = ord($text[$i]);
3114
                }
3115
                if (isset($this->fonts[$cf]['differences'][$char])) {
3116
                    // then this character is being replaced by another
3117
                    $name = $this->fonts[$cf]['differences'][$char];
3118 View Code Duplication
                    if (isset($this->fonts[$cf]['C'][$name])) {
3119
                        $w+=$this->fonts[$cf]['C'][$name];
3120
                    }
3121 View Code Duplication
                } elseif (isset($this->fonts[$cf]['C'][$char])) {
3122
                    $w+=$this->fonts[$cf]['C'][$char];
3123
                }
3124
            }
3125
        }
3126
        $this->currentTextState = $store_currentTextState;
3127
        $this->setCurrentFont();
3128
        return $w*$size/1000;
3129
    }
3130
3131
    /**
3132
     * do a part of the calculation for sorting out the justification of the text
3133
     *
3134
     * @access private
3135
     */
3136
    private function adjustWrapText($text, $actual, $width, &$x, &$adjust, $justification)
3137
    {
3138
        switch ($justification) {
3139
            case 'left':
3140
                return;
3141
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
3142
            case 'right':
3143
                $x+=$width-$actual;
3144
                break;
3145
            case 'center':
3146
            case 'centre':
3147
                $x+=($width-$actual)/2;
3148
                break;
3149
            case 'full':
3150
                // count the number of words
3151
                $words = explode(' ', $text);
3152
                $nspaces=count($words)-1;
3153
                if ($nspaces>0) {
3154
                    $adjust = ($width-$actual)/$nspaces;
3155
                } else {
3156
                    $adjust=0;
3157
                }
3158
                break;
3159
        }
3160
    }
3161
3162
    /**
3163
     * add text to the page, but ensure that it fits within a certain width
3164
     * if it does not fit then put in as much as possible, splitting at word boundaries
3165
     * and return the remainder.
3166
     * justification and angle can also be specified for the text
3167
     * @access public
3168
     */
3169
    public function addTextWrap($x, $y, $width, $size, $text, $justification = 'left', $angle = 0, $test = 0)
3170
    {
3171
        // this will display the text, and if it goes beyond the width $width, will backtrack to the
3172
        // previous space or hyphen, and return the remainder of the text.
3173
3174
        // $justification can be set to 'left','right','center','centre','full'
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...
3175
3176
        // need to store the initial text state, as this will change during the width calculation
3177
        // but will need to be re-set before printing, so that the chars work out right
3178
        $store_currentTextState = $this->currentTextState;
3179
3180
        if (!$this->numFonts) {
3181
            $this->selectFont(dirname(__FILE__) . '/fonts/Helvetica');
3182
        }
3183
        if ($width<=0) {
3184
            // error, pretend it printed ok, otherwise risking a loop
3185
            return '';
3186
        }
3187
        $w=0;
3188
        $break=0;
3189
        $breakWidth=0;
3190
        $len=strlen($text);
3191
        $cf = $this->currentFont;
3192
        $tw = $width/$size*1000;
3193
        for ($i=0; $i<$len; $i++) {
3194
            $f=1;
3195
            $directive = $this->checkTextDirective($text, $i, $f);
3196
            if ($directive) {
3197
                if ($f) {
3198
                    $this->setCurrentFont();
3199
                    $cf = $this->currentFont;
3200
                }
3201
                $i=$i+$directive-1;
3202
            } else {
3203
                $cOrd = ord($text[$i]);
3204 View Code Duplication
                if (isset($this->fonts[$cf]['differences'][$cOrd])) {
3205
                    // then this character is being replaced by another
3206
                    $cOrd2 = $this->fonts[$cf]['differences'][$cOrd];
3207
                } else {
3208
                    $cOrd2 = $cOrd;
3209
                }
3210
3211 View Code Duplication
                if (isset($this->fonts[$cf]['C'][$cOrd2])) {
3212
                    $w+=$this->fonts[$cf]['C'][$cOrd2];
3213
                }
3214
                if ($w>$tw) {
3215
                    // then we need to truncate this line
3216
                    if ($break>0) {
3217
                        // then we have somewhere that we can split :)
3218
                        if ($text[$break]==' ') {
3219
                            $tmp = substr($text, 0, $break);
3220
                        } else {
3221
                            $tmp = substr($text, 0, $break+1);
3222
                        }
3223
                        $adjust=0;
3224
                        $this->adjustWrapText($tmp, $breakWidth, $width, $x, $adjust, $justification);
3225
3226
                        // reset the text state
3227
                        $this->currentTextState = $store_currentTextState;
3228
                        $this->setCurrentFont();
3229
                        if (!$test) {
3230
                            $this->addText($x, $y, $size, $tmp, $angle, $adjust);
3231
                        }
3232
                        return substr($text, $break+1);
3233
                    } else {
3234
                        // just split before the current character
3235
                        $tmp = substr($text, 0, $i);
3236
                        $adjust=0;
3237
                        $ctmp=ord($text[$i]);
3238 View Code Duplication
                        if (isset($this->fonts[$cf]['differences'][$ctmp])) {
3239
                            $ctmp=$this->fonts[$cf]['differences'][$ctmp];
3240
                        }
3241
                        $tmpw=($w-$this->fonts[$cf]['C'][$ctmp])*$size/1000;
3242
                        $this->adjustWrapText($tmp, $tmpw, $width, $x, $adjust, $justification);
3243
                        // reset the text state
3244
                        $this->currentTextState = $store_currentTextState;
3245
                        $this->setCurrentFont();
3246
                        if (!$test) {
3247
                            $this->addText($x, $y, $size, $tmp, $angle, $adjust);
3248
                        }
3249
                        return substr($text, $i);
3250
                    }
3251
                }
3252
                if ($text[$i]=='-') {
3253
                    $break=$i;
3254
                    $breakWidth = $w*$size/1000;
3255
                }
3256
                if ($text[$i]==' ') {
3257
                    $break=$i;
3258
                    $ctmp=ord($text[$i]);
3259 View Code Duplication
                    if (isset($this->fonts[$cf]['differences'][$ctmp])) {
3260
                        $ctmp=$this->fonts[$cf]['differences'][$ctmp];
3261
                    }
3262
                    $breakWidth = ($w-$this->fonts[$cf]['C'][$ctmp])*$size/1000;
3263
                }
3264
            }
3265
        }
3266
        // then there was no need to break this line
3267
        if ($justification=='full') {
3268
            $justification='left';
3269
        }
3270
        $adjust=0;
3271
        $tmpw=$w*$size/1000;
3272
        $this->adjustWrapText($text, $tmpw, $width, $x, $adjust, $justification);
3273
        // reset the text state
3274
        $this->currentTextState = $store_currentTextState;
3275
        $this->setCurrentFont();
3276
        if (!$test) {
3277
            $this->addText($x, $y, $size, $text, $angle, $adjust, $angle);
0 ignored issues
show
The call to Intraface_LegacyCpdf::addText() has too many arguments starting with $angle.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3278
        }
3279
        return '';
3280
    }
3281
3282
    /**
3283
     * this will be called at a new page to return the state to what it was on the
3284
     * end of the previous page, before the stack was closed down
3285
     * This is to get around not being able to have open 'q' across pages
3286
     * @access public
3287
     */
3288
    public function saveState($pageEnd = 0)
3289
    {
3290
        if ($pageEnd) {
3291
            // this will be called at a new page to return the state to what it was on the
3292
            // end of the previous page, before the stack was closed down
3293
            // This is to get around not being able to have open 'q' across pages
3294
            $opt = $this->stateStack[$pageEnd]; // ok to use this as stack starts numbering at 1
3295
            $this->setColor($opt['col']['r'], $opt['col']['g'], $opt['col']['b'], 1);
3296
            $this->setStrokeColor($opt['str']['r'], $opt['str']['g'], $opt['str']['b'], 1);
3297
            $this->objects[$this->currentContents]['c'].="\n".$opt['lin'];
3298
            // $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...
3299
        } else {
3300
            $this->nStateStack++;
3301
            $this->stateStack[$this->nStateStack]=array(
3302
                'col'=>$this->currentColour
3303
                ,'str'=>$this->currentStrokeColour
3304
                ,'lin'=>$this->currentLineStyle
3305
            );
3306
        }
3307
        $this->objects[$this->currentContents]['c'].="\nq";
3308
    }
3309
3310
    /**
3311
     * restore a previously saved state
3312
     * @access public
3313
     */
3314
    public function restoreState($pageEnd = 0)
3315
    {
3316
        if (!$pageEnd) {
3317
            $n = $this->nStateStack;
3318
            $this->currentColour = $this->stateStack[$n]['col'];
3319
            $this->currentStrokeColour = $this->stateStack[$n]['str'];
3320
            $this->objects[$this->currentContents]['c'].="\n".$this->stateStack[$n]['lin'];
3321
            $this->currentLineStyle = $this->stateStack[$n]['lin'];
3322
            unset($this->stateStack[$n]);
3323
            $this->nStateStack--;
3324
        }
3325
        $this->objects[$this->currentContents]['c'].="\nQ";
3326
    }
3327
3328
    /**
3329
     * make a loose object, the output will go into this object, until it is closed, then will revert to
3330
     * the current one.
3331
     * this object will not appear until it is included within a page.
3332
     * the function will return the object number
3333
     * @access public
3334
     */
3335
    public function openObject()
3336
    {
3337
        $this->nStack++;
3338
        $this->stack[$this->nStack]=array('c'=>$this->currentContents,'p'=>$this->currentPage);
3339
        // add a new object of the content type, to hold the data flow
3340
        $this->numObj++;
3341
        $this->o_contents($this->numObj, 'new');
3342
        $this->currentContents=$this->numObj;
3343
        $this->looseObjects[$this->numObj]=1;
3344
3345
        return $this->numObj;
3346
    }
3347
3348
    /**
3349
    * open an existing object for editing
3350
    * @access public
3351
    */
3352
    public function reopenObject($id)
3353
    {
3354
        $this->nStack++;
3355
        $this->stack[$this->nStack]=array('c'=>$this->currentContents,'p'=>$this->currentPage);
3356
        $this->currentContents=$id;
3357
       // also if this object is the primary contents for a page, then set the current page to its parent
3358
        if (isset($this->objects[$id]['onPage'])) {
3359
            $this->currentPage = $this->objects[$id]['onPage'];
3360
        }
3361
    }
3362
3363
    /**
3364
     * close an object
3365
     * @access public
3366
     */
3367
    public function closeObject()
3368
    {
3369
        // close the object, as long as there was one open in the first place, which will be indicated by
3370
        // an objectId on the stack.
3371
        if ($this->nStack>0) {
3372
            $this->currentContents=$this->stack[$this->nStack]['c'];
3373
            $this->currentPage=$this->stack[$this->nStack]['p'];
3374
            $this->nStack--;
3375
            // easier to probably not worry about removing the old entries, they will be overwritten
3376
            // if there are new ones.
3377
        }
3378
    }
3379
3380
    /**
3381
     * stop an object from appearing on pages from this point on
3382
     * @access public
3383
     */
3384
    public function stopObject($id)
3385
    {
3386
        // if an object has been appearing on pages up to now, then stop it, this page will
3387
        // be the last one that could contian it.
3388
        if (isset($this->addLooseObjects[$id])) {
3389
            $this->addLooseObjects[$id]='';
3390
        }
3391
    }
3392
3393
    /**
3394
     * after an object has been created, it wil only show if it has been added, using this function.
3395
     * @access public
3396
     */
3397
    public function addObject($id, $options = 'add')
3398
    {
3399
        // add the specified object to the page
3400
        if (isset($this->looseObjects[$id]) && $this->currentContents!=$id) {
3401
            // then it is a valid object, and it is not being added to itself
3402
            switch ($options) {
3403
                case 'all':
3404
                    // then this object is to be added to this page (done in the next block) and
3405
                    // all future new pages.
3406
                    $this->addLooseObjects[$id]='all';
3407
                case 'add':
3408
                    if (isset($this->objects[$this->currentContents]['onPage'])) {
3409
                        // then the destination contents is the primary for the page
3410
                        // (though this object is actually added to that page)
3411
                        $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
3412
                    }
3413
                    break;
3414 View Code Duplication
                case 'even':
3415
                    $this->addLooseObjects[$id]='even';
3416
                    $pageObjectId=$this->objects[$this->currentContents]['onPage'];
3417
                    if ($this->objects[$pageObjectId]['info']['pageNum']%2==0) {
3418
                        $this->addObject($id); // hacky huh :)
3419
                    }
3420
                    break;
3421 View Code Duplication
                case 'odd':
3422
                    $this->addLooseObjects[$id]='odd';
3423
                    $pageObjectId=$this->objects[$this->currentContents]['onPage'];
3424
                    if ($this->objects[$pageObjectId]['info']['pageNum']%2==1) {
3425
                        $this->addObject($id); // hacky huh :)
3426
                    }
3427
                    break;
3428
                case 'next':
3429
                    $this->addLooseObjects[$id]='all';
3430
                    break;
3431
                case 'nexteven':
3432
                    $this->addLooseObjects[$id]='even';
3433
                    break;
3434
                case 'nextodd':
3435
                    $this->addLooseObjects[$id]='odd';
3436
                    break;
3437
            }
3438
        }
3439
    }
3440
3441
    /**
3442
     * add content to the documents info object
3443
     * @access public
3444
     */
3445
    public function addInfo($label, $value = 0)
3446
    {
3447
        // this will only work if the label is one of the valid ones.
3448
        // modify this so that arrays can be passed as well.
3449
        // if $label is an array then assume that it is key=>value pairs
3450
        // else assume that they are both scalar, anything else will probably error
3451
        if (is_array($label)) {
3452
            foreach ($label as $l => $v) {
3453
                $this->o_info($this->infoObject, $l, $v);
3454
            }
3455
        } else {
3456
            $this->o_info($this->infoObject, $label, $value);
3457
        }
3458
    }
3459
3460
    /**
3461
     * set the viewer preferences of the document, it is up to the browser to obey these.
3462
     * @access public
3463
     */
3464
    public function setPreferences($label, $value = 0)
3465
    {
3466
        // this will only work if the label is one of the valid ones.
3467
        if (is_array($label)) {
3468
            foreach ($label as $l => $v) {
3469
                $this->o_catalog($this->catalogId, 'viewerPreferences', array($l=>$v));
0 ignored issues
show
array($l => $v) 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...
3470
            }
3471
        } else {
3472
            $this->o_catalog($this->catalogId, 'viewerPreferences', array($label=>$value));
0 ignored issues
show
array($label => $value) is of type array<?,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...
3473
        }
3474
    }
3475
3476
    /**
3477
     * extract an integer from a position in a byte stream
3478
     *
3479
     * @access private
3480
     */
3481
    private function getBytes(&$data, $pos, $num)
3482
    {
3483
        // return the integer represented by $num bytes from $pos within $data
3484
        $ret=0;
3485
        for ($i=0; $i<$num; $i++) {
3486
            $ret=$ret*256;
3487
            $ret+=ord($data[$pos+$i]);
3488
        }
3489
        return $ret;
3490
    }
3491
3492
    /**
3493
     * reads the PNG chunk
3494
     * @param $data - binary part of the png image
3495
     * @access private
3496
     */
3497
    private function readPngChunks(&$data)
3498
    {
3499
        $default = array('info'=> array(), 'transparency'=> null, 'idata'=> null, 'pdata'=> null, 'haveHeader'=> false);
3500
        // set pointer
3501
        $p = 8;
3502
        $len = strlen($data);
3503
        // cycle through the file, identifying chunks
3504
        while ($p<$len) {
3505
            $chunkLen = $this->getBytes($data, $p, 4);
3506
            $chunkType = substr($data, $p+4, 4);
3507
            //error_log($chunkType. ' - '.$chunkLen);
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...
3508
            switch ($chunkType) {
3509
                case 'IHDR':
3510
                //this is where all the file information comes from
3511
                    $default['info']['width']=$this->getBytes($data, $p+8, 4);
3512
                    $default['info']['height']=$this->getBytes($data, $p+12, 4);
3513
                    $default['info']['bitDepth']=ord($data[$p+16]);
3514
                    $default['info']['colorType']=ord($data[$p+17]);
3515
                    $default['info']['compressionMethod']=ord($data[$p+18]);
3516
                    $default['info']['filterMethod']=ord($data[$p+19]);
3517
                    $default['info']['interlaceMethod']=ord($data[$p+20]);
3518
                
3519
                    $this->debug('readPngChunks: ColorType is' . $default['info']['colorType'], E_USER_NOTICE);
3520
                
3521
                    $default['haveHeader'] = true;
3522
                
3523
                    if ($default['info']['compressionMethod']!=0) {
3524
                        $error = true;
0 ignored issues
show
$error 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...
3525
                        $errormsg = "unsupported compression method";
0 ignored issues
show
$errormsg 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...
3526
                    }
3527
                    if ($default['info']['filterMethod']!=0) {
3528
                        $error = true;
0 ignored issues
show
$error 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...
3529
                        $errormsg = "unsupported filter method";
0 ignored issues
show
$errormsg 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...
3530
                    }
3531
                
3532
                    $default['transparency'] = array('type'=> null, 'data' => null);
3533
                
3534
                    if ($default['info']['colorType'] == 3) { // indexed color, rbg
3535
                        // corresponding to entries in the plte chunk
3536
                        // Alpha for palette index 0: 1 byte
3537
                        // Alpha for palette index 1: 1 byte
3538
                        // ...etc...
3539
        
3540
                        // there will be one entry for each palette entry. up until the last non-opaque entry.
3541
                        // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
3542
                        $default['transparency']['type']='indexed';
3543
                        //$numPalette = strlen($default['pdata'])/3;
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...
3544
                        $trans=0;
3545
                        for ($i=$chunkLen; $i>=0; $i--) {
3546
                            if (ord($data[$p+8+$i])==0) {
3547
                                $trans=$i;
3548
                            }
3549
                        }
3550
                        $default['transparency']['data'] = $trans;
3551
                    } elseif ($default['info']['colorType'] == 0) { // grayscale
3552
                        // corresponding to entries in the plte chunk
3553
                        // Gray: 2 bytes, range 0 .. (2^bitdepth)-1
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
3554
        
3555
                        // $transparency['grayscale']=$this->getBytes($data,$p+8,2); // g = grayscale
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...
3556
                        $default['transparency']['type']='indexed';
3557
                        $default['transparency']['data'] = ord($data[$p+8+1]);
3558
                    } elseif ($default['info']['colorType'] == 2) { // truecolor
3559
                        // corresponding to entries in the plte chunk
3560
                        // Red: 2 bytes, range 0 .. (2^bitdepth)-1
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
3561
                        // Green: 2 bytes, range 0 .. (2^bitdepth)-1
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
3562
                        // Blue: 2 bytes, range 0 .. (2^bitdepth)-1
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
3563
                        $default['transparency']['r']=$this->getBytes($data, $p+8, 2); // r from truecolor
3564
                        $default['transparency']['g']=$this->getBytes($data, $p+10, 2); // g from truecolor
3565
                        $default['transparency']['b']=$this->getBytes($data, $p+12, 2); // b from truecolor
3566
                    } elseif ($default['info']['colorType'] == 6 || $default['info']['colorType'] == 4) {
3567
                        // set transparency type to "alpha" and proceed with it in $this->o_image later
3568
                        $default['transparency']['type'] = 'alpha';
3569
                    
3570
                        $img = imagecreatefromstring($data);
3571
                    
3572
                        $imgalpha = imagecreate($default['info']['width'], $default['info']['height']);
3573
                        // generate gray scale palette (0 -> 255)
3574
                        for ($c = 0; $c < 256; ++$c) {
3575
                            ImageColorAllocate($imgalpha, $c, $c, $c);
3576
                        }
3577
                        // extract alpha channel
3578
                        for ($xpx = 0; $xpx < $default['info']['width']; ++$xpx) {
3579
                            for ($ypx = 0; $ypx < $default['info']['height']; ++$ypx) {
3580
                                $colorBits = imagecolorat($img, $xpx, $ypx);
3581
                                $color = imagecolorsforindex($img, $colorBits);
3582
                                $color['alpha'] = (((127 - $color['alpha']) / 127) * 255);
3583
                                imagesetpixel($imgalpha, $xpx, $ypx, $color['alpha']);
3584
                            }
3585
                        }
3586
                        $tmpfile_alpha=tempnam($this->tempPath, 'ezImg');
3587
                    
3588
                        imagepng($imgalpha, $tmpfile_alpha);
3589
                        imagedestroy($imgalpha);
3590
                    
3591
                        $alphaData = file_get_contents($tmpfile_alpha);
3592
                        // nested method call to receive info on alpha image
3593
                        $alphaImg = $this->readPngChunks($alphaData);
3594
                        // use 'pdate' to fill alpha image as "palette". But it s the alpha channel
3595
                        $default['pdata'] = $alphaImg['idata'];
3596
                    
3597
                        // generate true color image with no alpha channel
3598
                        $tmpfile_tt=tempnam($this->tempPath, 'ezImg');
3599
                    
3600
                        $imgplain = imagecreatetruecolor($default['info']['width'], $default['info']['height']);
3601
                        imagecopy($imgplain, $img, 0, 0, 0, 0, $default['info']['width'], $default['info']['height']);
3602
                        imagepng($imgplain, $tmpfile_tt);
3603
                        imagedestroy($imgplain);
3604
                    
3605
                        $ttData = file_get_contents($tmpfile_tt);
3606
                        $ttImg = $this->readPngChunks($ttData);
3607
                    
3608
                        $default['idata'] = $ttImg['idata'];
3609
                    
3610
                        // remove temp files
3611
                        unlink($tmpfile_alpha);
3612
                        unlink($tmpfile_tt);
3613
                        // return to addPngImage prematurely. IDAT has already been read and PLTE is not required
3614
                        return $default;
3615
                    }
3616
                    break;
3617
                case 'PLTE':
3618
                    $default['pdata'] = substr($data, $p+8, $chunkLen);
3619
                    break;
3620
                case 'IDAT':
3621
                    $default['idata'] .= substr($data, $p+8, $chunkLen);
3622
                    break;
3623
                case 'tRNS': // this HEADER info is optional. More info: rfc2083 (http://tools.ietf.org/html/rfc2083)
3624
                    // error_log('OPTIONAL HEADER -tRNS- exist:');
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...
3625
                    // this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
3626
                    // KS End new code
3627
                    break;
3628
                default:
3629
                    break;
3630
            }
3631
            $p += $chunkLen+12;
3632
        }
3633
        return $default;
3634
    }
3635
    
3636
    /**
3637
     * add a PNG image into the document, from a file
3638
     * this should work with remote files
3639
     * @access public
3640
     */
3641
    public function addPngFromFile($file, $x, $y, $w = 0, $h = 0)
3642
    {
3643
        // read in a png file, interpret it, then add to the system
3644
        $error = false;
3645
        $errormsg = "";
3646
        
3647
        $this->debug('addPngFromFile: opening image ' . $file);
3648
        
3649
        $data = file_get_contents($file);
3650
        
3651
        if ($data === false) {
3652
            $this->debug('addPngFromFile: trouble opening file ' . $file, E_USER_WARNING);
3653
            return;
3654
        }
3655
        
3656
        $header = chr(137).chr(80).chr(78).chr(71).chr(13).chr(10).chr(26).chr(10);
3657
        if (substr($data, 0, 8)!=$header) {
3658
            $this->debug('addPngFromFile: Invalid PNG header for file: ' . $file, E_USER_WARNING);
3659
            return;
3660
        }
3661
        
3662
        $iChunk = $this->readPngChunks($data);
3663
        
3664
        if (!$iChunk['haveHeader']) {
3665
            $error = true;
3666
            $errormsg = "information header is missing.";
3667
        }
3668
        if (isset($iChunk['info']['interlaceMethod']) && $iChunk['info']['interlaceMethod']) {
3669
            $error = true;
3670
            $errormsg = "There appears to be no support for interlaced images in pdf.";
3671
        }
3672
        
3673
        if ($iChunk['info']['bitDepth'] > 8) {
3674
            $error = true;
3675
            $errormsg = "only bit depth of 8 or less is supported.";
3676
        }
3677
3678
        if ($iChunk['info']['colorType'] == 1 || $iChunk['info']['colorType'] == 5 || $iChunk['info']['colorType']== 7) {
3679
            $error = true;
3680
            $errormsg = 'Unsupported PNG color type: '.$iChunk['info']['colorType'];
3681
        } elseif (isset($iChunk['info'])) {
3682
            switch ($iChunk['info']['colorType']) {
3683
                case 3:
3684
                    $color = 'DeviceRGB';
3685
                    $ncolor=1;
3686
                    break;
3687
                case 6:
3688
                case 2:
3689
                    $color = 'DeviceRGB';
3690
                    $ncolor=3;
3691
                    break;
3692
                case 4:
3693
                case 0:
3694
                    $color = 'DeviceGray';
3695
                    $ncolor=1;
3696
                    break;
3697
            }
3698
        }
3699
            
3700
        if ($error) {
3701
            $this->debug('addPngFromFile: '.$errormsg, E_USER_WARNING);
3702
            return;
3703
        }
3704 View Code Duplication
        if ($w==0) {
3705
            $w=$h/$iChunk['info']['height']*$iChunk['info']['width'];
3706
        }
3707 View Code Duplication
        if ($h==0) {
3708
            $h=$w*$iChunk['info']['height']/$iChunk['info']['width'];
3709
        }
3710
        
3711
        if ($this->hashed) {
3712
            $oHash = md5($iChunk['idata']);
3713
        }
3714
        if (isset($oHash) && isset($this->objectHash[$oHash])) {
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3715
            $label = $this->objectHash[$oHash];
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3716
        } else {
3717
            $this->numImages++;
3718
            $label='I'.$this->numImages;
3719
            $this->numObj++;
3720
            
3721
            if (isset($oHash)) {
3722
                $this->objectHash[$oHash] = $label;
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3723
            }
3724
            
3725
            $options = array('label'=>$label,'data'=>$iChunk['idata'],'bitsPerComponent'=>$iChunk['info']['bitDepth'],'pdata'=>$iChunk['pdata']
3726
                                      ,'iw'=>$iChunk['info']['width'],'ih'=>$iChunk['info']['height'],'type'=>'png','color'=>$color,'ncolor'=>$ncolor);
0 ignored issues
show
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...
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...
3727
            if (isset($iChunk['transparency'])) {
3728
                $options['transparency']=$iChunk['transparency'];
3729
            }
3730
            $this->o_image($this->numObj, 'new', $options);
0 ignored issues
show
$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...
3731
        }
3732
        
3733
        $this->objects[$this->currentContents]['c'].="\nq ".sprintf('%.3F', $w)." 0 0 ".sprintf('%.3F', $h)." ".sprintf('%.3F', $x)." ".sprintf('%.3F', $y)." cm";
3734
        $this->objects[$this->currentContents]['c'].=" /".$label.' Do';
3735
        $this->objects[$this->currentContents]['c'].=" Q";
3736
    }
3737
    
3738
    /**
3739
     * add a JPEG image into the document, from a file
3740
     * @access public
3741
     */
3742
    public function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
3743
    {
3744
        // attempt to add a jpeg image straight from a file, using no GD commands
3745
        // note that this function is unable to operate on a remote file.
3746
3747
        if (!file_exists($img)) {
3748
            return;
3749
        }
3750
3751
        $tmp=getimagesize($img);
3752
        $imageWidth=$tmp[0];
3753
        $imageHeight=$tmp[1];
3754
3755
        if (isset($tmp['channels'])) {
3756
            $channels = $tmp['channels'];
3757
        } else {
3758
            $channels = 3;
3759
        }
3760
3761
        if ($w<=0 && $h<=0) {
3762
            $w=$imageWidth;
3763
        }
3764
        if ($w==0) {
3765
            $w=$h/$imageHeight*$imageWidth;
3766
        }
3767
        if ($h==0) {
3768
            $h=$w*$imageHeight/$imageWidth;
3769
        }
3770
3771
        $data = file_get_contents($img);
3772
3773
        $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels);
3774
    }
3775
3776
    /**
3777
     * read gif image from file, converts it into an JPEG (no transparancy) and display it
3778
     * @param $img - file path ti gif image
3779
     * @param $x - coord x
3780
     * @param $y - y cord
3781
     * @param $w - width
3782
     * @param $h - height
3783
     * @access public
3784
     */
3785
    public function addGifFromFile($img, $x, $y, $w = 0, $h = 0)
3786
    {
3787
        if (!file_exists($img)) {
3788
            return;
3789
        }
3790
        
3791
        if (!function_exists("imagecreatefromgif")) {
3792
            $this->debug('addGifFromFile: Missing GD function imageCreateFromGif', E_USER_ERROR);
3793
            return;
3794
        }
3795
        
3796
        $tmp=getimagesize($img);
3797
        $imageWidth=$tmp[0];
3798
        $imageHeight=$tmp[1];
3799
        
3800
        
3801
        if ($w<=0 && $h<=0) {
3802
            $w=$imageWidth;
3803
        }
3804
        if ($w==0) {
3805
            $w=$h/$imageHeight*$imageWidth;
3806
        }
3807
        if ($h==0) {
3808
            $h=$w*$imageHeight/$imageWidth;
3809
        }
3810
        
3811
        $imgres = imagecreatefromgif($img);
3812
        $tmpName=tempnam($this->tempPath, 'img');
3813
        imagejpeg($imgres, $tmpName, 90);
3814
        
3815
        $this->addJpegFromFile($tmpName, $x, $y, $w, $h);
3816
    }
3817
    
3818
     /**
3819
     * add an image into the document, from a GD object
3820
     * this function is not all that reliable, and I would probably encourage people to use
3821
     * the file based functions
3822
     * @param $img - gd image resource
3823
     * @param $x coord x
3824
     * @param $y coord y
3825
     * @param $w width
3826
     * @param $h height
3827
     * @param $quality image quality
3828
     * @access protected
3829
     */
3830
    protected function addImage(&$img, $x, $y, $w = 0, $h = 0, $quality = 75)
3831
    {
3832
        // add a new image into the current location, as an external object
3833
        // add the image at $x,$y, and with width and height as defined by $w & $h
3834
3835
        // note that this will only work with full colour images and makes them jpg images for display
3836
        // later versions could present lossless image formats if there is interest.
3837
3838
        // there seems to be some problem here in that images that have quality set above 75 do not appear
3839
        // not too sure why this is, but in the meantime I have restricted this to 75.
3840
        if ($quality>75) {
3841
            $quality=75;
3842
        }
3843
3844
        // if the width or height are set to zero, then set the other one based on keeping the image
3845
        // height/width ratio the same, if they are both zero, then give up :)
3846
        $imageWidth=imagesx($img);
3847
        $imageHeight=imagesy($img);
3848
3849
        if ($w<=0 && $h<=0) {
3850
            return;
3851
        }
3852
        if ($w==0) {
3853
            $w=$h/$imageHeight*$imageWidth;
3854
        }
3855
        if ($h==0) {
3856
            $h=$w*$imageHeight/$imageWidth;
3857
        }
3858
3859
        $tmpName=tempnam($this->tempPath, 'img');
3860
        imagejpeg($img, $tmpName, $quality);
3861
        
3862
        $data = file_get_contents($tmpName);
3863
        if ($data === false) {
3864
            $this->debug('addImage: trouble opening image resource', E_USER_WARNING);
3865
        }
3866
        unlink($tmpName);
3867
        $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight);
3868
    }
3869
3870
    /**
3871
     * common code used by the two JPEG adding functions
3872
     * @access private
3873
     */
3874
    private function addJpegImage_common(&$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3)
3875
    {
3876
        // note that this function is not to be called externally
3877
        // it is just the common code between the GD and the file options
3878
        if ($this->hashed) {
3879
            $oHash = md5($data);
3880
        }
3881
        if (isset($oHash) && isset($this->objectHash[$oHash])) {
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3882
            $label = $this->objectHash[$oHash];
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3883
        } else {
3884
            $this->numImages++;
3885
            $label='I'.$this->numImages;
3886
            $this->numObj++;
3887
            
3888
            if (isset($oHash)) {
3889
                $this->objectHash[$oHash] = $label;
0 ignored issues
show
The property objectHash does not seem to exist. Did you mean objectHashes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
3890
            }
3891
            
3892
            $this->o_image($this->numObj, 'new', array('label'=>$label,'data'=>$data,'iw'=>$imageWidth,'ih'=>$imageHeight,'channels'=>$channels));
0 ignored issues
show
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...
3893
        }
3894
3895
        $this->objects[$this->currentContents]['c'].="\nq ".sprintf('%.3F', $w)." 0 0 ".sprintf('%.3F', $h)." ".sprintf('%.3F', $x)." ".sprintf('%.3F', $y)." cm";
3896
        $this->objects[$this->currentContents]['c'].=" /".$label.' Do';
3897
        $this->objects[$this->currentContents]['c'].=" Q";
3898
    }
3899
3900
    /**
3901
     * specify where the document should open when it first starts
3902
     * @access public
3903
     */
3904 View Code Duplication
    public function openHere($style, $a = 0, $b = 0, $c = 0)
0 ignored issues
show
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...
3905
    {
3906
        // this function will open the document at a specified page, in a specified style
3907
        // the values for style, and the required paramters are:
3908
        // 'XYZ'  left, top, zoom
3909
        // 'Fit'
3910
        // 'FitH' top
3911
        // 'FitV' left
3912
        // '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...
3913
        // 'FitB'
3914
        // 'FitBH' top
3915
        // 'FitBV' left
3916
        $this->numObj++;
3917
        $this->o_destination($this->numObj, 'new', array('page'=>$this->currentPage,'type'=>$style,'p1'=>$a,'p2'=>$b,'p3'=>$c));
0 ignored issues
show
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...
3918
        $id = $this->catalogId;
3919
        $this->o_catalog($id, 'openHere', $this->numObj);
3920
    }
3921
3922
    /**
3923
     * create a labelled destination within the document
3924
     * @access public
3925
     */
3926 View Code Duplication
    public function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
0 ignored issues
show
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...
3927
    {
3928
        // associates the given label with the destination, it is done this way so that a destination can be specified after
3929
        // it has been linked to
3930
        // styles are the same as the 'openHere' function
3931
        $this->numObj++;
3932
        $this->o_destination($this->numObj, 'new', array('page'=>$this->currentPage,'type'=>$style,'p1'=>$a,'p2'=>$b,'p3'=>$c));
0 ignored issues
show
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...
3933
        $id = $this->numObj;
3934
        // store the label->idf relationship, note that this means that labels can be used only once
3935
        $this->destinations["$label"]=$id;
3936
    }
3937
3938
    /**
3939
     * define font families, this is used to initialize the font families for the default fonts
3940
     * and for the user to add new ones for their fonts. The default bahavious can be overridden should
3941
     * that be desired.
3942
     * @access public
3943
     */
3944
    public function setFontFamily($family, $options = '')
3945
    {
3946
        if (is_array($options)) {
3947
            // the user is trying to set a font family
3948
            // note that this can also be used to set the base ones to something else
3949
            if (strlen($family)) {
3950
                $this->fontFamilies[$family] = $options;
3951
            }
3952
        }
3953
    }
3954
3955
    /**
3956
     * used to add messages for use in debugging
3957
     * @access protected
3958
     */
3959 3
    protected function debug($message, $error_type = E_USER_NOTICE)
3960
    {
3961 3
        if ($error_type <= $this->DEBUGLEVEL) {
3962
            switch (strtolower($this->DEBUG)) {
3963
                default:
3964
                case 'none':
3965
                    break;
3966
                case 'error_log':
3967
                    trigger_error($message, $error_type);
3968
                    break;
3969
                case 'variable':
3970
                    $this->messages.=$message."\n";
3971
                    break;
3972
            }
3973
        }
3974 3
    }
3975
3976
    /**
3977
     * a few functions which should allow the document to be treated transactionally.
3978
     *
3979
     * @param string $action WHAT IS THIS?
3980
     * @return void
3981
     * @access protected
3982
     */
3983
    public function transaction($action)
3984
    {
3985
        switch ($action) {
3986
            case 'start':
3987
                // store all the data away into the checkpoint variable
3988
                $data = get_object_vars($this);
3989
                $this->checkpoint = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data of type array is incompatible with the declared type string 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...
3990
                unset($data);
3991
                break;
3992
            case 'commit':
3993
                if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
3994
                    $tmp = $this->checkpoint['checkpoint'];
3995
                    $this->checkpoint = $tmp;
3996
                    unset($tmp);
3997
                } else {
3998
                    $this->checkpoint='';
3999
                }
4000
                break;
4001 View Code Duplication
            case 'rewind':
4002
                // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
4003
                if (is_array($this->checkpoint)) {
4004
                    // can only abort if were inside a checkpoint
4005
                    $tmp = $this->checkpoint;
4006
                    foreach ($tmp as $k => $v) {
4007
                        if ($k != 'checkpoint') {
4008
                            $this->$k=$v;
4009
                        }
4010
                    }
4011
                    unset($tmp);
4012
                }
4013
                break;
4014 View Code Duplication
            case 'abort':
4015
                if (is_array($this->checkpoint)) {
4016
                    // can only abort if were inside a checkpoint
4017
                    $tmp = $this->checkpoint;
4018
                    foreach ($tmp as $k => $v) {
4019
                        $this->$k=$v;
4020
                    }
4021
                    unset($tmp);
4022
                }
4023
                break;
4024
        }
4025
    }
4026
} // end of class
4027