Source code for tagit.model.meta_adapter

"""Read and write metadata (IPTC, EXIF) to image files.

The *MetaAdapter* class defines the interface. The adapter is put into a
*model* instance to handle reads and writes to image files. Since there's
several libraries which help us with this, we can select which one to use.

Currently, the only library supported is PyExiv2. That's due to its simplicity
in usage and installation. In the future, an apdater GExiv2 will be written.

Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2016

"""
# STANDARD IMPORTS
import pyexiv2
import os
import datetime
from StringIO import StringIO

# INNER-MODULE IMPORTS

# CONSTANTS
IPTC_KEY_TAGS = 'Iptc.Application2.Keywords'

EXIF_KEY_METRICS = {
      'exposure'          : 'Exif.Photo.ExposureTime'
    , 'aperture'          : 'Exif.Photo.FNumber'
    , 'iso'               : 'Exif.Photo.ISOSpeedRatings'
    , 'flash'             : 'Exif.Photo.Flash'
    , 'focal_length'      : 'Exif.Photo.FocalLength'
    , 'focal_length_35'   : 'Exif.Photo.FocalLengthIn35mmFilm'
}

EXIF_KEY_ARTIST = 'Exif.Image.Artist'
EXIF_KEY_COPYRIGHT = 'Exif.Image.Copyright'

EXIF_KEYS_DATE = [
      'Exif.Photo.DateTimeOriginal'
    , 'Exif.Photo.DateTimeDigitized'
    , 'Exif.Image.DateTime'
    ]

EXIF_KEYS_DIMENSIONS = [
      'Exif.Photo.PixelXDimension'
    , 'Exif.Photo.PixelYDimension'
    , 'Exif.Image.Orientation'
    ]

# EXPORTS
__all__ = ('MetaAdapter', 'MetaAdapter_PyExiv2')

## CODE ##

[docs]class MetaAdapter(object): """Handle IPTC/EXIF reads and writes to files. """
[docs] def get(self, path): """Read IPTC keywords from *path*. Return a list of tags. *path* has to be a readable file (not checked). """ abstract()
[docs] def set(self, path, tags): """Write *tags* to IPTC of image *path*. *path* has to be a writeable file (not checked). Image data is written. Existing tags are overwritten. """ abstract()
[docs] def add(self, path, tags): """Add *tags* to IPTC of image *path*. *path* has to be a writeable file (not checked). Image data is written. *tags* is added to the existing tags. """ abstract()
[docs] def set_thumbnail(self, path, image): """Write thumbnail *image* into *path*. *path* has to be a writeable file (not checked). Image data is written. *image* is written as thumbnail. """ abstract()
[docs] def get_thumbnail(self, path): """Return the thumbnail embedded in image *path*. *path* has to be a readable file. """ abstract()
[docs] def get_date(self, path): """Return the image date. """ abstract()
[docs] def get_author(self, path): """Return the image author. """ abstract()
[docs] def get_metrics(self, path): """Return the image metrics. """ abstract()
[docs] def get_dimensions(self, path): """Return width, height and orientation of *path*. """ abstract()
[docs]class MetaAdapter_PyExiv2(MetaAdapter): """Makes use of the pyexiv2 library to manipulate files. """ def get(self, path): data = pyexiv2.ImageMetadata(path) data.read() try: return data[IPTC_KEY_TAGS].values except KeyError: return [] def set(self, path, tags): data = pyexiv2.ImageMetadata(path) data.read() data[IPTC_KEY_TAGS].values = tags data.write() def add(self, path, tags): data = pyexiv2.ImageMetadata(path) data.read() data[IPTC_KEY_TAGS].values += tags data.write() def set_thumbnail(self, path, image): meta = pyexiv2.ImageMetadata(path) meta.read() meta.exif_thumbnail.data = image meta.write() def get_thumbnail(self, path): meta = pyexiv2.ImageMetadata(path) meta.read() if len(meta.exif_thumbnail.data) > 0: return StringIO(meta.exif_thumbnail.data) return None def get_date(self, path): meta = pyexiv2.ImageMetadata(path) meta.read() for key in EXIF_KEYS_DATE: if key in meta: return meta[key].value stat = os.stat(path) date = datetime.datetime.fromtimestamp(min(stat.st_ctime, stat.st_mtime)) return date def get_metrics(self, path): meta = pyexiv2.ImageMetadata(path) meta.read() metrics = {} for k_int, k_exif in EXIF_KEY_METRICS.iteritems(): if k_exif in meta: metrics[k_int] = float(meta[k_exif].value) if 'flash' in metrics: if metrics['flash'] == 1: metrics['flash'] = True else: metrics['flash'] = False return metrics def get_author(self, path): meta = pyexiv2.ImageMetadata(path) meta.read() if EXIF_KEY_ARTIST in meta: return meta[EXIF_KEY_ARTIST].strip() elif EXIF_KEY_COPYRIGHT in meta: return meta[EXIF_KEY_COPYRIGHT].strip() return "" def get_dimensions(self, path): meta = pyexiv2.ImageMetadata(path) meta.read() width, height, orientation = [key in meta and int(meta[key].value) or 0 for key in EXIF_KEYS_DIMENSIONS] return width, height, orientation ## EOF ##