EGOCMS  24.0
EGOTEC Content-Managament-System
Ego_Image.php
gehe zur Dokumentation dieser Datei
1 <?php
8 class Ego_Image_Exception extends Exception {
9  const INIT_FAILED = 1;
10 }
11 
17 class Ego_Image {
23  const EXIF_ALL = 0;
24  const EXIF_COPYRIGHT = 1;
25 
31  public $image = '';
32 
38  private $vector = false;
39 
45  private $icon = false;
46 
52  private $avif = false;
53 
59  private $imagick = null;
60 
66  public function __construct($file = '') {
67  if ($file) {
68  $this->load($file);
69  }
70  }
71 
75  public function __destruct() {
76  $this->free();
77  }
78 
87  public function load($file) {
88  $this->free();
89 
90  // Vektorgrafik erkennen und nicht mit Imagick bearbeiten
91  try {
92  require_once 'base/Ego_MimeType.php';
93  $mime = new Ego_MimeType();
94  $mime_type = $mime->autoDetect($file);
95 
96  $this->vector = in_array($mime_type, array('image/svg', 'image/svg+xml', 'image/x-icon', 'image/vnd.microsoft.icon'));
97  $this->icon = in_array($mime_type, array('image/x-icon', 'image/vnd.microsoft.icon'));
98  $this->avif = $mime_type == 'image/avif';
99 
100  $this->imagick = $this->vector || $this->icon ? null : new Imagick($file);
101 
102  if (!$this->vector) {
103  $this->image = $this->imagick->getImageFilename();
104  $this->imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
105 
106  // AVIF Features aktivieren
107  if ($this->avif) {
108  $this->imagick->setImageFormat('avif');
109  }
110  } else {
111  $this->image = $file;
112  }
113  } catch (Exception $e) {
114  throw new Ego_Image_Exception($e->getMessage(), Ego_Image_Exception::INIT_FAILED);
115  }
116  }
117 
123  public function isVector() {
124  return $this->vector;
125  }
126 
135  public function save($file, $type = '', $quality = 100) {
136  if ($this->vector) {
137  return Ego_System::copy($this->image, $file);
138  }
139 
140  if (in_array($type, ['jpg', 'jpeg', 'webp', 'avif'])) {
141  $this->imagick->setImageCompressionQuality($quality);
142  }
143  return $this->imagick->writeImages($file, true);
144  }
145 
151  public function free() {
152  if ($this->imagick) {
153  $this->imagick->clear();
154  }
155  }
156 
165  private function modify($action, $params = array()) {
166  if ($this->vector) {
167  return;
168  }
169 
177  $resize = function(Imagick &$image) use ($params) {
178  $image->scaleImage($params['width'], $params['height'], $params['width'] && $params['height']);
179  };
180 
187  $crop = function(Imagick &$image) use ($params) {
188  $image->cropImage($params['width'], $params['height'], $params['x'], $params['y']);
189  $image->setImagePage($params['width'], $params['height'], 0, 0);
190  };
191 
198  $rotate = function(Imagick &$image) use ($params) {
199  $image->rotateImage(new ImagickPixel('#00000000'), $params['deg']);
200  };
201 
208  $mirror = function(Imagick &$image) use ($params) {
209  if ($params['horizontal']) {
210  $image->flopImage();
211  }
212  if ($params['vertical']) {
213  $image->flipImage();
214  }
215  };
216 
223  $grayscale = function(Imagick &$image) {
224  $image->setImageType(Imagick::IMGTYPE_GRAYSCALEMATTE);
225  };
226 
227  if (isset($$action)) {
228  if ($this->imagick->getNumberImages() > 1) {
229  // Bei einer Animation muss die Aktion für jedes Frame ausgeführt werden
230  $image = $this->imagick->coalesceImages();
231  foreach ($image as $frame) {
232  $$action($frame);
233  }
234  $this->imagick = $image->optimizeImageLayers();
235  } else {
236  $$action($this->imagick);
237  }
238  } else {
239  throw new Exception('Invalid modify action.');
240  }
241  }
242 
253  public function crop($x1, $y1, $x2, $y2) {
254  $this->modify('crop', array(
255  'width' => $x2 - $x1,
256  'height' => $y2 - $y1,
257  'x' => $x1,
258  'y' => $y1
259  ));
260  }
261 
269  public function rotate($deg) {
270  $this->modify('rotate', array(
271  'deg' => $deg
272  ));
273  }
274 
283  public function mirror($vertical, $horizontal) {
284  $this->modify('mirror', array(
285  'vertical' => $vertical,
286  'horizontal' => $horizontal
287  ));
288  }
289 
296  public function grayscale() {
297  $this->modify('grayscale');
298  }
299 
308  public function resize($width, $height) {
309  $this->modify('resize', array(
310  'width' => $width,
311  'height' => $height
312  ));
313  }
314 
322  public function scaleByX($width) {
323  $this->resize($width, 0);
324  }
325 
333  public function scaleByY($height) {
334  $this->resize(0, $height);
335  }
336 
344  public function watermark($file) {
345  if ($this->vector) {
346  return;
347  }
348 
349  $watermark = new Imagick($file);
350 
351  // Bei Bedarf das Wasserzeichen skalieren
352  $image_w = $this->imagick->getImageWidth();
353  $image_h = $this->imagick->getImageHeight();
354  $watermark_w = $watermark->getImageWidth();
355  $watermark_h = $watermark->getImageHeight();
356 
357  if ($image_w < $watermark_w || $image_h < $watermark_h) {
358  $watermark->scaleImage($image_w, 0);
359  $watermark_w = $watermark->getImageWidth();
360  $watermark_h = $watermark->getImageHeight();
361  if ($image_h < $watermark_h) {
362  $watermark->scaleImage(0, $image_h);
363  $watermark_w = $watermark->getImageWidth();
364  $watermark_h = $watermark->getImageHeight();
365  }
366  }
367 
368  $x = floor(($image_w - $watermark_w) / 2);
369  $y = floor(($image_h - $watermark_h) / 2);
370 
371  $this->imagick->compositeImage($watermark, Imagick::COMPOSITE_OVER, $x, $y);
372  }
373 
382  public function thumbnail($width, $height, $params = []) {
383  if ($this->vector) {
384  return null;
385  }
386 
387  $type = $params['type'] ?? 'png';
388 
389  $this->imagick->setImageFormat($type);
390  $this->imagick->scaleImage($width, $height ? $height : $width, true);
391  $this->imagick->setImageAlphaChannel(Imagick::VIRTUALPIXELMETHOD_WHITE);
392 
393  Ego_System::mkdir($GLOBALS['egotec_conf']['bin_dir'] . 'tmp');
394  $file = tempnam($GLOBALS['egotec_conf']['bin_dir'] . 'tmp', 'thumbnail');
395  if ($this->save($file, $type, $params['quality'] ?? $GLOBALS['egotec_conf']['image']['quality'] ?? 100)) {
396  return $file;
397  }
398  return null;
399  }
400 
408  public function convert($format, $params = []) {
409  if (!$this->vector) {
410  $this->imagick->setImageFormat($format);
411 
412  if (!empty($params['output'])) {
413  // Datei in übergebenen Pfad ablegen
414  $file = $params['output'];
415  if (!Ego_System::file_exists($file)) {
417  }
418  } else {
419  // Datei in temporären Pfad ablegen
420  Ego_System::mkdir($GLOBALS['egotec_conf']['bin_dir'] . 'tmp');
421  $file = tempnam($GLOBALS['egotec_conf']['bin_dir'] . 'tmp', 'converted_' . $format);
422  }
423  if ($this->save($file, $format, $params['quality'] ?? $GLOBALS['egotec_conf']['image']['quality'] ?? 100)) {
424  return $file;
425  } elseif (empty($params['output'])) {
426  @unlink($file);
427  }
428  }
429  return null;
430  }
431 
441  public function getDiffImage($file) {
442  if ($this->vector) {
443  return null;
444  }
445 
446  try {
447  $result = $this->imagick->compareImages(new Imagick($file), Imagick::METRIC_MEANSQUAREERROR);
448  return 'data:' . $result[0]->getImageMimeType() . ';base64,' . base64_encode($result[0]->getImageBlob());
449  } catch (Exception $e) {
450  return null;
451  }
452  }
453 
459  public function getImageWidth() {
460  return $this->vector ? 0 : $this->imagick->getImageWidth();
461  }
462 
468  public function getImageHeight() {
469  return $this->vector ? 0 : $this->imagick->getImageHeight();
470  }
471 
477  public function getImageType() {
478  if ($this->vector) {
479  return 'svg';
480  }
481 
482  $data = @getimagesize($this->image);
483  if (is_array($data)){
484  switch($data[2]) {
485  case 1:
486  return 'gif';
487  case 2:
488  return 'jpeg';
489  case 3:
490  return 'png';
491  case 4:
492  return 'swf';
493  case 5:
494  return 'psd';
495  case 6:
496  return 'bmp';
497  case 7:
498  case 8:
499  return 'tiff';
500  case 18:
501  return 'webp';
502  case 19:
503  return 'avif';
504  }
505  }
506  return '';
507  }
508 
514  public function getMimeType() {
515  return $this->vector ? 'image/svg+xml' : $this->imagick->getImageMimeType();
516  }
517 
527  public function getExif(int $entry = self::EXIF_ALL) {
528  if (
529  !$this->vector
530  && function_exists('exif_read_data')
531  && ($data = @exif_read_data($this->imagick->getImageFilename())) !== false
532  ) {
533  switch ($entry) {
534  case self::EXIF_ALL:
535  return $data;
536 
538  $copyrightKey = '';
539 
540  foreach ($GLOBALS['site']->conf['exif']['import']['default'] as $field) {
541  if ($field['copyright']) {
542  $copyrightKey = $GLOBALS['site']->admin['exif_import']['exif_' . md5($field['title']) . '_keys'];
543  break;
544  }
545  }
546 
547  if ($copyrightKey && $data[$copyrightKey]) {
548  $value = $data[$copyrightKey];
549  } else {
550  $value = trim($data['Copyright'] ?? $data['COMPUTED']['Copyright'] ?? $data['Artist']);
551  }
552 
553  if (empty($value) && isset($data['COMMENT'])) {
554  if (is_array($data['COMMENT'])) {
555  $value = implode(', ', $data['COMMENT']);
556  } else {
557  $value = $data['COMMENT'];
558  }
559  }
560 
561  return trim(str_replace('©', '', $value));
562  }
563  }
564 
565  return null;
566  }
567 
575  public function importExif(): array {
576  if ($this->vector) {
577  return [];
578  }
579 
580  $imageData = [];
581  $fields = array_merge((array) $GLOBALS['site']->conf['exif']['import']['default'], (array) $GLOBALS['site']->conf['exif']['import']['custom']);
582 
583  // EXIF-Daten auslesen
584  $exifData = @exif_read_data($this->imagick->getImageFilename()) ?: [];
585 
586  // IPTC-Daten auslesen
587  getimagesize($this->imagick->getImageFilename(), $info);
588  $iptcData = @iptcparse($info ? (string) $info['APP13'] : '') ?: [];
589 
590  $getData = function($item) use ($exifData, $iptcData) {
591  if ($item['copyright']) {
592  return $this->getExif(self::EXIF_COPYRIGHT);
593  }
594 
595  $keyString = $GLOBALS['site']->admin['exif_import']['exif_' . md5($item['title']) . '_keys'];
596  $value = [];
597 
598  if ($keyString && ($keys = explode(',', $keyString))) {
599  foreach ($keys as $key) {
600  $value = $exifData[$key] ?? $iptcData[$key] ?? [];
601 
602  if ($value) {
603  break;
604  }
605  }
606  }
607 
608  return $value;
609  };
610 
611  foreach ($fields as $field) {
612  if ($GLOBALS['site']->admin['exif_import']['exif_' . md5($field['title']) . '_keys']) {
613  $imageData[] = array_merge($field, ['value' => $getData($field)]);
614  }
615  }
616 
617  return $imageData;
618  }
619 
626  public static function getDimensions($file) {
633  $fallback = function() use ($file) {
634  try {
635  $image = new Ego_Image($file);
636  return [$image->getImageWidth(), $image->getImageHeight()];
637  } catch (Exception $e) {
638  return [0, 0];
639  }
640  };
641 
642  try {
643  $size = getimagesize($file);
644  if (empty($size) || ($size[0] === 0 && $size[1] === 0)) {
645  return $fallback();
646  }
647  return $size;
648  } catch (Exception $e) {
649  return $fallback();
650  }
651  }
652 }
getImageHeight()
Definition: Ego_Image.php:468
const EXIF_ALL
Definition: Ego_Image.php:23
scaleByX($width)
Definition: Ego_Image.php:322
getImageType()
Definition: Ego_Image.php:477
__destruct()
Definition: Ego_Image.php:75
crop($x1, $y1, $x2, $y2)
Definition: Ego_Image.php:253
watermark($file)
Definition: Ego_Image.php:344
scaleByY($height)
Definition: Ego_Image.php:333
convert($format, $params=[])
Definition: Ego_Image.php:408
getDiffImage($file)
Definition: Ego_Image.php:441
rotate($deg)
Definition: Ego_Image.php:269
getImageWidth()
Definition: Ego_Image.php:459
resize($width, $height)
Definition: Ego_Image.php:308
load($file)
Definition: Ego_Image.php:87
mirror($vertical, $horizontal)
Definition: Ego_Image.php:283
thumbnail($width, $height, $params=[])
Definition: Ego_Image.php:382
getExif(int $entry=self::EXIF_ALL)
Definition: Ego_Image.php:527
const EXIF_COPYRIGHT
Definition: Ego_Image.php:24
static getDimensions($file)
Definition: Ego_Image.php:626
save($file, $type='', $quality=100)
Definition: Ego_Image.php:135
__construct($file='')
Definition: Ego_Image.php:66
static file_put_contents($filename, $data, $flags=0, $context=null)
static file_exists($file)
static mkdir($dir, $mode=0755, $recursive=true)
Definition: Ego_System.php:669
static copy($src, $dest, $except='', $useLinks=false, $noArchive=false, $preserveDate=false)