diff --git a/Dockerfile b/Dockerfile index f797d2b..b95d3a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y \ tesseract-ocr \ tesseract-ocr-deu \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ - && docker-php-ext-install pdo pdo_mysql zip gd \ + && docker-php-ext-install pdo pdo_mysql zip gd exif \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Install Composer diff --git a/src/Services/ImageCropService.php b/src/Services/ImageCropService.php index 679b895..a1eb095 100644 --- a/src/Services/ImageCropService.php +++ b/src/Services/ImageCropService.php @@ -12,7 +12,8 @@ class ImageCropService /** * Schneidet ein Bild auf den angegebenen Bereich zu (manueller Zuschnitt). - * Koordinaten und Maße beziehen sich auf das Originalbild in Pixeln. + * Koordinaten und Maße beziehen sich auf das EXIF-normalisierte Bild, + * also auf das, was der Browser dem Nutzer anzeigt. * Gibt true zurück, wenn das Bild gespeichert wurde. */ public function cropToRect(string $path, int $x, int $y, int $w, int $h): bool @@ -29,6 +30,10 @@ class ImageCropService return false; } + // EXIF-Orientierung anwenden, damit Crop-Koordinaten mit der + // Browser-Darstellung übereinstimmen. + $img = $this->applyExifOrientation($img, $path, $mime); + $imgW = imagesx($img); $imgH = imagesy($img); @@ -72,6 +77,10 @@ class ImageCropService return false; } + // EXIF-Orientierung normalisieren, damit Randerkennnung auf dem + // korrekt ausgerichteten Bild arbeitet. + $img = $this->applyExifOrientation($img, $path, $mime); + $w = imagesx($img); $h = imagesy($img); @@ -178,6 +187,46 @@ class ImageCropService return ['top' => $top, 'bottom' => $bottom, 'left' => $left, 'right' => $right]; } + /** + * Wendet die EXIF-Orientierung eines JPEGs auf das GdImage an und gibt + * ein korrekt ausgerichtetes Bild zurück. PNG-Bilder werden unverändert + * zurückgegeben. Nach dem Anwenden ist die Orientierung im Pixelinhalt + * kodiert; beim Speichern via imagejpeg() entfällt der EXIF-Tag. + */ + private function applyExifOrientation(\GdImage $img, string $path, string $mime): \GdImage + { + if ($mime !== 'image/jpeg' || !function_exists('exif_read_data')) { + return $img; + } + + $exif = @exif_read_data($path); + $orientation = (int)($exif['Orientation'] ?? 1); + + if ($orientation <= 1) { + return $img; // keine Drehung nötig + } + + // Horizontale Spiegelung vor einer etwaigen Drehung + if (in_array($orientation, [2, 5, 7], true)) { + imageflip($img, IMG_FLIP_HORIZONTAL); + } + + $degrees = match ($orientation) { + 3, 4 => 180, + 5, 6 => -90, // 90° im Uhrzeigersinn + 7, 8 => 90, // 90° gegen den Uhrzeigersinn + default => 0, + }; + + if ($degrees !== 0) { + $rotated = imagerotate($img, $degrees, 0); + imagedestroy($img); + return $rotated instanceof \GdImage ? $rotated : $img; + } + + return $img; + } + private function isWhite(int $color): bool { $r = ($color >> 16) & 0xFF;