Fix: EXIF-Orientierung beim Bild-Crop normalisieren

This commit is contained in:
fds2610-bot 2026-04-10 20:42:13 +02:00
parent e1f356d0cf
commit bd9d0c670d
2 changed files with 51 additions and 2 deletions

View file

@ -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

View file

@ -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;