Completed
Push — master ( 6706d8...93737b )
by
unknown
07:43
created

GD::stream()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 33
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 24
nc 33
nop 2
1
<?php
2
/**
3
 * @package dompdf
4
 * @link    http://dompdf.github.com/
5
 * @author  Benj Carson <[email protected]>
6
 * @author  Fabien Ménager <[email protected]>
7
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
 */
9
namespace Dompdf\Adapter;
10
11
use Dompdf\Canvas;
12
use Dompdf\Dompdf;
13
use Dompdf\Image\Cache;
14
use Dompdf\Helpers;
15
16
/**
17
 * Image rendering interface
18
 *
19
 * Renders to an image format supported by GD (jpeg, gif, png, xpm).
20
 * Not super-useful day-to-day but handy nonetheless
21
 *
22
 * @package dompdf
23
 */
24
class GD implements Canvas
25
{
26
    /**
27
     * @var Dompdf
28
     */
29
    private $_dompdf;
30
31
    /**
32
     * Resource handle for the image
33
     *
34
     * @var resource
35
     */
36
    private $_img;
37
38
    /**
39
     * Resource handle for the image
40
     *
41
     * @var resource[]
42
     */
43
    private $_imgs;
44
45
    /**
46
     * Apparent canvas width in pixels
47
     *
48
     * @var int
49
     */
50
    private $_width;
51
52
    /**
53
     * Apparent canvas height in pixels
54
     *
55
     * @var int
56
     */
57
    private $_height;
58
59
    /**
60
     * Actual image width in pixels
61
     *
62
     * @var int
63
     */
64
    private $_actual_width;
65
66
    /**
67
     * Actual image height in pixels
68
     *
69
     * @var int
70
     */
71
    private $_actual_height;
72
73
    /**
74
     * Current page number
75
     *
76
     * @var int
77
     */
78
    private $_page_number;
79
80
    /**
81
     * Total number of pages
82
     *
83
     * @var int
84
     */
85
    private $_page_count;
86
87
    /**
88
     * Image antialias factor
89
     *
90
     * @var float
91
     */
92
    private $_aa_factor;
93
94
    /**
95
     * Allocated colors
96
     *
97
     * @var array
98
     */
99
    private $_colors;
100
101
    /**
102
     * Background color
103
     *
104
     * @var int
105
     */
106
    private $_bg_color;
107
108
    /**
109
     * Background color array
110
     *
111
     * @var int
112
     */
113
    private $_bg_color_array;
114
115
    /**
116
     * Actual DPI
117
     *
118
     * @var int
119
     */
120
    private $dpi;
121
122
    /**
123
     * Amount to scale font sizes
124
     *
125
     * Font sizes are 72 DPI, GD internally uses 96. Scale them proportionally.
126
     * 72 / 96 = 0.75.
127
     *
128
     * @var float
129
     */
130
    const FONT_SCALE = 0.75;
131
132
    /**
133
     * Class constructor
134
     *
135
     * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
136
     * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
137
     * @param Dompdf $dompdf
138
     * @param float $aa_factor Anti-aliasing factor, 1 for no AA
139
     * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
140
     */
141
    public function __construct($size = 'letter', $orientation = "portrait", Dompdf $dompdf, $aa_factor = 1.0, $bg_color = array(1, 1, 1, 0))
142
    {
143
144
        if (!is_array($size)) {
145
            $size = strtolower($size);
146
147
            if (isset(CPDF::$PAPER_SIZES[$size])) {
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...
148
                $size = CPDF::$PAPER_SIZES[$size];
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...
149
            } else {
150
                $size = CPDF::$PAPER_SIZES["letter"];
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...
151
            }
152
        }
153
154 View Code Duplication
        if (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...
155
            list($size[2], $size[3]) = array($size[3], $size[2]);
156
        }
157
158
        $this->_dompdf = $dompdf;
159
160
        $this->dpi = $this->get_dompdf()->getOptions()->getDpi();
161
162
        if ($aa_factor < 1) {
163
            $aa_factor = 1;
164
        }
165
166
        $this->_aa_factor = $aa_factor;
0 ignored issues
show
Documentation Bug introduced by
It seems like $aa_factor can also be of type integer. However, the property $_aa_factor 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...
167
168
        $size[2] *= $aa_factor;
169
        $size[3] *= $aa_factor;
170
171
        $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 double. However, the property $_width is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
172
        $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 double. However, the property $_height is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
173
174
        $this->_actual_width = $this->_upscale($this->_width);
0 ignored issues
show
Documentation Bug introduced by
The property $_actual_width was declared of type integer, but $this->_upscale($this->_width) is of type double. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
175
        $this->_actual_height = $this->_upscale($this->_height);
0 ignored issues
show
Documentation Bug introduced by
The property $_actual_height was declared of type integer, but $this->_upscale($this->_height) is of type double. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
176
177
        if (is_null($bg_color) || !is_array($bg_color)) {
178
            // Pure white bg
179
            $bg_color = array(1, 1, 1, 0);
180
        }
181
182
        $this->_bg_color_array = $bg_color;
0 ignored issues
show
Documentation Bug introduced by
It seems like $bg_color of type array is incompatible with the declared type integer of property $_bg_color_array.

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...
183
184
        $this->new_page();
185
    }
186
187
    /**
188
     * @return Dompdf
189
     */
190
    public function get_dompdf()
191
    {
192
        return $this->_dompdf;
193
    }
194
195
    /**
196
     * Return the GF image resource
197
     *
198
     * @return resource
199
     */
200
    public function get_image()
201
    {
202
        return $this->_img;
203
    }
204
205
    /**
206
     * Return the image's width in pixels
207
     *
208
     * @return float
209
     */
210
    public function get_width()
211
    {
212
        return $this->_width / $this->_aa_factor;
213
    }
214
215
    /**
216
     * Return the image's height in pixels
217
     *
218
     * @return float
219
     */
220
    public function get_height()
221
    {
222
        return $this->_height / $this->_aa_factor;
223
    }
224
225
    /**
226
     * Returns the current page number
227
     * @return int
228
     */
229
    public function get_page_number()
230
    {
231
        return $this->_page_number;
232
    }
233
234
    /**
235
     * Returns the total number of pages in the document
236
     * @return int
237
     */
238
    public function get_page_count()
239
    {
240
        return $this->_page_count;
241
    }
242
243
    /**
244
     * Sets the current page number
245
     *
246
     * @param int $num
247
     */
248
    public function set_page_number($num)
249
    {
250
        $this->_page_number = $num;
251
    }
252
253
    /**
254
     * Sets the page count
255
     *
256
     * @param int $count
257
     */
258
    public function set_page_count($count)
259
    {
260
        $this->_page_count = $count;
261
    }
262
263
    /**
264
     * Sets the opacity
265
     *
266
     * @param $opacity
267
     * @param $mode
268
     */
269
    public function set_opacity($opacity, $mode = "Normal")
270
    {
271
        // FIXME
272
    }
273
274
    /**
275
     * Allocate a new color.  Allocate with GD as needed and store
276
     * previously allocated colors in $this->_colors.
277
     *
278
     * @param array $color The new current color
279
     * @return int           The allocated color
280
     */
281
    private function _allocate_color($color)
282
    {
283
        $a = isset($color["alpha"]) ? $color["alpha"] : 1;
284
285
        if (isset($color["c"])) {
286
            $color = Helpers::cmyk_to_rgb($color);
287
        }
288
289
        list($r, $g, $b) = $color;
290
291
        $r *= 255;
292
        $g *= 255;
293
        $b *= 255;
294
        $a = 127 - ($a * 127);
295
296
        // Clip values
297
        $r = $r > 255 ? 255 : $r;
298
        $g = $g > 255 ? 255 : $g;
299
        $b = $b > 255 ? 255 : $b;
300
        $a = $a > 127 ? 127 : $a;
301
302
        $r = $r < 0 ? 0 : $r;
303
        $g = $g < 0 ? 0 : $g;
304
        $b = $b < 0 ? 0 : $b;
305
        $a = $a < 0 ? 0 : $a;
306
307
        $key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
308
309
        if (isset($this->_colors[$key])) {
310
            return $this->_colors[$key];
311
        }
312
313
        if ($a != 0) {
314
            $this->_colors[$key] = imagecolorallocatealpha($this->get_image(), $r, $g, $b, $a);
315
        } else {
316
            $this->_colors[$key] = imagecolorallocate($this->get_image(), $r, $g, $b);
317
        }
318
319
        return $this->_colors[$key];
320
    }
321
322
    /**
323
     * Scales value up to the current canvas DPI from 72 DPI
324
     *
325
     * @param float $length
326
     * @return float
327
     */
328
    private function _upscale($length)
329
    {
330
        return ($length * $this->dpi) / 72 * $this->_aa_factor;
331
    }
332
333
    /**
334
     * Scales value down from the current canvas DPI to 72 DPI
335
     *
336
     * @param float $length
337
     * @return float
338
     */
339
    private function _downscale($length)
340
    {
341
        return ($length / $this->dpi * 72) / $this->_aa_factor;
342
    }
343
344
    /**
345
     * Draws a line from x1,y1 to x2,y2
346
     *
347
     * See {@link Style::munge_color()} for the format of the color array.
348
     * See {@link Cpdf::setLineStyle()} for a description of the format of the
349
     * $style parameter (aka dash).
350
     *
351
     * @param float $x1
352
     * @param float $y1
353
     * @param float $x2
354
     * @param float $y2
355
     * @param array $color
356
     * @param float $width
357
     * @param array $style
358
     */
359
    public function line($x1, $y1, $x2, $y2, $color, $width, $style = null)
360
    {
361
362
        // Scale by the AA factor and DPI
363
        $x1 = $this->_upscale($x1);
364
        $y1 = $this->_upscale($y1);
365
        $x2 = $this->_upscale($x2);
366
        $y2 = $this->_upscale($y2);
367
        $width = $this->_upscale($width);
368
369
        $c = $this->_allocate_color($color);
370
371
        // Convert the style array if required
372
        if (is_array($style) && count($style) > 0) {
373
            $gd_style = array();
374
375
            if (count($style) == 1) {
376 View Code Duplication
                for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
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...
377
                    $gd_style[] = $c;
378
                }
379
380 View Code Duplication
                for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
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...
381
                    $gd_style[] = $this->_bg_color;
382
                }
383
            } else {
384
                $i = 0;
385
                foreach ($style as $length) {
386
                    if ($i % 2 == 0) {
387
                        // 'On' pattern
388 View Code Duplication
                        for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
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...
389
                            $gd_style[] = $c;
390
                        }
391
392
                    } else {
393
                        // Off pattern
394 View Code Duplication
                        for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
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...
395
                            $gd_style[] = $this->_bg_color;
396
                        }
397
                    }
398
                    $i++;
399
                }
400
            }
401
402
            if (!empty($gd_style)) {
403
                imagesetstyle($this->get_image(), $gd_style);
404
                $c = IMG_COLOR_STYLED;
405
            }
406
        }
407
408
        imagesetthickness($this->get_image(), $width);
409
410
        imageline($this->get_image(), $x1, $y1, $x2, $y2, $c);
411
    }
412
413
    /**
414
     * @param float $x1
415
     * @param float $y1
416
     * @param float $r1
417
     * @param float $r2
418
     * @param float $astart
419
     * @param float $aend
420
     * @param array $color
421
     * @param float $width
422
     * @param array $style
423
     */
424
    public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array())
425
    {
426
        // @todo
427
    }
428
429
    /**
430
     * Draws a rectangle at x1,y1 with width w and height h
431
     *
432
     * See {@link Style::munge_color()} for the format of the color array.
433
     * See {@link Cpdf::setLineStyle()} for a description of the $style
434
     * parameter (aka dash)
435
     *
436
     * @param float $x1
437
     * @param float $y1
438
     * @param float $w
439
     * @param float $h
440
     * @param array $color
441
     * @param float $width
442
     * @param array $style
443
     */
444
    public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null)
445
    {
446
447
        // Scale by the AA factor and DPI
448
        $x1 = $this->_upscale($x1);
449
        $y1 = $this->_upscale($y1);
450
        $w = $this->_upscale($w);
451
        $h = $this->_upscale($h);
452
        $width = $this->_upscale($width);
453
454
        $c = $this->_allocate_color($color);
455
456
        // Convert the style array if required
457 View Code Duplication
        if (is_array($style) && count($style) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
458
            $gd_style = array();
459
460
            foreach ($style as $length) {
461
                for ($i = 0; $i < $length; $i++) {
462
                    $gd_style[] = $c;
463
                }
464
            }
465
466
            if (!empty($gd_style)) {
467
                imagesetstyle($this->get_image(), $gd_style);
468
                $c = IMG_COLOR_STYLED;
469
            }
470
        }
471
472
        imagesetthickness($this->get_image(), $width);
473
474
        imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
475
    }
476
477
    /**
478
     * Draws a filled rectangle at x1,y1 with width w and height h
479
     *
480
     * See {@link Style::munge_color()} for the format of the color array.
481
     *
482
     * @param float $x1
483
     * @param float $y1
484
     * @param float $w
485
     * @param float $h
486
     * @param array $color
487
     */
488
    public function filled_rectangle($x1, $y1, $w, $h, $color)
489
    {
490
        // Scale by the AA factor and DPI
491
        $x1 = $this->_upscale($x1);
492
        $y1 = $this->_upscale($y1);
493
        $w = $this->_upscale($w);
494
        $h = $this->_upscale($h);
495
496
        $c = $this->_allocate_color($color);
497
498
        imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
499
    }
500
501
    /**
502
     * Starts a clipping rectangle at x1,y1 with width w and height h
503
     *
504
     * @param float $x1
505
     * @param float $y1
506
     * @param float $w
507
     * @param float $h
508
     */
509
    public function clipping_rectangle($x1, $y1, $w, $h)
510
    {
511
        // @todo
512
    }
513
514
    public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
515
    {
516
        // @todo
517
    }
518
519
    /**
520
     * Ends the last clipping shape
521
     */
522
    public function clipping_end()
523
    {
524
        // @todo
525
    }
526
527
    /**
528
     *
529
     */
530
    public function save()
531
    {
532
        $this->get_dompdf()->getOptions()->setDpi(72);
533
    }
534
535
    /**
536
     *
537
     */
538
    public function restore()
539
    {
540
        $this->get_dompdf()->getOptions()->setDpi($this->dpi);
541
    }
542
543
    /**
544
     * @param $angle
545
     * @param $x
546
     * @param $y
547
     */
548
    public function rotate($angle, $x, $y)
549
    {
550
        // @todo
551
    }
552
553
    /**
554
     * @param $angle_x
555
     * @param $angle_y
556
     * @param $x
557
     * @param $y
558
     */
559
    public function skew($angle_x, $angle_y, $x, $y)
560
    {
561
        // @todo
562
    }
563
564
    /**
565
     * @param $s_x
566
     * @param $s_y
567
     * @param $x
568
     * @param $y
569
     */
570
    public function scale($s_x, $s_y, $x, $y)
571
    {
572
        // @todo
573
    }
574
575
    /**
576
     * @param $t_x
577
     * @param $t_y
578
     */
579
    public function translate($t_x, $t_y)
580
    {
581
        // @todo
582
    }
583
584
    /**
585
     * @param $a
586
     * @param $b
587
     * @param $c
588
     * @param $d
589
     * @param $e
590
     * @param $f
591
     */
592
    public function transform($a, $b, $c, $d, $e, $f)
593
    {
594
        // @todo
595
    }
596
597
    /**
598
     * Draws a polygon
599
     *
600
     * The polygon is formed by joining all the points stored in the $points
601
     * array.  $points has the following structure:
602
     * <code>
603
     * array(0 => x1,
604
     *       1 => y1,
605
     *       2 => x2,
606
     *       3 => y2,
607
     *       ...
608
     *       );
609
     * </code>
610
     *
611
     * See {@link Style::munge_color()} for the format of the color array.
612
     * See {@link Cpdf::setLineStyle()} for a description of the $style
613
     * parameter (aka dash)
614
     *
615
     * @param array $points
616
     * @param array $color
617
     * @param float $width
618
     * @param array $style
619
     * @param bool $fill Fills the polygon if true
620
     */
621
    public function polygon($points, $color, $width = null, $style = null, $fill = false)
622
    {
623
624
        // Scale each point by the AA factor and DPI
625
        foreach (array_keys($points) as $i) {
626
            $points[$i] = $this->_upscale($points[$i]);
627
        }
628
629
        $c = $this->_allocate_color($color);
630
631
        // Convert the style array if required
632 View Code Duplication
        if (is_array($style) && count($style) > 0 && !$fill) {
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...
633
            $gd_style = array();
634
635
            foreach ($style as $length) {
636
                for ($i = 0; $i < $length; $i++) {
637
                    $gd_style[] = $c;
638
                }
639
            }
640
641
            if (!empty($gd_style)) {
642
                imagesetstyle($this->get_image(), $gd_style);
643
                $c = IMG_COLOR_STYLED;
644
            }
645
        }
646
647
        imagesetthickness($this->get_image(), $width);
648
649
        if ($fill) {
650
            imagefilledpolygon($this->get_image(), $points, count($points) / 2, $c);
651
        } else {
652
            imagepolygon($this->get_image(), $points, count($points) / 2, $c);
653
        }
654
    }
655
656
    /**
657
     * Draws a circle at $x,$y with radius $r
658
     *
659
     * See {@link Style::munge_color()} for the format of the color array.
660
     * See {@link Cpdf::setLineStyle()} for a description of the $style
661
     * parameter (aka dash)
662
     *
663
     * @param float $x
664
     * @param float $y
665
     * @param float $r
666
     * @param array $color
667
     * @param float $width
668
     * @param array $style
669
     * @param bool $fill Fills the circle if true
670
     */
671
    public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false)
672
    {
673
        // Scale by the AA factor and DPI
674
        $x = $this->_upscale($x);
675
        $y = $this->_upscale($y);
676
        $r = $this->_upscale($r);
677
678
        $c = $this->_allocate_color($color);
679
680
        // Convert the style array if required
681 View Code Duplication
        if (is_array($style) && count($style) > 0 && !$fill) {
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...
682
            $gd_style = array();
683
684
            foreach ($style as $length) {
685
                for ($i = 0; $i < $length; $i++) {
686
                    $gd_style[] = $c;
687
                }
688
            }
689
690
            if (!empty($gd_style)) {
691
                imagesetstyle($this->get_image(), $gd_style);
692
                $c = IMG_COLOR_STYLED;
693
            }
694
        }
695
696
        imagesetthickness($this->get_image(), $width);
697
698
        if ($fill) {
699
            imagefilledellipse($this->get_image(), $x, $y, $r, $r, $c);
700
        } else {
701
            imageellipse($this->get_image(), $x, $y, $r, $r, $c);
702
        }
703
    }
704
705
    /**
706
     * Add an image to the pdf.
707
     * The image is placed at the specified x and y coordinates with the
708
     * given width and height.
709
     *
710
     * @param string $img_url the path to the image
711
     * @param float $x x position
712
     * @param float $y y position
713
     * @param int $w width (in pixels)
714
     * @param int $h height (in pixels)
715
     * @param string $resolution
716
     * @return void
717
     *
718
     * @throws \Exception
719
     * @internal param string $img_type the type (e.g. extension) of the image
720
     */
721
    public function image($img_url, $x, $y, $w, $h, $resolution = "normal")
722
    {
723
        $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext());
724
725
        if (!$img_type) {
726
            return;
727
        }
728
729
        $func_name = "imagecreatefrom$img_type";
730 View Code Duplication
        if (!function_exists($func_name)) {
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...
731
            if (!method_exists("Dompdf\Helpers", $func_name)) {
732
                throw new \Exception("Function $func_name() not found.  Cannot convert $type image: $img_url.  Please install the image PHP extension.");
733
            }
734
            $func_name = "\\Dompdf\\Helpers::" . $func_name;
735
        }
736
        $src = @call_user_func($func_name, $img_url);
737
738
        if (!$src) {
739
            return; // Probably should add to $_dompdf_errors or whatever here
740
        }
741
742
        // Scale by the AA factor and DPI
743
        $x = $this->_upscale($x);
744
        $y = $this->_upscale($y);
745
746
        $w = $this->_upscale($w);
747
        $h = $this->_upscale($h);
748
749
        $img_w = imagesx($src);
750
        $img_h = imagesy($src);
751
752
        imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
753
    }
754
755
    /**
756
     * Writes text at the specified x and y coordinates
757
     * See {@link Style::munge_color()} for the format of the color array.
758
     *
759
     * @param float $x
760
     * @param float $y
761
     * @param string $text the text to write
762
     * @param string $font the font file to use
763
     * @param float $size the font size, in points
764
     * @param array $color
765
     * @param float $word_spacing word spacing adjustment
766
     * @param float $char_spacing
767
     * @param float $angle Text angle
768
     *
769
     * @return void
770
     */
771
    public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
772
    {
773
        // Scale by the AA factor and DPI
774
        $x = $this->_upscale($x);
775
        $y = $this->_upscale($y);
776
        $size = $this->_upscale($size) * self::FONT_SCALE;
777
778
        $h = $this->get_font_height_actual($font, $size);
779
        $c = $this->_allocate_color($color);
780
781
        // imagettftext() converts numeric entities to their respective
782
        // character. Preserve any originally double encoded entities to be
783
        // represented as is.
784
        // eg: &amp;#160; will render &#160; rather than its character.
785
        $text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&#38;\1', $text);
786
787
        $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
788
789
        $font = $this->get_ttf_file($font);
790
791
        // FIXME: word spacing
792
        imagettftext($this->get_image(), $size, $angle, $x, $y + $h, $c, $font, $text);
793
    }
794
795
    public function javascript($code)
796
    {
797
        // Not implemented
798
    }
799
800
    /**
801
     * Add a named destination (similar to <a name="foo">...</a> in html)
802
     *
803
     * @param string $anchorname The name of the named destination
804
     */
805
    public function add_named_dest($anchorname)
806
    {
807
        // Not implemented
808
    }
809
810
    /**
811
     * Add a link to the pdf
812
     *
813
     * @param string $url The url to link to
814
     * @param float $x The x position of the link
815
     * @param float $y The y position of the link
816
     * @param float $width The width of the link
817
     * @param float $height The height of the link
818
     */
819
    public function add_link($url, $x, $y, $width, $height)
820
    {
821
        // Not implemented
822
    }
823
824
    /**
825
     * Add meta information to the PDF
826
     *
827
     * @param string $label label of the value (Creator, Producer, etc.)
828
     * @param string $value the text to set
829
     */
830
    public function add_info($label, $value)
831
    {
832
        // N/A
833
    }
834
835
    /**
836
     * @param string $view
837
     * @param array $options
838
     */
839
    public function set_default_view($view, $options = array())
840
    {
841
        // N/A
842
    }
843
844
    /**
845
     * Calculates text size, in points
846
     *
847
     * @param string $text the text to be sized
848
     * @param string $font the desired font
849
     * @param float $size the desired font size
850
     * @param float $word_spacing word spacing, if any
851
     * @param float $char_spacing char spacing, if any
852
     *
853
     * @return float
854
     */
855
    public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
856
    {
857
        $font = $this->get_ttf_file($font);
858
        $size = $this->_upscale($size) * self::FONT_SCALE;
859
860
        // imagettfbbox() converts numeric entities to their respective
861
        // character. Preserve any originally double encoded entities to be
862
        // represented as is.
863
        // eg: &amp;#160; will render &#160; rather than its character.
864
        $text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&#38;\1', $text);
865
866
        $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
867
868
        // FIXME: word spacing
869
        list($x1, , $x2) = imagettfbbox($size, 0, $font, $text);
870
871
        // Add additional 1pt to prevent text overflow issues
872
        return $this->_downscale($x2 - $x1) + 1;
873
    }
874
875
    /**
876
     * @param $font
877
     * @return string
878
     */
879
    public function get_ttf_file($font)
880
    {
881
        if ( stripos($font, ".ttf") === false ) {
882
            $font .= ".ttf";
883
        }
884
885
        if (!file_exists($font)) {
886
            $font_metrics = $this->_dompdf->getFontMetrics();
887
            $font = $font_metrics->getFont($this->_dompdf->getOptions()->getDefaultFont()) . ".ttf";
888
            if (!file_exists($font)) {
889
                if (strpos($font, "mono")) {
890
                    $font = $font_metrics->getFont("DejaVu Mono") . ".ttf";
891
                } elseif (strpos($font, "sans") !== false) {
892
                    $font = $font_metrics->getFont("DejaVu Sans") . ".ttf";
893
                } elseif (strpos($font, "serif")) {
894
                    $font = $font_metrics->getFont("DejaVu Serif") . ".ttf";
895
                } else {
896
                    $font = $font_metrics->getFont("DejaVu Sans") . ".ttf";
897
                }
898
            }
899
        }
900
901
        return $font;
902
    }
903
904
    /**
905
     * Calculates font height, in points
906
     *
907
     * @param string $font
908
     * @param float $size
909
     * @return float
910
     */
911
    public function get_font_height($font, $size)
912
    {
913
        $size = $this->_upscale($size) * self::FONT_SCALE;
914
915
        $height = $this->get_font_height_actual($font, $size);
916
917
        return $this->_downscale($height);
918
    }
919
920
    private function get_font_height_actual($font, $size)
921
    {
922
        $font = $this->get_ttf_file($font);
923
        $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
924
925
        // FIXME: word spacing
926
        list(, $y2, , , , $y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
927
        return ($y2 - $y1) * $ratio;
928
    }
929
930
    /**
931
     * @param string $font
932
     * @param float $size
933
     * @return float
934
     */
935
    public function get_font_baseline($font, $size)
936
    {
937
        $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
938
        return $this->get_font_height($font, $size) / $ratio;
939
    }
940
941
    /**
942
     * Starts a new page
943
     *
944
     * Subsequent drawing operations will appear on the new page.
945
     */
946
    public function new_page()
947
    {
948
        $this->_page_number++;
949
        $this->_page_count++;
950
951
        $this->_img = imagecreatetruecolor($this->_actual_width, $this->_actual_height);
952
953
        $this->_bg_color = $this->_allocate_color($this->_bg_color_array);
0 ignored issues
show
Documentation introduced by
$this->_bg_color_array is of type integer, 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...
954
        imagealphablending($this->_img, true);
955
        imagesavealpha($this->_img, true);
956
        imagefill($this->_img, 0, 0, $this->_bg_color);
957
958
        $this->_imgs[] = $this->_img;
959
    }
960
961
    public function open_object()
962
    {
963
        // N/A
964
    }
965
966
    public function close_object()
967
    {
968
        // N/A
969
    }
970
971
    public function add_object()
972
    {
973
        // N/A
974
    }
975
976
    public function page_text()
977
    {
978
        // N/A
979
    }
980
981
    /**
982
     * Streams the image to the client.
983
     *
984
     * @param string $filename The filename to present to the client.
985
     * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
986
     *     'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
987
     */
988
    public function stream($filename, $options = array())
989
    {
990
        if (headers_sent()) {
991
            die("Unable to stream image: headers already sent");
0 ignored issues
show
Coding Style Compatibility introduced by
The method stream() contains an exit expression.

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

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

Loading history...
992
        }
993
994
        if (!isset($options["type"])) $options["type"] = "png";
995
        if (!isset($options["Attachment"])) $options["Attachment"] = true;
996
        $type = strtolower($options["type"]);
997
998
        switch ($type) {
999
            case "jpg":
1000
            case "jpeg":
1001
                $contentType = "image/jpeg";
1002
                $extension = ".jpg";
1003
                break;
1004
            case "png":
1005
            default:
1006
                $contentType = "image/png";
1007
                $extension = ".png";
1008
                break;
1009
        }
1010
1011
        header("Cache-Control: private");
1012
        header("Content-Type: $contentType");
1013
1014
        $filename = str_replace(array("\n", "'"), "", basename($filename, ".$type")) . $extension;
1015
        $attachment = $options["Attachment"] ? "attachment" : "inline";
1016
        header(Helpers::buildContentDispositionHeader($attachment, $filename));
1017
1018
        $this->_output($options);
1019
        flush();
1020
    }
1021
1022
    /**
1023
     * Returns the image as a string.
1024
     *
1025
     * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
1026
     *     'page' => Number of the page to output (defaults to the first).
1027
     * @return string
1028
     */
1029
    public function output($options = array())
1030
    {
1031
        ob_start();
1032
1033
        $this->_output($options);
1034
1035
        return ob_get_clean();
1036
    }
1037
1038
    /**
1039
     * Outputs the image stream directly.
1040
     *
1041
     * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
1042
     *     'page' => Number of the page to output (defaults to the first).
1043
     */
1044
    private function _output($options = array())
1045
    {
1046
        if (!isset($options["type"])) $options["type"] = "png";
1047
        if (!isset($options["page"])) $options["page"] = 1;
1048
        $type = strtolower($options["type"]);
1049
1050
        if (isset($this->_imgs[$options["page"] - 1])) {
1051
            $img = $this->_imgs[$options["page"] - 1];
1052
        } else {
1053
            $img = $this->_imgs[0];
1054
        }
1055
1056
        // Perform any antialiasing
1057
        if ($this->_aa_factor != 1) {
1058
            $dst_w = $this->_actual_width / $this->_aa_factor;
1059
            $dst_h = $this->_actual_height / $this->_aa_factor;
1060
            $dst = imagecreatetruecolor($dst_w, $dst_h);
1061
            imagecopyresampled($dst, $img, 0, 0, 0, 0,
1062
                $dst_w, $dst_h,
1063
                $this->_actual_width, $this->_actual_height);
1064
        } else {
1065
            $dst = $img;
1066
        }
1067
1068
        switch ($type) {
1069
            case "jpg":
1070
            case "jpeg":
1071
                if (!isset($options["quality"])) {
1072
                    $options["quality"] = 75;
1073
                }
1074
1075
                imagejpeg($dst, null, $options["quality"]);
1076
                break;
1077
            case "png":
1078
            default:
1079
                imagepng($dst);
1080
                break;
1081
        }
1082
1083
        if ($this->_aa_factor != 1) {
1084
            imagedestroy($dst);
1085
        }
1086
    }
1087
}
1088