Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
234 / 234
100.00% covered (success)
100.00%
26 / 26
CRAP
100.00% covered (success)
100.00%
1 / 1
Parser
100.00% covered (success)
100.00%
234 / 234
100.00% covered (success)
100.00%
26 / 26
82
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createImageFromFile
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 createImageFromStream
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 loadImageFromFile
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
10
 loadImageFromStream
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
11
 setX
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setY
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setIndex
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getX
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getY
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWidth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHeight
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConvertedImage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIndex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStream
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getXObject
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getObjects
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 parse
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 parseImageData
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
15
 parsePng
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
9
 parseJpeg
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 createResource
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 convertToJpeg
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 convertImage
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 resizeImage
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
5
 readInt
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Pdf\Build\Image;
16
17use Pop\Pdf\Build\PdfObject\StreamObject;
18
19/**
20 * Pdf image parser class
21 *
22 * @category   Pop
23 * @package    Pop\Pdf
24 * @author     Nick Sagona, III <nick@popphp.org>
25 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
26 * @license    https://www.popphp.org/license     New BSD License
27 * @version    6.2.0
28 */
29class Parser
30{
31
32    /**
33     * Image width
34     * @var int
35     */
36    protected int $width = 0;
37
38    /**
39     * Image height
40     * @var int
41     */
42    protected int $height = 0;
43
44    /**
45     * Image resize value
46     * @var ?array
47     */
48    protected ?array $resize = null;
49
50    /**
51     * Image mime
52     * @var ?string
53     */
54    protected ?string $mime = null;
55
56    /**
57     * Image color mode
58     * @var mixed
59     */
60    protected mixed $colorMode = null;
61
62    /**
63     * Number of channels in the image
64     * @var ?int
65     */
66    protected ?int $channels = null;
67
68    /**
69     * Image bit-depth
70     * @var int
71     */
72    protected int $depth = 0;
73
74    /**
75     * Image basename
76     * @var ?string
77     */
78    protected ?string $basename = null;
79
80    /**
81     * Image filename
82     * @var ?string
83     */
84    protected ?string $filename = null;
85
86    /**
87     * Image extension
88     * @var ?string
89     */
90    protected ?string $extension = null;
91
92    /**
93     * Image fullpath
94     * @var ?string
95     */
96    protected ?string $fullpath = null;
97
98    /**
99     * Image stream
100     * @var ?string
101     */
102    protected ?string $stream = null;
103
104    /**
105     * Image total number of colors
106     * @var int
107     */
108    protected int $colorTotal = 0;
109
110    /**
111     * Flag for if the image has an alpha channel
112     * @var bool
113     */
114    protected bool $alpha = false;
115
116    /**
117     * Image data
118     * @var mixed
119     */
120    protected mixed $imageData = null;
121
122    /**
123     * Image data length
124     * @var ?int
125     */
126    protected ?int$imageDataLength = null;
127
128    /**
129     * GD image resource
130     * @var mixed
131     */
132    protected mixed $resource = null;
133
134    /**
135     * Image X Coordinate
136     * @var int
137     */
138    protected int $x = 0;
139
140    /**
141     * Image Y Coordinate
142     * @var int
143     */
144    protected int $y = 0;
145
146    /**
147     * Image object index
148     * @var ?int
149     */
150    protected ?int $index = null;
151
152    /**
153     * Image objects
154     * @var array
155     */
156    protected array $objects = [];
157
158    /**
159     * Converted GIF to PNG image
160     * @var ?string
161     */
162    protected ?string $convertedImage = null;
163
164    /**
165     * Resized image
166     * @var ?string
167     */
168    protected ?string $resizedImage = null;
169
170    /**
171     * Constructor
172     *
173     * Instantiate a image parser object
174     *
175     * @param  int $x
176     * @param  int $y
177     */
178    public function __construct(int $x, int $y)
179    {
180        $this->setX($x);
181        $this->setY($y);
182    }
183
184    /**
185     * Create image from file
186     *
187     * @param  string $imageFile
188     * @param  int    $x
189     * @param  int    $y
190     * @param  ?array $resize
191     * @param  bool   $preserveResolution
192     * @return Parser
193     */
194    public static function createImageFromFile(
195        string $imageFile, int $x, int $y, ?array $resize = null, bool $preserveResolution = false
196    ): Parser
197    {
198        $parser = new self($x, $y);
199        $parser->loadImageFromFile($imageFile, $resize, $preserveResolution);
200        return $parser;
201    }
202
203    /**
204     * Create image from stream
205     *
206     * @param  string $imageStream
207     * @param  int    $x
208     * @param  int    $y
209     * @param  ?array $resize
210     * @param  bool   $preserveResolution
211     * @return Parser
212     */
213    public static function createImageFromStream(
214        string $imageStream, int $x, int $y, ?array $resize = null, bool $preserveResolution = false
215    ): Parser
216    {
217        $parser = new self($x, $y);
218        $parser->loadImageFromStream($imageStream, $resize, $preserveResolution);
219        return $parser;
220    }
221
222    /**
223     * Load image from file
224     *
225     * @param  string $imageFile
226     * @param  ?array $resize
227     * @param  bool   $preserveResolution
228     * @throws Exception
229     * @return Parser
230     */
231    public function loadImageFromFile(string $imageFile, ?array $resize = null, bool $preserveResolution = false): Parser
232    {
233        $parts           = pathinfo($imageFile);
234        $this->fullpath  = realpath($imageFile);
235        $this->basename  = $parts['basename'];
236        $this->filename  = $parts['filename'];
237        $this->extension = (isset($parts['extension']) && ($parts['extension'] != '')) ? $parts['extension'] : null;
238        $this->stream    = null;
239
240        // Convert GIF image to PNG
241        if (strtolower($this->extension) == 'gif') {
242            $this->convertImage();
243        } else {
244            $this->mime = (strtolower($this->extension) == 'png') ? 'image/png' : 'image/jpeg';
245        }
246
247        // If resize dimensions are passed
248        if (($resize !== null) && !($preserveResolution)) {
249            $this->resizeImage($resize);
250        }
251
252        $imgSize = getimagesize($this->fullpath);
253
254        if ($resize !== null) {
255            $this->resize = $resize;
256        }
257
258        $this->width    = $imgSize[0];
259        $this->height   = $imgSize[1];
260        $this->channels = (isset($imgSize['channels'])) ? $imgSize['channels'] : null;
261        $this->depth    = (isset($imgSize['bits'])) ? $imgSize['bits'] : null;
262
263        $this->imageData       = file_get_contents($this->fullpath);
264        $this->imageDataLength = strlen($this->imageData);
265
266        $this->parseImageData();
267
268        return $this;
269    }
270
271    /**
272     * Load image from stream
273     *
274     * @param  string $imageStream
275     * @param  ?array $resize
276     * @param  bool   $preserveResolution
277     * @throws Exception
278     * @return Parser
279     */
280    public function loadImageFromStream(string $imageStream, ?array $resize = null, bool $preserveResolution = false): Parser
281    {
282        $this->stream = $imageStream;
283        $imgSize      = getimagesizefromstring($this->stream);
284
285        $this->fullpath = null;
286
287        switch ($imgSize['mime']) {
288            case 'image/jpeg':
289                $this->basename  = 'image.jpg';
290                $this->filename  = 'image';
291                $this->extension = 'jpg';
292                break;
293            case 'image/gif':
294                $this->basename  = 'image.gif';
295                $this->filename  = 'image';
296                $this->extension = 'gif';
297                break;
298            case 'image/png':
299                $this->basename  = 'image.png';
300                $this->filename  = 'image';
301                $this->extension = 'png';
302                break;
303        }
304
305        // Convert GIF image to PNG
306        if (strtolower($this->extension) == 'gif') {
307            $this->convertImage();
308        } else {
309            $this->mime = (strtolower($this->extension) == 'png') ? 'image/png' : 'image/jpeg';
310        }
311
312        // If resize dimensions are passed
313        if (($resize !== null) && !($preserveResolution)) {
314            $this->resizeImage($resize);
315            // resizeImage() rewrites $this->stream to the resized bytes -
316            // $imgSize must be re-read from it, or /Width, /Height and
317            // /Columns end up declaring the original, pre-resize size while
318            // the embedded pixel data is actually the resized size.
319            $imgSize = getimagesizefromstring($this->stream);
320        }
321
322        if ($resize !== null) {
323            $this->resize = $resize;
324        }
325
326        $this->width    = $imgSize[0];
327        $this->height   = $imgSize[1];
328        $this->channels = (isset($imgSize['channels'])) ? $imgSize['channels'] : null;
329        $this->depth    = (isset($imgSize['bits'])) ? $imgSize['bits'] : null;
330
331        $this->imageData       = $this->stream;
332        $this->imageDataLength = strlen($this->imageData);
333
334        $this->parseImageData();
335
336        return $this;
337    }
338
339    /**
340     * Set the X coordinate
341     *
342     * @param  int $x
343     * @return Parser
344     */
345    public function setX(int $x): Parser
346    {
347        $this->x = $x;
348        return $this;
349    }
350
351    /**
352     * Set the Y coordinate
353     *
354     * @param  int $y
355     * @return Parser
356     */
357    public function setY(int $y): Parser
358    {
359        $this->y = (int)$y;
360        return $this;
361    }
362
363    /**
364     * Set the image object index
365     *
366     * @param  int $i
367     * @return Parser
368     */
369    public function setIndex(int $i): Parser
370    {
371        $this->index = $i;
372        return $this;
373    }
374
375    /**
376     * Get the X coordinate
377     *
378     * @return int
379     */
380    public function getX(): int
381    {
382        return $this->x;
383    }
384
385    /**
386     * Get the Y coordinate
387     *
388     * @return int
389     */
390    public function getY(): int
391    {
392        return $this->y;
393    }
394
395    /**
396     * Get the width
397     *
398     * @return int
399     */
400    public function getWidth(): int
401    {
402        return $this->width;
403    }
404
405    /**
406     * Get the height
407     *
408     * @return int
409     */
410    public function getHeight(): int
411    {
412        return $this->height;
413    }
414
415    /**
416     * Get the converted image
417     *
418     * @return ?string
419     */
420    public function getConvertedImage(): ?string
421    {
422        return $this->convertedImage;
423    }
424
425    /**
426     * Get the image object index
427     *
428     * @return ?int
429     */
430    public function getIndex(): ?int
431    {
432        return $this->index;
433    }
434
435    /**
436     * Get the image stream
437     *
438     * @return string
439     */
440    public function getStream(): string
441    {
442        $width  = $this->resize['width'] ?? $this->width;
443        $height = $this->resize['height'] ?? $this->height;
444        return "\n\nq\n\n1 0 0 1 {$this->x} {$this->y} cm\n{$width} 0 0 {$height} 0 0 cm\n/I{$this->index} Do\n\nQ\n\n";
445    }
446
447    /**
448     * Get the XObject
449     *
450     * @return string
451     */
452    public function getXObject(): string
453    {
454        return "/I{$this->index} {$this->index} 0 R";
455    }
456
457    /**
458     * Get the image objects
459     *
460     * @throws Exception
461     * @return array
462     */
463    public function getObjects(): array
464    {
465        if (count($this->objects) == 0) {
466            $this->parse();
467        }
468        return $this->objects;
469    }
470
471    /**
472     * Parse the image data and create the image objects
473     *
474     * @throws Exception
475     * @return void
476     */
477    public function parse(): void
478    {
479        if ($this->index === null) {
480            throw new Exception('Error: The image index has not been set.');
481        }
482        if ($this->mime == 'image/png') {
483            $this->parsePng();
484        } else {
485            $this->parseJpeg();
486        }
487    }
488
489    /**
490     * Parse image data
491     *
492     * @return void
493     */
494    protected function parseImageData(): void
495    {
496        if (str_starts_with(strtolower($this->extension), 'jp')) {
497            switch ($this->channels) {
498                case 1:
499                    $this->colorMode = 'Gray';
500                    break;
501                case 3:
502                    $this->colorMode = 'RGB';
503                    break;
504                case 4:
505                    $this->colorMode = 'CMYK';
506                    break;
507            }
508        } else if (strtolower($this->extension) == 'png') {
509            $colorType  = ord($this->imageData[25]);
510            switch ($colorType) {
511                case 0:
512                    $this->channels  = 1;
513                    $this->colorMode = 'Gray';
514                    break;
515                case 2:
516                    $this->channels  = 3;
517                    $this->colorMode = 'RGB';
518                    break;
519                case 3:
520                    $this->channels  = 3;
521                    $this->colorMode = 'Indexed';
522                    break;
523                case 4:
524                    $this->channels  = 1;
525                    $this->colorMode = 'Gray';
526                    $this->alpha     = true;
527                    break;
528                case 6:
529                    $this->channels  = 3;
530                    $this->colorMode = 'RGB';
531                    $this->alpha     = true;
532                    break;
533            }
534        }
535
536        $this->createResource();
537
538        // Image clean up if the image was converted or resized.
539        if (($this->convertedImage !== null) && file_exists($this->convertedImage)) {
540            unlink($this->convertedImage);
541        }
542        if (($this->resizedImage !== null) && file_exists($this->resizedImage)) {
543            unlink($this->resizedImage);
544        }
545    }
546
547    /**
548     * Parse the PNG image data
549     *
550     * @throws Exception
551     * @return void
552     */
553    protected function parsePng(): void
554    {
555        // Define some PNG image-specific variables.
556        $PLTE      = null;
557        $TRNS      = null;
558        $maskIndex = null;
559        $mask      = null;
560        $palIndex  = null;
561
562        // Determine the PNG colorspace.
563        if ($this->colorMode == 'Gray') {
564            $numOfColors = 1;
565            $colorSpace  = '/DeviceGray';
566        } else if (stripos($this->colorMode, 'RGB') !== false) {
567            $numOfColors = 3;
568            $colorSpace  = '/DeviceRGB';
569        } else {
570            $numOfColors = 1;
571            $palIndex    = $this->index + 1;
572
573            // If the PNG is indexed, parse and read the palette and any transparencies that might exist.
574            if (str_contains($this->imageData, 'PLTE')) {
575                $lenByte   = substr($this->imageData, (strpos($this->imageData, "PLTE") - 4), 4);
576                $palLength = $this->readInt($lenByte);
577                $PLTE      = substr($this->imageData, (strpos($this->imageData, "PLTE") + 4), $palLength);
578                $mask      = null;
579
580                // If a transparency exists, parse it and set the mask accordingly, along with the palette.
581                if (str_contains($this->imageData, 'tRNS')) {
582                    $lenByte   = substr($this->imageData, (strpos($this->imageData, "tRNS") - 4), 4);
583                    $TRNS      = substr($this->imageData, (strpos($this->imageData, "tRNS") + 4), $this->readInt($lenByte));
584                    $maskIndex = strpos($TRNS, chr(0));
585                    $mask      = "    /Mask [" . $maskIndex . " " . $maskIndex . "]\n";
586                }
587            }
588
589            $this->colorTotal = imagecolorstotal($this->resource);
590            $colorSpace       = "[/Indexed /DeviceRGB " . ($this->colorTotal - 1) . " " . $palIndex . " 0 R]";
591        }
592
593        // Parse header data, bits and color type
594        $lenByte   = substr($this->imageData, (strpos($this->imageData, "IHDR") - 4), 4);
595        $header    = substr($this->imageData, (strpos($this->imageData, "IHDR") + 4), $this->readInt($lenByte));
596        $bits      = ord(substr($header, 8, 1));
597        $colorType = ord(substr($header, 9, 1));
598
599        // Make sure the PNG does not contain a true alpha channel.
600        if (($colorType >= 4) && (($bits == 8) || ($bits == 16))) {
601            throw new Exception('Error: PNG alpha channels are not supported. Only 8-bit transparent PNG images are supported.');
602        }
603
604        // Parse and set the PNG image data and data length.
605        $lenByte = substr($this->imageData, (strpos($this->imageData, "IDAT") - 4), 4);
606        $this->imageDataLength = $this->readInt($lenByte);
607        $IDAT = substr($this->imageData, (strpos($this->imageData, "IDAT") + 4), $this->imageDataLength);
608
609        // Add the image to the objects array.
610        $this->objects[$this->index] = StreamObject::parse(
611            "{$this->index} 0 obj\n<<\n    /Type /XObject\n    /Subtype /Image\n    /Width " . $this->width .
612            "\n    /Height " . $this->height . "\n    /ColorSpace {$colorSpace}\n    /BitsPerComponent " . $bits .
613            "\n    /Filter /FlateDecode\n    /DecodeParms <</Predictor 15 /Colors {$numOfColors} /BitsPerComponent " .
614            $bits . " /Columns " . $this->width . ">>\n{$mask}    /Length {$this->imageDataLength}\n>>\nstream\n{$IDAT}\nendstream\nendobj\n"
615        );
616
617        // If it exists, add the image palette to the objects array.
618        if ($PLTE != '') {
619            $this->objects[$palIndex] = StreamObject::parse(
620                "{$palIndex} 0 obj\n<<\n    /Length " . $palLength . "\n>>\nstream\n{$PLTE}\nendstream\nendobj\n"
621            );
622            $this->objects[$palIndex]->setPalette(true);
623        }
624    }
625
626    /**
627     * Parse the JPG image data
628     *
629     * @return void
630     */
631    protected function parseJpeg(): void
632    {
633        // Add the image to the objects array.
634        $colorMode  = (strtolower($this->colorMode) == 'srgb') ? 'RGB' : $this->colorMode;
635        $colorSpace = ($this->colorMode == 'CMYK') ? "/DeviceCMYK\n    /Decode [1 0 1 0 1 0 1 0]" : "/Device" . $colorMode;
636        $this->objects[$this->index] = StreamObject::parse(
637            "{$this->index} 0 obj\n<<\n    /Type /XObject\n    /Subtype /Image\n    /Width " . $this->width .
638            "\n    /Height " . $this->height . "\n    /ColorSpace {$colorSpace}\n    /BitsPerComponent 8\n    " .
639            "/Filter /DCTDecode\n    /Length {$this->imageDataLength}\n>>\nstream\n{$this->imageData}\nendstream\nendobj\n"
640        );
641    }
642
643    /**
644     * Create a new image resource based on the current image type
645     * of the image object.
646     *
647     * @return void
648     */
649    protected function createResource(): void
650    {
651        if ($this->stream !== null) {
652            $this->resource = imagecreatefromstring($this->stream);
653        } else if (file_exists($this->fullpath)) {
654            // GIFs are always converted to PNG (convertImage()) before
655            // $this->mime is set, so it's never 'image/gif' here.
656            switch ($this->mime) {
657                case 'image/png':
658                    $this->resource = imagecreatefrompng($this->fullpath);
659                    break;
660                case 'image/jpeg':
661                    $this->resource = imagecreatefromjpeg($this->fullpath);
662                    break;
663            }
664        }
665    }
666
667    /**
668     * Method to convert the image to Jpg
669     *
670     * @param  int $quality
671     * @return void
672     */
673    public function convertToJpeg(int $quality = 70): void
674    {
675        $this->convertedImage = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . $this->filename . '_' . time() . '.jpg';
676
677        $result = imagecreatetruecolor($this->width, $this->height);
678        imagecopyresampled(
679            $result, $this->resource, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height
680        );
681
682        $this->resource = $result;
683        $this->width    = imagesx($this->resource);
684        $this->height   = imagesy($this->resource);
685
686        imagejpeg($this->resource, $this->convertedImage, $quality);
687    }
688
689    /**
690     * Method to convert the image from GIF to PNG.
691     *
692     * @return void
693     */
694    protected function convertImage(): void
695    {
696        // Define the temp converted image.
697        $this->convertedImage = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . $this->filename . '_' . time() . '.png';
698
699        // Convert the GIF to PNG, save and clear the output buffer.
700
701        $resource = ($this->stream !== null) ?
702            imagecreatefromstring($this->stream) : imagecreatefromgif($this->fullpath);
703
704        imageinterlace($resource, false);
705        imagepng($resource, $this->convertedImage);
706
707        // Change the type of the image object to the new,
708        // requested image type.
709        $this->extension = 'png';
710        $this->mime      = 'image/png';
711
712        // Redefine the image object properties with the new values.
713        if ($this->stream !== null) {
714            $this->stream = file_get_contents($this->convertedImage);
715        } else {
716            $this->fullpath = $this->convertedImage;
717        }
718        $this->basename = basename($this->convertedImage);
719        $this->filename = basename($this->convertedImage, '.png');
720    }
721
722    /**
723     * Method to resize the image.
724     *
725     * @param  array $resize
726     * @return void
727     */
728    protected function resizeImage(array $resize): void
729    {
730        // Define the temp resized image.
731        $this->resizedImage = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . $this->filename . '_' . time() . '.' . $this->extension;
732
733        // Get image properties.
734        if ($this->stream !== null) {
735            $imgSize  = getimagesizefromstring($this->stream);
736            $resource = imagecreatefromstring($this->stream);
737        } else {
738            $imgSize  = getimagesize($this->fullpath);
739            $resource = ($this->mime == 'image/png') ?
740                imagecreatefrompng($this->fullpath) : imagecreatefromjpeg($this->fullpath);
741        }
742
743        $width  = $imgSize[0];
744        $height = $imgSize[1];
745        $output = imagecreatetruecolor($resize['width'], $resize['height']);
746
747        imagecopyresampled($output, $resource, 0, 0, 0, 0, $resize['width'], $resize['height'], $width, $height);
748
749        if ($this->mime == 'image/png') {
750            imagepng($output, $this->resizedImage, 1);
751            $this->filename = basename($this->resizedImage, '.png');
752        } else {
753            imagejpeg($output, $this->resizedImage, 90);
754            $this->filename = basename($this->resizedImage, '.jpg');
755        }
756
757        $this->basename = basename($this->resizedImage);
758
759        if ($this->stream !== null) {
760            $this->stream = file_get_contents($this->resizedImage);
761        } else {
762            $this->fullpath = $this->resizedImage;
763        }
764    }
765
766    /**
767     * Method to read an unsigned integer.
768     *
769     * @param  string $data
770     * @return int
771     */
772    protected function readInt(string $data): int
773    {
774        $ary = unpack('Nlength', $data);
775        return $ary['length'];
776    }
777
778}