changeset 3330:7b47f48d31f3

plugin XEP-0264: fix orientation of thumbnails: Rotation of thumbnailis is now modified according to EXIF orientation data. Original image is not modified as user may want to keep it, and orientation transformation can modify encoding parameters. Update `pillow` minimum version to the first one with `exif_transpose` implementation.
author Goffi <goffi@goffi.org>
date Thu, 13 Aug 2020 23:46:18 +0200
parents 15612c0fb421
children b1e9f17fbb5a
files sat/plugins/plugin_xep_0264.py setup.py
diffstat 2 files changed, 14 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0264.py	Thu Aug 13 23:45:59 2020 +0200
+++ b/sat/plugins/plugin_xep_0264.py	Thu Aug 13 23:46:18 2020 +0200
@@ -34,7 +34,7 @@
 import hashlib
 
 try:
-    from PIL import Image
+    from PIL import Image, ImageOps
 except:
     raise exceptions.MissingModule(
         "Missing module pillow, please download/install it from https://python-pillow.github.io"
@@ -138,7 +138,9 @@
         """
         return hashlib.sha256(repr((image_uid, size)).encode()).hexdigest()
 
-    def _blockingGenThumb(self, source_path, size=None, max_age=None, image_uid=None):
+    def _blockingGenThumb(
+            self, source_path, size=None, max_age=None, image_uid=None,
+            fix_orientation=True):
         """Generate a thumbnail for image
 
         This is a blocking method and must be executed in a thread
@@ -152,16 +154,21 @@
             return Failure(exceptions.DataError("Can't open image"))
 
         img.thumbnail(size)
+        if fix_orientation:
+            img = ImageOps.exif_transpose(img)
+
         uid = self.getThumbId(image_uid or source_path, size)
 
         with self.host.common_cache.cacheData(
             PLUGIN_INFO[C.PI_IMPORT_NAME], uid, MIME_TYPE, max_age
         ) as f:
             img.save(f, SAVE_FORMAT)
+            log.debug(f"fixed orientation for {f.name}")
 
         return img.size, uid
 
-    def generateThumbnail(self, source_path, size=None, max_age=None, image_uid=None):
+    def generateThumbnail(
+        self, source_path, size=None, max_age=None, image_uid=None, fix_orientation=True):
         """Generate a thumbnail of image
 
         @param source_path(unicode): absolute path to source image
@@ -172,13 +179,14 @@
         @param image_uid(unicode, None): unique ID to identify the image
             use hash whenever possible
             if None, source_path will be used
+        @param fix_orientation(bool): if True, fix orientation using EXIF data
         @return D(tuple[tuple[int,int], unicode]): tuple with:
             - size of the thumbnail
             - unique Id of the thumbnail
         """
-
         d = threads.deferToThread(
-            self._blockingGenThumb, source_path, size, max_age, image_uid=image_uid
+            self._blockingGenThumb, source_path, size, max_age, image_uid=image_uid,
+            fix_orientation=fix_orientation
         )
         d.addErrback(
             lambda failure_: log.error("thumbnail generation error: {}".format(failure_))
--- a/setup.py	Thu Aug 13 23:45:59 2020 +0200
+++ b/setup.py	Thu Aug 13 23:46:18 2020 +0200
@@ -33,7 +33,7 @@
     'miniupnpc',
     'mutagen',
     'netifaces',
-    'pillow',
+    'pillow >= 6.0.0',
     'progressbar2',
     'cryptography',
     'pygments',