mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 17:07:07 +00:00
upload: Replace exif_rotate with Pillow exif_transpose.
Fixes #18599. Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
committed by
Tim Abbott
parent
6289803368
commit
14f0594795
@@ -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)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user