Completed
Push — master ( 5bdcba...189d26 )
by Roberto
04:56 queued 02:29
created

FPDF::textString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 7
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
4
class FPDF
5
{
6
    const FPDF_VERSION = '1.81';
7
    const FPDF_FONTPATH = 'font/';
8
    
9
    protected $page;               // current page number
10
    protected $n;                  // current object number
11
    protected $offsets;            // array of object offsets
12
    protected $buffer;             // buffer holding in-memory PDF
13
    protected $pages;              // array containing pages
14
    protected $state;              // current document state
15
    protected $compress;           // compression flag
16
    protected $k;                  // scale factor (number of points in user unit)
17
    protected $defOrientation;     // default orientation
18
    protected $curOrientation;     // current orientation
19
    protected $stdPageSizes;       // standard page sizes
20
    protected $defPageSize;        // default page size
21
    protected $curPageSize;        // current page size
22
    protected $curRotation;        // current page rotation
23
    protected $pageInfo;           // page-related data
24
    protected $wPt, $hPt;          // dimensions of current page in points
25
    protected $w, $h;              // dimensions of current page in user unit
26
    protected $lMargin;            // left margin
27
    protected $tMargin;            // top margin
28
    protected $rMargin;            // right margin
29
    protected $bMargin;            // page break margin
30
    protected $cMargin;            // cell margin
31
    protected $x, $y;              // current position in user unit
32
    protected $lasth;              // height of last printed cell
33
    protected $lineWidth;          // line width in user unit
34
    protected $fontpath;           // path containing fonts
35
    protected $coreFonts;          // array of core font names
36
    protected $fonts;              // array of used fonts
37
    protected $fontFiles;          // array of font files
38
    protected $encodings;          // array of encodings
39
    protected $cmaps;              // array of ToUnicode CMaps
40
    protected $fontFamily;         // current font family
41
    protected $fontStyle;          // current font style
42
    protected $underline;          // underlining flag
43
    protected $currentFont;        // current font info
44
    protected $fontSizePt;         // current font size in points
45
    protected $fontSize;           // current font size in user unit
46
    protected $drawColor;          // commands for drawing color
47
    protected $fillColor;          // commands for filling color
48
    protected $textColor;          // commands for text color
49
    protected $colorFlag;          // indicates whether fill and text colors are different
50
    protected $withAlpha;          // indicates whether alpha channel is used
51
    protected $ws;                 // word spacing
52
    protected $images;             // array of used images
53
    protected $pageLinks;          // array of links in pages
54
    protected $links;              // array of internal links
55
    protected $autoPageBreak;      // automatic page breaking
56
    protected $pageBreakTrigger;   // threshold used to trigger page breaks
57
    protected $inHeader;           // flag set when processing header
58
    protected $infooter;           // flag set when processing footer
59
    protected $aliasNbPages;       // alias for total number of pages
60
    protected $zoomMode;           // zoom display mode
61
    protected $layoutMode;         // layout display mode
62
    protected $metadata;           // document properties
63
    protected $pdfVersion;         // PDF version number
64
    
65
    public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
66
    {
67
        // Some checks
68
        $this->doChecks();
69
        // Initialization of properties
70
        $this->state = 0;
71
        $this->page = 0;
72
        $this->n = 2;
73
        $this->buffer = '';
74
        $this->pages = [];
75
        $this->pageInfo = [];
76
        $this->fonts = [];
77
        $this->fontFiles = [];
78
        $this->encodings = [];
79
        $this->cmaps = [];
80
        $this->images = [];
81
        $this->links = [];
82
        $this->inHeader = false;
83
        $this->infooter = false;
84
        $this->lasth = 0;
85
        $this->fontFamily = '';
86
        $this->fontStyle = '';
87
        $this->fontSizePt = 12;
88
        $this->underline = false;
89
        $this->drawColor = '0 G';
90
        $this->fillColor = '0 g';
91
        $this->textColor = '0 g';
92
        $this->colorFlag = false;
93
        $this->withAlpha = false;
94
        $this->ws = 0;
95
        
96
        $this->fontpath = __DIR__. FPDF_FONTPATH;
97
        
98
        // Core fonts
99
        $this->coreFonts = [
100
            'courier',
101
            'helvetica',
102
            'times',
103
            'symbol',
104
            'zapfdingbats'
105
        ];
106
        
107
        switch($unit) {
108
            case 'pt';
109
                $this->k = 1;
110
                break;
111
            case 'cm';
112
                $this->k = 72 / 2.54;
113
                break;
114
            case 'in';
115
                $this->k = 72;
116
                break;
117
            case 'mm';
118
            default:
119
                $this->k = 72 / 25.4;
120
                
121
        }
122
        
123
        // Page sizes
124
        $this->stdPageSizes = [
125
            'a3' => [841.89, 1190.55],
126
            'a4' => [595.28, 841.89],
127
            'a5' => [420.94, 595.28],
128
            'letter' => [612, 792],
129
            'legal' => [612, 1008]
130
        ];
131
        
132
        $size = $this->getPageSize($size);
133
        $this->defPageSize = $size;
134
        $this->curPageSize = $size;
135
        // Page orientation
136
        $orientation = strtolower($orientation);
137
        if ($orientation == 'p' || $orientation == 'portrait') {
138
            $this->defOrientation = 'P';
139
            $this->w = $size[0];
140
            $this->h = $size[1];
141
        } elseif ($orientation == 'l' || $orientation == 'landscape') {
142
            $this->defOrientation = 'L';
143
            $this->w = $size[1];
144
            $this->h = $size[0];
145
        } else {
146
            $this->defOrientation = 'P';
147
            $this->w = $size[0];
148
            $this->h = $size[1];
149
        }    
150
        $this->curOrientation = $this->defOrientation;
151
        $this->wPt = $this->w * $this->k;
152
        $this->hPt = $this->h * $this->k;
153
        // Page rotation
154
        $this->curRotation = 0;
155
        // Page margins (1 cm)
156
        $margin = 28.35 / $this->k;
157
        $this->setMargins($margin, $margin);
158
        // Interior cell margin (1 mm)
159
        $this->cMargin = $margin / 10;
160
        // Line width (0.2 mm)
161
        $this->lineWidth = .567 / $this->k;
162
        // Automatic page break
163
        $this->setautoPageBreak(true, 2 * $margin);
164
        // Default display mode
165
        $this->setDisplayMode('default');
166
        // Enable compression
167
        $this->setCompression(true);
168
        // Set default PDF version number
169
        $this->pdfVersion = '1.3';
170
    }
171
    
172
    public function setMargins($left, $top, $right = null)
173
    {
174
        // Set left, top and right margins
175
        $this->lMargin = $left;
176
        $this->tMargin = $top;
177
        if ($right === null) {
178
            $right = $left;
179
        }    
180
        $this->rMargin = $right;
181
    }
182
    
183
    public function setLeftMargin($margin)
184
    {
185
        // Set left margin
186
        $this->lMargin = $margin;
187
        if ($this->page > 0 && $this->x < $margin)
188
            $this->x = $margin;
189
    }
190
    
191
    public function setTopMargin($margin)
192
    {
193
        // Set top margin
194
        $this->tMargin = $margin;
195
    }
196
    
197
    public function setRightMargin($margin)
198
    {
199
        // Set right margin
200
        $this->rMargin = $margin;
201
    }
202
    
203
    public function setautoPageBreak($auto, $margin = 0)
204
    {
205
        // Set auto page break mode and triggering margin
206
        $this->autoPageBreak = $auto;
207
        $this->bMargin = $margin;
208
        $this->pageBreakTrigger = $this->h - $margin;
209
    }
210
    public function setDisplayMode($zoom, $layout = 'default')
211
    {
212
        // Set display mode in viewer
213
        if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || !is_string($zoom)) {
214
            $this->zoomMode = $zoom;
215
        } else {
216
            $this->zoomMode = 'fullpage';
217
        }    
218
        if ($layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default') {
219
            $this->layoutMode = $layout;
220
        } else {
221
            $this->layoutMode = 'single';
222
        }    
223
    }
224
    
225
    public function setCompression($compress)
226
    {
227
        // Set page compression
228
        if (function_exists('gzcompress')) {
229
            $this->compress = $compress;
230
        } else {
231
            $this->compress = false;
232
        }    
233
    }
234
    
235
    public function setTitle($title, $isUTF8 = false)
236
    {
237
        // Title of document
238
        $this->metadata['Title'] = $isUTF8 ? $title : utf8_encode($title);
239
    }
240
    
241
    public function setAuthor($author, $isUTF8 = false)
242
    {
243
        // Author of document
244
        $this->metadata['Author'] = $isUTF8 ? $author : utf8_encode($author);
245
    }
246
    
247
    public function setSubject($subject, $isUTF8 = false)
248
    {
249
        // Subject of document
250
        $this->metadata['Subject'] = $isUTF8 ? $subject : utf8_encode($subject);
251
    }
252
    
253
    public function setKeywords($keywords, $isUTF8 = false)
254
    {
255
        // Keywords of document
256
        $this->metadata['Keywords'] = $isUTF8 ? $keywords : utf8_encode($keywords);
257
    }
258
    
259
    public function setCreator($creator, $isUTF8 = false)
260
    {
261
        // Creator of document
262
        $this->metadata['Creator'] = $isUTF8 ? $creator : utf8_encode($creator);
263
    }
264
    
265
    public function aliasNbPages($alias = '{nb}')
266
    {
267
        // Define an alias for total number of pages
268
        $this->aliasNbPages = $alias;
269
    }
270
    
271
    public function error($msg)
272
    {
273
        // Fatal error
274
        throw new \Exception('FPDF error: ' . $msg);
275
    }
276
    
277
    public function close()
278
    {
279
        // Terminate document
280
        if ($this->state == 3)
281
            return;
282
        if ($this->page == 0)
283
            $this->addPage();
284
        // Page footer
285
        $this->infooter = true;
286
        $this->footer();
287
        $this->infooter = false;
288
        // close page
289
        $this->endPage();
290
        // close document
291
        $this->endDoc();
292
    }
293
    
294
    public function addPage($orientation = '', $size = '', $rotation = 0)
295
    {
296
        // Start a new page
297
        if ($this->state == 3) {
298
            $this->error('The document is closed');
299
        }    
300
        $family = $this->fontFamily;
301
        $style = $this->fontStyle . ($this->underline ? 'U' : '');
302
        $fontsize = $this->fontSizePt;
303
        $lw = $this->lineWidth;
304
        $dc = $this->drawColor;
305
        $fc = $this->fillColor;
306
        $tc = $this->textColor;
307
        $cf = $this->colorFlag;
308
        if ($this->page > 0) {
309
            // Page footer
310
            $this->infooter = true;
311
            $this->footer();
312
            $this->infooter = false;
313
            // close page
314
            $this->endPage();
315
        }
316
        // Start new page
317
        $this->beginPage($orientation, $size, $rotation);
318
        // Set line cap style to square
319
        $this->out('2 J');
320
        // Set line width
321
        $this->lineWidth = $lw;
322
        $this->out(sprintf('%.2F w', $lw * $this->k));
323
        // Set font
324
        if ($family)
325
            $this->setFont($family, $style, $fontsize);
326
        // Set colors
327
        $this->drawColor = $dc;
328
        if ($dc != '0 G')
329
            $this->out($dc);
330
        $this->fillColor = $fc;
331
        if ($fc != '0 g')
332
            $this->out($fc);
333
        $this->textColor = $tc;
334
        $this->colorFlag = $cf;
335
        // Page header
336
        $this->inHeader = true;
337
        $this->header();
338
        $this->inHeader = false;
339
        // Restore line width
340
        if ($this->lineWidth != $lw) {
341
            $this->lineWidth = $lw;
342
            $this->out(sprintf('%.2F w', $lw * $this->k));
343
        }
344
        // Restore font
345
        if ($family) {
346
            $this->setFont($family, $style, $fontsize);
347
        }    
348
        // Restore colors
349
        if ($this->drawColor != $dc) {
350
            $this->drawColor = $dc;
351
            $this->out($dc);
352
        }
353
        if ($this->fillColor != $fc) {
354
            $this->fillColor = $fc;
355
            $this->out($fc);
356
        }
357
        $this->textColor = $tc;
358
        $this->colorFlag = $cf;
359
    }
360
    
361
    public function header()
362
    {
363
        // To be implemented in your own inherited class
364
    }
365
    
366
    public function footer()
367
    {
368
        // To be implemented in your own inherited class
369
    }
370
    
371
    public function pageNo()
372
    {
373
        // Get current page number
374
        return $this->page;
375
    }
376
    public function setdrawColor($r, $g = null, $b = null)
377
    {
378
        // Set color for all stroking operations
379
        if (($r == 0 && $g == 0 && $b == 0) || $g === null) {
380
            $this->drawColor = sprintf('%.3F G', $r / 255);
381
        } else {
382
            $this->drawColor = sprintf('%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255);
383
        }
384
        if ($this->page > 0) {
385
            $this->out($this->drawColor);
386
        }
387
    }
388
    
389
    public function setfillColor($r, $g = null, $b = null)
390
    {
391
        // Set color for all filling operations
392
        if (($r == 0 && $g == 0 && $b == 0) || $g === null)
393
            $this->fillColor = sprintf('%.3F g', $r / 255);
394
        else
395
            $this->fillColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255);
396
        $this->colorFlag = ($this->fillColor != $this->textColor);
397
        if ($this->page > 0)
398
            $this->out($this->fillColor);
399
    }
400
    
401
    public function settextColor($r, $g = null, $b = null)
402
    {
403
        // Set color for text
404
        if (($r == 0 && $g == 0 && $b == 0) || $g === null) {
405
            $this->textColor = sprintf('%.3F g', $r / 255);
406
        } else {
407
            $this->textColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255);
408
        }
409
        $this->colorFlag = ($this->fillColor != $this->textColor);
410
    }
411
    
412
    public function getStringWidth($s)
413
    {
414
        // Get width of a string in the current font
415
        $s = (string) $s;
416
        $cw = &$this->currentFont['cw'];
417
        $w = 0;
418
        $l = strlen($s);
419
        for ($i = 0; $i < $l; $i++) {
420
            $w += $cw[$s[$i]];
421
        }    
422
        return $w * $this->fontSize / 1000;
423
    }
424
    
425
    public function setlineWidth($width)
426
    {
427
        // Set line width
428
        $this->lineWidth = $width;
429
        if ($this->page > 0) {
430
            $this->out(sprintf('%.2F w', $width * $this->k));
431
        }    
432
    }
433
    
434
    public function line($x1, $y1, $x2, $y2)
435
    {
436
        // Draw a line
437
        $this->out(sprintf('%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k));
438
    }
439
    
440
    public function rect($x, $y, $w, $h, $style = '')
441
    {
442
        // Draw a rectangle
443
        if ($style == 'F') {
444
            $op = 'f';
445
        } elseif ($style == 'FD' || $style == 'DF') {
446
            $op = 'B';
447
        } else {
448
            $op = 'S';
449
        }    
450
        $this->out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
451
    }
452
    
453
    public function addFont($family, $style = '', $file = '')
454
    {
455
        // Add a TrueType, OpenType or Type1 font
456
        $family = strtolower($family);
457
        if ($file == '') {
458
            $file = str_replace(' ', '', $family) . strtolower($style) . '.php';
459
        }    
460
        $style = strtoupper($style);
461
        if ($style == 'IB') {
462
            $style = 'BI';
463
        }    
464
        $fontkey = $family . $style;
465
        if (isset($this->fonts[$fontkey])) {
466
            return;
467
        }    
468
        $info = $this->loadFont($file);
469
        $info['i'] = count($this->fonts) + 1;
470
        if (!empty($info['file'])) {
471
            // Embedded font
472
            if ($info['type'] == 'TrueType') {
473
                $this->fontFiles[$info['file']] = ['length1' => $info['originalsize']];
474
            } else {
475
                $this->fontFiles[$info['file']] = ['length1' => $info['size1'], 'length2' => $info['size2']];
476
            } 
477
        }
478
        $this->fonts[$fontkey] = $info;
479
    }
480
    
481
    public function setFont($family, $style = '', $size = 0)
482
    {
483
        // Select a font; size given in points
484
        if ($family == '') {
485
            $family = $this->fontFamily;
486
        } else {
487
            $family = strtolower($family);
488
        }    
489
        $style = strtoupper($style);
490
        if (strpos($style, 'U') !== false) {
491
            $this->underline = true;
492
            $style = str_replace('U', '', $style);
493
        } else {
494
            $this->underline = false;
495
        }    
496
        if ($style == 'IB') {
497
            $style = 'BI';
498
        } 
499
        if ($size == 0) {
500
            $size = $this->fontSizePt;
501
        }
502
        // Test if font is already selected
503
        if ($this->fontFamily == $family && $this->fontStyle == $style && $this->fontSizePt == $size) {
504
            return;
505
        }
506
        // Test if font is already loaded
507
        $fontkey = $family . $style;
508
        if (!isset($this->fonts[$fontkey])) {
509
            // Test if one of the core fonts
510
            if ($family == 'arial') {
511
                $family = 'helvetica';
512
            }
513
            if (in_array($family, $this->coreFonts)) {
514
                if ($family == 'symbol' || $family == 'zapfdingbats') {
515
                    $style = '';
516
                }
517
                $fontkey = $family . $style;
518
                if (!isset($this->fonts[$fontkey])) {
519
                    $this->addFont($family, $style);
520
                }
521
            } else {
522
                $this->error('Undefined font: ' . $family . ' ' . $style);
523
            }
524
        }
525
        // Select it
526
        $this->fontFamily = $family;
527
        $this->fontStyle = $style;
528
        $this->fontSizePt = $size;
529
        $this->fontSize = $size / $this->k;
530
        $this->currentFont = &$this->fonts[$fontkey];
531
        if ($this->page > 0) {
532
            $this->out(sprintf('BT /F%d %.2F Tf ET', $this->currentFont['i'], $this->fontSizePt));
533
        }
534
    }
535
    
536
    public function setfontSize($size)
537
    {
538
        // Set font size in points
539
        if ($this->fontSizePt == $size) {
540
            return;
541
        }
542
        $this->fontSizePt = $size;
543
        $this->fontSize = $size / $this->k;
544
        if ($this->page > 0) {
545
            $this->out(sprintf('BT /F%d %.2F Tf ET', $this->currentFont['i'], $this->fontSizePt));
546
        }
547
    }
548
    
549
    public function addLink()
550
    {
551
        // Create a new internal link
552
        $n = count($this->links) + 1;
553
        $this->links[$n] = [0, 0];
554
        return $n;
555
    }
556
    
557
    public function setLink($link, $y = 0, $page = -1)
558
    {
559
        // Set destination of internal link
560
        if ($y == -1) {
561
            $y = $this->y;
562
        }
563
        if ($page == -1) {
564
            $page = $this->page;
565
        }
566
        $this->links[$link] = [$page, $y];
567
    }
568
    
569
    public function link($x, $y, $w, $h, $link)
570
    {
571
        // Put a link on the page
572
        $this->pageLinks[$this->page][] = [
573
            $x * $this->k,
574
            $this->hPt - $y * $this->k,
575
            $w * $this->k,
576
            $h * $this->k,
577
            $link
578
        ];
579
    }
580
    
581
    public function text($x, $y, $txt)
582
    {
583
        // output a string
584
        if (!isset($this->currentFont)) {
585
            $this->error('No font has been set');
586
        }
587
        $s = sprintf('BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $this->escape($txt));
588
        if ($this->underline && $txt != '') {
589
            $s .= ' ' . $this->doUnderLine($x, $y, $txt);
590
        }
591
        if ($this->colorFlag) {
592
            $s = 'q ' . $this->textColor . ' ' . $s . ' Q';
593
        }
594
        $this->out($s);
595
    }
596
    
597
    public function acceptPageBreak()
598
    {
599
        // Accept automatic page break or not
600
        return $this->autoPageBreak;
601
    }
602
    
603
    public function cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '')
604
    {
605
        // output a cell
606
        $k = $this->k;
607
        if ($this->y + $h > $this->pageBreakTrigger && !$this->inHeader && !$this->infooter && $this->acceptPageBreak()) {
608
            // Automatic page break
609
            $x = $this->x;
610
            $ws = $this->ws;
611
            if ($ws > 0) {
612
                $this->ws = 0;
613
                $this->out('0 Tw');
614
            }
615
            $this->addPage($this->curOrientation, $this->curPageSize, $this->curRotation);
616
            $this->x = $x;
617
            if ($ws > 0) {
618
                $this->ws = $ws;
619
                $this->out(sprintf('%.3F Tw', $ws * $k));
620
            }
621
        }
622
        if ($w == 0) {
623
            $w = $this->w - $this->rMargin - $this->x;
624
        }
625
        $s = '';
626
        if ($fill || $border == 1) {
627
            if ($fill) {
628
                $op = ($border == 1) ? 'B' : 'f';
629
            } else {
630
                $op = 'S';
631
            }
632
            $s = sprintf('%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op);
633
        }
634
        if (is_string($border)) {
635
            $x = $this->x;
636
            $y = $this->y;
637
            if (strpos($border, 'L') !== false) {
638
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k);
639
            }
640
            if (strpos($border, 'T') !== false) {
641
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k);
642
            }
643
            if (strpos($border, 'R') !== false) {
644
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
645
            }
646
            if (strpos($border, 'B') !== false) {
647
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
648
            }
649
        }
650
        if ($txt !== '') {
651
            if (!isset($this->currentFont)) {
652
                $this->error('No font has been set');
653
            }
654
            if ($align == 'R') {
655
                $dx = $w - $this->cMargin - $this->getStringWidth($txt);
656
            } elseif ($align == 'C') {
657
                $dx = ($w - $this->getStringWidth($txt)) / 2;
658
            } else {
659
                $dx = $this->cMargin;
660
            }
661
            if ($this->colorFlag) {
662
                $s .= 'q ' . $this->textColor . ' ';
663
            }
664
            $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->fontSize)) * $k, $this->escape($txt));
665
            if ($this->underline) {
666
                $s .= ' ' . $this->doUnderLine($this->x + $dx, $this->y + .5 * $h + .3 * $this->fontSize, $txt);
667
            }
668
            if ($this->colorFlag) {
669
                $s .= ' Q';
670
            }
671
            if ($link) {
672
                $this->link($this->x + $dx, $this->y + .5 * $h - .5 * $this->fontSize, $this->getStringWidth($txt), $this->fontSize, $link);
673
            }
674
        }    
675
        if ($s) {
676
            $this->out($s);
677
        }
678
        $this->lasth = $h;
679
        if ($ln > 0) {
680
            // Go to next line
681
            $this->y += $h;
682
            if ($ln == 1) {
683
                $this->x = $this->lMargin;
684
            }
685
        } else {
686
            $this->x += $w;
687
        }
688
    }
689
    
690
    public function multiCell($w, $h, $txt, $border = 0, $align = 'J', $fill = false)
691
    {
692
        // output text with automatic or explicit line breaks
693
        if (!isset($this->currentFont)) {
694
            $this->error('No font has been set');
695
        }
696
        $cw = &$this->currentFont['cw'];
697
        if ($w == 0) {
698
            $w = $this->w - $this->rMargin - $this->x;
699
        }
700
        $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->fontSize;
701
        $s = str_replace("\r", '', $txt);
702
        $nb = strlen($s);
703
        if ($nb > 0 && $s[$nb - 1] == "\n") {
704
            $nb--;
705
        }
706
        $b = 0;
707
        if ($border) {
708
            if ($border == 1) {
709
                $border = 'LTRB';
710
                $b = 'LRT';
711
                $b2 = 'LR';
712
            } else {
713
                $b2 = '';
714
                if (strpos($border, 'L') !== false) {
715
                    $b2 .= 'L';
716
                }
717
                if (strpos($border, 'R') !== false) {
718
                    $b2 .= 'R';
719
                }
720
                $b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
721
            }
722
        }
723
        $sep = -1;
724
        $i = 0;
725
        $j = 0;
726
        $l = 0;
727
        $ns = 0;
728
        $nl = 1;
729
        while ($i < $nb) {
730
            // Get next character
731
            $c = $s[$i];
732
            if ($c == "\n") {
733
                // Explicit line break
734
                if ($this->ws > 0) {
735
                    $this->ws = 0;
736
                    $this->out('0 Tw');
737
                }
738
                $this->cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
739
                $i++;
740
                $sep = -1;
741
                $j = $i;
742
                $l = 0;
743
                $ns = 0;
744
                $nl++;
745
                if ($border && $nl == 2) {
746
                    $b = $b2;
0 ignored issues
show
Bug introduced by
The variable $b2 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...
747
                }
748
                continue;
749
            }
750
            if ($c == ' ') {
751
                $sep = $i;
752
                $ls = $l;
753
                $ns++;
754
            }
755
            $l += $cw[$c];
756
            if ($l > $wmax) {
757
                // Automatic line break
758
                if ($sep == -1) {
759
                    if ($i == $j) {
760
                        $i++;
761
                    }
762
                    if ($this->ws > 0) {
763
                        $this->ws = 0;
764
                        $this->out('0 Tw');
765
                    }
766
                    $this->cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
767
                } else {
768
                    if ($align == 'J') {
769
                        $this->ws = ($ns > 1) ? ($wmax - $ls) / 1000 * $this->fontSize / ($ns - 1) : 0;
0 ignored issues
show
Bug introduced by
The variable $ls 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...
770
                        $this->out(sprintf('%.3F Tw', $this->ws * $this->k));
771
                    }
772
                    $this->cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
773
                    $i = $sep + 1;
774
                }
775
                $sep = -1;
776
                $j = $i;
777
                $l = 0;
778
                $ns = 0;
779
                $nl++;
780
                if ($border && $nl == 2) {
781
                    $b = $b2;
782
                }
783
            } else {
784
                $i++;
785
            }
786
        }
787
        // Last chunk
788
        if ($this->ws > 0) {
789
            $this->ws = 0;
790
            $this->out('0 Tw');
791
        }
792
        if ($border && strpos($border, 'B') !== false) {
793
            $b .= 'B';
794
        }
795
        $this->cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
796
        $this->x = $this->lMargin;
797
    }
798
    
799
    public function write($h, $txt, $link = '')
800
    {
801
        // output text in flowing mode
802
        if (!isset($this->currentFont)) {
803
            $this->error('No font has been set');
804
        }
805
        $cw = &$this->currentFont['cw'];
806
        $w = $this->w - $this->rMargin - $this->x;
807
        $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->fontSize;
808
        $s = str_replace("\r", '', $txt);
809
        $nb = strlen($s);
810
        $sep = -1;
811
        $i = 0;
812
        $j = 0;
813
        $l = 0;
814
        $nl = 1;
815
        while ($i < $nb) {
816
            // Get next character
817
            $c = $s[$i];
818
            if ($c == "\n") {
819
                // Explicit line break
820
                $this->cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link);
821
                $i++;
822
                $sep = -1;
823
                $j = $i;
824
                $l = 0;
825
                if ($nl == 1) {
826
                    $this->x = $this->lMargin;
827
                    $w = $this->w - $this->rMargin - $this->x;
828
                    $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->fontSize;
829
                }
830
                $nl++;
831
                continue;
832
            }
833
            if ($c == ' ') {
834
                $sep = $i;
835
            }
836
            $l += $cw[$c];
837
            if ($l > $wmax) {
838
                // Automatic line break
839
                if ($sep == -1) {
840
                    if ($this->x > $this->lMargin) {
841
                        // Move to next line
842
                        $this->x = $this->lMargin;
843
                        $this->y += $h;
844
                        $w = $this->w - $this->rMargin - $this->x;
845
                        $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->fontSize;
846
                        $i++;
847
                        $nl++;
848
                        continue;
849
                    }
850
                    if ($i == $j) {
851
                        $i++;
852
                    }
853
                    $this->cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link);
854
                } else {
855
                    $this->cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', false, $link);
856
                    $i = $sep + 1;
857
                }
858
                $sep = -1;
859
                $j = $i;
860
                $l = 0;
861
                if ($nl == 1) {
862
                    $this->x = $this->lMargin;
863
                    $w = $this->w - $this->rMargin - $this->x;
864
                    $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->fontSize;
865
                }
866
                $nl++;
867
            } else {
868
                $i++;
869
            }
870
        }
871
        // Last chunk
872
        if ($i != $j) {
873
            $this->cell($l / 1000 * $this->fontSize, $h, substr($s, $j), 0, 0, '', false, $link);
874
        }
875
    }
876
    
877
    public function ln($h = null)
878
    {
879
        // Line feed; default value is the last cell height
880
        $this->x = $this->lMargin;
881
        if ($h === null) {
882
            $this->y += $this->lasth;
883
        } else {
884
            $this->y += $h;
885
        }
886
    }
887
    
888
    public function image($file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '')
889
    {
890
        // Put an image on the page
891
        if ($file == '') {
892
            $this->error('Image file name is empty');
893
        }
894
        if (!isset($this->images[$file])) {
895
            // First use of this image, get info
896
            if ($type == '') {
897
                $pos = strrpos($file, '.');
898
                if (!$pos) {
899
                    $this->error('Image file has no extension and no type was specified: ' . $file);
900
                }
901
                $type = substr($file, $pos + 1);
902
            }
903
            $type = strtolower($type);
904
            if ($type == 'jpeg') {
905
                $type = 'jpg';
906
            }
907
            $mtd = '_parse' . $type;
908
            if (!method_exists($this, $mtd)) {
909
                $this->error('Unsupported image type: ' . $type);
910
            }
911
            $info = $this->$mtd($file);
912
            $info['i'] = count($this->images) + 1;
913
            $this->images[$file] = $info;
914
        } else {
915
            $info = $this->images[$file];
916
        }
917
        // Automatic width and height calculation if needed
918
        if ($w == 0 && $h == 0) {
919
            // Put image at 96 dpi
920
            $w = -96;
921
            $h = -96;
922
        }
923
        if ($w < 0) {
924
            $w = -$info['w'] * 72 / $w / $this->k;
925
        }
926
        if ($h < 0) {
927
            $h = -$info['h'] * 72 / $h / $this->k;
928
        }
929
        if ($w == 0) {
930
            $w = $h * $info['w'] / $info['h'];
931
        }
932
        if ($h == 0) {
933
            $h = $w * $info['h'] / $info['w'];
934
        }
935
        // Flowing mode
936
        if ($y === null) {
937
            if ($this->y + $h > $this->pageBreakTrigger && !$this->inHeader && !$this->infooter && $this->acceptPageBreak()) {
938
                // Automatic page break
939
                $x2 = $this->x;
940
                $this->addPage($this->curOrientation, $this->curPageSize, $this->curRotation);
941
                $this->x = $x2;
942
            }
943
            $y = $this->y;
944
            $this->y += $h;
945
        }
946
        if ($x === null) {
947
            $x = $this->x;
948
        }
949
        $this->out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ($this->h - ($y + $h)) * $this->k, $info['i']));
950
        if ($link) {
951
            $this->link($x, $y, $w, $h, $link);
952
        }
953
    }
954
    
955
    public function getPageWidth()
956
    {
957
        // Get current page width
958
        return $this->w;
959
    }
960
    
961
    public function getPageHeight()
962
    {
963
        // Get current page height
964
        return $this->h;
965
    }
966
    
967
    public function getX()
968
    {
969
        // Get x position
970
        return $this->x;
971
    }
972
    
973
    public function setX($x)
974
    {
975
        // Set x position
976
        if ($x >= 0) {
977
            $this->x = $x;
978
        } else {
979
            $this->x = $this->w + $x;
980
        }
981
    }
982
    
983
    public function getY()
984
    {
985
        // Get y position
986
        return $this->y;
987
    }
988
    
989
    public function setY($y, $resetX = true)
990
    {
991
        // Set y position and optionally reset x
992
        if ($y >= 0)
993
            $this->y = $y;
994
        else
995
            $this->y = $this->h + $y;
996
        if ($resetX)
997
            $this->x = $this->lMargin;
998
    }
999
    
1000
    public function setXY($x, $y)
1001
    {
1002
        // Set x and y positions
1003
        $this->setX($x);
1004
        $this->setY($y, false);
1005
    }
1006
    
1007
    public function output($dest = '', $name = '', $isUTF8 = false)
1008
    {
1009
        // output PDF to some destination
1010
        $this->close();
1011
        if (strlen($name) == 1 && strlen($dest) != 1) {
1012
            // Fix parameter order
1013
            $tmp = $dest;
1014
            $dest = $name;
1015
            $name = $tmp;
1016
        }
1017
        if ($dest == '') {
1018
            $dest = 'I';
1019
        }
1020
        if ($name == '') {
1021
            $name = 'doc.pdf';
1022
        }
1023
        switch (strtoupper($dest)) {
1024
            case 'I':
1025
                // Send to standard output
1026
                $this->checkOutput();
1027
                if (PHP_SAPI != 'cli') {
1028
                    // We send to a browser
1029
                    header('Content-Type: application/pdf');
1030
                    header('Content-Disposition: inline; ' . $this->httpencode('filename', $name, $isUTF8));
1031
                    header('Cache-Control: private, max-age=0, must-revalidate');
1032
                    header('Pragma: public');
1033
                }
1034
                echo $this->buffer;
1035
                break;
1036
            case 'D':
1037
                // Download file
1038
                $this->checkOutput();
1039
                header('Content-Type: application/x-download');
1040
                header('Content-Disposition: attachment; ' . $this->httpencode('filename', $name, $isUTF8));
1041
                header('Cache-Control: private, max-age=0, must-revalidate');
1042
                header('Pragma: public');
1043
                echo $this->buffer;
1044
                break;
1045
            case 'F':
1046
                // Save to local file
1047
                if (!fileput_contents($name, $this->buffer))
1048
                    $this->error('Unable to create output file: ' . $name);
1049
                break;
1050
            case 'S':
1051
                // Return as a string
1052
                return $this->buffer;
1053
            default:
1054
                $this->error('Incorrect output destination: ' . $dest);
1055
        }
1056
        return '';
1057
    }
1058
    
1059
1060
    protected function doChecks()
1061
    {
1062
        // Check mbstring overloading
1063
        if (ini_get('mbstring.func_overload') & 2) {
1064
            $this->error('mbstring overloading must be disabled');
1065
        }
1066
    }
1067
    
1068
    protected function checkOutput()
1069
    {
1070
        if (PHP_SAPI != 'cli') {
1071
            if (headers_sent($file, $line))
1072
                $this->error("Some data has already been output, can't send PDF file (output started at $file:$line)");
1073
        }
1074
        if (ob_get_length()) {
1075
            // The output buffer is not empty
1076
            if (preg_match('/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents())) {
1077
                // It contains only a UTF-8 BOM and/or whitespace, let's clean it
1078
                ob_clean();
1079
            } else {
1080
                $this->error("Some data has already been output, can't send PDF file");
1081
            }
1082
        }
1083
    }
1084
    
1085
    protected function getPageSize($size)
1086
    {
1087
        if (is_string($size)) {
1088
            $size = strtolower($size);
1089
            if (!isset($this->stdPageSizes[$size])) {
1090
                $this->error('Unknown page size: ' . $size);
1091
            }
1092
            $a = $this->stdPageSizes[$size];
1093
            return [$a[0] / $this->k, $a[1] / $this->k];
1094
        } else {
1095
            if ($size[0] > $size[1]) {
1096
                return [$size[1], $size[0]];
1097
            } else {
1098
                return $size;
1099
            }
1100
        }
1101
    }
1102
    
1103
    protected function beginPage($orientation, $size, $rotation)
1104
    {
1105
        $this->page++;
1106
        $this->pages[$this->page] = '';
1107
        $this->state = 2;
1108
        $this->x = $this->lMargin;
1109
        $this->y = $this->tMargin;
1110
        $this->fontFamily = '';
1111
        // Check page size and orientation
1112
        if ($orientation == '') {
1113
            $orientation = $this->defOrientation;
1114
        } else {
1115
            $orientation = strtoupper($orientation[0]);
1116
        }
1117
        if ($size == '') {
1118
            $size = $this->defPageSize;
1119
        } else {
1120
            $size = $this->getPageSize($size);
1121
        }
1122
        if ($orientation != $this->curOrientation || $size[0] != $this->curPageSize[0] || $size[1] != $this->curPageSize[1]) {
1123
            // New size or orientation
1124
            if ($orientation == 'P') {
1125
                $this->w = $size[0];
1126
                $this->h = $size[1];
1127
            } else {
1128
                $this->w = $size[1];
1129
                $this->h = $size[0];
1130
            }
1131
            $this->wPt = $this->w * $this->k;
1132
            $this->hPt = $this->h * $this->k;
1133
            $this->pageBreakTrigger = $this->h - $this->bMargin;
1134
            $this->curOrientation = $orientation;
1135
            $this->curPageSize = $size;
1136
        }
1137
        if ($orientation != $this->defOrientation || $size[0] != $this->defPageSize[0] || $size[1] != $this->defPageSize[1]) {
1138
            $this->pageInfo[$this->page]['size'] = [$this->wPt, $this->hPt];
1139
        }
1140
        if ($rotation != 0) {
1141
            if ($rotation % 90 != 0) {
1142
                $this->error('Incorrect rotation value: ' . $rotation);
1143
            }
1144
            $this->curRotation = $rotation;
1145
            $this->pageInfo[$this->page]['rotation'] = $rotation;
1146
        }
1147
    }
1148
    
1149
    protected function endPage()
1150
    {
1151
        $this->state = 1;
1152
    }
1153
    
1154
    protected function loadFont($font)
1155
    {
1156
        // Load a font definition file from the font directory
1157
        if (strpos($font, '/') !== false || strpos($font, "\\") !== false) {
1158
            $this->error('Incorrect font definition file name: ' . $font);
1159
        }
1160
        include($this->fontpath . $font);
1161
        if (!isset($name)) {
0 ignored issues
show
Bug introduced by
The variable $name seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
1162
            $this->error('Could not include font definition file');
1163
        }
1164
        if (isset($enc)) {
0 ignored issues
show
Bug introduced by
The variable $enc seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1165
            $enc = strtolower($enc);
1166
        }
1167
        if (!isset($subsetted)) {
0 ignored issues
show
Bug introduced by
The variable $subsetted seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1168
            $subsetted = false;
1169
        }
1170
        return get_defined_vars();
1171
    }
1172
    
1173
    protected function isAscii($s)
1174
    {
1175
        // Test if string is ASCII
1176
        $nb = strlen($s);
1177
        for ($i = 0; $i < $nb; $i++) {
1178
            if (ord($s[$i]) > 127) {
1179
                return false;
1180
            }
1181
        }
1182
        return true;
1183
    }
1184
    
1185
    protected function httpencode($param, $value, $isUTF8)
1186
    {
1187
        // Encode HTTP header field parameter
1188
        if ($this->isAscii($value)) {
1189
            return $param . '="' . $value . '"';
1190
        }
1191
        if (!$isUTF8) {
1192
            $value = utf8_encode($value);
1193
        }
1194
        if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
1195
            return $param . '="' . rawurlencode($value) . '"';
1196
        } else {
1197
            return $param . "*=UTF-8''" . rawurlencode($value);
1198
        }
1199
    }
1200
    
1201
    protected function utf8ToUtf16($s)
1202
    {
1203
        // Convert UTF-8 to UTF-16BE with BOM
1204
        $res = "\xFE\xFF";
1205
        $nb = strlen($s);
1206
        $i = 0;
1207
        while ($i < $nb) {
1208
            $c1 = ord($s[$i++]);
1209
            if ($c1 >= 224) {
1210
                // 3-byte character
1211
                $c2 = ord($s[$i++]);
1212
                $c3 = ord($s[$i++]);
1213
                $res .= chr((($c1 & 0x0F) << 4) + (($c2 & 0x3C) >> 2));
1214
                $res .= chr((($c2 & 0x03) << 6) + ($c3 & 0x3F));
1215
            } elseif ($c1 >= 192) {
1216
                // 2-byte character
1217
                $c2 = ord($s[$i++]);
1218
                $res .= chr(($c1 & 0x1C) >> 2);
1219
                $res .= chr((($c1 & 0x03) << 6) + ($c2 & 0x3F));
1220
            } else {
1221
                // Single-byte character
1222
                $res .= "\0" . chr($c1);
1223
            }
1224
        }
1225
        return $res;
1226
    }
1227
    
1228
    protected function escape($s)
1229
    {
1230
        // Escape special characters
1231
        if (strpos($s, '(') !== false || strpos($s, ')') !== false || strpos($s, '\\') !== false || strpos($s, "\r") !== false) {
1232
            return str_replace(['\\', '(', ')', "\r"], ['\\\\', '\\(', '\\)', '\\r'], $s);
1233
        } else {
1234
            return $s;
1235
        }
1236
    }
1237
    
1238
    protected function textString($s)
1239
    {
1240
        // Format a text string
1241
        if (!$this->isAscii($s)) {
1242
            $s = $this->utf8ToUtf16($s);
1243
        }
1244
        return '(' . $this->escape($s) . ')';
1245
    }
1246
    
1247
    protected function doUnderLine($x, $y, $txt)
1248
    {
1249
        // Underline text
1250
        $up = $this->currentFont['up'];
1251
        $ut = $this->currentFont['ut'];
1252
        $w = $this->getStringWidth($txt) + $this->ws * substr_count($txt, ' ');
1253
        return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->fontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->fontSizePt);
1254
    }
1255
    
1256
    protected function parseJpg($file)
1257
    {
1258
        // Extract info from a JPEG file
1259
        $a = getimagesize($file);
1260
        if (!$a) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $a of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1261
            $this->error('Missing or incorrect image file: ' . $file);
1262
        }
1263
        if ($a[2] != 2) {
1264
            $this->error('Not a JPEG file: ' . $file);
1265
        }
1266
        if (!isset($a['channels']) || $a['channels'] == 3) {
1267
            $colspace = 'DeviceRGB';
1268
        } elseif ($a['channels'] == 4) {
1269
            $colspace = 'DeviceCMYK';
1270
        } else {
1271
            $colspace = 'DeviceGray';
1272
        }
1273
        $bpc = isset($a['bits']) ? $a['bits'] : 8;
1274
        $data = file_get_contents($file);
1275
        return ['w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data];
1276
    }
1277
    
1278
    protected function parsePng($file)
1279
    {
1280
        // Extract info from a PNG file
1281
        $f = fopen($file, 'rb');
1282
        if (!$f) {
1283
            $this->error('Can\'t open image file: ' . $file);
1284
        }
1285
        $info = $this->parsePngstream($f, $file);
1286
        fclose($f);
1287
        return $info;
1288
    }
1289
    
1290
    protected function parsePngstream($f, $file)
1291
    {
1292
        // Check signature
1293
        if ($this->readStream($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
1294
            $this->error('Not a PNG file: ' . $file);
1295
        }
1296
        // Read header chunk
1297
        $this->readStream($f, 4);
1298
        if ($this->readStream($f, 4) != 'IHDR') {
1299
            $this->error('Incorrect PNG file: ' . $file);
1300
        }
1301
        $w = $this->readInt($f);
1302
        $h = $this->readInt($f);
1303
        $bpc = ord($this->readStream($f, 1));
1304
        if ($bpc > 8) {
1305
            $this->error('16-bit depth not supported: ' . $file);
1306
        }
1307
        $ct = ord($this->readStream($f, 1));
1308
        if ($ct == 0 || $ct == 4) {
1309
            $colspace = 'DeviceGray';
1310
        } elseif ($ct == 2 || $ct == 6) {
1311
            $colspace = 'DeviceRGB';
1312
        } elseif ($ct == 3) {
1313
            $colspace = 'Indexed';
1314
        } else {
1315
            $this->error('Unknown color type: ' . $file);
1316
        }
1317
        if (ord($this->readStream($f, 1)) != 0) {
1318
            $this->error('Unknown compression method: ' . $file);
1319
        }
1320
        if (ord($this->readStream($f, 1)) != 0) {
1321
            $this->error('Unknown filter method: ' . $file);
1322
        }
1323
        if (ord($this->readStream($f, 1)) != 0) {
1324
            $this->error('Interlacing not supported: ' . $file);
1325
        }
1326
        $this->readStream($f, 4);
1327
        $dp = '/Predictor 15 /Colors ' . ($colspace == 'DeviceRGB' ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w;
0 ignored issues
show
Bug introduced by
The variable $colspace 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...
1328
        // Scan chunks looking for palette, transparency and image data
1329
        $pal = '';
1330
        $trns = '';
1331
        $data = '';
1332
        do {
1333
            $n = $this->readInt($f);
1334
            $type = $this->readStream($f, 4);
1335
            if ($type == 'PLTE') {
1336
                // Read palette
1337
                $pal = $this->readStream($f, $n);
1338
                $this->readStream($f, 4);
1339
            } elseif ($type == 'tRNS') {
1340
                // Read transparency info
1341
                $t = $this->readStream($f, $n);
1342
                if ($ct == 0) {
1343
                    $trns = [ord(substr($t, 1, 1))];
1344
                } elseif ($ct == 2) {
1345
                    $trns = [ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))];
1346
                } else {
1347
                    $pos = strpos($t, chr(0));
1348
                    if ($pos !== false) {
1349
                        $trns = [$pos];
1350
                    }
1351
                }
1352
                $this->readStream($f, 4);
1353
            } elseif ($type == 'IDAT') {
1354
                // Read image data block
1355
                $data .= $this->readStream($f, $n);
1356
                $this->readStream($f, 4);
1357
            } elseif ($type == 'IEND') {
1358
                break;
1359
            } else {
1360
                $this->readStream($f, $n + 4);
1361
            }
1362
        } while ($n);
1363
        if ($colspace == 'Indexed' && empty($pal)) {
1364
            $this->error('Missing palette in ' . $file);
1365
        }
1366
        $info = ['w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'dp' => $dp, 'pal' => $pal, 'trns' => $trns];
1367
        if ($ct >= 4) {
1368
            // Extract alpha channel
1369
            if (!function_exists('gzuncompress')) {
1370
                $this->error('Zlib not available, can\'t handle alpha channel: ' . $file);
1371
            }
1372
            $data = gzuncompress($data);
1373
            $color = '';
1374
            $alpha = '';
1375
            if ($ct == 4) {
1376
                // Gray image
1377
                $len = 2 * $w;
1378
                for ($i = 0; $i < $h; $i++) {
1379
                    $pos = (1 + $len) * $i;
1380
                    $color .= $data[$pos];
1381
                    $alpha .= $data[$pos];
1382
                    $line = substr($data, $pos + 1, $len);
1383
                    $color .= preg_replace('/(.)./s', '$1', $line);
1384
                    $alpha .= preg_replace('/.(.)/s', '$1', $line);
1385
                }
1386
            } else {
1387
                // RGB image
1388
                $len = 4 * $w;
1389
                for ($i = 0; $i < $h; $i++) {
1390
                    $pos = (1 + $len) * $i;
1391
                    $color .= $data[$pos];
1392
                    $alpha .= $data[$pos];
1393
                    $line = substr($data, $pos + 1, $len);
1394
                    $color .= preg_replace('/(.{3})./s', '$1', $line);
1395
                    $alpha .= preg_replace('/.{3}(.)/s', '$1', $line);
1396
                }
1397
            }
1398
            unset($data);
1399
            $data = gzcompress($color);
1400
            $info['smask'] = gzcompress($alpha);
1401
            $this->withAlpha = true;
1402
            if ($this->pdfVersion < '1.4') {
1403
                $this->pdfVersion = '1.4';
1404
            }
1405
        }
1406
        $info['data'] = $data;
1407
        return $info;
1408
    }
1409
    
1410
    protected function readStream($f, $n)
1411
    {
1412
        // Read n bytes from stream
1413
        $res = '';
1414
        while ($n > 0 && !feof($f)) {
1415
            $s = fread($f, $n);
1416
            if ($s === false) {
1417
                $this->error('Error while reading stream');
1418
            }
1419
            $n -= strlen($s);
1420
            $res .= $s;
1421
        }
1422
        if ($n > 0) {
1423
            $this->error('Unexpected end of stream');
1424
        }
1425
        return $res;
1426
    }
1427
    
1428
    protected function readInt($f)
1429
    {
1430
        // Read a 4-byte integer from stream
1431
        $a = unpack('Ni', $this->readStream($f, 4));
1432
        return $a['i'];
1433
    }
1434
    
1435
    protected function parseGif($file)
1436
    {
1437
        // Extract info from a GIF file (via PNG conversion)
1438
        if (!function_exists('imagepng')) {
1439
            $this->error('GD extension is required for GIF support');
1440
        }
1441
        if (!function_exists('imagecreatefromgif')) {
1442
            $this->error('GD has no GIF read support');
1443
        }
1444
        $im = imagecreatefromgif($file);
1445
        if (!$im) {
1446
            $this->error('Missing or incorrect image file: ' . $file);
1447
        }
1448
        imageinterlace($im, 0);
1449
        ob_start();
1450
        imagepng($im);
1451
        $data = ob_get_clean();
1452
        imagedestroy($im);
1453
        $f = fopen('php://temp', 'rb+');
1454
        if (!$f) {
1455
            $this->error('Unable to create memory stream');
1456
        }
1457
        fwrite($f, $data);
1458
        rewind($f);
1459
        $info = $this->parsePngstream($f, $file);
1460
        fclose($f);
1461
        return $info;
1462
    }
1463
    
1464
    protected function out($s)
1465
    {
1466
        // Add a line to the document
1467
        if ($this->state == 2) {
1468
            $this->pages[$this->page] .= $s . "\n";
1469
        } elseif ($this->state == 1) {
1470
            $this->put($s);
1471
        } elseif ($this->state == 0) {
1472
            $this->error('No page has been added yet');
1473
        } elseif ($this->state == 3) {
1474
            $this->error('The document is closed');
1475
        }
1476
    }
1477
    
1478
    protected function put($s)
1479
    {
1480
        $this->buffer .= $s . "\n";
1481
    }
1482
    
1483
    protected function getOffset()
1484
    {
1485
        return strlen($this->buffer);
1486
    }
1487
    
1488
    protected function newObj($n = null)
1489
    {
1490
        // Begin a new object
1491
        if ($n === null) {
1492
            $n = ++$this->n;
1493
        }
1494
        $this->offsets[$n] = $this->getOffset();
1495
        $this->put($n . ' 0 obj');
1496
    }
1497
    
1498
    protected function putStream($data)
1499
    {
1500
        $this->put('stream');
1501
        $this->put($data);
1502
        $this->put('endstream');
1503
    }
1504
    
1505
    protected function putStreamobject($data)
1506
    {
1507
        if ($this->compress) {
1508
            $entries = '/Filter /FlateDecode ';
1509
            $data = gzcompress($data);
1510
        } else {
1511
            $entries = '';
1512
        }
1513
        $entries .= '/Length ' . strlen($data);
1514
        $this->newObj();
1515
        $this->put('<<' . $entries . '>>');
1516
        $this->putStream($data);
1517
        $this->put('endobj');
1518
    }
1519
    
1520
    protected function putpage($n)
1521
    {
1522
        $this->newObj();
1523
        $this->put('<</Type /Page');
1524
        $this->put('/Parent 1 0 R');
1525
        if (isset($this->pageInfo[$n]['size'])) {
1526
            $this->put(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pageInfo[$n]['size'][0], $this->pageInfo[$n]['size'][1]));
1527
        }
1528
        if (isset($this->pageInfo[$n]['rotation'])) {
1529
            $this->put('/Rotate ' . $this->pageInfo[$n]['rotation']);
1530
        }
1531
        $this->put('/Resources 2 0 R');
1532
        if (isset($this->pageLinks[$n])) {
1533
            // Links
1534
            $annots = '/Annots [';
1535
            foreach ($this->pageLinks[$n] as $pl) {
1536
                $rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
1537
                $annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
1538
                if (is_string($pl[4])) {
1539
                    $annots .= '/A <</S /URI /URI ' . $this->textString($pl[4]) . '>>>>';
1540
                } else {
1541
                    $l = $this->links[$pl[4]];
1542
                    if (isset($this->pageInfo[$l[0]]['size'])) {
1543
                        $h = $this->pageInfo[$l[0]]['size'][1];
1544
                    } else {
1545
                        $h = ($this->defOrientation == 'P') ? $this->defPageSize[1] * $this->k : $this->defPageSize[0] * $this->k;
1546
                    }
1547
                    $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->pageInfo[$l[0]]['n'], $h - $l[1] * $this->k);
1548
                }
1549
            }
1550
            $this->put($annots . ']');
1551
        }
1552
        if ($this->withAlpha) {
1553
            $this->put('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
1554
        }
1555
        $this->put('/Contents ' . ($this->n + 1) . ' 0 R>>');
1556
        $this->put('endobj');
1557
        // Page content
1558
        if (!empty($this->aliasNbPages)) {
1559
            $this->pages[$n] = str_replace($this->aliasNbPages, $this->page, $this->pages[$n]);
1560
        }
1561
        $this->putStreamobject($this->pages[$n]);
1562
    }
1563
    
1564
    protected function putPages()
1565
    {
1566
        $nb = $this->page;
1567
        for ($n = 1; $n <= $nb; $n++) {
1568
            $this->pageInfo[$n]['n'] = $this->n + 1 + 2 * ($n - 1);
1569
        }
1570
        for ($n = 1; $n <= $nb; $n++) {
1571
            $this->putpage($n);
1572
        }
1573
        // Pages root
1574
        $this->newObj(1);
1575
        $this->put('<</Type /Pages');
1576
        $kids = '/Kids [';
1577
        for ($n = 1; $n <= $nb; $n++)
1578
            $kids .= $this->pageInfo[$n]['n'] . ' 0 R ';
1579
        $this->put($kids . ']');
1580
        $this->put('/Count ' . $nb);
1581
        if ($this->defOrientation == 'P') {
1582
            $w = $this->defPageSize[0];
1583
            $h = $this->defPageSize[1];
1584
        } else {
1585
            $w = $this->defPageSize[1];
1586
            $h = $this->defPageSize[0];
1587
        }
1588
        $this->put(sprintf('/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k));
1589
        $this->put('>>');
1590
        $this->put('endobj');
1591
    }
1592
    
1593
    protected function putFonts()
1594
    {
1595
        foreach ($this->fontFiles as $file => $info) {
1596
            // Font file embedding
1597
            $this->newObj();
1598
            $this->fontFiles[$file]['n'] = $this->n;
1599
            $font = file_get_contents($this->fontpath . $file, true);
1600
            if (!$font) {
1601
                $this->error('Font file not found: ' . $file);
1602
            }
1603
            $compressed = (substr($file, -2) == '.z');
1604
            if (!$compressed && isset($info['length2'])) {
1605
                $font = substr($font, 6, $info['length1']) . substr($font, 6 + $info['length1'] + 6, $info['length2']);
1606
            }
1607
            $this->put('<</Length ' . strlen($font));
1608
            if ($compressed) {
1609
                $this->put('/Filter /FlateDecode');
1610
            }
1611
            $this->put('/Length1 ' . $info['length1']);
1612
            if (isset($info['length2'])) {
1613
                $this->put('/Length2 ' . $info['length2'] . ' /Length3 0');
1614
            }
1615
            $this->put('>>');
1616
            $this->putStream($font);
1617
            $this->put('endobj');
1618
        }
1619
        foreach ($this->fonts as $k => $font) {
1620
            // Encoding
1621
            if (isset($font['diff'])) {
1622
                if (!isset($this->encodings[$font['enc']])) {
1623
                    $this->newObj();
1624
                    $this->put('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $font['diff'] . ']>>');
1625
                    $this->put('endobj');
1626
                    $this->encodings[$font['enc']] = $this->n;
1627
                }
1628
            }
1629
            // ToUnicode CMap
1630
            if (isset($font['uv'])) {
1631
                if (isset($font['enc'])) {
1632
                    $cmapkey = $font['enc'];
1633
                } else {
1634
                    $cmapkey = $font['name'];
1635
                }
1636
                if (!isset($this->cmaps[$cmapkey])) {
1637
                    $cmap = $this->toUnicodeCmap($font['uv']);
1638
                    $this->putStreamobject($cmap);
1639
                    $this->cmaps[$cmapkey] = $this->n;
1640
                }
1641
            }
1642
            // Font object
1643
            $this->fonts[$k]['n'] = $this->n + 1;
1644
            $type = $font['type'];
1645
            $name = $font['name'];
1646
            if ($font['subsetted']) {
1647
                $name = 'AAAAAA+' . $name;
1648
            }
1649
            if ($type == 'Core') {
1650
                // Core font
1651
                $this->newObj();
1652
                $this->put('<</Type /Font');
1653
                $this->put('/BaseFont /' . $name);
1654
                $this->put('/Subtype /Type1');
1655
                if ($name != 'Symbol' && $name != 'ZapfDingbats') {
1656
                    $this->put('/Encoding /WinAnsiEncoding');
1657
                }
1658
                if (isset($font['uv'])) {
1659
                    $this->put('/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R');
0 ignored issues
show
Bug introduced by
The variable $cmapkey 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...
1660
                }
1661
                $this->put('>>');
1662
                $this->put('endobj');
1663
            } elseif ($type == 'Type1' || $type == 'TrueType') {
1664
                // Additional Type1 or TrueType/OpenType font
1665
                $this->newObj();
1666
                $this->put('<</Type /Font');
1667
                $this->put('/BaseFont /' . $name);
1668
                $this->put('/Subtype /' . $type);
1669
                $this->put('/FirstChar 32 /LastChar 255');
1670
                $this->put('/Widths ' . ($this->n + 1) . ' 0 R');
1671
                $this->put('/FontDescriptor ' . ($this->n + 2) . ' 0 R');
1672
                if (isset($font['diff'])) {
1673
                    $this->put('/Encoding ' . $this->encodings[$font['enc']] . ' 0 R');
1674
                } else {
1675
                    $this->put('/Encoding /WinAnsiEncoding');
1676
                }
1677
                if (isset($font['uv'])) {
1678
                    $this->put('/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R');
1679
                }
1680
                $this->put('>>');
1681
                $this->put('endobj');
1682
                // Widths
1683
                $this->newObj();
1684
                $cw = &$font['cw'];
1685
                $s = '[';
1686
                for ($i = 32; $i <= 255; $i++) {
1687
                    $s .= $cw[chr($i)] . ' ';
1688
                }
1689
                $this->put($s . ']');
1690
                $this->put('endobj');
1691
                // Descriptor
1692
                $this->newObj();
1693
                $s = '<</Type /FontDescriptor /FontName /' . $name;
1694
                foreach ($font['desc'] as $k => $v) {
1695
                    $s .= ' /' . $k . ' ' . $v;
1696
                }
1697
                if (!empty($font['file'])) {
1698
                    $s .= ' /FontFile' . ($type == 'Type1' ? '' : '2') . ' ' . $this->fontFiles[$font['file']]['n'] . ' 0 R';
1699
                }
1700
                $this->put($s . '>>');
1701
                $this->put('endobj');
1702
            } else {
1703
                // Allow for additional types
1704
                $mtd = 'put' . strtolower($type);
1705
                if (!method_exists($this, $mtd)) {
1706
                    $this->error('Unsupported font type: ' . $type);
1707
                }
1708
                $this->$mtd($font);
1709
            }
1710
        }
1711
    }
1712
    
1713
    protected function toUnicodeCmap($uv)
1714
    {
1715
        $ranges = '';
1716
        $nbr = 0;
1717
        $chars = '';
1718
        $nbc = 0;
1719
        foreach ($uv as $c => $v) {
1720
            if (is_array($v)) {
1721
                $ranges .= sprintf("<%02X> <%02X> <%04X>\n", $c, $c + $v[1] - 1, $v[0]);
1722
                $nbr++;
1723
            } else {
1724
                $chars .= sprintf("<%02X> <%04X>\n", $c, $v);
1725
                $nbc++;
1726
            }
1727
        }
1728
        $s = "/CIDInit /ProcSet findresource begin\n";
1729
        $s .= "12 dict begin\n";
1730
        $s .= "begincmap\n";
1731
        $s .= "/CIDSystemInfo\n";
1732
        $s .= "<</Registry (Adobe)\n";
1733
        $s .= "/Ordering (UCS)\n";
1734
        $s .= "/Supplement 0\n";
1735
        $s .= ">> def\n";
1736
        $s .= "/CMapName /Adobe-Identity-UCS def\n";
1737
        $s .= "/CMapType 2 def\n";
1738
        $s .= "1 begincodespacerange\n";
1739
        $s .= "<00> <FF>\n";
1740
        $s .= "endcodespacerange\n";
1741
        if ($nbr > 0) {
1742
            $s .= "$nbr beginbfrange\n";
1743
            $s .= $ranges;
1744
            $s .= "endbfrange\n";
1745
        }
1746
        if ($nbc > 0) {
1747
            $s .= "$nbc beginbfchar\n";
1748
            $s .= $chars;
1749
            $s .= "endbfchar\n";
1750
        }
1751
        $s .= "endcmap\n";
1752
        $s .= "CMapName currentdict /CMap defineresource pop\n";
1753
        $s .= "end\n";
1754
        $s .= "end";
1755
        return $s;
1756
    }
1757
    
1758
    protected function putImages()
1759
    {
1760
        foreach (array_keys($this->images) as $file) {
1761
            $this->putImage($this->images[$file]);
1762
            unset($this->images[$file]['data']);
1763
            unset($this->images[$file]['smask']);
1764
        }
1765
    }
1766
    
1767
    protected function putImage(&$info)
1768
    {
1769
        $this->newObj();
1770
        $info['n'] = $this->n;
1771
        $this->put('<</Type /XObject');
1772
        $this->put('/Subtype /Image');
1773
        $this->put('/Width ' . $info['w']);
1774
        $this->put('/Height ' . $info['h']);
1775
        if ($info['cs'] == 'Indexed') {
1776
            $this->put('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal']) / 3 - 1) . ' ' . ($this->n + 1) . ' 0 R]');
1777
        } else {
1778
            $this->put('/ColorSpace /' . $info['cs']);
1779
            if ($info['cs'] == 'DeviceCMYK')
1780
                $this->put('/Decode [1 0 1 0 1 0 1 0]');
1781
        }
1782
        $this->put('/BitsPerComponent ' . $info['bpc']);
1783
        if (isset($info['f'])) {
1784
            $this->put('/Filter /' . $info['f']);
1785
        }
1786
        if (isset($info['dp'])) {
1787
            $this->put('/DecodeParms <<' . $info['dp'] . '>>');
1788
        }
1789
        if (isset($info['trns']) && is_array($info['trns'])) {
1790
            $trns = '';
1791
            for ($i = 0; $i < count($info['trns']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1792
                $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
1793
            }
1794
            $this->put('/Mask [' . $trns . ']');
1795
        }
1796
        if (isset($info['smask'])) {
1797
            $this->put('/SMask ' . ($this->n + 1) . ' 0 R');
1798
        }
1799
        $this->put('/Length ' . strlen($info['data']) . '>>');
1800
        $this->putStream($info['data']);
1801
        $this->put('endobj');
1802
        // Soft mask
1803
        if (isset($info['smask'])) {
1804
            $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns ' . $info['w'];
1805
            $smask = ['w' => $info['w'], 'h' => $info['h'], 'cs' => 'DeviceGray', 'bpc' => 8, 'f' => $info['f'], 'dp' => $dp, 'data' => $info['smask']];
1806
            $this->putImage($smask);
1807
        }
1808
        // Palette
1809
        if ($info['cs'] == 'Indexed') {
1810
            $this->putStreamobject($info['pal']);
1811
        }
1812
    }
1813
    
1814
    protected function putXobjectDict()
1815
    {
1816
        foreach ($this->images as $image) {
1817
            $this->put('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
1818
        }
1819
    }
1820
    
1821
    protected function putResourceDict()
1822
    {
1823
        $this->put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
1824
        $this->put('/Font <<');
1825
        foreach ($this->fonts as $font) {
1826
            $this->put('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
1827
        }
1828
        $this->put('>>');
1829
        $this->put('/XObject <<');
1830
        $this->putXobjectDict();
1831
        $this->put('>>');
1832
    }
1833
    
1834
    protected function putResources()
1835
    {
1836
        $this->putFonts();
1837
        $this->putImages();
1838
        // Resource dictionary
1839
        $this->newObj(2);
1840
        $this->put('<<');
1841
        $this->putResourceDict();
1842
        $this->put('>>');
1843
        $this->put('endobj');
1844
    }
1845
    
1846
    protected function putInfo()
1847
    {
1848
        $this->metadata['Producer'] = 'FPDF ' . FPDF_VERSION;
1849
        $this->metadata['CreationDate'] = 'D:' . @date('YmdHis');
1850
        foreach ($this->metadata as $key => $value) {
1851
            $this->put('/' . $key . ' ' . $this->textString($value));
1852
        }
1853
    }
1854
    
1855
    protected function putCatalog()
1856
    {
1857
        $n = $this->pageInfo[1]['n'];
1858
        $this->put('/Type /Catalog');
1859
        $this->put('/Pages 1 0 R');
1860
        if ($this->zoomMode == 'fullpage') {
1861
            $this->put('/OpenAction [' . $n . ' 0 R /Fit]');
1862
        } elseif ($this->zoomMode == 'fullwidth') {
1863
            $this->put('/OpenAction [' . $n . ' 0 R /FitH null]');
1864
        } elseif ($this->zoomMode == 'real') {
1865
            $this->put('/OpenAction [' . $n . ' 0 R /XYZ null null 1]');
1866
        } elseif (!is_string($this->zoomMode)) {
1867
            $this->put('/OpenAction [' . $n . ' 0 R /XYZ null null ' . sprintf('%.2F', $this->zoomMode / 100) . ']');
1868
        }
1869
        if ($this->layoutMode == 'single') {
1870
            $this->put('/PageLayout /SinglePage');
1871
        } elseif ($this->layoutMode == 'continuous') {
1872
            $this->put('/PageLayout /OneColumn');
1873
        } elseif ($this->layoutMode == 'two') {
1874
            $this->put('/PageLayout /TwoColumnLeft');
1875
        }
1876
    }
1877
    
1878
    protected function putHeader()
1879
    {
1880
        $this->put('%PDF-' . $this->pdfVersion);
1881
    }
1882
    
1883
    protected function putTrailer()
1884
    {
1885
        $this->put('/Size ' . ($this->n + 1));
1886
        $this->put('/Root ' . $this->n . ' 0 R');
1887
        $this->put('/Info ' . ($this->n - 1) . ' 0 R');
1888
    }
1889
    
1890
    protected function endDoc()
1891
    {
1892
        $this->putHeader();
1893
        $this->putPages();
1894
        $this->putResources();
1895
        // Info
1896
        $this->newObj();
1897
        $this->put('<<');
1898
        $this->putInfo();
1899
        $this->put('>>');
1900
        $this->put('endobj');
1901
        // Catalog
1902
        $this->newObj();
1903
        $this->put('<<');
1904
        $this->putCatalog();
1905
        $this->put('>>');
1906
        $this->put('endobj');
1907
        // Cross-ref
1908
        $offset = $this->getOffset();
1909
        $this->put('xref');
1910
        $this->put('0 ' . ($this->n + 1));
1911
        $this->put('0000000000 65535 f ');
1912
        for ($i = 1; $i <= $this->n; $i++) {
1913
            $this->put(sprintf('%010d 00000 n ', $this->offsets[$i]));
1914
        }
1915
        // Trailer
1916
        $this->put('trailer');
1917
        $this->put('<<');
1918
        $this->putTrailer();
1919
        $this->put('>>');
1920
        $this->put('startxref');
1921
        $this->put($offset);
1922
        $this->put('%%EOF');
1923
        $this->state = 3;
1924
    }
1925
}
1926