1 | <?php |
||||
2 | /* For licensing terms, see /license.txt */ |
||||
3 | |||||
4 | /** |
||||
5 | * Image class |
||||
6 | * This class provides a layer to manage images. |
||||
7 | * |
||||
8 | * @author Julio Montoya <[email protected]> |
||||
9 | * |
||||
10 | * @todo move in a DB configuration setting |
||||
11 | */ |
||||
12 | class Image |
||||
13 | { |
||||
14 | public $image_wrapper = null; |
||||
15 | |||||
16 | /** |
||||
17 | * Image constructor. |
||||
18 | * |
||||
19 | * @param string $path |
||||
20 | */ |
||||
21 | public function __construct($path) |
||||
22 | { |
||||
23 | if (IMAGE_PROCESSOR == 'gd') { |
||||
24 | $this->image_wrapper = new GDWrapper($path); |
||||
25 | } else { |
||||
26 | if (class_exists('Imagick')) { |
||||
27 | $this->image_wrapper = new ImagickWrapper($path); |
||||
28 | } else { |
||||
29 | echo Display::return_message( |
||||
30 | 'Class Imagick not found', |
||||
31 | 'warning' |
||||
32 | ); |
||||
33 | exit; |
||||
34 | } |
||||
35 | } |
||||
36 | } |
||||
37 | |||||
38 | public function resize($max_size_for_picture) |
||||
39 | { |
||||
40 | $image_size = $this->get_image_size($this->image_wrapper->path); |
||||
41 | $width = $image_size['width']; |
||||
42 | $height = $image_size['height']; |
||||
43 | if ($width >= $height) { |
||||
44 | if ($width >= $max_size_for_picture) { |
||||
45 | // scale height |
||||
46 | $new_height = round($height * ($max_size_for_picture / $width)); |
||||
47 | $this->image_wrapper->resize( |
||||
48 | $max_size_for_picture, |
||||
49 | $new_height, |
||||
50 | 0 |
||||
51 | ); |
||||
52 | } |
||||
53 | } else { // height > $width |
||||
54 | if ($height >= $max_size_for_picture) { |
||||
55 | // scale width |
||||
56 | $new_width = round($width * ($max_size_for_picture / $height)); |
||||
57 | $this->image_wrapper->resize( |
||||
58 | $new_width, |
||||
59 | $max_size_for_picture, |
||||
60 | 0 |
||||
61 | ); |
||||
62 | } |
||||
63 | } |
||||
64 | } |
||||
65 | |||||
66 | /** |
||||
67 | * @param string $cropParameters |
||||
68 | * |
||||
69 | * @return bool |
||||
70 | */ |
||||
71 | public function crop($cropParameters) |
||||
72 | { |
||||
73 | $image_size = $this->get_image_size($this->image_wrapper->path); |
||||
74 | $src_width = $image_size['width']; |
||||
75 | $src_height = $image_size['height']; |
||||
76 | $cropParameters = explode(',', $cropParameters); |
||||
77 | |||||
78 | if (isset($cropParameters[0]) && isset($cropParameters[1])) { |
||||
79 | $x = intval($cropParameters[0]); |
||||
80 | $y = intval($cropParameters[1]); |
||||
81 | $width = intval($cropParameters[2]); |
||||
82 | $height = intval($cropParameters[3]); |
||||
83 | |||||
84 | $image = $this->image_wrapper->crop( |
||||
85 | $x, |
||||
86 | $y, |
||||
87 | $width, |
||||
88 | $height, |
||||
89 | $src_width, |
||||
90 | $src_height |
||||
91 | ); |
||||
92 | |||||
93 | return $image; |
||||
94 | } |
||||
95 | |||||
96 | return false; |
||||
97 | } |
||||
98 | |||||
99 | /** |
||||
100 | * @param string $file |
||||
101 | * @param int $compress |
||||
102 | * @param null $convert_file_to |
||||
103 | * |
||||
104 | * @return bool |
||||
105 | */ |
||||
106 | public function send_image( |
||||
107 | $file = '', |
||||
108 | $compress = -1, |
||||
109 | $convert_file_to = null |
||||
110 | ) { |
||||
111 | return $this->image_wrapper->send_image( |
||||
112 | $file, |
||||
113 | $compress, |
||||
114 | $convert_file_to |
||||
115 | ); |
||||
116 | } |
||||
117 | |||||
118 | /** |
||||
119 | * @return array |
||||
120 | */ |
||||
121 | public function get_image_size() |
||||
122 | { |
||||
123 | return $this->image_wrapper->get_image_size(); |
||||
124 | } |
||||
125 | |||||
126 | /** |
||||
127 | * @return array |
||||
128 | */ |
||||
129 | public function get_image_info() |
||||
130 | { |
||||
131 | return $this->image_wrapper->get_image_info(); |
||||
132 | } |
||||
133 | |||||
134 | public function convert2bw() |
||||
135 | { |
||||
136 | $this->image_wrapper->convert2bw(); |
||||
137 | } |
||||
138 | } |
||||
139 | |||||
140 | /** |
||||
141 | * Image wrapper class. |
||||
142 | */ |
||||
143 | abstract class ImageWrapper |
||||
144 | { |
||||
145 | public $debug = true; |
||||
146 | public $path; |
||||
147 | public $width; |
||||
148 | public $height; |
||||
149 | public $type; |
||||
150 | public $allowed_extensions = ['jpeg', 'jpg', 'png', 'gif']; |
||||
151 | public $image_validated = false; |
||||
152 | |||||
153 | public function __construct($path) |
||||
154 | { |
||||
155 | if (empty($path)) { |
||||
156 | return false; |
||||
157 | } |
||||
158 | $this->path = $path; |
||||
159 | $this->set_image_wrapper(); //Creates image obj |
||||
160 | } |
||||
161 | |||||
162 | abstract public function set_image_wrapper(); |
||||
163 | |||||
164 | abstract public function fill_image_info(); |
||||
165 | |||||
166 | abstract public function get_image_size(); |
||||
167 | |||||
168 | abstract public function resize($thumbw, $thumbh, $border, $specific_size = false); |
||||
169 | |||||
170 | abstract public function crop($x, $y, $width, $height, $src_width, $src_height); |
||||
171 | |||||
172 | abstract public function send_image($file = '', $compress = -1, $convert_file_to = null); |
||||
173 | |||||
174 | /** |
||||
175 | * @return array |
||||
176 | */ |
||||
177 | public function get_image_info() |
||||
178 | { |
||||
179 | return [ |
||||
180 | 'width' => $this->width, |
||||
181 | 'height' => $this->height, |
||||
182 | 'type' => $this->type, |
||||
183 | ]; |
||||
184 | } |
||||
185 | } |
||||
186 | |||||
187 | /** |
||||
188 | * Imagick Chamilo wrapper. |
||||
189 | * |
||||
190 | * @author jmontoya |
||||
191 | */ |
||||
192 | class ImagickWrapper extends ImageWrapper |
||||
193 | { |
||||
194 | public $image; |
||||
195 | public $filter = Imagick::FILTER_LANCZOS; |
||||
196 | |||||
197 | /** |
||||
198 | * ImagickWrapper constructor. |
||||
199 | * |
||||
200 | * @param $path |
||||
201 | */ |
||||
202 | public function __construct($path) |
||||
203 | { |
||||
204 | parent::__construct($path); |
||||
205 | } |
||||
206 | |||||
207 | public function set_image_wrapper() |
||||
208 | { |
||||
209 | if ($this->debug) { |
||||
210 | error_log('Image::set_image_wrapper loaded'); |
||||
211 | } |
||||
212 | try { |
||||
213 | if (file_exists($this->path)) { |
||||
214 | $this->image = new Imagick($this->path); |
||||
215 | |||||
216 | if ($this->image) { |
||||
217 | $this->fill_image_info(); //Fills height, width and type |
||||
218 | } |
||||
219 | } else { |
||||
220 | if ($this->debug) { |
||||
221 | error_log('Image::image does not exist'); |
||||
222 | } |
||||
223 | } |
||||
224 | } catch (ImagickException $e) { |
||||
225 | if ($this->debug) { |
||||
226 | error_log($e->getMessage()); |
||||
227 | } |
||||
228 | } |
||||
229 | } |
||||
230 | |||||
231 | public function fill_image_info() |
||||
232 | { |
||||
233 | $image_info = $this->image->identifyImage(); |
||||
234 | $this->width = $image_info['geometry']['width']; |
||||
235 | $this->height = $image_info['geometry']['height']; |
||||
236 | $this->type = strtolower($this->image->getImageFormat()); |
||||
237 | |||||
238 | if (in_array($this->type, $this->allowed_extensions)) { |
||||
239 | $this->image_validated = true; |
||||
240 | if ($this->debug) { |
||||
241 | error_log('image_validated true'); |
||||
242 | } |
||||
243 | } |
||||
244 | } |
||||
245 | |||||
246 | public function get_image_size() |
||||
247 | { |
||||
248 | $imagesize = ['width' => 0, 'height' => 0]; |
||||
249 | if ($this->image_validated) { |
||||
250 | $imagesize = $this->image->getImageGeometry(); |
||||
251 | } |
||||
252 | |||||
253 | return $imagesize; |
||||
254 | } |
||||
255 | |||||
256 | //@todo implement border logic case for Imagick |
||||
257 | public function resize($thumbw, $thumbh, $border, $specific_size = false) |
||||
258 | { |
||||
259 | if (!$this->image_validated) { |
||||
260 | return false; |
||||
261 | } |
||||
262 | |||||
263 | if ($specific_size) { |
||||
264 | $width = $thumbw; |
||||
265 | $height = $thumbh; |
||||
266 | } else { |
||||
267 | $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0; |
||||
268 | $width = (int) ($this->width * $scale); |
||||
269 | $height = (int) ($this->height * $scale); |
||||
270 | } |
||||
271 | $result = $this->image->resizeImage($width, $height, $this->filter, 1); |
||||
272 | $this->width = $thumbw; |
||||
273 | $this->height = $thumbh; |
||||
274 | } |
||||
275 | |||||
276 | /** |
||||
277 | * @author José Loguercio <[email protected]> |
||||
278 | * |
||||
279 | * @param int $x coordinate of the cropped region top left corner |
||||
280 | * @param int $y coordinate of the cropped region top left corner |
||||
281 | * @param int $width the width of the crop |
||||
282 | * @param int $height the height of the crop |
||||
283 | * @param int $src_width the source width of the original image |
||||
284 | * @param int $src_height the source height of the original image |
||||
285 | * |
||||
286 | * @return bool |
||||
287 | */ |
||||
288 | public function crop($x, $y, $width, $height, $src_width, $src_height) |
||||
289 | { |
||||
290 | if (!$this->image_validated) { |
||||
291 | return false; |
||||
292 | } |
||||
293 | $this->image->cropimage($width, $height, $x, $y); |
||||
294 | $this->width = $width; |
||||
295 | $this->height = $height; |
||||
296 | |||||
297 | return true; |
||||
298 | } |
||||
299 | |||||
300 | public function send_image( |
||||
301 | $file = '', |
||||
302 | $compress = -1, |
||||
303 | $convert_file_to = null |
||||
304 | ) { |
||||
305 | if (!$this->image_validated) { |
||||
306 | return false; |
||||
307 | } |
||||
308 | $type = $this->type; |
||||
309 | if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) { |
||||
310 | $type = $convert_file_to; |
||||
311 | } |
||||
312 | switch ($type) { |
||||
313 | case 'jpeg': |
||||
314 | case 'jpg': |
||||
315 | if (!$file) { |
||||
316 | header("Content-type: image/jpeg"); |
||||
317 | } |
||||
318 | break; |
||||
319 | case 'png': |
||||
320 | if (!$file) { |
||||
321 | header("Content-type: image/png"); |
||||
322 | } |
||||
323 | break; |
||||
324 | case 'gif': |
||||
325 | if (!$file) { |
||||
326 | header("Content-type: image/gif"); |
||||
327 | } |
||||
328 | break; |
||||
329 | } |
||||
330 | $result = false; |
||||
331 | try { |
||||
332 | $result = $this->image->writeImage($file); |
||||
333 | } catch (ImagickException $e) { |
||||
334 | if ($this->debug) { |
||||
335 | error_log($e->getMessage()); |
||||
336 | } |
||||
337 | } |
||||
338 | |||||
339 | if (!$file) { |
||||
340 | echo $this->image; |
||||
341 | $this->image->clear(); |
||||
342 | $this->image->destroy(); |
||||
343 | } else { |
||||
344 | $this->image->clear(); |
||||
345 | $this->image->destroy(); |
||||
346 | |||||
347 | return $result; |
||||
348 | } |
||||
349 | } |
||||
350 | } |
||||
351 | |||||
352 | /** |
||||
353 | * php-gd wrapper. |
||||
354 | */ |
||||
355 | class GDWrapper extends ImageWrapper |
||||
356 | { |
||||
357 | public $bg; |
||||
358 | |||||
359 | /** |
||||
360 | * GDWrapper constructor. |
||||
361 | * |
||||
362 | * @param $path |
||||
363 | */ |
||||
364 | public function __construct($path) |
||||
365 | { |
||||
366 | parent::__construct($path); |
||||
367 | } |
||||
368 | |||||
369 | public function set_image_wrapper() |
||||
370 | { |
||||
371 | $handler = null; |
||||
372 | $this->fill_image_info(); |
||||
373 | |||||
374 | switch ($this->type) { |
||||
375 | case 0: |
||||
376 | $handler = false; |
||||
377 | break; |
||||
378 | case 1: |
||||
379 | $handler = @imagecreatefromgif($this->path); |
||||
380 | $this->type = 'gif'; |
||||
381 | break; |
||||
382 | case 2: |
||||
383 | $handler = @imagecreatefromjpeg($this->path); |
||||
384 | $this->type = 'jpg'; |
||||
385 | break; |
||||
386 | case 3: |
||||
387 | $handler = @imagecreatefrompng($this->path); |
||||
388 | $this->type = 'png'; |
||||
389 | break; |
||||
390 | } |
||||
391 | if ($handler) { |
||||
392 | $this->image_validated = true; |
||||
393 | $this->bg = $handler; |
||||
394 | @imagealphablending($this->bg, false); |
||||
395 | @imagesavealpha($this->bg, true); |
||||
396 | } |
||||
397 | } |
||||
398 | |||||
399 | /** |
||||
400 | * @return array |
||||
401 | */ |
||||
402 | public function get_image_size() |
||||
403 | { |
||||
404 | $return_array = ['width' => 0, 'height' => 0]; |
||||
405 | if ($this->image_validated) { |
||||
406 | $return_array = ['width' => $this->width, 'height' => $this->height]; |
||||
407 | } |
||||
408 | |||||
409 | return $return_array; |
||||
410 | } |
||||
411 | |||||
412 | public function fill_image_info() |
||||
413 | { |
||||
414 | if (file_exists($this->path)) { |
||||
415 | $image_info = getimagesize($this->path); |
||||
416 | $this->width = $image_info[0]; |
||||
417 | $this->height = $image_info[1]; |
||||
418 | $this->type = $image_info[2]; |
||||
419 | } else { |
||||
420 | $this->width = 0; |
||||
421 | $this->height = 0; |
||||
422 | $this->type = 0; |
||||
423 | } |
||||
424 | } |
||||
425 | |||||
426 | public function resize($thumbw, $thumbh, $border, $specific_size = false) |
||||
427 | { |
||||
428 | if (!$this->image_validated) { |
||||
429 | return false; |
||||
430 | } |
||||
431 | if (1 == $border) { |
||||
432 | if ($specific_size) { |
||||
433 | $width = $thumbw; |
||||
434 | $height = $thumbh; |
||||
435 | } else { |
||||
436 | $scale = min($thumbw / $this->width, $thumbh / $this->height); |
||||
437 | $width = (int) ($this->width * $scale); |
||||
438 | $height = (int) ($this->height * $scale); |
||||
439 | } |
||||
440 | $deltaw = (int) (($thumbw - $width) / 2); |
||||
441 | $deltah = (int) (($thumbh - $height) / 2); |
||||
442 | $dst_img = @ImageCreateTrueColor($thumbw, $thumbh); |
||||
443 | @imagealphablending($dst_img, false); |
||||
444 | @imagesavealpha($dst_img, true); |
||||
445 | |||||
446 | if (!empty($this->color)) { |
||||
447 | @imagefill($dst_img, 0, 0, $this->color); |
||||
448 | } |
||||
449 | $this->width = $thumbw; |
||||
450 | $this->height = $thumbh; |
||||
451 | } elseif (0 == $border) { |
||||
452 | if ($specific_size) { |
||||
453 | $width = $thumbw; |
||||
454 | $height = $thumbh; |
||||
455 | } else { |
||||
456 | $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0; |
||||
457 | $width = (int) ($this->width * $scale); |
||||
458 | $height = (int) ($this->height * $scale); |
||||
459 | } |
||||
460 | $deltaw = 0; |
||||
461 | $deltah = 0; |
||||
462 | $dst_img = @ImageCreateTrueColor($width, $height); |
||||
463 | @imagealphablending($dst_img, false); |
||||
464 | @imagesavealpha($dst_img, true); |
||||
465 | $this->width = $width; |
||||
466 | $this->height = $height; |
||||
467 | } |
||||
468 | $src_img = $this->bg; |
||||
469 | @ImageCopyResampled( |
||||
470 | $dst_img, |
||||
471 | $src_img, |
||||
472 | $deltaw, |
||||
473 | $deltah, |
||||
474 | 0, |
||||
475 | 0, |
||||
476 | $width, |
||||
477 | $height, |
||||
478 | ImageSX($src_img), |
||||
479 | ImageSY($src_img) |
||||
480 | ); |
||||
481 | $this->bg = $dst_img; |
||||
482 | @imagedestroy($src_img); |
||||
483 | } |
||||
484 | |||||
485 | /** |
||||
486 | * @author José Loguercio <[email protected]> |
||||
487 | * |
||||
488 | * @param int $x coordinate of the cropped region top left corner |
||||
489 | * @param int $y coordinate of the cropped region top left corner |
||||
490 | * @param int $width the width of the crop |
||||
491 | * @param int $height the height of the crop |
||||
492 | * @param int $src_width the source width of the original image |
||||
493 | * @param int $src_height the source height of the original image |
||||
494 | */ |
||||
495 | public function crop($x, $y, $width, $height, $src_width, $src_height) |
||||
496 | { |
||||
497 | if (!$this->image_validated) { |
||||
498 | return false; |
||||
499 | } |
||||
500 | $this->width = $width; |
||||
501 | $this->height = $height; |
||||
502 | $src = null; |
||||
503 | $dest = @imagecreatetruecolor($width, $height); |
||||
504 | $type = $this->type; |
||||
505 | switch ($type) { |
||||
506 | case 'jpeg': |
||||
507 | case 'jpg': |
||||
508 | $src = @imagecreatefromjpeg($this->path); |
||||
509 | @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height); |
||||
510 | @imagejpeg($dest, $this->path); |
||||
511 | break; |
||||
512 | case 'png': |
||||
513 | $src = @imagecreatefrompng($this->path); |
||||
514 | @imagealphablending($dest, false); |
||||
0 ignored issues
–
show
|
|||||
515 | @imagesavealpha($dest, true); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
imagesavealpha() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||||
516 | @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height); |
||||
517 | @imagepng($dest, $this->path); |
||||
518 | break; |
||||
519 | case 'gif': |
||||
520 | $src = @imagecreatefromgif($this->path); |
||||
521 | @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height); |
||||
522 | @imagegif($dest, $this->path); |
||||
523 | break; |
||||
524 | default: |
||||
525 | return 0; |
||||
526 | } |
||||
527 | @imagedestroy($dest); |
||||
528 | @imagedestroy($src); |
||||
529 | } |
||||
530 | |||||
531 | /** |
||||
532 | * @param string $file |
||||
533 | * @param int $compress |
||||
534 | * @param null $convert_file_to |
||||
535 | * |
||||
536 | * @return bool|int |
||||
537 | */ |
||||
538 | public function send_image($file = '', $compress = -1, $convert_file_to = null) |
||||
539 | { |
||||
540 | if (!$this->image_validated) { |
||||
541 | return false; |
||||
542 | } |
||||
543 | $compress = (int) $compress; |
||||
544 | $type = $this->type; |
||||
545 | if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) { |
||||
546 | $type = $convert_file_to; |
||||
547 | } |
||||
548 | switch ($type) { |
||||
549 | case 'jpeg': |
||||
550 | case 'jpg': |
||||
551 | if (!$file) { |
||||
552 | header("Content-type: image/jpeg"); |
||||
553 | } |
||||
554 | if (-1 == $compress) { |
||||
555 | $compress = 100; |
||||
556 | } |
||||
557 | |||||
558 | return imagejpeg($this->bg, $file, $compress); |
||||
559 | break; |
||||
560 | case 'png': |
||||
561 | if (!$file) { |
||||
562 | header("Content-type: image/png"); |
||||
563 | } |
||||
564 | if (-1 != $compress) { |
||||
565 | @imagetruecolortopalette($this->bg, true, $compress); |
||||
566 | } |
||||
567 | |||||
568 | return imagepng($this->bg, $file, $compress); |
||||
569 | break; |
||||
570 | case 'gif': |
||||
571 | if (!$file) { |
||||
572 | header("Content-type: image/gif"); |
||||
573 | } |
||||
574 | if (-1 != $compress) { |
||||
575 | @imagetruecolortopalette($this->bg, true, $compress); |
||||
576 | } |
||||
577 | |||||
578 | return imagegif($this->bg, $file, $compress); |
||||
579 | break; |
||||
580 | default: |
||||
581 | return 0; |
||||
582 | } |
||||
583 | // TODO: Occupied memory is not released, because the following fragment of code is actually dead. |
||||
584 | @imagedestroy($this->bg); |
||||
585 | } |
||||
586 | |||||
587 | /** |
||||
588 | * Convert image to black & white. |
||||
589 | */ |
||||
590 | public function convert2bw() |
||||
591 | { |
||||
592 | if (!$this->image_validated) { |
||||
593 | return false; |
||||
594 | } |
||||
595 | |||||
596 | $dest_img = imagecreatetruecolor(imagesx($this->bg), imagesy($this->bg)); |
||||
597 | /* copy ignore the transparent color |
||||
598 | * so that we can use black (0,0,0) as transparent, which is what |
||||
599 | * the image is filled with when created. |
||||
600 | */ |
||||
601 | $transparent = imagecolorallocate($dest_img, 0, 0, 0); |
||||
602 | imagealphablending($dest_img, false); |
||||
603 | imagesavealpha($dest_img, true); |
||||
604 | imagecolortransparent($dest_img, $transparent); |
||||
605 | imagecopy( |
||||
606 | $dest_img, |
||||
607 | $this->bg, |
||||
608 | 0, |
||||
609 | 0, |
||||
610 | 0, |
||||
611 | 0, |
||||
612 | imagesx($this->bg), |
||||
613 | imagesx($this->bg) |
||||
614 | ); |
||||
615 | imagefilter($dest_img, IMG_FILTER_GRAYSCALE); |
||||
616 | $this->bg = $dest_img; |
||||
617 | |||||
618 | return true; |
||||
619 | } |
||||
620 | } |
||||
621 |
If you suppress an error, we recommend checking for the error condition explicitly: