terom@120: """ terom@120: The various Exif backends that we have terom@120: """ terom@120: terom@120: from __future__ import with_statement terom@120: terom@120: import utils terom@120: terom@122: MIRROR_NONE = 0 terom@122: MIRROR_HORIZONTAL = 1 terom@122: MIRROR_VERTICAL = 2 terom@122: terom@122: ROTATE_NONE = 0 terom@122: ROTATE_90 = 90 terom@122: ROTATE_180 = 180 terom@122: ROTATE_270 = 270 terom@122: terom@120: class ExifHandler (object) : terom@120: """ terom@120: Our generic interface for Exif data terom@120: """ terom@120: terom@120: def __init__ (self, file) : terom@120: """ terom@120: Load Exif data from the given File. terom@120: """ terom@120: terom@120: self.file = file terom@120: terom@122: def image_tag_value (self, tag, default=None) : terom@122: """ terom@122: Load and return the raw binary value for the given main image tag. terom@122: """ terom@122: terom@122: return default terom@122: terom@120: def image_tag (self, tag, default=None) : terom@120: """ terom@120: Load and return the human-readable unicode-safe value for the given main image tag. terom@120: """ terom@120: terom@122: return default terom@120: terom@120: def image_tags (self, tags) : terom@120: """ terom@120: Load and return the given tags for the main image as a sequence of (tag, value) tuples , following the same terom@120: rules as image_tag, except there should mainly be only one call to this function, encompassing all the terom@120: relevant tags. terom@120: terom@120: The returned list should omit those values which were not found. terom@120: """ terom@120: terom@120: for tag in tags : terom@120: # try and fetch value terom@120: value = self.image_tag(tag) terom@120: terom@120: # filter out default not-found terom@120: if value is not None : terom@120: yield tag, value terom@122: terom@122: terom@122: def get_orientation (self) : terom@122: """ terom@122: Get the orientation of the image in terms of mirroring and rotation in integer degrees clockwise. terom@122: terom@122: Returns a (MIRROR_*, ROTATE_*) tuple, or None if the Orientation tag could not be found. terom@122: terom@122: Returns None if it could not be found. terom@122: """ terom@122: terom@122: # load value terom@122: orientation = self.image_tag_value('Orientation') terom@122: terom@122: # map to tuple or None terom@122: # XXX: these are from EXIFpy, verify terom@122: return { terom@122: 1: (MIRROR_NONE, ROTATE_NONE ), # Horizontal (normal) terom@122: 2: (MIRROR_HORIZONTAL, ROTATE_NONE ), # Mirrored horizontal terom@122: 3: (MIRROR_NONE, ROTATE_180 ), # Rotated 180 terom@122: 4: (MIRROR_VERTICAL, ROTATE_NONE ), # Mirrored vertical terom@122: 5: (MIRROR_HORIZONTAL, ROTATE_270 ), # Mirrored horizontal then rotated 90 CCW terom@122: 6: (MIRROR_NONE, ROTATE_90 ), # Rotated 90 CW terom@122: 7: (MIRROR_HORIZONTAL, ROTATE_90 ), # Mirrored horizontal then rotated 90 CW terom@122: 8: (MIRROR_NONE, ROTATE_270 ), # Rotated 90 CCW terom@122: }.get(orientation, None) terom@120: terom@120: try : terom@120: import pyexiv2 terom@120: terom@120: except ImportError : terom@120: PyExiv2_Handler = None terom@120: terom@120: else : terom@120: class PyExiv2_Handler (ExifHandler) : terom@120: NAME = 'pyexiv2' terom@120: terom@120: def __init__ (self, file) : terom@120: """ terom@120: Load as pyexiv2.Image terom@120: """ terom@120: terom@120: # create terom@120: self.image = pyexiv2.Image(file.path) terom@120: terom@120: # load terom@120: self.image.readMetadata() terom@120: terom@122: def image_tag_value (self, tag, default=None) : terom@122: # try with likely prefixes terom@122: for prefix in ('Exif.Photo', 'Exif.Image') : terom@122: try : terom@122: return self.image["%s.%s" % (prefix, tag)] terom@122: terom@122: except (IndexError, KeyError) : terom@122: # nope... terom@122: continue terom@122: terom@122: else : terom@122: # not ofund terom@122: return default terom@122: terom@120: def image_tag (self, tag, default=None) : terom@121: # try with likely prefixes terom@121: for prefix in ('Exif.Photo', 'Exif.Image') : terom@121: try : terom@121: return self.image.interpretedExifValue("%s.%s" % (prefix, tag)) terom@121: terom@121: except (IndexError, KeyError) : terom@121: # nope... terom@121: continue terom@121: terom@121: else : terom@121: # not ofund terom@120: return default terom@120: terom@120: try : terom@120: from lib import EXIF terom@120: terom@120: except ImportError : terom@120: EXIF_Handler = None terom@120: terom@120: else : terom@120: class EXIF_Handler (ExifHandler) : terom@120: NAME = 'EXIFpy' terom@120: terom@120: def __init__ (self, file) : terom@120: """ terom@120: Load using EXIF.process_file terom@120: """ terom@120: terom@120: with file.open('rb') as fh : terom@120: self.tags = EXIF.process_file(fh) terom@120: terom@120: def image_tag (self, tag, default=None) : terom@121: # try with likely prefixes terom@121: for prefix in ('Image', 'EXIF') : terom@121: name = prefix + ' ' + tag terom@121: terom@121: if name in self.tags : terom@121: return self.tags[name] terom@121: terom@121: else : terom@121: # not found terom@121: return default terom@120: terom@120: # ExifHandler implementations to use, in preference order terom@120: EXIF_HANDLERS = [ terom@120: # reasonably fast, but requires the native library and extension module terom@120: PyExiv2_Handler, terom@120: terom@120: # pure-python, but very, very slow terom@120: EXIF_Handler, terom@120: ] terom@120: terom@120: # default ExifHandler to use terom@120: EXIF_HANDLER = utils.first(EXIF_HANDLERS) terom@120: terom@120: # ExifHandler implementations by name terom@130: EXIF_HANDLERS_BY_NAME = dict((h.NAME, h) for h in EXIF_HANDLERS if h) terom@120: terom@120: def load (config, file) : terom@120: """ terom@120: Load Exif data using the configured Exif handler, and return the instance. terom@120: terom@120: Returns None if there wasn't any Exif handler available for use. terom@120: """ terom@120: terom@120: if config.exif_handler : terom@120: # explicit terom@120: exif_handler = config.exif_handler terom@120: terom@120: elif config.exif_handler_name : terom@120: # by name terom@120: exif_handler = EXIF_HANDLERS_BY_NAME[config.exif_handler_name] terom@120: terom@120: else : terom@120: # default terom@120: exif_handler = EXIF_HANDLER terom@120: terom@120: if exif_handler : terom@120: # found one, use it terom@120: return exif_handler(file) terom@120: terom@120: else : terom@120: # nothing :( terom@120: return None terom@120: