aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Tilloy <olivier@tilloy.net>2009-11-04 10:05:25 +0100
committerOlivier Tilloy <olivier@tilloy.net>2009-11-04 10:05:25 +0100
commita40fa3744742d67b87bb5c04d0ad33a426d3f314 (patch)
tree5e166c4b58e4dda5229b4358c4169cc2d3abbd86
parent0104da13ef883e1e7476b5434fca96135ba5aaed (diff)
downloadpyexiv2-a40fa3744742d67b87bb5c04d0ad33a426d3f314.tar.gz
Preliminary investigation to implement the *Tag classes in C++.
-rw-r--r--src/exiv2wrapper.cpp141
-rw-r--r--src/exiv2wrapper.hpp67
-rw-r--r--src/exiv2wrapper_python.cpp31
-rw-r--r--src/pyexiv2/exif2.py307
4 files changed, 546 insertions, 0 deletions
diff --git a/src/exiv2wrapper.cpp b/src/exiv2wrapper.cpp
index ba31d99..96c840a 100644
--- a/src/exiv2wrapper.cpp
+++ b/src/exiv2wrapper.cpp
@@ -468,6 +468,147 @@ void Image::setThumbnailFromJpegFile(const std::string path)
}
*/
+
+ExifTag::ExifTag(const std::string& key): _key(key), _datum(_key)
+{
+ const uint16_t tag = _datum.tag();
+ const Exiv2::IfdId ifd = _datum.ifdId();
+ _type = Exiv2::TypeInfo::typeName(Exiv2::ExifTags::tagType(tag, ifd));
+ _name = Exiv2::ExifTags::tagName(tag, ifd);
+ _title = Exiv2::ExifTags::tagTitle(tag, ifd);
+ _label = Exiv2::ExifTags::tagLabel(tag, ifd);
+ _description = Exiv2::ExifTags::tagDesc(tag, ifd);
+ _sectionName = Exiv2::ExifTags::sectionName(tag, ifd);
+ _sectionDescription = Exiv2::ExifTags::sectionDesc(tag, ifd);
+ _value = _datum.toString();
+}
+
+void ExifTag::setValue(const std::string& value)
+{
+ _datum.setValue(value);
+ _value = _datum.toString();
+}
+
+const std::string ExifTag::getKey()
+{
+ return _key.key();
+}
+
+const std::string ExifTag::getType()
+{
+ return _type;
+}
+
+const std::string ExifTag::getName()
+{
+ return _name;
+}
+
+const std::string ExifTag::getTitle()
+{
+ return _title;
+}
+
+const std::string ExifTag::getLabel()
+{
+ return _label;
+}
+
+const std::string ExifTag::getDescription()
+{
+ return _description;
+}
+
+const std::string ExifTag::getSectionName()
+{
+ return _sectionName;
+}
+
+const std::string ExifTag::getSectionDescription()
+{
+ return _sectionDescription;
+}
+
+const std::string ExifTag::getValue()
+{
+ return _value;
+}
+
+
+IptcTag::IptcTag(const std::string& key): _key(key), _datum(_key)
+{
+ const uint16_t tag = _datum.tag();
+ const uint16_t record = _datum.record();
+ _type = Exiv2::TypeInfo::typeName(Exiv2::IptcDataSets::dataSetType(tag, record));
+ _name = Exiv2::IptcDataSets::dataSetName(tag, record);
+ _title = Exiv2::IptcDataSets::dataSetTitle(tag, record);
+ _description = Exiv2::IptcDataSets::dataSetDesc(tag, record);
+ // What is the photoshop name anyway? Where is it used?
+ _photoshopName = Exiv2::IptcDataSets::dataSetPsName(tag, record);
+ _repeatable = Exiv2::IptcDataSets::dataSetRepeatable(tag, record);
+ _recordName = Exiv2::IptcDataSets::recordName(record);
+ _recordDescription = Exiv2::IptcDataSets::recordDesc(record);
+ _value = _datum.toString();
+}
+
+void IptcTag::setValue(const std::string& value)
+{
+ _datum.setValue(value);
+ _value = _datum.toString();
+}
+
+const std::string IptcTag::getKey()
+{
+ return _key.key();
+}
+
+const std::string IptcTag::getType()
+{
+ return _type;
+}
+
+const std::string IptcTag::getName()
+{
+ return _name;
+}
+
+const std::string IptcTag::getTitle()
+{
+ return _title;
+}
+
+const std::string IptcTag::getDescription()
+{
+ return _description;
+}
+
+const std::string IptcTag::getPhotoshopName()
+{
+ return _photoshopName;
+}
+
+const bool IptcTag::isRepeatable()
+{
+ return _repeatable;
+}
+
+const std::string IptcTag::getRecordName()
+{
+ return _recordName;
+}
+
+const std::string IptcTag::getRecordDescription()
+{
+ return _recordDescription;
+}
+
+const std::string IptcTag::getValue()
+{
+ return _value;
+}
+
+
+
// TODO: update the errors code to reflect changes from src/error.cpp in libexiv2
void translateExiv2Error(Exiv2::Error const& error)
{
diff --git a/src/exiv2wrapper.hpp b/src/exiv2wrapper.hpp
index 9a728f8..8f593a8 100644
--- a/src/exiv2wrapper.hpp
+++ b/src/exiv2wrapper.hpp
@@ -155,6 +155,73 @@ private:
bool _dataRead;
};
+
+class ExifTag
+{
+public:
+ // Constructor
+ ExifTag(const std::string& key);
+
+ void setValue(const std::string& value);
+
+ const std::string getKey();
+ const std::string getType();
+ const std::string getName();
+ const std::string getTitle();
+ const std::string getLabel();
+ const std::string getDescription();
+ const std::string getSectionName();
+ const std::string getSectionDescription();
+ const std::string getValue();
+
+private:
+ Exiv2::ExifKey _key;
+ Exiv2::Exifdatum _datum;
+ std::string _type;
+ std::string _name;
+ std::string _title;
+ std::string _label;
+ std::string _description;
+ std::string _sectionName;
+ std::string _sectionDescription;
+ std::string _value;
+};
+
+
+class IptcTag
+{
+public:
+ // Constructor
+ IptcTag(const std::string& key);
+
+ void setValue(const std::string& value);
+
+ const std::string getKey();
+ const std::string getType();
+ const std::string getName();
+ const std::string getTitle();
+ const std::string getDescription();
+ const std::string getPhotoshopName();
+ const bool isRepeatable();
+ const std::string getRecordName();
+ const std::string getRecordDescription();
+ const std::string getValue();
+
+private:
+ Exiv2::IptcKey _key;
+ Exiv2::Iptcdatum _datum;
+ std::string _type;
+ std::string _name;
+ std::string _title;
+ std::string _description;
+ std::string _photoshopName;
+ bool _repeatable;
+ std::string _recordName;
+ std::string _recordDescription;
+ std::string _value;
+};
+
+
// Translate an Exiv2 generic exception into a Python exception
void translateExiv2Error(Exiv2::Error const& error);
diff --git a/src/exiv2wrapper_python.cpp b/src/exiv2wrapper_python.cpp
index c4eb9d8..54967a5 100644
--- a/src/exiv2wrapper_python.cpp
+++ b/src/exiv2wrapper_python.cpp
@@ -75,5 +75,36 @@ BOOST_PYTHON_MODULE(libexiv2python)
// .def("setComment", &Image::setComment)
// .def("clearComment", &Image::clearComment)
;
+
+ class_<ExifTag>("ExifTag", init<std::string>())
+
+ .def("_setValue", &ExifTag::setValue)
+
+ .def("_getKey", &ExifTag::getKey)
+ .def("_getType", &ExifTag::getType)
+ .def("_getName", &ExifTag::getName)
+ .def("_getTitle", &ExifTag::getTitle)
+ .def("_getLabel", &ExifTag::getLabel)
+ .def("_getDescription", &ExifTag::getDescription)
+ .def("_getSectionName", &ExifTag::getSectionName)
+ .def("_getSectionDescription", &ExifTag::getSectionDescription)
+ .def("_getValue", &ExifTag::getValue)
+ ;
+
+ class_<IptcTag>("IptcTag", init<std::string>())
+
+ .def("_setValue", &IptcTag::setValue)
+
+ .def("_getKey", &IptcTag::getKey)
+ .def("_getType", &IptcTag::getType)
+ .def("_getName", &IptcTag::getName)
+ .def("_getTitle", &IptcTag::getTitle)
+ .def("_getDescription", &IptcTag::getDescription)
+ .def("_getPhotoshopName", &IptcTag::getPhotoshopName)
+ .def("_isRepeatable", &IptcTag::isRepeatable)
+ .def("_getRecordName", &IptcTag::getRecordName)
+ .def("_getRecordDescription", &IptcTag::getRecordDescription)
+ .def("_getValue", &IptcTag::getValue)
+ ;
}
diff --git a/src/pyexiv2/exif2.py b/src/pyexiv2/exif2.py
new file mode 100644
index 0000000..d73c072
--- /dev/null
+++ b/src/pyexiv2/exif2.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+
+# ******************************************************************************
+#
+# Copyright (C) 2006-2009 Olivier Tilloy <olivier@tilloy.net>
+#
+# This file is part of the pyexiv2 distribution.
+#
+# pyexiv2 is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# pyexiv2 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with pyexiv2; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
+#
+# Author: Olivier Tilloy <olivier@tilloy.net>
+#
+# ******************************************************************************
+
+import libexiv2python
+
+from pyexiv2.utils import Rational
+
+import time
+import datetime
+
+
+class ExifValueError(ValueError):
+
+ """
+ Exception raised when failing to parse the value of an EXIF tag.
+
+ @ivar value: the value that fails to be parsed
+ @type value: C{str}
+ @ivar type: the EXIF type of the tag
+ @type type: C{str}
+ """
+
+ def __init__(self, value, type):
+ self.value = value
+ self.type = type
+
+ def __str__(self):
+ return 'Invalid value for EXIF type [%s]: [%s]' % \
+ (self.type, self.value)
+
+
+class ExifTag(libexiv2python.ExifTag):
+
+ """
+ DOCME
+ """
+
+ # According to the EXIF specification, the only accepted format for an Ascii
+ # value representing a datetime is '%Y:%m:%d %H:%M:%S', but it seems that
+ # others formats can be found in the wild.
+ _datetime_formats = ('%Y:%m:%d %H:%M:%S',
+ '%Y-%m-%d %H:%M:%S',
+ '%Y-%m-%dT%H:%M:%SZ')
+
+ _date_formats = ('%Y:%m:%d',)
+
+ def __init__(self, key, value=None):
+ """
+ DOCME
+ """
+ #libexiv2python.ExifTag.__init__(key)
+ super(ExifTag, self).__init__(key)
+ if value is not None:
+ self._set_value(value)
+ else:
+ self._raw_value = None
+ self._value = None
+
+ def _convert_to_string(self, value):
+ """
+ DOCME
+ """
+ # TODO: implement me
+ return str(value)
+
+ @property
+ def key(self):
+ return self._getKey()
+
+ @property
+ def type(self):
+ return self._getType()
+
+ @property
+ def name(self):
+ return self._getName()
+
+ @property
+ def title(self):
+ return self._getTitle()
+
+ @property
+ def label(self):
+ return self._getLabel()
+
+ @property
+ def description(self):
+ return self._getDescription()
+
+ @property
+ def section_name(self):
+ return self._getSectionName()
+
+ @property
+ def section_description(self):
+ return self._getSectionDescription()
+
+ def _get_value(self):
+ return self._value
+
+ def _set_value(self, new_value):
+ self._value = new_value
+ self._raw_value = self._convert_to_string(new_value)
+ self._setValue(self._raw_value)
+
+ value = property(fget=_get_value, fset=_set_value, doc=None)
+
+ def _convert_to_python(self, value):
+ """
+ Convert one raw value to its corresponding python type.
+
+ @param value: the raw value to be converted
+ @type value: C{str}
+
+ @return: the value converted to its corresponding python type
+ @rtype: depends on C{self.type} (DOCME)
+
+ @raise ExifValueError: if the conversion fails
+ """
+ if self.type == 'Ascii':
+ # The value may contain a Datetime
+ for format in self._datetime_formats:
+ try:
+ t = time.strptime(value, format)
+ except ValueError:
+ continue
+ else:
+ return datetime.datetime(*t[:6])
+ # Or a Date (e.g. Exif.GPSInfo.GPSDateStamp)
+ for format in self._date_formats:
+ try:
+ t = time.strptime(value, format)
+ except ValueError:
+ continue
+ else:
+ return datetime.date(*t[:3])
+ # Default to string.
+ # There is currently no charset conversion.
+ # TODO: guess the encoding and decode accordingly into unicode
+ # where relevant.
+ return value
+
+ elif self.type == 'Byte':
+ return value
+
+ elif self.type == 'Short':
+ try:
+ return int(value)
+ except ValueError:
+ raise ExifValueError(value, self.type)
+
+ elif self.type in ('Long', 'SLong'):
+ try:
+ return long(value)
+ except ValueError:
+ raise ExifValueError(value, self.type)
+
+ elif self.type in ('Rational', 'SRational'):
+ try:
+ r = Rational.from_string(value)
+ except (ValueError, ZeroDivisionError):
+ raise ExifValueError(value, self.type)
+ else:
+ if self.type == 'Rational' and r.numerator < 0:
+ raise ExifValueError(value, self.type)
+ return r
+
+ elif self.type == 'Undefined':
+ # There is currently no charset conversion.
+ # TODO: guess the encoding and decode accordingly into unicode
+ # where relevant.
+ return self.fvalue
+
+ raise ExifValueError(value, self.type)
+
+ def _convert_to_string(self, value):
+ """
+ Convert one value to its corresponding string representation, suitable
+ to pass to libexiv2.
+
+ @param value: the value to be converted
+ @type value: depends on C{self.type} (DOCME)
+
+ @return: the value converted to its corresponding string representation
+ @rtype: C{str}
+
+ @raise ExifValueError: if the conversion fails
+ """
+ if self.type == 'Ascii':
+ if type(value) is datetime.datetime:
+ return value.strftime(self._datetime_formats[0])
+ elif type(value) is datetime.date:
+ if self.key == 'Exif.GPSInfo.GPSDateStamp':
+ # Special case
+ return value.strftime(self._date_formats[0])
+ else:
+ return value.strftime('%s 00:00:00' % self._date_formats[0])
+ elif type(value) is unicode:
+ try:
+ return value.encode('utf-8')
+ except UnicodeEncodeError:
+ raise ExifValueError(value, self.type)
+ elif type(value) is str:
+ return value
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'Byte':
+ if type(value) is unicode:
+ try:
+ return value.encode('utf-8')
+ except UnicodeEncodeError:
+ raise ExifValueError(value, self.type)
+ elif type(value) is str:
+ return value
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'Short':
+ if type(value) is int and value >= 0:
+ return str(value)
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'Long':
+ if type(value) in (int, long) and value >= 0:
+ return str(value)
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'SLong':
+ if type(value) in (int, long):
+ return str(value)
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'Rational':
+ if type(value) is Rational and value.numerator >= 0:
+ return str(value)
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'SRational':
+ if type(value) is Rational:
+ return str(value)
+ else:
+ raise ExifValueError(value, self.type)
+
+ elif self.type == 'Undefined':
+ if type(value) is unicode:
+ try:
+ return value.encode('utf-8')
+ except UnicodeEncodeError:
+ raise ExifValueError(value, self.type)
+ elif type(value) is str:
+ return value
+ else:
+ raise ExifValueError(value, self.type)
+
+ raise ExifValueError(value, self.type)
+
+ def __str__(self):
+ """
+ Return a string representation of the value of the EXIF tag suitable to
+ pass to libexiv2 to set it.
+
+ @rtype: C{str}
+ """
+ return self._convert_to_string(self._value)
+
+ def __repr__(self):
+ """
+ Return a string representation of the EXIF tag for debugging purposes.
+
+ @rtype: C{str}
+ """
+ left = '%s [%s]' % (self.key, self.type)
+ if self.type == 'Undefined' and len(self._value) > 100:
+ right = '(Binary value suppressed)'
+ else:
+ #right = self.fvalue
+ right = str(self)
+ return '<%s = %s>' % (left, right)
+