Passed
Push — master ( 6f9ff4...75f13c )
by
unknown
01:05
created

PDFLib::setPDFLibParameter()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
/**
3
 * @package dompdf
4
 * @link    http://dompdf.github.com/
5
 * @author  Benj Carson <[email protected]>
6
 * @author  Helmut Tischer <[email protected]>
7
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
 */
9
10
namespace Dompdf\Adapter;
11
12
use Dompdf\Canvas;
13
use Dompdf\Dompdf;
14
use Dompdf\Helpers;
15
use Dompdf\Exception;
16
use Dompdf\Image\Cache;
17
use Dompdf\PhpEvaluator;
18
19
/**
20
 * PDF rendering interface
21
 *
22
 * Dompdf\Adapter\PDFLib provides a simple, stateless interface to the one
23
 * provided by PDFLib.
24
 *
25
 * Unless otherwise mentioned, all dimensions are in points (1/72 in).
26
 * The coordinate origin is in the top left corner and y values
27
 * increase downwards.
28
 *
29
 * See {@link http://www.pdflib.com/} for more complete documentation
30
 * on the underlying PDFlib functions.
31
 *
32
 * @package dompdf
33
 */
34
class PDFLib implements Canvas
35
{
36
37
    /**
38
     * Dimensions of paper sizes in points
39
     *
40
     * @var array;
41
     */
42
    static public $PAPER_SIZES = array(); // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below.
43
44
    /**
45
     * Whether to create PDFs in memory or on disk
46
     *
47
     * @var bool
48
     */
49
    static $IN_MEMORY = true;
50
51
    /**
52
     * Saves the major version of PDFLib for compatibility requests
53
     *
54
     * @var null|int
55
     */
56
    static private $MAJOR_VERSION = null;
57
58
59
    /**
60
     * Transforms the list of native fonts into PDFLib compatible names (casesensitive)
61
     *
62
     * @var array
63
     */
64
    static public $nativeFontsTpPDFLib = array(
65
        "courier"               => "Courier",
66
        "courier-bold"          => "Courier-Bold",
67
        "courier-oblique"       => "Courier-Oblique",
68
        "courier-boldoblique"   => "Courier-BoldOblique",
69
        "helvetica"             => "Helvetica",
70
        "helvetica-bold"        => "Helvetica-Bold",
71
        "helvetica-oblique"     => "Helvetica-Oblique",
72
        "helvetica-boldoblique" => "Helvetica-BoldOblique",
73
        "times"                 => "Times-Roman",
74
        "times-roman"           => "Times-Roman",
75
        "times-bold"            => "Times-Bold",
76
        "times-italic"          => "Times-Italic",
77
        "times-bolditalic"      => "Times-BoldItalic",
78
        "symbol"                => "Symbol",
79
        "zapfdinbats"           => "ZapfDingbats",
80
        "zapfdingbats"          => "ZapfDingbats",
81
    );
82
83
    /**
84
     * @var \Dompdf\Dompdf
85
     */
86
    private $_dompdf;
87
88
    /**
89
     * Instance of PDFLib class
90
     *
91
     * @var \PDFLib
92
     */
93
    private $_pdf;
94
95
    /**
96
     * Name of temporary file used for PDFs created on disk
97
     *
98
     * @var string
99
     */
100
    private $_file;
101
102
    /**
103
     * PDF width, in points
104
     *
105
     * @var float
106
     */
107
    private $_width;
108
109
    /**
110
     * PDF height, in points
111
     *
112
     * @var float
113
     */
114
    private $_height;
115
116
    /**
117
     * Last fill color used
118
     *
119
     * @var array
120
     */
121
    private $_last_fill_color;
122
123
    /**
124
     * Last stroke color used
125
     *
126
     * @var array
127
     */
128
    private $_last_stroke_color;
129
130
    /**
131
     * The current opacity level
132
     *
133
     * @var array
134
     */
135
    private $_current_opacity;
136
137
    /**
138
     * Cache of image handles
139
     *
140
     * @var array
141
     */
142
    private $_imgs;
143
144
    /**
145
     * Cache of font handles
146
     *
147
     * @var array
148
     */
149
    private $_fonts;
150
151
    /**
152
     * Cache of fontFile checks
153
     *
154
     * @var array
155
     */
156
    private $_fontsFiles;
157
158
    /**
159
     * List of objects (templates) to add to multiple pages
160
     *
161
     * @var array
162
     */
163
    private $_objs;
164
165
    /**
166
     * List of gstate objects created for this PDF (for reuse)
167
     *
168
     * @var array
169
     */
170
    private $_gstates = array();
171
172
    /**
173
     * Current page number
174
     *
175
     * @var int
176
     */
177
    private $_page_number;
178
179
    /**
180
     * Total number of pages
181
     *
182
     * @var int
183
     */
184
    private $_page_count;
185
186
    /**
187
     * Text to display on every page
188
     *
189
     * @var array
190
     */
191
    private $_page_text;
192
193
    /**
194
     * Array of pages for accesing after rendering is initially complete
195
     *
196
     * @var array
197
     */
198
    private $_pages;
199
200
    /**
201
     * Class constructor
202
     *
203
     * @param string|array $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or
204
     *                            an array(xmin,ymin,xmax,ymax)
205
     * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
206
     * @param Dompdf $dompdf
207
     */
208
    public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf)
209
    {
210 View Code Duplication
        if (is_array($paper)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
211
            $size = $paper;
212
        } elseif (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
213
            $size = self::$PAPER_SIZES[mb_strtolower($paper)];
214
        } else {
215
            $size = self::$PAPER_SIZES["letter"];
216
        }
217
218 View Code Duplication
        if (mb_strtolower($orientation) === "landscape") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
219
            list($size[2], $size[3]) = array($size[3], $size[2]);
220
        }
221
222
        $this->_width = $size[2] - $size[0];
0 ignored issues
show
Documentation Bug introduced by
It seems like $size[2] - $size[0] can also be of type integer. However, the property $_width is declared as type double. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
223
        $this->_height = $size[3] - $size[1];
0 ignored issues
show
Documentation Bug introduced by
It seems like $size[3] - $size[1] can also be of type integer. However, the property $_height is declared as type double. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
224
225
        $this->_dompdf = $dompdf;
226
227
        $this->_pdf = new \PDFLib();
228
229
        $license = $dompdf->getOptions()->getPdflibLicense();
230
        if (strlen($license) > 0) {
231
            $this->setPDFLibParameter("license", $license);
232
        }
233
234
        $this->setPDFLibParameter("textformat", "utf8");
235
        if ($this->getPDFLibMajorVersion() >= 7) {
236
            $this->setPDFLibParameter("errorpolicy", "return");
237
            //            $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}');
238
            //            $this->_pdf->set_option('errorpolicy=exception');
239
        } else {
240
            $this->setPDFLibParameter("fontwarning", "false");
241
        }
242
243
        $searchPath = $this->_dompdf->getOptions()->getFontDir();
244
        if (empty($searchPath) === false) {
245
            $this->_pdf->set_option('searchpath={' . $searchPath . '}');
246
        }
247
248
        // fetch PDFLib version information for the producer field
249
        $this->_pdf->set_info("Producer Addendum", sprintf("%s + PDFLib %s", $dompdf->version, $this->getPDFLibMajorVersion()));
0 ignored issues
show
Documentation introduced by
The property $version is declared private in Dompdf\Dompdf. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
250
251
        // Silence pedantic warnings about missing TZ settings
252
        $tz = @date_default_timezone_get();
253
        date_default_timezone_set("UTC");
254
        $this->_pdf->set_info("Date", date("Y-m-d"));
255
        date_default_timezone_set($tz);
256
257
        if (self::$IN_MEMORY) {
258
            $this->_pdf->begin_document("", "");
259
        } else {
260
            $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
261
            $tmp_name = @tempnam($tmp_dir, "libdompdf_pdf_");
262
            @unlink($tmp_name);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
263
            $this->_file = "$tmp_name.pdf";
264
            $this->_pdf->begin_document($this->_file, "");
265
        }
266
267
        $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
268
269
        $this->_page_number = $this->_page_count = 1;
270
        $this->_page_text = array();
271
272
        $this->_imgs = array();
273
        $this->_fonts = array();
274
        $this->_objs = array();
275
    }
276
277
    /**
278
     * @return Dompdf
279
     */
280
    function get_dompdf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

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

Loading history...
281
    {
282
        return $this->_dompdf;
283
    }
284
285
    /**
286
     * Close the pdf
287
     */
288
    protected function _close()
289
    {
290
        $this->_place_objects();
291
292
        // Close all pages
293
        $this->_pdf->suspend_page("");
294
        for ($p = 1; $p <= $this->_page_count; $p++) {
295
            $this->_pdf->resume_page("pagenumber=$p");
296
            $this->_pdf->end_page_ext("");
297
        }
298
299
        $this->_pdf->end_document("");
300
    }
301
302
303
    /**
304
     * Returns the PDFLib instance
305
     *
306
     * @return PDFLib
307
     */
308
    public function get_pdflib()
309
    {
310
        return $this->_pdf;
311
    }
312
313
    /**
314
     * Add meta information to the PDF
315
     *
316
     * @param string $label label of the value (Creator, Producter, etc.)
317
     * @param string $value the text to set
318
     */
319
    public function add_info($label, $value)
320
    {
321
        $this->_pdf->set_info($label, $value);
322
    }
323
324
    /**
325
     * Opens a new 'object' (template in PDFLib-speak)
326
     *
327
     * While an object is open, all drawing actions are recorded to the
328
     * object instead of being drawn on the current page.  Objects can
329
     * be added later to a specific page or to several pages.
330
     *
331
     * The return value is an integer ID for the new object.
332
     *
333
     * @see PDFLib::close_object()
334
     * @see PDFLib::add_object()
335
     *
336
     * @return int
337
     */
338
    public function open_object()
339
    {
340
        $this->_pdf->suspend_page("");
341
        $ret = $this->_pdf->begin_template($this->_width, $this->_height);
342
        $this->_pdf->save();
343
        $this->_objs[$ret] = array("start_page" => $this->_page_number);
344
345
        return $ret;
346
    }
347
348
    /**
349
     * Reopen an existing object (NOT IMPLEMENTED)
350
     * PDFLib does not seem to support reopening templates.
351
     *
352
     * @param int $object the ID of a previously opened object
353
     *
354
     * @throws Exception
355
     * @return void
356
     */
357
    public function reopen_object($object)
0 ignored issues
show
Unused Code introduced by
The parameter $object 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...
358
    {
359
        throw new Exception("PDFLib does not support reopening objects.");
360
    }
361
362
    /**
363
     * Close the current template
364
     *
365
     * @see PDFLib::open_object()
366
     */
367
    public function close_object()
368
    {
369
        $this->_pdf->restore();
370
        $this->_pdf->end_template();
371
        $this->_pdf->resume_page("pagenumber=" . $this->_page_number);
372
    }
373
374
    /**
375
     * Adds the specified object to the document
376
     *
377
     * $where can be one of:
378
     * - 'add' add to current page only
379
     * - 'all' add to every page from the current one onwards
380
     * - 'odd' add to all odd numbered pages from now on
381
     * - 'even' add to all even numbered pages from now on
382
     * - 'next' add the object to the next page only
383
     * - 'nextodd' add to all odd numbered pages from the next one
384
     * - 'nexteven' add to all even numbered pages from the next one
385
     *
386
     * @param int    $object the object handle returned by open_object()
387
     * @param string $where
388
     */
389
    public function add_object($object, $where = 'all')
390
    {
391
392
        if (mb_strpos($where, "next") !== false) {
393
            $this->_objs[$object]["start_page"]++;
394
            $where = str_replace("next", "", $where);
395
            if ($where == "") {
396
                $where = "add";
397
            }
398
        }
399
400
        $this->_objs[$object]["where"] = $where;
401
    }
402
403
    /**
404
     * Stops the specified template from appearing in the document.
405
     *
406
     * The object will stop being displayed on the page following the
407
     * current one.
408
     *
409
     * @param int $object
410
     */
411
    public function stop_object($object)
412
    {
413
414
        if (!isset($this->_objs[$object])) {
415
            return;
416
        }
417
418
        $start = $this->_objs[$object]["start_page"];
419
        $where = $this->_objs[$object]["where"];
420
421
        // Place the object on this page if required
422 View Code Duplication
        if ($this->_page_number >= $start &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
423
            (($this->_page_number % 2 == 0 && $where === "even") ||
424
                ($this->_page_number % 2 == 1 && $where === "odd") ||
425
                ($where === "all"))
426
        ) {
427
            $this->_pdf->fit_image($object, 0, 0, "");
428
        }
429
430
        $this->_objs[$object] = null;
431
        unset($this->_objs[$object]);
432
    }
433
434
    /**
435
     * Add all active objects to the current page
436
     */
437
    protected function _place_objects()
438
    {
439
440
        foreach ($this->_objs as $obj => $props) {
441
            $start = $props["start_page"];
442
            $where = $props["where"];
443
444
            // Place the object on this page if required
445 View Code Duplication
            if ($this->_page_number >= $start &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
446
                (($this->_page_number % 2 == 0 && $where === "even") ||
447
                    ($this->_page_number % 2 == 1 && $where === "odd") ||
448
                    ($where === "all"))
449
            ) {
450
                $this->_pdf->fit_image($obj, 0, 0, "");
451
            }
452
        }
453
454
    }
455
456
    /**
457
     * @return float|mixed
458
     */
459
    public function get_width()
460
    {
461
        return $this->_width;
462
    }
463
464
    /**
465
     * @return float|mixed
466
     */
467
    public function get_height()
468
    {
469
        return $this->_height;
470
    }
471
472
    /**
473
     * @return int
474
     */
475
    public function get_page_number()
476
    {
477
        return $this->_page_number;
478
    }
479
480
    /**
481
     * @return int
482
     */
483
    public function get_page_count()
484
    {
485
        return $this->_page_count;
486
    }
487
488
    /**
489
     * @param $num
490
     */
491
    public function set_page_number($num)
492
    {
493
        $this->_page_number = (int)$num;
494
    }
495
496
    /**
497
     * @param int $count
498
     */
499
    public function set_page_count($count)
500
    {
501
        $this->_page_count = (int)$count;
502
    }
503
504
    /**
505
     * Sets the line style
506
     *
507
     * @param float  $width
508
     * @param        $cap
509
     * @param string $join
510
     * @param array  $dash
511
     *
512
     * @return void
513
     */
514
    protected function _set_line_style($width, $cap, $join, $dash)
515
    {
516
        if (count($dash) == 1) {
517
            $dash[] = $dash[0];
518
        }
519
520
        if ($this->getPDFLibMajorVersion() >= 9) {
521
            if (count($dash) > 1) {
522
                $this->_pdf->set_graphics_option("dasharray={" . implode(" ", $dash) . "}");
523
            } else {
524
                $this->_pdf->set_graphics_option("dasharray=none");
525
            }
526
        } else {
527
            if (count($dash) > 1) {
528
                $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}");
529
            } else {
530
                $this->_pdf->setdash(0, 0);
531
            }
532
        }
533
534 View Code Duplication
        switch ($join) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
535
            case "miter":
536
                if ($this->getPDFLibMajorVersion() >= 9) {
537
                    $this->_pdf->set_graphics_option('linejoin=0');
538
                } else {
539
                    $this->_pdf->setlinejoin(0);
540
                }
541
                break;
542
543
            case "round":
544
                if ($this->getPDFLibMajorVersion() >= 9) {
545
                    $this->_pdf->set_graphics_option('linejoin=1');
546
                } else {
547
                    $this->_pdf->setlinejoin(1);
548
                }
549
                break;
550
551
            case "bevel":
552
                if ($this->getPDFLibMajorVersion() >= 9) {
553
                    $this->_pdf->set_graphics_option('linejoin=2');
554
                } else {
555
                    $this->_pdf->setlinejoin(2);
556
                }
557
                break;
558
559
            default:
560
                break;
561
        }
562
563 View Code Duplication
        switch ($cap) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
564
            case "butt":
565
                if ($this->getPDFLibMajorVersion() >= 9) {
566
                    $this->_pdf->set_graphics_option('linecap=0');
567
                } else {
568
                    $this->_pdf->setlinecap(0);
569
                }
570
                break;
571
572
            case "round":
573
                if ($this->getPDFLibMajorVersion() >= 9) {
574
                    $this->_pdf->set_graphics_option('linecap=1');
575
                } else {
576
                    $this->_pdf->setlinecap(1);
577
                }
578
                break;
579
580
            case "square":
581
                if ($this->getPDFLibMajorVersion() >= 9) {
582
                    $this->_pdf->set_graphics_option('linecap=2');
583
                } else {
584
                    $this->_pdf->setlinecap(2);
585
                }
586
                break;
587
588
            default:
589
                break;
590
        }
591
592
        $this->_pdf->setlinewidth($width);
593
    }
594
595
    /**
596
     * Sets the line color
597
     *
598
     * @param array $color array(r,g,b)
599
     */
600 View Code Duplication
    protected function _set_stroke_color($color)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
601
    {
602
        if ($this->_last_stroke_color == $color) {
603
            //return;
604
        }
605
606
        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
607
        if (isset($this->_current_opacity)) {
608
            $alpha *= $this->_current_opacity;
609
        }
610
611
        $this->_last_stroke_color = $color;
612
613
        if (isset($color[3])) {
614
            $type = "cmyk";
615
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
616
        } elseif (isset($color[2])) {
617
            $type = "rgb";
618
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
619
        } else {
620
            $type = "gray";
621
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
622
        }
623
624
        $this->_set_stroke_opacity($alpha);
625
        $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4);
626
    }
627
628
    /**
629
     * Sets the fill color
630
     *
631
     * @param array $color array(r,g,b)
632
     */
633 View Code Duplication
    protected function _set_fill_color($color)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
634
    {
635
        if ($this->_last_fill_color == $color) {
636
            return;
637
        }
638
639
        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
640
        if (isset($this->_current_opacity)) {
641
            $alpha *= $this->_current_opacity;
642
        }
643
644
        $this->_last_fill_color = $color;
645
646
        if (isset($color[3])) {
647
            $type = "cmyk";
648
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
649
        } elseif (isset($color[2])) {
650
            $type = "rgb";
651
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
652
        } else {
653
            $type = "gray";
654
            list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
655
        }
656
657
        $this->_set_fill_opacity($alpha);
658
        $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4);
659
    }
660
661
    /**
662
     * Sets the fill opacity
663
     *
664
     * @param $opacity
665
     * @param $mode
666
     */
667
    public function _set_fill_opacity($opacity, $mode = "Normal")
668
    {
669
        if ($mode === "Normal" && is_null($opacity) === false) {
670
            $this->_set_gstate("opacityfill=$opacity");
671
        }
672
    }
673
674
    /**
675
     * Sets the stroke opacity
676
     *
677
     * @param $opacity
678
     * @param $mode
679
     */
680
    public function _set_stroke_opacity($opacity, $mode = "Normal")
681
    {
682
        if ($mode === "Normal" && is_null($opacity) === false) {
683
            $this->_set_gstate("opacitystroke=$opacity");
684
        }
685
    }
686
687
    /**
688
     * Sets the opacity
689
     *
690
     * @param $opacity
691
     * @param $mode
692
     */
693
    public function set_opacity($opacity, $mode = "Normal")
694
    {
695
        if ($mode === "Normal" && is_null($opacity) === false) {
696
            $this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity");
697
            $this->_current_opacity = $opacity;
0 ignored issues
show
Documentation Bug introduced by
It seems like $opacity of type double is incompatible with the declared type array of property $_current_opacity.

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...
698
        }
699
    }
700
701
    /**
702
     * Sets the gstate
703
     *
704
     * @param $gstate_options
705
     * @return int
706
     */
707
    public function _set_gstate($gstate_options)
708
    {
709
        if (($gstate = array_search($gstate_options, $this->_gstates)) === false) {
710
            $gstate = $this->_pdf->create_gstate($gstate_options);
711
            $this->_gstates[$gstate] = $gstate_options;
712
        }
713
714
        return $this->_pdf->set_gstate($gstate);
715
    }
716
717
    public function set_default_view($view, $options = array())
718
    {
719
        // TODO
720
        // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf
721
        /**
722
         * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window.
723
         * fitrect Fit the rectangle specified by left, bottom, right, and top to the window.
724
         * fitvisible Fit the visible contents of the page (the ArtBox) to the window.
725
         * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window.
726
         * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window.
727
         * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window.
728
         * fitwindow Fit the complete page to the window.
729
         * fixed
730
         */
731
        //$this->setPDFLibParameter("openaction", $view);
732
    }
733
734
    /**
735
     * Loads a specific font and stores the corresponding descriptor.
736
     *
737
     * @param string $font
738
     * @param string $encoding
739
     * @param string $options
740
     *
741
     * @return int the font descriptor for the font
742
     */
743
    protected function _load_font($font, $encoding = null, $options = "")
744
    {
745
        // Fix for PDFLibs case-sensitive font names
746
        $baseFont = basename($font);
747
        $isNativeFont = false;
748
        if (isset(self::$nativeFontsTpPDFLib[$baseFont])) {
749
            $font = self::$nativeFontsTpPDFLib[$baseFont];
750
            $isNativeFont = true;
751
        }
752
753
        // Check if the font is a native PDF font
754
        // Embed non-native fonts
755
        $test = strtolower($baseFont);
756
        if (in_array($test, DOMPDF::$nativeFonts)) {
757
            $font = basename($font);
758
        } else {
759
            // Embed non-native fonts
760
            $options .= " embedding=true";
761
        }
762
763
        if (is_null($encoding)) {
764
            // Unicode encoding is only available for the commerical
765
            // version of PDFlib and not PDFlib-Lite
766
            if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) {
767
                $encoding = "unicode";
768
            } else {
769
                $encoding = "auto";
770
            }
771
        }
772
773
        $key = "$font:$encoding:$options";
774
        if (isset($this->_fonts[$key])) {
775
            return $this->_fonts[$key];
776
        }
777
778
        // Native fonts are build in, just load it
779
        if ($isNativeFont) {
780
            $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options);
781
782
            return $this->_fonts[$key];
783
        }
784
785
        $fontOutline = $this->getPDFLibParameter("FontOutline", 1);
786
        if ($fontOutline === "" || $fontOutline <= 0) {
787
            $families = $this->_dompdf->getFontMetrics()->getFontFamilies();
788
            foreach ($families as $files) {
789
                foreach ($files as $file) {
790
                    $face = basename($file);
791
                    $afm = null;
792
793
                    if (isset($this->_fontsFiles[$face])) {
794
                        continue;
795
                    }
796
797
                    // Prefer ttfs to afms
798
                    if (file_exists("$file.ttf")) {
799
                        $outline = "$file.ttf";
800
                    } elseif (file_exists("$file.TTF")) {
801
                        $outline = "$file.TTF";
802
                    } elseif (file_exists("$file.pfb")) {
803
                        $outline = "$file.pfb";
804
                        if (file_exists("$file.afm")) {
805
                            $afm = "$file.afm";
806
                        }
807
                    } elseif (file_exists("$file.PFB")) {
808
                        $outline = "$file.PFB";
809
                        if (file_exists("$file.AFM")) {
810
                            $afm = "$file.AFM";
811
                        }
812
                    } else {
813
                        continue;
814
                    }
815
816
                    $this->_fontsFiles[$face] = true;
817
818
                    if ($this->getPDFLibMajorVersion() >= 9) {
819
                        $this->setPDFLibParameter("FontOutline", '{' . "$face=$outline" . '}');
820
                    } else {
821
                        $this->setPDFLibParameter("FontOutline", "\{$face\}=\{$outline\}");
822
                    }
823
824
                    if (is_null($afm)) {
825
                        continue;
826
                    }
827
                    if ($this->getPDFLibMajorVersion() >= 9) {
828
                        $this->setPDFLibParameter("FontAFM", '{' . "$face=$afm" . '}');
829
                    } else {
830
                        $this->setPDFLibParameter("FontAFM", "\{$face\}=\{$afm\}");
831
                    }
832
                }
833
            }
834
        }
835
836
        $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options);
837
838
        return $this->_fonts[$key];
839
    }
840
841
    /**
842
     * Remaps y coords from 4th to 1st quadrant
843
     *
844
     * @param float $y
845
     * @return float
846
     */
847
    protected function y($y)
848
    {
849
        return $this->_height - $y;
850
    }
851
852
    /**
853
     * @param float $x1
854
     * @param float $y1
855
     * @param float $x2
856
     * @param float $y2
857
     * @param array $color
858
     * @param float $width
859
     * @param array $style
860
     */
861
    public function line($x1, $y1, $x2, $y2, $color, $width, $style = null)
862
    {
863
        $this->_set_line_style($width, "butt", "", $style);
0 ignored issues
show
Bug introduced by
It seems like $style defined by parameter $style on line 861 can also be of type null; however, Dompdf\Adapter\PDFLib::_set_line_style() does only seem to accept array, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
864
        $this->_set_stroke_color($color);
865
866
        $y1 = $this->y($y1);
867
        $y2 = $this->y($y2);
868
869
        $this->_pdf->moveto($x1, $y1);
870
        $this->_pdf->lineto($x2, $y2);
871
        $this->_pdf->stroke();
872
873
        $this->_set_stroke_opacity($this->_current_opacity, "Normal");
874
    }
875
876
    /**
877
     * Draw line at the specified coordinates on every page.
878
     *
879
     * See {@link Style::munge_color()} for the format of the colour array.
880
     *
881
     * @param float $x1
882
     * @param float $y1
883
     * @param float $x2
884
     * @param float $y2
885
     * @param array $color
886
     * @param float $width
887
     * @param array $style optional
888
     */
889
    public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = array())
890
    {
891
        $_t = 'line';
892
        $this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
893
    }
894
895
    /**
896
     * @param float $x1
897
     * @param float $y1
898
     * @param float $r1
899
     * @param float $r2
900
     * @param float $astart
901
     * @param float $aend
902
     * @param array $color
903
     * @param float $width
904
     * @param array $style
905
     */
906 View Code Duplication
    public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
907
    {
908
        $this->_set_line_style($width, "butt", "", $style);
909
        $this->_set_stroke_color($color);
910
911
        $y1 = $this->y($y1);
912
913
        $this->_pdf->arc($x1, $y1, $r1, $astart, $aend);
914
        $this->_pdf->stroke();
915
916
        $this->_set_stroke_opacity($this->_current_opacity, "Normal");
917
    }
918
919
    /**
920
     * @param float $x1
921
     * @param float $y1
922
     * @param float $w
923
     * @param float $h
924
     * @param array $color
925
     * @param float $width
926
     * @param null  $style
927
     */
928 View Code Duplication
    public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
929
    {
930
        $this->_set_stroke_color($color);
931
        $this->_set_line_style($width, "butt", "", $style);
0 ignored issues
show
Documentation introduced by
$style is of type null, but the function expects a array.

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...
932
933
        $y1 = $this->y($y1) - $h;
934
935
        $this->_pdf->rect($x1, $y1, $w, $h);
936
        $this->_pdf->stroke();
937
938
        $this->_set_stroke_opacity($this->_current_opacity, "Normal");
939
    }
940
941
    /**
942
     * @param float $x1
943
     * @param float $y1
944
     * @param float $w
945
     * @param float $h
946
     * @param array $color
947
     */
948
    public function filled_rectangle($x1, $y1, $w, $h, $color)
949
    {
950
        $this->_set_fill_color($color);
951
952
        $y1 = $this->y($y1) - $h;
953
954
        $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
955
        $this->_pdf->fill();
956
957
        $this->_set_fill_opacity($this->_current_opacity, "Normal");
958
    }
959
960
    /**
961
     * @param float $x1
962
     * @param float $y1
963
     * @param float $w
964
     * @param float $h
965
     */
966
    public function clipping_rectangle($x1, $y1, $w, $h)
967
    {
968
        $this->_pdf->save();
969
970
        $y1 = $this->y($y1) - $h;
971
972
        $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
973
        $this->_pdf->clip();
974
    }
975
976
    /**
977
     * @param float $x1
978
     * @param float $y1
979
     * @param float $w
980
     * @param float $h
981
     * @param float $rTL
982
     * @param float $rTR
983
     * @param float $rBR
984
     * @param float $rBL
985
     */
986
    public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
987
    {
988
        $this->clipping_rectangle($x1, $y1, $w, $h);
989
    }
990
991
    /**
992
     *
993
     */
994
    public function clipping_end()
995
    {
996
        $this->_pdf->restore();
997
    }
998
999
    /**
1000
     *
1001
     */
1002
    public function save()
1003
    {
1004
        $this->_pdf->save();
1005
    }
1006
1007
    function restore()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

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

Loading history...
1008
    {
1009
        $this->_pdf->restore();
1010
    }
1011
1012
    /**
1013
     * @param $angle
1014
     * @param $x
1015
     * @param $y
1016
     */
1017 View Code Duplication
    public function rotate($angle, $x, $y)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1018
    {
1019
        $pdf = $this->_pdf;
1020
        $pdf->translate($x, $this->_height - $y);
1021
        $pdf->rotate(-$angle);
1022
        $pdf->translate(-$x, -$this->_height + $y);
1023
    }
1024
1025
    /**
1026
     * @param $angle_x
1027
     * @param $angle_y
1028
     * @param $x
1029
     * @param $y
1030
     */
1031 View Code Duplication
    public function skew($angle_x, $angle_y, $x, $y)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1032
    {
1033
        $pdf = $this->_pdf;
1034
        $pdf->translate($x, $this->_height - $y);
1035
        $pdf->skew($angle_y, $angle_x); // Needs to be inverted
1036
        $pdf->translate(-$x, -$this->_height + $y);
1037
    }
1038
1039
    /**
1040
     * @param $s_x
1041
     * @param $s_y
1042
     * @param $x
1043
     * @param $y
1044
     */
1045 View Code Duplication
    public function scale($s_x, $s_y, $x, $y)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1046
    {
1047
        $pdf = $this->_pdf;
1048
        $pdf->translate($x, $this->_height - $y);
1049
        $pdf->scale($s_x, $s_y);
1050
        $pdf->translate(-$x, -$this->_height + $y);
1051
    }
1052
1053
    /**
1054
     * @param $t_x
1055
     * @param $t_y
1056
     */
1057
    public function translate($t_x, $t_y)
1058
    {
1059
        $this->_pdf->translate($t_x, -$t_y);
1060
    }
1061
1062
    /**
1063
     * @param $a
1064
     * @param $b
1065
     * @param $c
1066
     * @param $d
1067
     * @param $e
1068
     * @param $f
1069
     */
1070
    public function transform($a, $b, $c, $d, $e, $f)
1071
    {
1072
        $this->_pdf->concat($a, $b, $c, $d, $e, $f);
1073
    }
1074
1075
    /**
1076
     * @param array $points
1077
     * @param array $color
1078
     * @param null  $width
1079
     * @param null  $style
1080
     * @param bool  $fill
1081
     */
1082
    public function polygon($points, $color, $width = null, $style = null, $fill = false)
1083
    {
1084
        $this->_set_fill_color($color);
1085
        $this->_set_stroke_color($color);
1086
1087
        if (!$fill && isset($width)) {
1088
            $this->_set_line_style($width, "square", "miter", $style);
0 ignored issues
show
Documentation introduced by
$style is of type null, but the function expects a array.

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...
1089
        }
1090
1091
        $y = $this->y(array_pop($points));
1092
        $x = array_pop($points);
1093
        $this->_pdf->moveto($x, $y);
1094
1095
        while (count($points) > 1) {
1096
            $y = $this->y(array_pop($points));
1097
            $x = array_pop($points);
1098
            $this->_pdf->lineto($x, $y);
1099
        }
1100
1101
        if ($fill) {
1102
            $this->_pdf->fill();
1103
        } else {
1104
            $this->_pdf->closepath_stroke();
1105
        }
1106
1107
        $this->_set_fill_opacity($this->_current_opacity, "Normal");
1108
        $this->_set_stroke_opacity($this->_current_opacity, "Normal");
1109
    }
1110
1111
    /**
1112
     * @param float $x
1113
     * @param float $y
1114
     * @param float $r
1115
     * @param array $color
1116
     * @param null  $width
1117
     * @param null  $style
1118
     * @param bool  $fill
1119
     */
1120
    public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
1121
    {
1122
        $this->_set_fill_color($color);
1123
        $this->_set_stroke_color($color);
1124
1125
        if (!$fill && isset($width)) {
1126
            $this->_set_line_style($width, "round", "round", $style);
0 ignored issues
show
Documentation introduced by
$style is of type null, but the function expects a array.

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...
1127
        }
1128
1129
        $y = $this->y($y);
1130
1131
        $this->_pdf->circle($x, $y, $r);
1132
1133
        if ($fill) {
1134
            $this->_pdf->fill();
1135
        } else {
1136
            $this->_pdf->stroke();
1137
        }
1138
1139
        $this->_set_fill_opacity($this->_current_opacity, "Normal");
1140
        $this->_set_stroke_opacity($this->_current_opacity, "Normal");
1141
    }
1142
1143
    /**
1144
     * @param string $img_url
1145
     * @param float  $x
1146
     * @param float  $y
1147
     * @param int    $w
1148
     * @param int    $h
1149
     * @param string $resolution
1150
     */
1151
    public function image($img_url, $x, $y, $w, $h, $resolution = "normal")
1152
    {
1153
        $w = (int)$w;
1154
        $h = (int)$h;
1155
1156
        $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext());
1157
1158
        if (!isset($this->_imgs[$img_url])) {
1159
            $this->_imgs[$img_url] = $this->_pdf->load_image($img_type, $img_url, "");
1160
        }
1161
1162
        $img = $this->_imgs[$img_url];
1163
1164
        $y = $this->y($y) - $h;
1165
        $this->_pdf->fit_image($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire');
1166
    }
1167
1168
    /**
1169
     * @param float  $x
1170
     * @param float  $y
1171
     * @param string $text
1172
     * @param string $font
1173
     * @param float  $size
1174
     * @param array  $color
1175
     * @param int    $word_spacing
1176
     * @param int    $char_spacing
1177
     * @param int    $angle
1178
     */
1179
    public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0, $char_spacing = 0, $angle = 0)
1180
    {
1181
        $fh = $this->_load_font($font);
1182
1183
        $this->_pdf->setfont($fh, $size);
1184
        $this->_set_fill_color($color);
1185
1186
        $y = $this->y($y) - $this->get_font_height($font, $size);
1187
1188
        $word_spacing = (float)$word_spacing;
1189
        $char_spacing = (float)$char_spacing;
1190
        $angle = -(float)$angle;
1191
1192
        $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing ");
1193
1194
        $this->_set_fill_opacity($this->_current_opacity, "Normal");
1195
    }
1196
1197
    /**
1198
     * @param string $code
1199
     */
1200
    public function javascript($code)
1201
    {
1202
        if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) {
1203
            $this->_pdf->create_action("JavaScript", $code);
1204
        }
1205
    }
1206
1207
    /**
1208
     * Add a named destination (similar to <a name="foo">...</a> in html)
1209
     *
1210
     * @param string $anchorname The name of the named destination
1211
     */
1212
    public function add_named_dest($anchorname)
1213
    {
1214
        $this->_pdf->add_nameddest($anchorname, "");
1215
    }
1216
1217
    /**
1218
     * Add a link to the pdf
1219
     *
1220
     * @param string $url    The url to link to
1221
     * @param float  $x      The x position of the link
1222
     * @param float  $y      The y position of the link
1223
     * @param float  $width  The width of the link
1224
     * @param float  $height The height of the link
1225
     */
1226
    public function add_link($url, $x, $y, $width, $height)
1227
    {
1228
        $y = $this->y($y) - $height;
1229
        if (strpos($url, '#') === 0) {
1230
            // Local link
1231
            $name = substr($url, 1);
1232
            if ($name) {
1233
                $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link',
1234
                    "contents={$url} destname=" . substr($url, 1) . " linewidth=0");
1235
            }
1236
        } else {
1237
            list($proto, $host, $path, $file) = Helpers::explode_url($url);
1238
1239
            if ($proto == "" || $proto === "file://") {
1240
                return; // Local links are not allowed
1241
            }
1242
            $url = Helpers::build_url($proto, $host, $path, $file);
1243
            $url = '{' . rawurldecode($url) . '}';
1244
1245
            $action = $this->_pdf->create_action("URI", "url=" . $url);
1246
            $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
1247
        }
1248
    }
1249
1250
    /**
1251
     * @param string $text
1252
     * @param string $font
1253
     * @param float  $size
1254
     * @param int    $word_spacing
1255
     * @param int    $letter_spacing
1256
     * @return mixed
1257
     */
1258
    public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0)
1259
    {
1260
        $fh = $this->_load_font($font);
1261
1262
        // Determine the additional width due to extra spacing
1263
        $num_spaces = mb_substr_count($text, " ");
1264
        $delta = $word_spacing * $num_spaces;
1265
1266
        if ($letter_spacing) {
1267
            $num_chars = mb_strlen($text);
1268
            $delta += ($num_chars - $num_spaces) * $letter_spacing;
1269
        }
1270
1271
        return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
1272
    }
1273
1274
    /**
1275
     * @param string $font
1276
     * @param float  $size
1277
     * @return float
1278
     */
1279
    public function get_font_height($font, $size)
1280
    {
1281
        $fh = $this->_load_font($font);
1282
1283
        $this->_pdf->setfont($fh, $size);
1284
1285
        $asc = $this->_pdf->get_value("ascender", $fh);
1286
        $desc = $this->_pdf->get_value("descender", $fh);
1287
1288
        // $desc is usually < 0,
1289
        $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
1290
1291
        return $size * ($asc - $desc) * $ratio;
1292
    }
1293
1294
    /**
1295
     * @param string $font
1296
     * @param float  $size
1297
     * @return float
1298
     */
1299
    public function get_font_baseline($font, $size)
1300
    {
1301
        $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
1302
1303
        return $this->get_font_height($font, $size) / $ratio * 1.1;
1304
    }
1305
1306
    /**
1307
     * Writes text at the specified x and y coordinates on every page
1308
     *
1309
     * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
1310
     * with their current values.
1311
     *
1312
     * See {@link Style::munge_color()} for the format of the color array.
1313
     *
1314
     * @param float  $x
1315
     * @param float  $y
1316
     * @param string $text       the text to write
1317
     * @param string $font       the font file to use
1318
     * @param float  $size       the font size, in points
1319
     * @param array  $color
1320
     * @param float  $word_space word spacing adjustment
1321
     * @param float  $char_space char spacing adjustment
1322
     * @param float  $angle      angle to write the text at, measured CW starting from the x-axis
1323
     */
1324
    public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
1325
    {
1326
        $_t = "text";
1327
        $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
1328
    }
1329
1330
    //........................................................................
1331
1332
    /**
1333
     * Processes a script on every page
1334
     *
1335
     * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
1336
     *
1337
     * This function can be used to add page numbers to all pages
1338
     * after the first one, for example.
1339
     *
1340
     * @param string $code the script code
1341
     * @param string $type the language type for script
1342
     */
1343
    public function page_script($code, $type = "text/php")
1344
    {
1345
        $_t = "script";
1346
        $this->_page_text[] = compact("_t", "code", "type");
1347
    }
1348
1349
    /**
1350
     *
1351
     */
1352
    public function new_page()
1353
    {
1354
        // Add objects to the current page
1355
        $this->_place_objects();
1356
1357
        $this->_pdf->suspend_page("");
1358
        $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
1359
        $this->_page_number = ++$this->_page_count;
1360
    }
1361
1362
    /**
1363
     * Add text to each page after rendering is complete
1364
     */
1365
    protected function _add_page_text()
1366
    {
1367
        if (!count($this->_page_text)) {
1368
            return;
1369
        }
1370
1371
        $eval = null;
1372
        $this->_pdf->suspend_page("");
1373
1374
        for ($p = 1; $p <= $this->_page_count; $p++) {
1375
            $this->_pdf->resume_page("pagenumber=$p");
1376
1377 View Code Duplication
            foreach ($this->_page_text as $pt) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1378
                extract($pt);
1379
1380
                switch ($_t) {
1381
                    case "text":
1382
                        $text = str_replace(array("{PAGE_NUM}", "{PAGE_COUNT}"),
1383
                            array($p, $this->_page_count), $text);
1384
                        $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
1385
                        break;
1386
1387
                    case "script":
1388
                        if (!$eval) {
1389
                            $eval = new PHPEvaluator($this);
1390
                        }
1391
                        $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count));
1392
                        break;
1393
1394
                    case 'line':
1395
                        $this->line( $x1, $y1, $x2, $y2, $color, $width, $style );
1396
                        break;
1397
1398
                }
1399
            }
1400
1401
            $this->_pdf->suspend_page("");
1402
        }
1403
1404
        $this->_pdf->resume_page("pagenumber=" . $this->_page_number);
1405
    }
1406
1407
    /**
1408
     * Streams the PDF to the client.
1409
     *
1410
     * @param string $filename The filename to present to the client.
1411
     * @param array  $options  Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1).
1412
     * @throws Exception
1413
     */
1414
    public function stream($filename = "document.pdf", $options = array())
1415
    {
1416
        if (headers_sent()) {
1417
            die("Unable to stream pdf: headers already sent");
1418
        }
1419
1420
        if (!isset($options["compress"])) {
1421
            $options["compress"] = true;
1422
        }
1423
        if (!isset($options["Attachment"])) {
1424
            $options["Attachment"] = true;
1425
        }
1426
1427
        $this->_add_page_text();
1428
1429
        if ($options["compress"]) {
1430
            $this->setPDFLibValue("compress", 6);
1431
        } else {
1432
            $this->setPDFLibValue("compress", 0);
1433
        }
1434
1435
        $this->_close();
1436
1437
        $data = "";
1438
1439
        if (self::$IN_MEMORY) {
1440
            $data = $this->_pdf->get_buffer();
1441
            $size = mb_strlen($data, "8bit");
1442
        } else {
1443
            $size = filesize($this->_file);
1444
        }
1445
1446
        header("Cache-Control: private");
1447
        header("Content-Type: application/pdf");
1448
        header("Content-Length: " . $size);
1449
1450
        $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf";
1451
        $attachment = $options["Attachment"] ? "attachment" : "inline";
1452
        header(Helpers::buildContentDispositionHeader($attachment, $filename));
1453
1454
        if (self::$IN_MEMORY) {
1455
            echo $data;
1456
        } else {
1457
            // Chunked readfile()
1458
            $chunk = (1 << 21); // 2 MB
1459
            $fh = fopen($this->_file, "rb");
1460
            if (!$fh) {
1461
                throw new Exception("Unable to load temporary PDF file: " . $this->_file);
1462
            }
1463
1464
            while (!feof($fh)) {
1465
                echo fread($fh, $chunk);
1466
            }
1467
            fclose($fh);
1468
1469
            //debugpng
1470
            if ($this->_dompdf->getOptions()->getDebugPng()) {
1471
                print '[pdflib stream unlink ' . $this->_file . ']';
1472
            }
1473
            if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) {
1474
                unlink($this->_file);
1475
            }
1476
            $this->_file = null;
1477
            unset($this->_file);
1478
        }
1479
1480
        flush();
1481
    }
1482
1483
    /**
1484
     * Returns the PDF as a string.
1485
     *
1486
     * @param array $options Associative array: 'compress' => 1 or 0 (default 1).
1487
     * @return string
1488
     */
1489
    public function output($options = array())
1490
    {
1491
        if (!isset($options["compress"])) {
1492
            $options["compress"] = true;
1493
        }
1494
1495
        $this->_add_page_text();
1496
1497
        if ($options["compress"]) {
1498
            $this->setPDFLibValue("compress", 6);
1499
        } else {
1500
            $this->setPDFLibValue("compress", 0);
1501
        }
1502
1503
        $this->_close();
1504
1505
        if (self::$IN_MEMORY) {
1506
            $data = $this->_pdf->get_buffer();
1507
        } else {
1508
            $data = file_get_contents($this->_file);
1509
1510
            //debugpng
1511
            if ($this->_dompdf->getOptions()->getDebugPng()) {
1512
                print '[pdflib output unlink ' . $this->_file . ']';
1513
            }
1514
            if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) {
1515
                unlink($this->_file);
1516
            }
1517
            $this->_file = null;
1518
            unset($this->_file);
1519
        }
1520
1521
        return $data;
1522
    }
1523
1524
    /**
1525
     * @param string $keyword
1526
     * @param string $optlist
1527
     * @return mixed
1528
     */
1529
    protected function getPDFLibParameter($keyword, $optlist = "")
1530
    {
1531
        if ($this->getPDFLibMajorVersion() >= 9) {
1532
            return $this->_pdf->get_option($keyword, "");
1533
        }
1534
1535
        return $this->_pdf->get_parameter($keyword, $optlist);
1536
    }
1537
1538
    /**
1539
     * @param string $keyword
1540
     * @param string $value
1541
     * @return mixed
1542
     */
1543
    protected function setPDFLibParameter($keyword, $value)
1544
    {
1545
        if ($this->getPDFLibMajorVersion() >= 9) {
1546
            return $this->_pdf->set_option($keyword . "=" . $value);
1547
        }
1548
1549
        return $this->_pdf->set_parameter($keyword, $value);
1550
    }
1551
1552
    /**
1553
     * @param string $keyword
1554
     * @param string $optlist
1555
     * @return mixed
1556
     */
1557
    protected function getPDFLibValue($keyword, $optlist = "")
1558
    {
1559
        if ($this->getPDFLibMajorVersion() >= 9) {
1560
            return $this->getPDFLibParameter($keyword, $optlist);
1561
        }
1562
1563
        return $this->_pdf->get_value($keyword);
1564
    }
1565
1566
    /**
1567
     * @param string $keyword
1568
     * @param string $value
1569
     * @return mixed
1570
     */
1571
    protected function setPDFLibValue($keyword, $value)
1572
    {
1573
        if ($this->getPDFLibMajorVersion() >= 9) {
1574
            return $this->setPDFLibParameter($keyword, $value);
1575
        }
1576
1577
        return $this->_pdf->set_value($keyword, $value);
1578
    }
1579
1580
    /**
1581
     * @return int
1582
     */
1583
    private function getPDFLibMajorVersion()
1584
    {
1585
        if (is_null(self::$MAJOR_VERSION)) {
1586
            if (method_exists($this->_pdf, "get_option")) {
1587
                self::$MAJOR_VERSION = abs(intval($this->_pdf->get_option("major", "")));
0 ignored issues
show
Documentation Bug introduced by
It seems like abs(intval($this->_pdf->get_option('major', ''))) can also be of type double. However, the property $MAJOR_VERSION is declared as type null|integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1588
            } else {
1589
                self::$MAJOR_VERSION = abs(intval($this->_pdf->get_value("major", "")));
0 ignored issues
show
Documentation Bug introduced by
It seems like abs(intval($this->_pdf->get_value('major', ''))) can also be of type double. However, the property $MAJOR_VERSION is declared as type null|integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1590
            }
1591
        }
1592
1593
        return self::$MAJOR_VERSION;
1594
    }
1595
}
1596
1597
// Workaround for idiotic limitation on statics...
1598
PDFLib::$PAPER_SIZES = CPDF::$PAPER_SIZES;
0 ignored issues
show
Bug introduced by
The property PAPER_SIZES cannot be accessed from this context as it is declared private in class Dompdf\Adapter\CPDF.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1599