"""
The various Exif backends that we have
"""
from __future__ import with_statement
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
"""
def __init__ (self, file) :
"""
Load Exif data from the given File.
"""
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.
"""
return default
def image_tags (self, tags) :
"""
Load and return the given tags for the main image as a sequence of (tag, value) tuples , following the same
rules as image_tag, except there should mainly be only one call to this function, encompassing all the
relevant tags.
The returned list should omit those values which were not found.
"""
for tag in tags :
# try and fetch value
value = self.image_tag(tag)
# 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
except ImportError :
PyExiv2_Handler = None
else :
class PyExiv2_Handler (ExifHandler) :
NAME = 'pyexiv2'
def __init__ (self, file) :
"""
Load as pyexiv2.Image
"""
# create
self.image = pyexiv2.Image(file.path)
# 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') :
try :
return self.image.interpretedExifValue("%s.%s" % (prefix, tag))
except (IndexError, KeyError) :
# nope...
continue
else :
# not ofund
return default
try :
from lib import EXIF
except ImportError :
EXIF_Handler = None
else :
class EXIF_Handler (ExifHandler) :
NAME = 'EXIFpy'
def __init__ (self, file) :
"""
Load using EXIF.process_file
"""
with file.open('rb') as fh :
self.tags = EXIF.process_file(fh)
def image_tag (self, tag, default=None) :
# try with likely prefixes
for prefix in ('Image', 'EXIF') :
name = prefix + ' ' + tag
if name in self.tags :
return self.tags[name]
else :
# not found
return default
# ExifHandler implementations to use, in preference order
EXIF_HANDLERS = [
# reasonably fast, but requires the native library and extension module
PyExiv2_Handler,
# pure-python, but very, very slow
EXIF_Handler,
]
# default ExifHandler to use
EXIF_HANDLER = utils.first(EXIF_HANDLERS)
# ExifHandler implementations by name
EXIF_HANDLERS_BY_NAME = dict((h.NAME, h) for h in EXIF_HANDLERS)
def load (config, file) :
"""
Load Exif data using the configured Exif handler, and return the instance.
Returns None if there wasn't any Exif handler available for use.
"""
if config.exif_handler :
# explicit
exif_handler = config.exif_handler
elif config.exif_handler_name :
# by name
exif_handler = EXIF_HANDLERS_BY_NAME[config.exif_handler_name]
else :
# default
exif_handler = EXIF_HANDLER
if exif_handler :
# found one, use it
return exif_handler(file)
else :
# nothing :(
return None