diff --git a/zerver/lib/upload.py b/zerver/lib/upload.py index f4904f0145..7f58f7ed90 100644 --- a/zerver/lib/upload.py +++ b/zerver/lib/upload.py @@ -25,7 +25,7 @@ from django.http import HttpRequest from django.urls import reverse from django.utils.translation import gettext as _ from jinja2.utils import Markup as mark_safe -from PIL import ExifTags, Image, ImageOps +from PIL import Image, ImageOps from PIL.GifImagePlugin import GifImageFile from PIL.Image import DecompressionBombError @@ -103,33 +103,10 @@ class BadImageError(JsonableError): code = ErrorCode.BAD_IMAGE -name_to_tag_num = {name: num for num, name in ExifTags.TAGS.items()} - -# https://stackoverflow.com/a/6218425 -def exif_rotate(image: Image) -> Image: - if not hasattr(image, "_getexif"): - return image - exif_data = image._getexif() - if exif_data is None: - return image - - exif_dict = dict(exif_data.items()) - orientation = exif_dict.get(name_to_tag_num["Orientation"]) - - if orientation == 3: - return image.rotate(180, expand=True) - elif orientation == 6: - return image.rotate(270, expand=True) - elif orientation == 8: - return image.rotate(90, expand=True) - - return image - - def resize_avatar(image_data: bytes, size: int = DEFAULT_AVATAR_SIZE) -> bytes: try: im = Image.open(io.BytesIO(image_data)) - im = exif_rotate(im) + im = ImageOps.exif_transpose(im) im = ImageOps.fit(im, (size, size), Image.ANTIALIAS) except OSError: raise BadImageError(_("Could not decode image; did you upload an image file?")) @@ -145,7 +122,7 @@ def resize_avatar(image_data: bytes, size: int = DEFAULT_AVATAR_SIZE) -> bytes: def resize_logo(image_data: bytes) -> bytes: try: im = Image.open(io.BytesIO(image_data)) - im = exif_rotate(im) + im = ImageOps.exif_transpose(im) im.thumbnail((8 * DEFAULT_AVATAR_SIZE, DEFAULT_AVATAR_SIZE), Image.ANTIALIAS) except OSError: raise BadImageError(_("Could not decode image; did you upload an image file?")) @@ -202,7 +179,7 @@ def resize_emoji(image_data: bytes, size: int = DEFAULT_EMOJI_SIZE) -> bytes: ) return resize_gif(im, size) if should_resize else image_data else: - im = exif_rotate(im) + im = ImageOps.exif_transpose(im) im = ImageOps.fit(im, (size, size), Image.ANTIALIAS) out = io.BytesIO() im.save(out, format=image_format) diff --git a/zerver/tests/images/img_no_exif.jpg b/zerver/tests/images/img_no_exif.jpg deleted file mode 100644 index 7baeb6b160..0000000000 Binary files a/zerver/tests/images/img_no_exif.jpg and /dev/null differ diff --git a/zerver/tests/images/img_orientation_3.jpg b/zerver/tests/images/img_orientation_3.jpg deleted file mode 100644 index b9624e050d..0000000000 Binary files a/zerver/tests/images/img_orientation_3.jpg and /dev/null differ diff --git a/zerver/tests/images/img_orientation_6.jpg b/zerver/tests/images/img_orientation_6.jpg deleted file mode 100644 index 7821fa5127..0000000000 Binary files a/zerver/tests/images/img_orientation_6.jpg and /dev/null differ diff --git a/zerver/tests/images/img_orientation_8.jpg b/zerver/tests/images/img_orientation_8.jpg deleted file mode 100644 index f8439f3642..0000000000 Binary files a/zerver/tests/images/img_orientation_8.jpg and /dev/null differ diff --git a/zerver/tests/test_upload.py b/zerver/tests/test_upload.py index 84be88fb3e..929abaa76a 100644 --- a/zerver/tests/test_upload.py +++ b/zerver/tests/test_upload.py @@ -51,7 +51,6 @@ from zerver.lib.upload import ( ZulipUploadBackend, delete_export_tarball, delete_message_image, - exif_rotate, resize_avatar, resize_emoji, sanitize_name, @@ -2153,38 +2152,6 @@ class UploadSpaceTests(UploadSerializeMixin, ZulipTestCase): self.assert_length(data2, self.realm.currently_used_upload_space_bytes()) -class ExifRotateTests(ZulipTestCase): - def test_image_do_not_rotate(self) -> None: - # Image does not have _getexif method. - with get_test_image_file("img.png") as f, Image.open(f) as img: - result = exif_rotate(img) - self.assertEqual(result, img) - - # Image with no exif data. - with get_test_image_file("img_no_exif.jpg") as f, Image.open(f) as img: - result = exif_rotate(img) - self.assertEqual(result, img) - - # Orientation of the image is 1. - with get_test_image_file("img.jpg") as f, Image.open(f) as img: - result = exif_rotate(img) - self.assertEqual(result, img) - - def test_image_rotate(self) -> None: - with mock.patch("PIL.Image.Image.rotate") as rotate: - with get_test_image_file("img_orientation_3.jpg") as f, Image.open(f) as img: - exif_rotate(img) - rotate.assert_called_with(180, expand=True) - - with get_test_image_file("img_orientation_6.jpg") as f, Image.open(f) as img: - exif_rotate(img) - rotate.assert_called_with(270, expand=True) - - with get_test_image_file("img_orientation_8.jpg") as f, Image.open(f) as img: - exif_rotate(img) - rotate.assert_called_with(90, expand=True) - - class DecompressionBombTests(ZulipTestCase): def setUp(self) -> None: super().setUp()