--- /tmp/exif_orientation.module.orig 2025-10-27 20:55:11.393158671 +0100 +++ web/modules/contrib/exif_orientation/exif_orientation.module 2025-10-27 20:55:33.413104934 +0100 @@ -60,12 +60,20 @@ * File entity. */ function _exif_orientation_rotate(FileInterface $file) { + // Prevent multiple rotations of the same file in a single request. + static $processed_files = []; + + $uri = $file->getFileUri(); + + // Skip if already processed in this request. + if (isset($processed_files[$uri])) { + return; + } + $mime_types = ['image/jpeg', 'image/png']; if (function_exists('exif_read_data') && in_array($file->getMimeType(), $mime_types)) { - $uri = $file->getFileUri(); - // Can't get stream wrapper if URI is without scheme. if (!StreamWrapperManager::getScheme($uri)) { $path = \Drupal::service('file_system')->realpath($uri); @@ -91,17 +99,25 @@ // Orientation numbers and corresponding degrees. // @note: Odd numbers are flipped images, would need different process. + // GD's imagerotate() rotates counter-clockwise, ImageMagick rotates clockwise. + $image = \Drupal::service('image.factory')->get($file->getFileUri()); + $toolkit = $image->getToolkitId(); + switch ($file_exif['Orientation']) { case 3: $degrees = 180; break; case 6: - $degrees = 90; + // EXIF: Rotate 90 CW + // GD rotates CCW, so use 270. ImageMagick rotates CW, so use 90. + $degrees = ($toolkit === 'gd') ? 270 : 90; break; case 8: - $degrees = 270; + // EXIF: Rotate 270 CW (or 90 CCW) + // GD rotates CCW, so use 90. ImageMagick rotates CW, so use 270. + $degrees = ($toolkit === 'gd') ? 90 : 270; break; default: @@ -109,9 +125,10 @@ } if ($degrees > 0) { - $image = \Drupal::service('image.factory')->get($file->getFileUri()); if ($image->rotate($degrees)) { $image->save(); + // Mark as processed after successful rotation. + $processed_files[$uri] = TRUE; } } }