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