--- a/degal/exif.py Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/exif.py Mon Jun 15 00:23:55 2009 +0300
@@ -6,6 +6,15 @@
import utils
+MIRROR_NONE = 0
+MIRROR_HORIZONTAL = 1
+MIRROR_VERTICAL = 2
+
+ROTATE_NONE = 0
+ROTATE_90 = 90
+ROTATE_180 = 180
+ROTATE_270 = 270
+
class ExifHandler (object) :
"""
Our generic interface for Exif data
@@ -18,12 +27,19 @@
self.file = file
+ def image_tag_value (self, tag, default=None) :
+ """
+ Load and return the raw binary value for the given main image tag.
+ """
+
+ return default
+
def image_tag (self, tag, default=None) :
"""
Load and return the human-readable unicode-safe value for the given main image tag.
"""
- abstract
+ return default
def image_tags (self, tags) :
"""
@@ -41,6 +57,32 @@
# filter out default not-found
if value is not None :
yield tag, value
+
+
+ def get_orientation (self) :
+ """
+ Get the orientation of the image in terms of mirroring and rotation in integer degrees clockwise.
+
+ Returns a (MIRROR_*, ROTATE_*) tuple, or None if the Orientation tag could not be found.
+
+ Returns None if it could not be found.
+ """
+
+ # load value
+ orientation = self.image_tag_value('Orientation')
+
+ # map to tuple or None
+ # XXX: these are from EXIFpy, verify
+ return {
+ 1: (MIRROR_NONE, ROTATE_NONE ), # Horizontal (normal)
+ 2: (MIRROR_HORIZONTAL, ROTATE_NONE ), # Mirrored horizontal
+ 3: (MIRROR_NONE, ROTATE_180 ), # Rotated 180
+ 4: (MIRROR_VERTICAL, ROTATE_NONE ), # Mirrored vertical
+ 5: (MIRROR_HORIZONTAL, ROTATE_270 ), # Mirrored horizontal then rotated 90 CCW
+ 6: (MIRROR_NONE, ROTATE_90 ), # Rotated 90 CW
+ 7: (MIRROR_HORIZONTAL, ROTATE_90 ), # Mirrored horizontal then rotated 90 CW
+ 8: (MIRROR_NONE, ROTATE_270 ), # Rotated 90 CCW
+ }.get(orientation, None)
try :
import pyexiv2
@@ -63,6 +105,20 @@
# load
self.image.readMetadata()
+ def image_tag_value (self, tag, default=None) :
+ # try with likely prefixes
+ for prefix in ('Exif.Photo', 'Exif.Image') :
+ try :
+ return self.image["%s.%s" % (prefix, tag)]
+
+ except (IndexError, KeyError) :
+ # nope...
+ continue
+
+ else :
+ # not ofund
+ return default
+
def image_tag (self, tag, default=None) :
# try with likely prefixes
for prefix in ('Exif.Photo', 'Exif.Image') :
--- a/degal/image.py Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/image.py Mon Jun 15 00:23:55 2009 +0300
@@ -63,6 +63,20 @@
return exif.load(self.config, self)
@lazy_load
+ def orientation (self) :
+ """
+ Loads the orientation of this image as a (mirroring, rotation) tuple, or None, if the information is not available.
+ """
+
+ if self.config.with_exif and self.exif :
+ # try and find it
+ return self.exif.get_orientation()
+
+ else :
+ # no exif data
+ return None
+
+ @lazy_load
def metadata (self) :
"""
Load and return the metadata for the image as a dictionary
--- a/degal/thumbnail.py Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/thumbnail.py Mon Jun 15 00:23:55 2009 +0300
@@ -63,15 +63,49 @@
height = thumb_height
return width, height
+
+ def resize (self, img) :
+ """
+ Resize the give image as needed.
+ """
+
+ return img.resize(self.size, resample=PIL.Image.ANTIALIAS)
+
+ def auto_orient (self, img, orient_info) :
+ """
+ Automatically orient the image using the given orientation info.
+ """
+
+ # unpack
+ mirroring, rotation = orient_info
+
+ if mirroring :
+ # XXX
+ pass
+
+ if rotation :
+ # since these are in steps of 90 degrees, it should keep the right size
+ # but gah, PIL wants these as counter-clockwise!
+ img = img.rotate(360 - rotation)
+
+ # ok
+ return img
def update (self) :
"""
Render new output thumbnail.
"""
+ # start with origional image
+ img = self.image.img
+
# create resized copy of main image, using our size
- thumb = self.image.img.resize(self.size, resample=PIL.Image.ANTIALIAS)
+ img = self.resize(img)
+
+ # got orientation info?
+ if self.image.orientation :
+ img = self.auto_orient(img, self.image.orientation)
# write it out
- thumb.save(self.path)
+ img.save(self.path)