aboutsummaryrefslogtreecommitdiffstats
path: root/src/pyexiv2.py
diff options
context:
space:
mode:
authorOlivier Tilloy <olivier@tilloy.net>2008-04-19 20:38:34 +0200
committerOlivier Tilloy <olivier@tilloy.net>2008-04-19 20:38:34 +0200
commiteec684f0ee33e579835d51ffadc6577290d6fcb0 (patch)
tree538fb2127f54e11754a0f176ab5d3e3b2d9e8cd0 /src/pyexiv2.py
parent4ef160462e5ea1a6bd809f632c5d5ee42adf7a81 (diff)
downloadpyexiv2-eec684f0ee33e579835d51ffadc6577290d6fcb0.tar.gz
Replace all tabs by spaces in the code.
Add a MetadataTag class to the python module.
Diffstat (limited to 'src/pyexiv2.py')
-rw-r--r--src/pyexiv2.py1386
1 files changed, 716 insertions, 670 deletions
diff --git a/src/pyexiv2.py b/src/pyexiv2.py
index 338a4b0..41660fa 100644
--- a/src/pyexiv2.py
+++ b/src/pyexiv2.py
@@ -28,11 +28,11 @@
# ******************************************************************************
"""
-Manipulation of EXIF and IPTC metadata embedded in image files.
+Manipulation of EXIF, IPTC and XMP metadata embedded in image files.
This module provides a single class, Image, and utility functions to manipulate
-EXIF and IPTC metadata embedded in image files such as JPEG and TIFF files.
-EXIF and IPTC metadata can be accessed in both read and write modes.
+EXIF, IPTC and XMP metadata embedded in image files such as JPEG and TIFF files.
+EXIF, IPTC and XMP metadata can be accessed in both read and write modes.
This module is a higher-level interface to the Python binding of the excellent
C++ library Exiv2, libpyexiv2.
@@ -57,706 +57,752 @@ A typical use of this binding would be:
"""
-import libpyexiv2
+import libexiv2python
+import os
import time
import datetime
import re
class FixedOffset(datetime.tzinfo):
- """
- Fixed offset from a local time east from UTC.
-
- Represent a fixed (positive or negative) offset from a local time in hours
- and minutes.
-
- Public methods:
- utcoffset -- return offset of local time from UTC, in minutes east of UTC
- dst -- return the daylight saving time (DST) adjustment, here always 0
- tzname -- return a string representation of the offset with format '±%H%M'
- """
-
- def __init__(self, offsetSign='+', offsetHours=0, offsetMinutes=0):
- """
- Constructor.
-
- Construct a FixedOffset object from an offset sign ('+' or '-') and an
- offset absolute value expressed in hours and minutes.
- No check on the validity of those values is performed, it is the
- responsibility of the caller to pass correct values to the constructor.
-
- Keyword arguments:
- offsetSign -- the sign of the offset ('+' or '-')
- offsetHours -- the absolute number of hours of the offset
- offsetMinutes -- the absolute number of minutes of the offset
- """
- self.offsetSign = offsetSign
- self.offsetHours = offsetHours
- self.offsetMinutes = offsetMinutes
-
- def utcoffset(self, dt):
- """
- Return offset of local time from UTC, in minutes east of UTC.
-
- Return offset of local time from UTC, in minutes east of UTC.
- If local time is west of UTC, this should be negative.
- The value returned is a datetime.timedelta object specifying a whole
- number of minutes in the range -1439 to 1439 inclusive.
-
- Keyword arguments:
- dt -- the datetime.time object representing the local time
- """
- totalOffsetMinutes = self.offsetHours * 60 + self.offsetMinutes
- if self.offsetSign == '-':
- totalOffsetMinutes = -totalOffsetMinutes
- return datetime.timedelta(minutes = totalOffsetMinutes)
-
- def dst(self, dt):
- """
- Return the daylight saving time (DST) adjustment.
-
- Return the daylight saving time (DST) adjustment.
- In this implementation, it is always nil, and the method return
- datetime.timedelta(0).
-
- Keyword arguments:
- dt -- the datetime.time object representing the local time
- """
- return datetime.timedelta(0)
-
- def tzname(self, dt):
- """
- Return a string representation of the offset.
-
- Return a string representation of the offset with format '±%H:%M'.
-
- Keyword arguments:
- dt -- the datetime.time object representing the local time
- """
- string = self.offsetSign
- string = string + ('%02d' % self.offsetHours) + ':'
- string = string + ('%02d' % self.offsetMinutes)
- return string
+ """
+ Fixed offset from a local time east from UTC.
+
+ Represent a fixed (positive or negative) offset from a local time in hours
+ and minutes.
+
+ Public methods:
+ utcoffset -- return offset of local time from UTC, in minutes east of UTC
+ dst -- return the daylight saving time (DST) adjustment, here always 0
+ tzname -- return a string representation of the offset with format '±%H%M'
+ """
+
+ def __init__(self, offsetSign='+', offsetHours=0, offsetMinutes=0):
+ """
+ Constructor.
+
+ Construct a FixedOffset object from an offset sign ('+' or '-') and an
+ offset absolute value expressed in hours and minutes.
+ No check on the validity of those values is performed, it is the
+ responsibility of the caller to pass correct values to the constructor.
+
+ Keyword arguments:
+ offsetSign -- the sign of the offset ('+' or '-')
+ offsetHours -- the absolute number of hours of the offset
+ offsetMinutes -- the absolute number of minutes of the offset
+ """
+ self.offsetSign = offsetSign
+ self.offsetHours = offsetHours
+ self.offsetMinutes = offsetMinutes
+
+ def utcoffset(self, dt):
+ """
+ Return offset of local time from UTC, in minutes east of UTC.
+
+ Return offset of local time from UTC, in minutes east of UTC.
+ If local time is west of UTC, this should be negative.
+ The value returned is a datetime.timedelta object specifying a whole
+ number of minutes in the range -1439 to 1439 inclusive.
+
+ Keyword arguments:
+ dt -- the datetime.time object representing the local time
+ """
+ totalOffsetMinutes = self.offsetHours * 60 + self.offsetMinutes
+ if self.offsetSign == '-':
+ totalOffsetMinutes = -totalOffsetMinutes
+ return datetime.timedelta(minutes = totalOffsetMinutes)
+
+ def dst(self, dt):
+ """
+ Return the daylight saving time (DST) adjustment.
+
+ Return the daylight saving time (DST) adjustment.
+ In this implementation, it is always nil, and the method return
+ datetime.timedelta(0).
+
+ Keyword arguments:
+ dt -- the datetime.time object representing the local time
+ """
+ return datetime.timedelta(0)
+
+ def tzname(self, dt):
+ """
+ Return a string representation of the offset.
+
+ Return a string representation of the offset with format '±%H:%M'.
+
+ Keyword arguments:
+ dt -- the datetime.time object representing the local time
+ """
+ string = self.offsetSign
+ string = string + ('%02d' % self.offsetHours) + ':'
+ string = string + ('%02d' % self.offsetMinutes)
+ return string
def UndefinedToString(undefined):
- """
- Convert an undefined string into its corresponding sequence of bytes.
+ """
+ Convert an undefined string into its corresponding sequence of bytes.
- Convert a string containing the ascii codes of a sequence of bytes, each
- followed by a blank space, into the corresponding string (e.g.
- "48 50 50 49 " will be converted into "0221").
- The Undefined type is defined in the EXIF specification.
+ Convert a string containing the ascii codes of a sequence of bytes, each
+ followed by a blank space, into the corresponding string (e.g.
+ "48 50 50 49 " will be converted into "0221").
+ The Undefined type is defined in the EXIF specification.
- Keyword arguments:
- undefined -- the string containing the ascii codes of a sequence of bytes
- """
- return ''.join(map(lambda x: chr(int(x)), undefined.rstrip().split(' ')))
+ Keyword arguments:
+ undefined -- the string containing the ascii codes of a sequence of bytes
+ """
+ return ''.join(map(lambda x: chr(int(x)), undefined.rstrip().split(' ')))
def StringToUndefined(sequence):
- """
- Convert a string containing a sequence of bytes into its undefined form.
+ """
+ Convert a string containing a sequence of bytes into its undefined form.
- Convert a string containing a sequence of bytes into the corresponding
- sequence of ascii codes, each followed by a blank space (e.g. "0221" will
- be converted into "48 50 50 49 ").
- The Undefined type is defined in the EXIF specification.
+ Convert a string containing a sequence of bytes into the corresponding
+ sequence of ascii codes, each followed by a blank space (e.g. "0221" will
+ be converted into "48 50 50 49 ").
+ The Undefined type is defined in the EXIF specification.
- Keyword arguments:
- sequence -- the string containing the sequence of bytes
- """
- return ''.join(map(lambda x: '%d ' % ord(x), sequence))
+ Keyword arguments:
+ sequence -- the string containing the sequence of bytes
+ """
+ return ''.join(map(lambda x: '%d ' % ord(x), sequence))
def StringToDateTime(string):
- """
- Try to convert a string containing a date and time to a datetime object.
-
- Try to convert a string containing a date and time to the corresponding
- datetime object. The conversion is done by trying several patterns for
- regular expression matching.
- If no pattern matches, the string is returned unchanged.
-
- Keyword arguments:
- string -- the string potentially containing a date and time
- """
- # Possible formats to try
- # According to the EXIF specification [http://www.exif.org/Exif2-2.PDF], the
- # only accepted format for a string field representing a datetime is
- # '%Y-%m-%d %H:%M:%S', but it seems that others formats can be found in the
- # wild, so this list could be extended to include new exotic formats.
- formats = ['%Y-%m-%d %H:%M:%S', '%Y:%m:%d %H:%M:%S', '%Y-%m-%dT%H:%M:%SZ']
-
- for format in formats:
- try:
- t = time.strptime(string, format)
- return datetime.datetime(*t[:6])
- except ValueError:
- # the tested format does not match, do nothing
- pass
-
- # none of the tested formats matched, return the original string unchanged
- return string
+ """
+ Try to convert a string containing a date and time to a datetime object.
+
+ Try to convert a string containing a date and time to the corresponding
+ datetime object. The conversion is done by trying several patterns for
+ regular expression matching.
+ If no pattern matches, the string is returned unchanged.
+
+ Keyword arguments:
+ string -- the string potentially containing a date and time
+ """
+ # Possible formats to try
+ # According to the EXIF specification [http://www.exif.org/Exif2-2.PDF], the
+ # only accepted format for a string field representing a datetime is
+ # '%Y-%m-%d %H:%M:%S', but it seems that others formats can be found in the
+ # wild, so this list could be extended to include new exotic formats.
+ # TODO: move the declaration of this list at module level
+ formats = ['%Y-%m-%d %H:%M:%S', '%Y:%m:%d %H:%M:%S', '%Y-%m-%dT%H:%M:%SZ']
+
+ for format in formats:
+ try:
+ t = time.strptime(string, format)
+ return datetime.datetime(*t[:6])
+ except ValueError:
+ # the tested format does not match, do nothing
+ pass
+
+ # none of the tested formats matched, return the original string unchanged
+ return string
def StringToDate(string):
- """
- Try to convert a string containing a date to a date object.
-
- Try to convert a string containing a date to the corresponding date object.
- The conversion is done by matching a regular expression.
- If the pattern does not match, the string is returned unchanged.
-
- Keyword arguments:
- string -- the string potentially containing a date
- """
- # According to the IPTC specification
- # [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf], the format
- # for a string field representing a date is '%Y%m%d'.
- # However, the string returned by exiv2 using method DateValue::toString()
- # is formatted using pattern '%Y-%m-%d'.
- format = '%Y-%m-%d'
- try:
- t = time.strptime(string, format)
- return datetime.date(*t[:3])
- except ValueError:
- # the tested format does not match, do nothing
- return string
+ """
+ Try to convert a string containing a date to a date object.
+
+ Try to convert a string containing a date to the corresponding date object.
+ The conversion is done by matching a regular expression.
+ If the pattern does not match, the string is returned unchanged.
+
+ Keyword arguments:
+ string -- the string potentially containing a date
+ """
+ # According to the IPTC specification
+ # [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf], the format
+ # for a string field representing a date is '%Y%m%d'.
+ # However, the string returned by exiv2 using method DateValue::toString()
+ # is formatted using pattern '%Y-%m-%d'.
+ format = '%Y-%m-%d'
+ try:
+ t = time.strptime(string, format)
+ return datetime.date(*t[:3])
+ except ValueError:
+ # the tested format does not match, do nothing
+ return string
def StringToTime(string):
- """
- Try to convert a string containing a time to a time object.
-
- Try to convert a string containing a time to the corresponding time object.
- The conversion is done by matching a regular expression.
- If the pattern does not match, the string is returned unchanged.
-
- Keyword arguments:
- string -- the string potentially containing a time
- """
- # According to the IPTC specification
- # [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf], the format
- # for a string field representing a time is '%H%M%S±%H%M'.
- # However, the string returned by exiv2 using method TimeValue::toString()
- # is formatted using pattern '%H:%M:%S±%H:%M'.
-
- if len(string) != 14:
- # the string is not correctly formatted, do nothing
- return string
-
- if (string[2] != ':') or (string[5] != ':') or (string[11] != ':'):
- # the string is not correctly formatted, do nothing
- return string
-
- offsetSign = string[8]
- if (offsetSign != '+') and (offsetSign != '-'):
- # the string is not correctly formatted, do nothing
- return string
-
- try:
- hours = int(string[:2])
- minutes = int(string[3:5])
- seconds = int(string[6:8])
- offsetHours = int(string[9:11])
- offsetMinutes = int(string[12:])
- except ValueError:
- # the string is not correctly formatted, do nothing
- return string
-
- try:
- offset = FixedOffset(offsetSign, offsetHours, offsetMinutes)
- localTime = datetime.time(hours, minutes, seconds, tzinfo=offset)
- except ValueError:
- # the values are out of range, do nothing
- return string
-
- return localTime
+ """
+ Try to convert a string containing a time to a time object.
+
+ Try to convert a string containing a time to the corresponding time object.
+ The conversion is done by matching a regular expression.
+ If the pattern does not match, the string is returned unchanged.
+
+ Keyword arguments:
+ string -- the string potentially containing a time
+ """
+ # According to the IPTC specification
+ # [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf], the format
+ # for a string field representing a time is '%H%M%S±%H%M'.
+ # However, the string returned by exiv2 using method TimeValue::toString()
+ # is formatted using pattern '%H:%M:%S±%H:%M'.
+
+ if len(string) != 14:
+ # the string is not correctly formatted, do nothing
+ return string
+
+ if (string[2] != ':') or (string[5] != ':') or (string[11] != ':'):
+ # the string is not correctly formatted, do nothing
+ return string
+
+ offsetSign = string[8]
+ if (offsetSign != '+') and (offsetSign != '-'):
+ # the string is not correctly formatted, do nothing
+ return string
+
+ try:
+ hours = int(string[:2])
+ minutes = int(string[3:5])
+ seconds = int(string[6:8])
+ offsetHours = int(string[9:11])
+ offsetMinutes = int(string[12:])
+ except ValueError:
+ # the string is not correctly formatted, do nothing
+ return string
+
+ try:
+ offset = FixedOffset(offsetSign, offsetHours, offsetMinutes)
+ localTime = datetime.time(hours, minutes, seconds, tzinfo=offset)
+ except ValueError:
+ # the values are out of range, do nothing
+ return string
+
+ return localTime
class Rational:
- """
- A class representing a rational number.
- """
+ """
+ A class representing a rational number.
+ """
- def __init__(self, numerator, denominator):
- """
- Constructor.
+ def __init__(self, numerator, denominator):
+ """
+ Constructor.
- Construct a rational number from its numerator and its denominator.
+ Construct a rational number from its numerator and its denominator.
- Keyword arguments:
- numerator -- the numerator
- denominator -- the denominator (if zero, will raise a ZeroDivisionError)
- """
- if int(denominator) == 0:
- raise ZeroDivisionError('Denominator of a rational number cannot be zero')
- self.numerator = long(numerator)
- self.denominator = long(denominator)
+ Keyword arguments:
+ numerator -- the numerator
+ denominator -- the denominator (if zero, will raise a ZeroDivisionError)
+ """
+ if int(denominator) == 0:
+ raise ZeroDivisionError('Denominator of a rational number cannot be zero')
+ self.numerator = long(numerator)
+ self.denominator = long(denominator)
- def __eq__(self, other):
- """
- Compare two rational numbers for equality.
+ def __eq__(self, other):
+ """
+ Compare two rational numbers for equality.
- Two rational numbers are equal if and only if their numerators are equal
- and their denominators are equal.
+ Two rational numbers are equal if and only if their numerators are equal
+ and their denominators are equal.
- Keyword arguments:
- other -- the rational number to compare to self for equality
- """
- return ((self.numerator == other.numerator) and
- (self.denominator == other.denominator))
+ Keyword arguments:
+ other -- the rational number to compare to self for equality
+ """
+ return ((self.numerator == other.numerator) and
+ (self.denominator == other.denominator))
- def __str__(self):
- """
- Return a string representation of the rational number.
- """
- return str(self.numerator) + '/' + str(self.denominator)
+ def __str__(self):
+ """
+ Return a string representation of the rational number.
+ """
+ return str(self.numerator) + '/' + str(self.denominator)
def StringToRational(string):
- """
- Try to convert a string containing a rational number to a Rational object.
-
- Try to convert a string containing a rational number to the corresponding
- Rational object.
- The conversion is done by matching a regular expression.
- If the pattern does not match, the Rational object with numerator=0 and
- denominator=1 is returned.
-
- Keyword arguments:
- string -- the string potentially containing a rational number
- """
- pattern = re.compile("(-?[0-9]+)/(-?[1-9][0-9]*)")
- match = pattern.match(string)
- if match == None:
- return Rational(0, 1)
- else:
- return Rational(*map(long, match.groups()))
+ """
+ Try to convert a string containing a rational number to a Rational object.
+
+ Try to convert a string containing a rational number to the corresponding
+ Rational object.
+ The conversion is done by matching a regular expression.
+ If the pattern does not match, the Rational object with numerator=0 and
+ denominator=1 is returned.
+
+ Keyword arguments:
+ string -- the string potentially containing a rational number
+ """
+ pattern = re.compile("(-?[0-9]+)/(-?[1-9][0-9]*)")
+ match = pattern.match(string)
+ if match == None:
+ return Rational(0, 1)
+ else:
+ return Rational(*map(long, match.groups()))
def ConvertToPythonType(tagFamily, tagType, tagValue):
- """
- Types a tag value using Python's built-in types or modules.
-
- Whenever possible, the value is typed using Python's built-in types or
- modules such as date when the value represents a date (e.g. the IPTC tag
- 'Iptc.Application2.DateCreated').
- For EXIF rational number, custom type pyexiv2.Rational is used.
-
- Keyword arguments:
- tagFamily -- the family of the tag ('Exif' or 'Iptc')
- tagType -- the type of the tag as defined in the EXIF and IPTC specifications
- tagValue -- the value of the tag as a raw string
- """
- value = tagValue
- if tagFamily == 'Exif':
- if tagType == 'Byte':
- pass
- elif tagType == 'Ascii':
- # try to guess if the value is a datetime
- value = StringToDateTime(tagValue)
- elif tagType == 'Short':
- value = int(tagValue)
- elif tagType == 'Long' or tagType == 'SLong':
- value = long(tagValue)
- elif tagType == 'Rational' or tagType == 'SRational':
- value = StringToRational(tagValue)
- elif tagType == 'Undefined':
- # tagValue is a sequence of bytes whose codes are written as a
- # string, each code being followed by a blank space (e.g.
- # "48 50 50 49 " for "0221").
- try:
- value = UndefinedToString(tagValue)
- except ValueError:
- # Some tags such as "Exif.Photo.UserComment" are marked as
- # Undefined but do not store their value as expected.
- # This should fix bug #173387.
- pass
- elif tagFamily == 'Iptc':
- if tagType == 'Short':
- value = int(tagValue)
- elif tagType == 'String':
- pass
- elif tagType == 'Date':
- value = StringToDate(tagValue)
- elif tagType == 'Time':
- value = StringToTime(tagValue)
- elif tagType == 'Undefined':
- pass
- return value
-
-class Image(libpyexiv2.Image):
-
- """
- Provide convenient methods for the manipulation of EXIF and IPTC metadata.
-
- Provide convenient methods for the manipulation of EXIF and IPTC metadata
- embedded in image files such as JPEG and TIFF files, using Python's built-in
- types and modules such as datetime.
- """
-
- def __init__(self, filename):
- if filename.__class__ is unicode:
- filename = filename.encode('utf-8')
- libpyexiv2.Image.__init__(self, filename)
- self.__exifTagsDict = {}
- self.__iptcTagsDict = {}
- self.__exifCached = False
- self.__iptcCached = False
-
- def __getExifTagValue(self, key):
- """
- Get the value associated to a key in EXIF metadata.
-
- Get the value associated to a key in EXIF metadata.
- Whenever possible, the value is typed using Python's built-in types or
- modules such as datetime when the value is composed of a date and a time
- (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal').
-
- Keyword arguments:
- key -- the EXIF key of the requested metadata tag
- """
- tagType, tagValue = self.__getExifTag(key)
- if tagType not in ('Byte', 'Ascii', 'Undefined'):
- values = [ConvertToPythonType('Exif', tagType, x) for x in tagValue.split()]
- if len(values) == 1:
- return values[0]
- else:
- return tuple(values)
- else:
- return ConvertToPythonType('Exif', tagType, tagValue)
-
- def __setExifTagValue(self, key, value):
- """
- Set the value associated to a key in EXIF metadata.
-
- Set the value associated to a key in EXIF metadata.
- The new value passed should be typed using Python's built-in types or
- modules such as datetime when the value is composed of a date and a time
- (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal'), the method takes care
- of converting it before setting the internal EXIF tag value.
-
- Keyword arguments:
- key -- the EXIF key of the requested metadata tag
- value -- the new value for the requested metadata tag
- """
- valueType = value.__class__
- if valueType == int or valueType == long:
- strVal = str(value)
- elif valueType == datetime.datetime:
- strVal = value.strftime('%Y:%m:%d %H:%M:%S')
- elif valueType == list or valueType == tuple:
- strVal = ' '.join([str(x) for x in value])
- else:
- # Value must already be a string.
- # Warning: no distinction is possible between values that really are
- # strings (type 'Ascii') and those that are supposed to be sequences
- # of bytes (type 'Undefined'), in which case value must be passed as
- # a string correctly formatted, using utility function
- # StringToUndefined().
- strVal = str(value)
- typeName, oldValue = self.__setExifTag(key, strVal)
- return typeName
-
- def __getIptcTagValue(self, key):
- """
- Get the value(s) associated to a key in IPTC metadata.
-
- Get the value associated to a key in IPTC metadata.
- Whenever possible, the value is typed using Python's built-in types or
- modules such as date when the value represents a date (e.g. the IPTC tag
- 'Iptc.Application2.DateCreated').
- If key represents a repeatable tag, a list of several values is
- returned. If not, or if it has only one repetition, the list simply has
- one element.
-
- Keyword arguments:
- key -- the IPTC key of the requested metadata tag
- """
- return [ConvertToPythonType('Iptc', *x) for x in self.__getIptcTag(key)]
-
- def __setIptcTagValue(self, key, value, index=0):
- """
- Set the value associated to a key in IPTC metadata.
-
- Set the value associated to a key in IPTC metadata.
- The new value passed should be typed using Python's built-in types or
- modules such as datetime when the value contains a date or a time
- (e.g. the IPTC tags 'Iptc.Application2.DateCreated' and
- 'Iptc.Application2.TimeCreated'), the method takes care
- of converting it before setting the internal IPTC tag value.
- If key references a repeatable tag, the parameter index (starting from
- 0 like a list index) is used to determine which of the repetitions is to
- be set. In case of an index greater than the highest existing one, adds
- a repetition of the tag. index defaults to 0 for (the majority of)
- non-repeatable tags.
-
- Keyword arguments:
- key -- the IPTC key of the requested metadata tag
- value -- the new value for the requested metadata tag
- index -- the index of the tag repetition to set (default value: 0)
- """
- if (index < 0):
- raise IndexError('Index must be greater than or equal to zero')
- valueType = value.__class__
- if valueType == int or valueType == long:
- strVal = str(value)
- elif valueType == datetime.date:
- strVal = value.strftime('%Y-%m-%d')
- elif valueType == datetime.time:
- # The only legal format for a time is '%H:%M:%S±%H:%M',
- # but if the UTC offset is absent (format '%H:%M:%S'), the time can
- # still be set (exiv2 is permissive).
- strVal = value.strftime('%H:%M:%S%Z')
- else:
- # Value must already be a string.
- # Warning: no distinction is possible between values that really are
- # strings (type 'String') and those that are of type 'Undefined'.
- # FIXME: for tags of type 'Undefined', this does not seem to work...
- strVal = str(value)
- typeName, oldValue = self.__setIptcTag(key, strVal, index)
- return typeName
-
- def __getitem__(self, key):
- """
- Read access implementation of the [] operator on Image objects.
-
- Get the value associated to a key in EXIF/IPTC metadata.
- The value is cached in an internal dictionary for later accesses.
-
- Whenever possible, the value is typed using Python's built-in types or
- modules such as datetime when the value is composed of a date and a time
- (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal') or date when the value
- represents a date (e.g. the IPTC tag 'Iptc.Application2.DateCreated').
-
- If key references a repeatable tag (IPTC only), a list of several values
- is returned. If not, or if it has only one repetition, the list simply
- has one element.
-
- Keyword arguments:
- key -- the [EXIF|IPTC] key of the requested metadata tag
- """
- if key.__class__ is not str:
- raise TypeError('Key must be of type string')
- tagFamily = key[:4]
- if tagFamily == 'Exif':
- try:
- return self.__exifTagsDict[key]
- except KeyError:
- value = self.__getExifTagValue(key)
- self.__exifTagsDict[key] = value
- return value
- elif tagFamily == 'Iptc':
- try:
- return self.__iptcTagsDict[key]
- except KeyError:
- value = self.__getIptcTagValue(key)
- if len(value) == 1:
- value = value[0]
- elif len(value) > 1:
- value = tuple(value)
- self.__iptcTagsDict[key] = value
- return value
- else:
- # This is exiv2's standard error message, all futures changes on
- # exiv2's side should be reflected here.
- # As a future development, consider i18n for error messages.
- raise IndexError("Invalid key `" + key + "'")
-
- def __setitem__(self, key, value):
- """
- Write access implementation of the [] operator on Image objects.
-
- Set the value associated to a key in EXIF/IPTC metadata.
- The value is cached in an internal dictionary for later accesses.
-
- The new value passed should be typed using Python's built-in types or
- modules such as datetime when the value contains a date and a time
- (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal' or the IPTC tags
- 'Iptc.Application2.DateCreated' and 'Iptc.Application2.TimeCreated'),
- the method takes care of converting it before setting the internal tag
- value.
-
- If key references a repeatable tag (IPTC only), value can be a list of
- values (the new values will overwrite the old ones, and an empty list of
- values will unset the tag).
-
- Keyword arguments:
- key -- the [EXIF|IPTC] key of the requested metadata tag
- value -- the new value for the requested metadata tag
- """
- if key.__class__ is not str:
- raise TypeError('Key must be of type string')
- tagFamily = key[:4]
- if tagFamily == 'Exif':
- if value is not None:
- # For datetime objects, microseconds are not supported by the
- # EXIF specification, so truncate them if present.
- if value.__class__ is datetime.datetime:
- value = value.replace(microsecond=0)
-
- typeName = self.__setExifTagValue(key, value)
- self.__exifTagsDict[key] = ConvertToPythonType(tagFamily, typeName, str(value))
- else:
- self.__deleteExifTag(key)
- if self.__exifTagsDict.has_key(key):
- del self.__exifTagsDict[key]
- elif tagFamily == 'Iptc':
- # The case of IPTC tags is a bit trickier since some tags are
- # repeatable. To simplify the process, parameter 'value' is
- # transformed into a tuple if it is not already one and then each of
- # its values is processed (set, that is) in a loop.
- newValues = value
- if newValues is None:
- # Setting the value to None does not really make sense, but can
- # in a way be seen as equivalent to deleting it, so this
- # behaviour is simulated by providing an empty list for 'value'.
- newValues = ()
- if newValues.__class__ is not tuple:
- if newValues.__class__ is list:
- # For flexibility, passing a list instead of a tuple works
- newValues = tuple(newValues)
- else:
- # Interpret the value as a single element
- newValues = (newValues,)
- try:
- oldValues = self.__iptcTagsDict[key]
- if oldValues.__class__ is not tuple:
- oldValues = (oldValues,)
- except KeyError:
- # The tag is not cached yet
- try:
- oldValues = self.__getitem__(key)
- except KeyError:
- # The tag is not set
- oldValues = ()
-
- # For time objects, microseconds are not supported by the IPTC
- # specification, so truncate them if present.
- tempNewValues = []
- for newValue in newValues:
- if newValue.__class__ is datetime.time:
- tempNewValues.append(newValue.replace(microsecond=0))
- else:
- tempNewValues.append(newValue)
- newValues = tuple(tempNewValues)
-
- # This loop processes the values one by one. There are 3 cases:
- # * if the two tuples are of the exact same size, each item in
- # oldValues is replaced by its new value in newValues;
- # * if newValues is longer than oldValues, each item in oldValues
- # is replaced by its new value in newValues and the new items
- # are appended at the end of oldValues;
- # * if newValues is shorter than oldValues, each item in newValues
- # replaces the corresponding one in oldValues and the trailing
- # extra items in oldValues are deleted.
- for i in xrange(max(len(oldValues), len(newValues))):
- try:
- typeName = self.__setIptcTagValue(key, newValues[i], i)
- except IndexError:
- try:
- self.__deleteIptcTag(key, min(len(oldValues), len(newValues)))
- except KeyError:
- pass
- if len(newValues) > 0:
- if len(newValues) == 1:
- newValues = newValues[0]
- self.__iptcTagsDict[key] = tuple([ConvertToPythonType(tagFamily, typeName, str(v)) for v in newValues])
- else:
- if self.__iptcTagsDict.has_key(key):
- del self.__iptcTagsDict[key]
- else:
- raise IndexError("Invalid key `" + key + "'")
-
- def __delitem__(self, key):
- """
- Implementation of the del operator for deletion on Image objects.
-
- Delete the value associated to a key in EXIF/IPTC metadata.
-
- If key references a repeatable tag (IPTC only), all the associated
- values will be deleted.
-
- Keyword arguments:
- key -- the [EXIF|IPTC] key of the requested metadata tag
- """
- if key.__class__ is not str:
- raise TypeError('Key must be of type string')
- tagFamily = key[:4]
- if tagFamily == 'Exif':
- self.__deleteExifTag(key)
- if self.__exifTagsDict.has_key(key):
- del self.__exifTagsDict[key]
- elif tagFamily == 'Iptc':
- try:
- oldValues = self.__iptcTagsDict[key]
- except KeyError:
- oldValues = self.__getIptcTag(key)
- for i in xrange(len(oldValues)):
- self.__deleteIptcTag(key, 0)
- if self.__iptcTagsDict.has_key(key):
- del self.__iptcTagsDict[key]
- else:
- raise IndexError("Invalid key `" + key + "'")
-
- def cacheAllExifTags(self):
- """
- Cache the EXIF tag values for faster subsequent access.
-
- Read the values of all the EXIF tags in the image and cache them in an
- internal dictionary so as to speed up subsequent accesses.
- """
- if not self.__exifCached:
- for key in self.exifKeys():
- self[key]
- self.__exifCached = True
-
- def cacheAllIptcTags(self):
- """
- Cache the IPTC tag values for faster subsequent access.
-
- Read the values of all the IPTC tags in the image and cache them in an
- internal dictionary so as to speed up subsequent accesses.
- """
- if not self.__iptcCached:
- for key in self.iptcKeys():
- self[key]
- self.__iptcCached = True
-
- def interpretedExifValue(self, key):
- """
- Get the interpreted value of an EXIF tag as presented by the exiv2 tool.
-
- For EXIF tags, the exiv2 command-line tool is capable of displaying
- user-friendly interpreted values, such as 'top, left' for the
- 'Exif.Image.Orientation' tag when it has value '1'. This method always
- returns a string containing this interpreted value for a given tag.
- Warning: calling this method will not cache the value in the internal
- dictionary.
-
- Keyword arguments:
- key -- the EXIF key of the requested metadata tag
- """
- # This method was added as a requirement tracked by bug #147534
- return self.__getExifTagToString(key)
-
- def copyMetadataTo(self, destImage):
- """
- Duplicate all the tags and the comment from this image to another one.
-
- Read all the values of the EXIF and IPTC tags and the comment and write
- them back to the new image.
-
- Keyword arguments:
- destImage -- the destination image to write the copied metadata back to
- """
- for key in self.exifKeys():
- destImage[key] = self[key]
- for key in self.iptcKeys():
- destImage[key] = self[key]
- destImage.setComment(self.getComment())
-
-def _test():
- print 'testing library pyexiv2...'
- # TODO: various tests
- print 'done.'
-
-if __name__ == '__main__':
- _test()
+ """
+ Types a tag value using Python's built-in types or modules.
+
+ Whenever possible, the value is typed using Python's built-in types or
+ modules such as date when the value represents a date (e.g. the IPTC tag
+ 'Iptc.Application2.DateCreated').
+ For EXIF rational number, custom type pyexiv2.Rational is used.
+
+ Keyword arguments:
+ tagFamily -- the family of the tag ('Exif' or 'Iptc')
+ tagType -- the type of the tag as defined in the EXIF and IPTC specifications
+ tagValue -- the value of the tag as a raw string
+ """
+ value = tagValue
+ if tagFamily == 'Exif':
+ if tagType == 'Byte':
+ pass
+ elif tagType == 'Ascii':
+ # try to guess if the value is a datetime
+ value = StringToDateTime(tagValue)
+ elif tagType == 'Short':
+ value = int(tagValue)
+ elif tagType == 'Long' or tagType == 'SLong':
+ value = long(tagValue)
+ elif tagType == 'Rational' or tagType == 'SRational':
+ value = StringToRational(tagValue)
+ elif tagType == 'Undefined':
+ # tagValue is a sequence of bytes whose codes are written as a
+ # string, each code being followed by a blank space (e.g.
+ # "48 50 50 49 " for "0221").
+ try:
+ value = UndefinedToString(tagValue)
+ except ValueError:
+ # Some tags such as "Exif.Photo.UserComment" are marked as
+ # Undefined but do not store their value as expected.
+ # This should fix bug #173387.
+ pass
+ elif tagFamily == 'Iptc':
+ if tagType == 'Short':
+ value = int(tagValue)
+ elif tagType == 'String':
+ pass
+ elif tagType == 'Date':
+ value = StringToDate(tagValue)
+ elif tagType == 'Time':
+ value = StringToTime(tagValue)
+ elif tagType == 'Undefined':
+ pass
+ return value
+
+class MetadataTag(object):
+
+ """
+ DOCME
+ """
+
+ def __init__(self, key, name, label, description, type, value, svalue):
+ """
+ DOCME
+ """
+ self.key = key
+ self.name = name
+ self.label = label
+ self.description = description
+ self.type = type
+ self.value = value
+ self.svalue = svalue
+
+ def __str__(self):
+ """
+ Return a string representation of the metadata tag.
+ """
+ r = 'Key = ' + self.key + os.linesep + \
+ 'Name = ' + self.name + os.linesep + \
+ 'Label = ' + self.label + os.linesep + \
+ 'Description = ' + self.description + os.linesep + \
+ 'Type = ' + self.type + os.linesep + \
+ 'Raw value = ' + self.value + os.linesep + \
+ 'Formatted value = ' + self.svalue
+ return r
+
+class ExifTag(MetadataTag):
+
+ """
+ DOCME
+ """
+
+ pass
+
+
+class Image(libexiv2python.Image):
+
+ """
+ Provide convenient methods for the manipulation of EXIF, IPTC and XMP
+ metadata.
+
+ Provide convenient methods for the manipulation of EXIF, IPTC and XMP
+ metadata embedded in image files such as JPEG and TIFF files, using Python's
+ built-in types and modules such as datetime.
+ """
+
+ def __init__(self, filename):
+ if filename.__class__ is unicode:
+ filename = filename.encode('utf-8')
+ libexiv2python.Image.__init__(self, filename)
+ self.__exifTagsDict = {}
+ #self.__iptcTagsDict = {}
+ self.__exifCached = False
+ #self.__iptcCached = False
+
+ def __get_exif_tag(self, key):
+ """
+ DOCME
+ """
+ tag = ExifTag(*self.__getExifTag(key))
+ # TODO: convert value to python type, other processing
+ return tag
+
+
+ def __getExifTagValue(self, key):
+ """
+ Get the value associated to a key in EXIF metadata.
+
+ Get the value associated to a key in EXIF metadata.
+ Whenever possible, the value is typed using Python's built-in types or
+ modules such as datetime when the value is composed of a date and a time
+ (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal').
+
+ Keyword arguments:
+ key -- the EXIF key of the requested metadata tag
+ """
+ tagType, tagValue = self.__getExifTag(key)
+ if tagType not in ('Byte', 'Ascii', 'Undefined'):
+ values = [ConvertToPythonType('Exif', tagType, x) for x in tagValue.split()]
+ if len(values) == 1:
+ return values[0]
+ else:
+ return tuple(values)
+ else:
+ return ConvertToPythonType('Exif', tagType, tagValue)
+
+ def __setExifTagValue(self, key, value):
+ """
+ Set the value associated to a key in EXIF metadata.
+
+ Set the value associated to a key in EXIF metadata.
+ The new value passed should be typed using Python's built-in types or
+ modules such as datetime when the value is composed of a date and a time
+ (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal'), the method takes care
+ of converting it before setting the internal EXIF tag value.
+
+ Keyword arguments:
+ key -- the EXIF key of the requested metadata tag
+ value -- the new value for the requested metadata tag
+ """
+ valueType = value.__class__
+ if valueType == int or valueType == long:
+ strVal = str(value)
+ elif valueType == datetime.datetime:
+ strVal = value.strftime('%Y:%m:%d %H:%M:%S')
+ elif valueType == list or valueType == tuple:
+ strVal = ' '.join([str(x) for x in value])
+ else:
+ # Value must already be a string.
+ # Warning: no distinction is possible between values that really are
+ # strings (type 'Ascii') and those that are supposed to be sequences
+ # of bytes (type 'Undefined'), in which case value must be passed as
+ # a string correctly formatted, using utility function
+ # StringToUndefined().
+ strVal = str(value)
+ typeName, oldValue = self.__setExifTag(key, strVal)
+ return typeName
+
+ def __getIptcTagValue(self, key):
+ """
+ Get the value(s) associated to a key in IPTC metadata.
+
+ Get the value associated to a key in IPTC metadata.
+ Whenever possible, the value is typed using Python's built-in types or
+ modules such as date when the value represents a date (e.g. the IPTC tag
+ 'Iptc.Application2.DateCreated').
+ If key represents a repeatable tag, a list of several values is
+ returned. If not, or if it has only one repetition, the list simply has
+ one element.
+
+ Keyword arguments:
+ key -- the IPTC key of the requested metadata tag
+ """
+ return [ConvertToPythonType('Iptc', *x) for x in self.__getIptcTag(key)]
+
+ def __setIptcTagValue(self, key, value, index=0):
+ """
+ Set the value associated to a key in IPTC metadata.
+
+ Set the value associated to a key in IPTC metadata.
+ The new value passed should be typed using Python's built-in types or
+ modules such as datetime when the value contains a date or a time
+ (e.g. the IPTC tags 'Iptc.Application2.DateCreated' and
+ 'Iptc.Application2.TimeCreated'), the method takes care
+ of converting it before setting the internal IPTC tag value.
+ If key references a repeatable tag, the parameter index (starting from
+ 0 like a list index) is used to determine which of the repetitions is to
+ be set. In case of an index greater than the highest existing one, adds
+ a repetition of the tag. index defaults to 0 for (the majority of)
+ non-repeatable tags.
+
+ Keyword arguments:
+ key -- the IPTC key of the requested metadata tag
+ value -- the new value for the requested metadata tag
+ index -- the index of the tag repetition to set (default value: 0)
+ """
+ if (index < 0):
+ raise IndexError('Index must be greater than or equal to zero')
+ valueType = value.__class__
+ if valueType == int or valueType == long:
+ strVal = str(value)
+ elif valueType == datetime.date:
+ strVal = value.strftime('%Y-%m-%d')
+ elif valueType == datetime.time:
+ # The only legal format for a time is '%H:%M:%S±%H:%M',
+ # but if the UTC offset is absent (format '%H:%M:%S'), the time can
+ # still be set (exiv2 is permissive).
+ strVal = value.strftime('%H:%M:%S%Z')
+ else:
+ # Value must already be a string.
+ # Warning: no distinction is possible between values that really are
+ # strings (type 'String') and those that are of type 'Undefined'.
+ # FIXME: for tags of type 'Undefined', this does not seem to work...
+ strVal = str(value)
+ typeName, oldValue = self.__setIptcTag(key, strVal, index)
+ return typeName
+
+ def __getitem__(self, key):
+ """
+ Read access implementation of the [] operator on Image objects.
+
+ Get the value associated to a key in EXIF/IPTC metadata.
+ The value is cached in an internal dictionary for later accesses.
+
+ Whenever possible, the value is typed using Python's built-in types or
+ modules such as datetime when the value is composed of a date and a time
+ (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal') or date when the value
+ represents a date (e.g. the IPTC tag 'Iptc.Application2.DateCreated').
+
+ If key references a repeatable tag (IPTC only), a list of several values
+ is returned. If not, or if it has only one repetition, the list simply
+ has one element.
+
+ Keyword arguments:
+ key -- the [EXIF|IPTC] key of the requested metadata tag
+ """
+ if key.__class__ is not str:
+ raise TypeError('Key must be of type string')
+ tagFamily = key[:4]
+ if tagFamily == 'Exif':
+ try:
+ return self.__exifTagsDict[key]
+ except KeyError:
+ value = self.__getExifTagValue(key)
+ self.__exifTagsDict[key] = value
+ return value
+ elif tagFamily == 'Iptc':
+ try:
+ return self.__iptcTagsDict[key]
+ except KeyError:
+ value = self.__getIptcTagValue(key)
+ if len(value) == 1:
+ value = value[0]
+ elif len(value) > 1:
+ value = tuple(value)
+ self.__iptcTagsDict[key] = value
+ return value
+ else:
+ # This is exiv2's standard error message, all futures changes on
+ # exiv2's side should be reflected here.
+ # As a future development, consider i18n for error messages.
+ raise IndexError("Invalid key `" + key + "'")
+
+ def __setitem__(self, key, value):
+ """
+ Write access implementation of the [] operator on Image objects.
+
+ Set the value associated to a key in EXIF/IPTC metadata.
+ The value is cached in an internal dictionary for later accesses.
+
+ The new value passed should be typed using Python's built-in types or
+ modules such as datetime when the value contains a date and a time
+ (e.g. the EXIF tag 'Exif.Photo.DateTimeOriginal' or the IPTC tags
+ 'Iptc.Application2.DateCreated' and 'Iptc.Application2.TimeCreated'),
+ the method takes care of converting it before setting the internal tag
+ value.
+
+ If key references a repeatable tag (IPTC only), value can be a list of
+ values (the new values will overwrite the old ones, and an empty list of
+ values will unset the tag).
+
+ Keyword arguments:
+ key -- the [EXIF|IPTC] key of the requested metadata tag
+ value -- the new value for the requested metadata tag
+ """
+ if key.__class__ is not str:
+ raise TypeError('Key must be of type string')
+ tagFamily = key[:4]
+ if tagFamily == 'Exif':
+ if value is not None:
+ # For datetime objects, microseconds are not supported by the
+ # EXIF specification, so truncate them if present.
+ if value.__class__ is datetime.datetime:
+ value = value.replace(microsecond=0)
+
+ typeName = self.__setExifTagValue(key, value)
+ self.__exifTagsDict[key] = ConvertToPythonType(tagFamily, typeName, str(value))
+ else:
+ self.__deleteExifTag(key)
+ if self.__exifTagsDict.has_key(key):
+ del self.__exifTagsDict[key]
+ elif tagFamily == 'Iptc':
+ # The case of IPTC tags is a bit trickier since some tags are
+ # repeatable. To simplify the process, parameter 'value' is
+ # transformed into a tuple if it is not already one and then each of
+ # its values is processed (set, that is) in a loop.
+ newValues = value
+ if newValues is None:
+ # Setting the value to None does not really make sense, but can
+ # in a way be seen as equivalent to deleting it, so this
+ # behaviour is simulated by providing an empty list for 'value'.
+ newValues = ()
+ if newValues.__class__ is not tuple:
+ if newValues.__class__ is list:
+ # For flexibility, passing a list instead of a tuple works
+ newValues = tuple(newValues)
+ else:
+ # Interpret the value as a single element
+ newValues = (newValues,)
+ try:
+ oldValues = self.__iptcTagsDict[key]
+ if oldValues.__class__ is not tuple:
+ oldValues = (oldValues,)
+ except KeyError:
+ # The tag is not cached yet
+ try:
+ oldValues = self.__getitem__(key)
+ except KeyError:
+ # The tag is not set
+ oldValues = ()
+
+ # For time objects, microseconds are not supported by the IPTC
+ # specification, so truncate them if present.
+ tempNewValues = []
+ for newValue in newValues:
+ if newValue.__class__ is datetime.time:
+ tempNewValues.append(newValue.replace(microsecond=0))
+ else:
+ tempNewValues.append(newValue)
+ newValues = tuple(tempNewValues)
+
+ # This loop processes the values one by one. There are 3 cases:
+ # * if the two tuples are of the exact same size, each item in
+ # oldValues is replaced by its new value in newValues;
+ # * if newValues is longer than oldValues, each item in oldValues
+ # is replaced by its new value in newValues and the new items
+ # are appended at the end of oldValues;
+ # * if newValues is shorter than oldValues, each item in newValues
+ # replaces the corresponding one in oldValues and the trailing
+ # extra items in oldValues are deleted.
+ for i in xrange(max(len(oldValues), len(newValues))):
+ try:
+ typeName = self.__setIptcTagValue(key, newValues[i], i)
+ except IndexError:
+ try:
+ self.__deleteIptcTag(key, min(len(oldValues), len(newValues)))
+ except KeyError:
+ pass
+ if len(newValues) > 0:
+ if len(newValues) == 1:
+ newValues = newValues[0]
+ self.__iptcTagsDict[key] = tuple([ConvertToPythonType(tagFamily, typeName, str(v)) for v in newValues])
+ else:
+ if self.__iptcTagsDict.has_key(key):
+ del self.__iptcTagsDict[key]
+ else:
+ raise IndexError("Invalid key `" + key + "'")
+
+ def __delitem__(self, key):
+ """
+ Implementation of the del operator for deletion on Image objects.
+
+ Delete the value associated to a key in EXIF/IPTC metadata.
+
+ If key references a repeatable tag (IPTC only), all the associated
+ values will be deleted.
+
+ Keyword arguments:
+ key -- the [EXIF|IPTC] key of the requested metadata tag
+ """
+ if key.__class__ is not str:
+ raise TypeError('Key must be of type string')
+ tagFamily = key[:4]
+ if tagFamily == 'Exif':
+ self.__deleteExifTag(key)
+ if self.__exifTagsDict.has_key(key):
+ del self.__exifTagsDict[key]
+ elif tagFamily == 'Iptc':
+ try:
+ oldValues = self.__iptcTagsDict[key]
+ except KeyError:
+ oldValues = self.__getIptcTag(key)
+ for i in xrange(len(oldValues)):
+ self.__deleteIptcTag(key, 0)
+ if self.__iptcTagsDict.has_key(key):
+ del self.__iptcTagsDict[key]
+ else:
+ raise IndexError("Invalid key `" + key + "'")
+
+ def cacheAllExifTags(self):
+ """
+ Cache the EXIF tag values for faster subsequent access.
+
+ Read the values of all the EXIF tags in the image and cache them in an
+ internal dictionary so as to speed up subsequent accesses.
+ """
+ if not self.__exifCached:
+ for key in self.exifKeys():
+ self[key]
+ self.__exifCached = True
+
+ def cacheAllIptcTags(self):
+ """
+ Cache the IPTC tag values for faster subsequent access.
+
+ Read the values of all the IPTC tags in the image and cache them in an
+ internal dictionary so as to speed up subsequent accesses.
+ """
+ if not self.__iptcCached:
+ for key in self.iptcKeys():
+ self[key]
+ self.__iptcCached = True
+
+ def interpretedExifValue(self, key):
+ """
+ Get the interpreted value of an EXIF tag as presented by the exiv2 tool.
+
+ For EXIF tags, the exiv2 command-line tool is capable of displaying
+ user-friendly interpreted values, such as 'top, left' for the
+ 'Exif.Image.Orientation' tag when it has value '1'. This method always
+ returns a string containing this interpreted value for a given tag.
+ Warning: calling this method will not cache the value in the internal
+ dictionary.
+
+ Keyword arguments:
+ key -- the EXIF key of the requested metadata tag
+ """
+ # This method was added as a requirement tracked by bug #147534
+ return self.__getExifTagToString(key)
+
+ def copyMetadataTo(self, destImage):
+ # TODO: add optional parameters exif=True, iptc=True, xmp=True, so that
+ # one can choose to copy only part of the metadata.
+ """
+ Duplicate all the tags and the comment from this image to another one.
+
+ Read all the values of the EXIF and IPTC tags and the comment and write
+ them back to the new image.
+
+ Keyword arguments:
+ destImage -- the destination image to write the copied metadata back to
+ """
+ for key in self.exifKeys():
+ destImage[key] = self[key]
+ for key in self.iptcKeys():
+ destImage[key] = self[key]
+ destImage.setComment(self.getComment())