aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/exiv2wrapper.cpp205
-rw-r--r--src/exiv2wrapper.hpp22
-rw-r--r--src/exiv2wrapper_python.cpp8
-rw-r--r--src/pyexiv2/exif.py9
-rw-r--r--src/pyexiv2/iptc.py9
-rw-r--r--src/pyexiv2/metadata.py64
-rw-r--r--src/pyexiv2/xmp.py9
-rw-r--r--test/exif.py18
-rw-r--r--test/iptc.py18
-rw-r--r--test/metadata.py461
-rw-r--r--test/xmp.py18
11 files changed, 216 insertions, 625 deletions
diff --git a/src/exiv2wrapper.cpp b/src/exiv2wrapper.cpp
index cc2d455..d8ee8e5 100644
--- a/src/exiv2wrapper.cpp
+++ b/src/exiv2wrapper.cpp
@@ -41,58 +41,6 @@
namespace exiv2wrapper
{
-// Static helper function to set the values of an IptcData for a given key
-static void set_iptc_tag_values(const std::string& key,
- Exiv2::IptcData* metadata,
- const boost::python::list& values)
-{
- Exiv2::IptcKey iptcKey = Exiv2::IptcKey(key);
- unsigned int index = 0;
- unsigned int max = boost::python::len(values);
- Exiv2::IptcMetadata::iterator iterator = metadata->findKey(iptcKey);
- while (index < max)
- {
- std::string value = boost::python::extract<std::string>(values[index++]);
- if (iterator != metadata->end())
- {
- // Override an existing value
- iterator->setValue(value);
- // Jump to the next datum matching the key
- ++iterator;
- while ((iterator != metadata->end()) && (iterator->key() != key))
- {
- ++iterator;
- }
- }
- else
- {
- // Append a new value
- Exiv2::Iptcdatum datum(iptcKey);
- datum.setValue(value);
- int state = metadata->add(datum);
- if (state == 6)
- {
- throw Exiv2::Error(NON_REPEATABLE);
- }
- // Reset iterator that has been invalidated by appending a datum
- iterator = metadata->end();
- }
- }
- // Erase the remaining values if any
- while (iterator != metadata->end())
- {
- if (iterator->key() == key)
- {
- iterator = metadata->erase(iterator);
- }
- else
- {
- ++iterator;
- }
- }
-}
-
-
void Image::_instantiate_image()
{
// If an exception is thrown, it has to be done outside of the
@@ -281,13 +229,6 @@ const ExifTag Image::getExifTag(std::string key)
return ExifTag(key, &_exifData[key], &_exifData);
}
-void Image::setExifTagValue(std::string key, std::string value)
-{
- CHECK_METADATA_READ
-
- _exifData[key] = value;
-}
-
void Image::deleteExifTag(std::string key)
{
CHECK_METADATA_READ
@@ -335,13 +276,6 @@ const IptcTag Image::getIptcTag(std::string key)
return IptcTag(key, &_iptcData);
}
-void Image::setIptcTagValues(std::string key, boost::python::list values)
-{
- CHECK_METADATA_READ
-
- set_iptc_tag_values(key, &_iptcData, values);
-}
-
void Image::deleteIptcTag(std::string key)
{
CHECK_METADATA_READ
@@ -395,47 +329,6 @@ const XmpTag Image::getXmpTag(std::string key)
return XmpTag(key, &_xmpData[key]);
}
-void Image::setXmpTagTextValue(const std::string& key, const std::string& value)
-{
- CHECK_METADATA_READ
-
- _xmpData[key].setValue(value);
-}
-
-void Image::setXmpTagArrayValue(const std::string& key, const boost::python::list& values)
-{
- CHECK_METADATA_READ
-
- Exiv2::Xmpdatum& datum = _xmpData[key];
- // Reset the value
- datum.setValue(0);
-
- for(boost::python::stl_input_iterator<std::string> iterator(values);
- iterator != boost::python::stl_input_iterator<std::string>();
- ++iterator)
- {
- datum.setValue(*iterator);
- }
-}
-
-void Image::setXmpTagLangAltValue(const std::string& key, const boost::python::dict& values)
-{
- CHECK_METADATA_READ
-
- Exiv2::Xmpdatum& datum = _xmpData[key];
- // Reset the value
- datum.setValue(0);
-
- for(boost::python::stl_input_iterator<std::string> iterator(values);
- iterator != boost::python::stl_input_iterator<std::string>();
- ++iterator)
- {
- std::string key = *iterator;
- std::string value = boost::python::extract<std::string>(values.get(key));
- datum.setValue("lang=\"" + key + "\" " + value);
- }
-}
-
void Image::deleteXmpTag(std::string key)
{
CHECK_METADATA_READ
@@ -571,6 +464,15 @@ void ExifTag::setRawValue(const std::string& value)
_datum->setValue(value);
}
+void ExifTag::setParentImage(Image& image)
+{
+ _data = image.getExifData();
+ std::string value = _datum->toString();
+ delete _datum;
+ _datum = &(*_data)[_key.key()];
+ _datum->setValue(value);
+}
+
const std::string ExifTag::getKey()
{
return _key.key();
@@ -681,7 +583,58 @@ void IptcTag::setRawValues(const boost::python::list& values)
throw Exiv2::Error(NON_REPEATABLE);
}
- set_iptc_tag_values(_key.key(), _data, values);
+ unsigned int index = 0;
+ unsigned int max = boost::python::len(values);
+ Exiv2::IptcMetadata::iterator iterator = _data->findKey(_key);
+ while (index < max)
+ {
+ std::string value = boost::python::extract<std::string>(values[index++]);
+ if (iterator != _data->end())
+ {
+ // Override an existing value
+ iterator->setValue(value);
+ // Jump to the next datum matching the key
+ ++iterator;
+ while ((iterator != _data->end()) && (iterator->key() != _key.key()))
+ {
+ ++iterator;
+ }
+ }
+ else
+ {
+ // Append a new value
+ Exiv2::Iptcdatum datum(_key);
+ datum.setValue(value);
+ int state = _data->add(datum);
+ if (state == 6)
+ {
+ throw Exiv2::Error(NON_REPEATABLE);
+ }
+ // Reset iterator that has been invalidated by appending a datum
+ iterator = _data->end();
+ }
+ }
+ // Erase the remaining values if any
+ while (iterator != _data->end())
+ {
+ if (iterator->key() == _key.key())
+ {
+ iterator = _data->erase(iterator);
+ }
+ else
+ {
+ ++iterator;
+ }
+ }
+}
+
+void IptcTag::setParentImage(Image& image)
+{
+ const boost::python::list values = getRawValues();
+ delete _data;
+ _from_data = true;
+ _data = image.getIptcData();
+ setRawValues(values);
}
const std::string IptcTag::getKey()
@@ -820,6 +773,42 @@ void XmpTag::setLangAltValue(const boost::python::dict& values)
}
}
+void XmpTag::setParentImage(Image& image)
+{
+ switch (Exiv2::XmpProperties::propertyType(_key))
+ {
+ case Exiv2::xmpText:
+ {
+ const std::string value = getTextValue();
+ delete _datum;
+ _from_datum = true;
+ _datum = &(*image.getXmpData())[_key.key()];
+ setTextValue(value);
+ break;
+ }
+ case Exiv2::xmpAlt:
+ case Exiv2::xmpBag:
+ case Exiv2::xmpSeq:
+ {
+ const boost::python::list value = getArrayValue();
+ delete _datum;
+ _from_datum = true;
+ _datum = &(*image.getXmpData())[_key.key()];
+ setArrayValue(value);
+ break;
+ }
+ case Exiv2::langAlt:
+ {
+ const boost::python::dict value = getLangAltValue();
+ delete _datum;
+ _from_datum = true;
+ _datum = &(*image.getXmpData())[_key.key()];
+ setLangAltValue(value);
+ break;
+ }
+ }
+}
+
const std::string XmpTag::getKey()
{
return _key.key();
diff --git a/src/exiv2wrapper.hpp b/src/exiv2wrapper.hpp
index 0408e1a..d929f8b 100644
--- a/src/exiv2wrapper.hpp
+++ b/src/exiv2wrapper.hpp
@@ -36,6 +36,8 @@
namespace exiv2wrapper
{
+class Image;
+
class ExifTag
{
public:
@@ -45,6 +47,7 @@ public:
~ExifTag();
void setRawValue(const std::string& value);
+ void setParentImage(Image& image);
const std::string getKey();
const std::string getType();
@@ -78,6 +81,7 @@ public:
~IptcTag();
void setRawValues(const boost::python::list& values);
+ void setParentImage(Image& image);
const std::string getKey();
const std::string getType();
@@ -116,6 +120,7 @@ public:
void setTextValue(const std::string& value);
void setArrayValue(const boost::python::list& values);
void setLangAltValue(const boost::python::dict& values);
+ void setParentImage(Image& image);
const std::string getKey();
const std::string getExiv2Type();
@@ -186,10 +191,6 @@ public:
// Throw an exception if the tag is not set.
const ExifTag getExifTag(std::string key);
- // Set the EXIF tag's value.
- // If the tag was not previously set, it is created.
- void setExifTagValue(std::string key, std::string value);
-
// Delete the required EXIF tag.
// Throw an exception if the tag was not set.
void deleteExifTag(std::string key);
@@ -207,10 +208,6 @@ public:
// Throw an exception if the tag is not set.
const IptcTag getIptcTag(std::string key);
- // Set the IPTC tag's values. If the tag was not previously set, it is
- // created.
- void setIptcTagValues(std::string key, boost::python::list values);
-
// Delete (all the repetitions of) the required IPTC tag.
// Throw an exception if the tag was not set.
void deleteIptcTag(std::string key);
@@ -221,10 +218,6 @@ public:
// Throw an exception if the tag is not set.
const XmpTag getXmpTag(std::string key);
- void setXmpTagTextValue(const std::string& key, const std::string& value);
- void setXmpTagArrayValue(const std::string& key, const boost::python::list& values);
- void setXmpTagLangAltValue(const std::string& key, const boost::python::dict& values);
-
// Delete the required XMP tag.
// Throw an exception if the tag was not set.
void deleteXmpTag(std::string key);
@@ -238,6 +231,11 @@ public:
// Return the image data buffer.
std::string getDataBuffer() const;
+ // Accessors
+ Exiv2::ExifData* getExifData() { return &_exifData; };
+ Exiv2::IptcData* getIptcData() { return &_iptcData; };
+ Exiv2::XmpData* getXmpData() { return &_xmpData; };
+
private:
std::string _filename;
Exiv2::byte* _data;
diff --git a/src/exiv2wrapper_python.cpp b/src/exiv2wrapper_python.cpp
index 5a03132..faa9b81 100644
--- a/src/exiv2wrapper_python.cpp
+++ b/src/exiv2wrapper_python.cpp
@@ -48,6 +48,7 @@ BOOST_PYTHON_MODULE(libexiv2python)
class_<ExifTag>("_ExifTag", init<std::string>())
.def("_setRawValue", &ExifTag::setRawValue)
+ .def("_setParentImage", &ExifTag::setParentImage)
.def("_getKey", &ExifTag::getKey)
.def("_getType", &ExifTag::getType)
@@ -63,6 +64,7 @@ BOOST_PYTHON_MODULE(libexiv2python)
class_<IptcTag>("_IptcTag", init<std::string>())
.def("_setRawValues", &IptcTag::setRawValues)
+ .def("_setParentImage", &IptcTag::setParentImage)
.def("_getKey", &IptcTag::getKey)
.def("_getType", &IptcTag::getType)
@@ -81,6 +83,7 @@ BOOST_PYTHON_MODULE(libexiv2python)
.def("_setTextValue", &XmpTag::setTextValue)
.def("_setArrayValue", &XmpTag::setArrayValue)
.def("_setLangAltValue", &XmpTag::setLangAltValue)
+ .def("_setParentImage", &XmpTag::setParentImage)
.def("_getKey", &XmpTag::getKey)
.def("_getExiv2Type", &XmpTag::getExiv2Type)
@@ -130,19 +133,14 @@ BOOST_PYTHON_MODULE(libexiv2python)
.def("_exifKeys", &Image::exifKeys)
.def("_getExifTag", &Image::getExifTag)
- .def("_setExifTagValue", &Image::setExifTagValue)
.def("_deleteExifTag", &Image::deleteExifTag)
.def("_iptcKeys", &Image::iptcKeys)
.def("_getIptcTag", &Image::getIptcTag)
- .def("_setIptcTagValues", &Image::setIptcTagValues)
.def("_deleteIptcTag", &Image::deleteIptcTag)
.def("_xmpKeys", &Image::xmpKeys)
.def("_getXmpTag", &Image::getXmpTag)
- .def("_setXmpTagTextValue", &Image::setXmpTagTextValue)
- .def("_setXmpTagArrayValue", &Image::setXmpTagArrayValue)
- .def("_setXmpTagLangAltValue", &Image::setXmpTagLangAltValue)
.def("_deleteXmpTag", &Image::deleteXmpTag)
.def("_previews", &Image::previews)
diff --git a/src/pyexiv2/exif.py b/src/pyexiv2/exif.py
index 6964b4b..2c485e2 100644
--- a/src/pyexiv2/exif.py
+++ b/src/pyexiv2/exif.py
@@ -72,9 +72,6 @@ class ExifTag(ListenerInterface):
- Short, SShort: [list of] int
- Rational, SRational: [list of] :class:`pyexiv2.utils.Rational`
- Undefined: string
-
- :attribute metadata: the parent metadata if any, or None
- :type metadata: :class:`pyexiv2.metadata.ImageMetadata`
"""
# According to the EXIF specification, the only accepted format for an Ascii
@@ -100,13 +97,15 @@ class ExifTag(ListenerInterface):
self._tag = _tag
else:
self._tag = libexiv2python._ExifTag(key)
- self.metadata = None
self._raw_value = None
self._value = None
self._value_cookie = False
if value is not None:
self._set_value(value)
+ def _set_owner(self, metadata):
+ self._tag._setParentImage(metadata._image)
+
@staticmethod
def _from_existing_tag(_tag):
# Build a tag from an already existing libexiv2python._ExifTag.
@@ -159,8 +158,6 @@ class ExifTag(ListenerInterface):
def _set_raw_value(self, value):
self._tag._setRawValue(value)
- if self.metadata is not None:
- self.metadata._set_exif_tag_value(self.key, value)
self._raw_value = value
self._value_cookie = True
diff --git a/src/pyexiv2/iptc.py b/src/pyexiv2/iptc.py
index f92e883..ac1954d 100644
--- a/src/pyexiv2/iptc.py
+++ b/src/pyexiv2/iptc.py
@@ -72,9 +72,6 @@ class IptcTag(ListenerInterface):
- Date: :class:`datetime.date`
- Time: :class:`datetime.time`
- Undefined: string
-
- :attribute metadata: the parent metadata if any, or None
- :type metadata: :class:`pyexiv2.metadata.ImageMetadata`
"""
# strptime is not flexible enough to handle all valid Time formats, we use a
@@ -96,13 +93,15 @@ class IptcTag(ListenerInterface):
self._tag = _tag
else:
self._tag = libexiv2python._IptcTag(key)
- self.metadata = None
self._raw_values = None
self._values = None
self._values_cookie = False
if values is not None:
self._set_values(values)
+ def _set_owner(self, metadata):
+ self._tag._setParentImage(metadata._image)
+
@staticmethod
def _from_existing_tag(_tag):
# Build a tag from an already existing libexiv2python._IptcTag
@@ -168,8 +167,6 @@ class IptcTag(ListenerInterface):
if not isinstance(values, (list, tuple)):
raise TypeError('Expecting a list of values')
self._tag._setRawValues(values)
- if self.metadata is not None:
- self.metadata._set_iptc_tag_values(self.key, values)
self._raw_values = values
self._values_cookie = True
diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py
index 730e99f..5cbfeb5 100644
--- a/src/pyexiv2/metadata.py
+++ b/src/pyexiv2/metadata.py
@@ -138,7 +138,6 @@ class ImageMetadata(object):
except KeyError:
_tag = self._image._getExifTag(key)
tag = ExifTag._from_existing_tag(_tag)
- tag.metadata = self
self._tags['exif'][key] = tag
return tag
@@ -150,7 +149,6 @@ class ImageMetadata(object):
except KeyError:
_tag = self._image._getIptcTag(key)
tag = IptcTag._from_existing_tag(_tag)
- tag.metadata = self
self._tags['iptc'][key] = tag
return tag
@@ -162,7 +160,6 @@ class ImageMetadata(object):
except KeyError:
_tag = self._image._getXmpTag(key)
tag = XmpTag._from_existing_tag(_tag)
- tag.metadata = self
self._tags['xmp'][key] = tag
return tag
@@ -190,23 +187,10 @@ class ImageMetadata(object):
else:
# As a handy shortcut, accept direct value assignment.
tag = ExifTag(key, tag_or_value)
- self._image._setExifTagValue(tag.key, tag.raw_value)
+ tag._set_owner(self)
self._tags['exif'][tag.key] = tag
if tag.key not in self.exif_keys:
self._keys['exif'].append(tag.key)
- tag.metadata = self
-
- def _set_exif_tag_value(self, key, value):
- # Overwrite the tag value for an already existing tag.
- # The tag is already in cache.
- # Warning: this is not meant to be called directly as it doesn't update
- # the internal cache (which would leave the object in an inconsistent
- # state).
- if key not in self.exif_keys:
- raise KeyError('Cannot set the value of an inexistent tag')
- if type(value) is not str:
- raise TypeError('Expecting a string')
- self._image._setExifTagValue(key, value)
def _set_iptc_tag(self, key, tag_or_values):
# Set an IPTC tag. If the tag already exists, its values are
@@ -216,27 +200,10 @@ class ImageMetadata(object):
else:
# As a handy shortcut, accept direct value assignment.
tag = IptcTag(key, tag_or_values)
- self._image._setIptcTagValues(tag.key, tag.raw_values)
+ tag._set_owner(self)
self._tags['iptc'][tag.key] = tag
if tag.key not in self.iptc_keys:
self._keys['iptc'].append(tag.key)
- tag.metadata = self
-
- def _set_iptc_tag_values(self, key, values):
- # Overwrite the tag values for an already existing tag.
- # The tag is already in cache.
- # Warning: this is not meant to be called directly as it doesn't update
- # the internal cache (which would leave the object in an inconsistent
- # state).
- # FIXME: this is sub-optimal as it sets all the values regardless of how
- # many of them really changed. Need to implement the same method with an
- # index/range parameter (here and in the C++ wrapper).
- if key not in self.iptc_keys:
- raise KeyError('Cannot set the value of an inexistent tag')
- if type(values) is not list or not \
- reduce(lambda x, y: x and type(y) is str, values, True):
- raise TypeError('Expecting a list of strings')
- self._image._setIptcTagValues(key, values)
def _set_xmp_tag(self, key, tag_or_value):
# Set an XMP tag. If the tag already exists, its value is overwritten.
@@ -245,35 +212,10 @@ class ImageMetadata(object):
else:
# As a handy shortcut, accept direct value assignment.
tag = XmpTag(key, tag_or_value)
- type = tag._tag._getExiv2Type()
- if type == 'XmpText':
- self._image._setXmpTagTextValue(tag.key, tag.raw_value)
- elif type in ('XmpAlt', 'XmpBag', 'XmpSeq'):
- self._image._setXmpTagArrayValue(tag.key, tag.raw_value)
- elif type == 'LangAlt':
- self._image._setXmpTagLangAltValue(tag.key, tag.raw_value)
+ tag._set_owner(self)
self._tags['xmp'][tag.key] = tag
if tag.key not in self.xmp_keys:
self._keys['xmp'].append(tag.key)
- tag.metadata = self
-
- def _set_xmp_tag_value(self, key, value):
- # Overwrite the tag value for an already existing tag.
- # The tag is already in cache.
- # Warning: this is not meant to be called directly as it doesn't update
- # the internal cache (which would leave the object in an inconsistent
- # state).
- if key not in self.xmp_keys:
- raise KeyError('Cannot set the value of an inexistent tag')
- type = self._tags['xmp'][key]._tag._getExiv2Type()
- if type == 'XmpText' and isinstance(value, str):
- self._image._setXmpTagTextValue(key, value)
- elif type in ('XmpAlt', 'XmpBag', 'XmpSeq') and isinstance(value, (list, tuple)):
- self._image._setXmpTagArrayValue(key, value)
- elif type == 'LangAlt' and isinstance(value, dict):
- self._image._setXmpTagLangAltValue(key, value)
- else:
- raise TypeError('Expecting either a string, a list, a tuple or a dict')
def __setitem__(self, key, tag_or_value):
"""
diff --git a/src/pyexiv2/xmp.py b/src/pyexiv2/xmp.py
index 63fe0a4..2142c41 100644
--- a/src/pyexiv2/xmp.py
+++ b/src/pyexiv2/xmp.py
@@ -80,9 +80,6 @@ class XmpTag(object):
- Thumbnail: *[not implemented yet]*
- URI, URL: string
- XPath: *[not implemented yet]*
-
- :attribute metadata: the parent metadata if any, or None
- :type metadata: :class:`pyexiv2.metadata.ImageMetadata`
"""
# FIXME: should inherit from ListenerInterface and implement observation of
@@ -108,13 +105,15 @@ class XmpTag(object):
self._tag = _tag
else:
self._tag = libexiv2python._XmpTag(key)
- self.metadata = None
self._raw_value = None
self._value = None
self._value_cookie = False
if value is not None:
self._set_value(value)
+ def _set_owner(self, metadata):
+ self._tag._setParentImage(metadata._image)
+
@staticmethod
def _from_existing_tag(_tag):
# Build a tag from an already existing libexiv2python._XmpTag
@@ -174,8 +173,6 @@ class XmpTag(object):
raise ValueError('Empty LangAlt')
self._tag._setLangAltValue(value)
- if self.metadata is not None:
- self.metadata._set_xmp_tag_value(self.key, value)
self._raw_value = value
self._value_cookie = True
diff --git a/test/exif.py b/test/exif.py
index 1352bac..e1cf8b9 100644
--- a/test/exif.py
+++ b/test/exif.py
@@ -32,14 +32,6 @@ from pyexiv2.utils import Rational
import datetime
-class ImageMetadataMock(object):
-
- tags = {}
-
- def _set_exif_tag_value(self, key, value):
- self.tags[key] = value
-
-
class TestExifTag(unittest.TestCase):
def test_convert_to_python_ascii(self):
@@ -315,17 +307,9 @@ class TestExifTag(unittest.TestCase):
# Invalid values
self.failUnlessRaises(ExifValueError, tag._convert_to_string, 3)
- def test_set_value_no_metadata(self):
- tag = ExifTag('Exif.Thumbnail.Orientation', 1) # top, left
- old_value = tag.value
- tag.value = 2
- self.failIfEqual(tag.value, old_value)
-
- def test_set_value_with_metadata(self):
+ def test_set_value(self):
tag = ExifTag('Exif.Thumbnail.Orientation', 1) # top, left
- tag.metadata = ImageMetadataMock()
old_value = tag.value
tag.value = 2
self.failIfEqual(tag.value, old_value)
- self.assertEqual(tag.metadata.tags[tag.key], '2')
diff --git a/test/iptc.py b/test/iptc.py
index 160c599..3b151a5 100644
--- a/test/iptc.py
+++ b/test/iptc.py
@@ -32,14 +32,6 @@ from pyexiv2.utils import FixedOffset
import datetime
-class ImageMetadataMock(object):
-
- tags = {}
-
- def _set_iptc_tag_values(self, key, values):
- self.tags[key] = values
-
-
class TestIptcTag(unittest.TestCase):
def test_convert_to_python_short(self):
@@ -192,17 +184,9 @@ class TestIptcTag(unittest.TestCase):
tag = IptcTag('Iptc.Application2.City', ['Seattle'])
self.failUnlessRaises(TypeError, tag._set_values, 'Barcelona')
- def test_set_values_no_metadata(self):
- tag = IptcTag('Iptc.Application2.City', ['Seattle'])
- old_values = tag.values
- tag.values = ['Barcelona']
- self.failIfEqual(tag.values, old_values)
-
- def test_set_values_with_metadata(self):
+ def test_set_values(self):
tag = IptcTag('Iptc.Application2.City', ['Seattle'])
- tag.metadata = ImageMetadataMock()
old_values = tag.values
tag.values = ['Barcelona']
self.failIfEqual(tag.values, old_values)
- self.assertEqual(tag.metadata.tags[tag.key], ['Barcelona'])
diff --git a/test/metadata.py b/test/metadata.py
index 237a510..86935ce 100644
--- a/test/metadata.py
+++ b/test/metadata.py
@@ -24,8 +24,6 @@
#
# ******************************************************************************
-import unittest
-
from pyexiv2.metadata import ImageMetadata
from pyexiv2.exif import ExifTag
from pyexiv2.iptc import IptcTag
@@ -33,169 +31,38 @@ from pyexiv2.xmp import XmpTag
from pyexiv2.utils import FixedOffset, Rational
import datetime
+import os
+import tempfile
+import unittest
-class _TagMock(object):
-
- def __init__(self, key, type, value):
- self.key = key
- self.type = type
- self.value = value
-
- def _getKey(self):
- return self.key
-
- def _getType(self):
- return self.type
-
- def _getRawValue(self):
- return self.value
-
-
-class _ExifTagMock(_TagMock):
-
- def __init__(self, key, type, value, human_value=None):
- super(_ExifTagMock, self).__init__(key, type, value)
- self.human_value = human_value
-
- def _getHumanValue(self):
- return self.human_value
-
- def _setRawValue(self, value):
- pass
-
-
-class _IptcTagMock(_TagMock):
-
- def _getRawValues(self):
- return self.value
-
- def _setRawValues(self, values):
- pass
-
-
-class _XmpTagMock(_TagMock):
-
- def __init__(self, key, type, exiv2_type, value):
- super(_XmpTagMock, self).__init__(key, type, value)
- self.exiv2_type = exiv2_type
-
- def _getExiv2Type(self):
- return self.exiv2_type
-
- def _getTextValue(self):
- return self.value
-
- def _getArrayValue(self):
- return self.value
-
- def _setTextValue(self, value):
- pass
-
- def _setArrayValue(self, values):
- pass
-
-
-class ImageMock(object):
-
- def __init__(self, filename):
- self.filename = filename
- self.read = False
- self.written = False
- self.tags = {'exif': {}, 'iptc': {}, 'xmp': {}}
-
- def _readMetadata(self):
- self.read = True
-
- def _writeMetadata(self):
- self.written = True
-
- def _exifKeys(self):
- return self.tags['exif'].keys()
-
- def _getExifTag(self, key):
- return self.tags['exif'][key]
-
- def _setExifTagValue(self, key, value):
- self.tags['exif'][key] = value
-
- def _deleteExifTag(self, key):
- try:
- del self.tags['exif'][key]
- except KeyError:
- pass
-
- def _iptcKeys(self):
- return self.tags['iptc'].keys()
-
- def _getIptcTag(self, key):
- return self.tags['iptc'][key]
-
- def _setIptcTagValues(self, key, values):
- self.tags['iptc'][key] = values
-
- def _deleteIptcTag(self, key):
- try:
- del self.tags['iptc'][key]
- except KeyError:
- pass
-
- def _xmpKeys(self):
- return self.tags['xmp'].keys()
-
- def _getXmpTag(self, key):
- return self.tags['xmp'][key]
-
- def _setXmpTagTextValue(self, key, value):
- self.tags['xmp'][key] = value
-
- def _setXmpTagArrayValue(self, key, value):
- self.tags['xmp'][key] = value
-
- def _setXmpTagLangAltValue(self, key, value):
- self.tags['xmp'][key] = value
-
- def _deleteXmpTag(self, key):
- try:
- del self.tags['xmp'][key]
- except KeyError:
- pass
-
- def _copyMetadata(self, other, exif=True, iptc=True, xmp=True):
- if exif:
- other.tags['exif'] = self.tags['exif']
- if iptc:
- other.tags['iptc'] = self.tags['iptc']
- if xmp:
- other.tags['xmp'] = self.tags['xmp']
+EMPTY_PNG_DATA = \
+ '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08' \
+ '\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc```\x00\x00\x00\x04' \
+ '\x00\x01\xf6\x178U\x00\x00\x00\x00IEND\xaeB`\x82'
class TestImageMetadata(unittest.TestCase):
def setUp(self):
- self.metadata = ImageMetadata('nofile')
- self.metadata._instantiate_image = lambda filename: ImageMock(filename)
-
- def _set_exif_tags(self):
- tags = {}
- tags['Exif.Image.Make'] = _ExifTagMock('Exif.Image.Make', 'Ascii', 'EASTMAN KODAK COMPANY')
- tags['Exif.Image.DateTime'] = _ExifTagMock('Exif.Image.DateTime', 'Ascii', '2009:02:09 13:33:20')
- tags['Exif.Photo.ExifVersion'] = _ExifTagMock('Exif.Photo.ExifVersion', 'Undefined', '48 50 50 49 ')
- self.metadata._image.tags['exif'] = tags
-
- def _set_iptc_tags(self):
- tags = {}
- tags['Iptc.Application2.Caption'] = _IptcTagMock('Iptc.Application2.Caption', 'String', ['blabla'])
- tags['Iptc.Application2.DateCreated'] = _IptcTagMock('Iptc.Application2.DateCreated', 'Date', ['2004-07-13'])
- self.metadata._image.tags['iptc'] = tags
-
- def _set_xmp_tags(self):
- tags = {}
- tags['Xmp.dc.format'] = _XmpTagMock('Xmp.dc.format', 'MIMEType', 'XmpText', 'image/jpeg')
- tags['Xmp.dc.subject'] = _XmpTagMock('Xmp.dc.subject', 'bag Text', 'XmpBag', 'image, test, pyexiv2')
- tags['Xmp.xmp.CreateDate'] = _XmpTagMock('Xmp.xmp.CreateDate', 'Date', 'XmpText', '2005-09-07T15:07:40-07:00')
- tags['Xmp.xmpMM.DocumentID'] = _XmpTagMock('Xmp.xmpMM.DocumentID', 'URI', 'XmpText', 'uuid:9A3B7F52214211DAB6308A7391270C13')
- self.metadata._image.tags['xmp'] = tags
+ # Create an empty image file
+ fd, self.pathname = tempfile.mkstemp(suffix='.png')
+ os.write(fd, EMPTY_PNG_DATA)
+ os.close(fd)
+ # Write some metadata
+ m = ImageMetadata(self.pathname)
+ m.read()
+ m['Exif.Image.Make'] = 'EASTMAN KODAK COMPANY'
+ m['Exif.Image.DateTime'] = datetime.datetime(2009, 2, 9, 13, 33, 20)
+ m['Iptc.Application2.Caption'] = ['blabla']
+ m['Iptc.Application2.DateCreated'] = [datetime.date(2004, 7, 13)]
+ m['Xmp.dc.format'] = ('image', 'jpeg')
+ m['Xmp.dc.subject'] = ['image', 'test', 'pyexiv2']
+ m.write()
+ self.metadata = ImageMetadata(self.pathname)
+
+ def tearDown(self):
+ os.remove(self.pathname)
######################
# Test general methods
@@ -205,39 +72,29 @@ class TestImageMetadata(unittest.TestCase):
self.assertEqual(self.metadata._image, None)
self.metadata.read()
self.failIfEqual(self.metadata._image, None)
- self.failUnless(self.metadata._image.read)
def test_read_nonexistent_file(self):
metadata = ImageMetadata('idontexist')
self.failUnlessRaises(IOError, metadata.read)
- def test_write(self):
- self.metadata.read()
- self.failIf(self.metadata._image.written)
- self.metadata.write()
- self.failUnless(self.metadata._image.written)
-
###########################
# Test EXIF-related methods
###########################
def test_exif_keys(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._keys['exif'], None)
keys = self.metadata.exif_keys
- self.assertEqual(len(keys), 3)
+ self.assertEqual(len(keys), 2)
self.assertEqual(self.metadata._keys['exif'], keys)
def test_get_exif_tag(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Get an existing tag
key = 'Exif.Image.Make'
tag = self.metadata._get_exif_tag(key)
self.assertEqual(type(tag), ExifTag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['exif'][key], tag)
# Try to get an nonexistent tag
key = 'Exif.Photo.Sharpness'
@@ -245,7 +102,6 @@ class TestImageMetadata(unittest.TestCase):
def test_set_exif_tag_wrong(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Try to set a tag with wrong type
tag = 'Not an exif tag'
@@ -254,120 +110,80 @@ class TestImageMetadata(unittest.TestCase):
def test_set_exif_tag_create(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Create a new tag
tag = ExifTag('Exif.Thumbnail.Orientation', 1)
- self.assertEqual(tag.metadata, None)
self.assert_(tag.key not in self.metadata.exif_keys)
self.metadata._set_exif_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assert_(tag.key in self.metadata.exif_keys)
self.assertEqual(self.metadata._tags['exif'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['exif'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['exif'][tag.key],
+ self.assert_(tag.key in self.metadata._image._exifKeys())
+ self.assertEqual(self.metadata._image._getExifTag(tag.key)._getRawValue(),
tag.raw_value)
def test_set_exif_tag_overwrite(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Overwrite an existing tag
tag = ExifTag('Exif.Image.DateTime', datetime.datetime(2009, 3, 20, 20, 32, 0))
- self.assertEqual(tag.metadata, None)
self.metadata._set_exif_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['exif'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['exif'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['exif'][tag.key],
+ self.assert_(tag.key in self.metadata._image._exifKeys())
+ self.assertEqual(self.metadata._image._getExifTag(tag.key)._getRawValue(),
tag.raw_value)
def test_set_exif_tag_overwrite_already_cached(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Overwrite an existing tag already cached
- key = 'Exif.Photo.ExifVersion'
+ key = 'Exif.Image.Make'
tag = self.metadata._get_exif_tag(key)
self.assertEqual(self.metadata._tags['exif'][key], tag)
- new_tag = ExifTag(key, '48 50 50 48 ', _tag=_ExifTagMock(key, 'Undefined', '48 50 50 48 ', '2.20'))
- self.assertEqual(new_tag.metadata, None)
+ new_tag = ExifTag(key, 'World Company')
self.metadata._set_exif_tag(key, new_tag)
- self.assertEqual(new_tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['exif'], {key: new_tag})
- self.assert_(self.metadata._image.tags['exif'].has_key(key))
- # Special case where the formatted value is used instead of the raw
- # value.
- self.assertEqual(self.metadata._image.tags['exif'][key], new_tag.raw_value)
+ self.assert_(key in self.metadata._image._exifKeys())
+ self.assertEqual(self.metadata._image._getExifTag(key)._getRawValue(),
+ new_tag.raw_value)
def test_set_exif_tag_direct_value_assignment(self):
self.metadata.read()
- self._set_exif_tags()
self.assertEqual(self.metadata._tags['exif'], {})
# Direct value assignment: pass a value instead of a fully-formed tag
key = 'Exif.Thumbnail.Orientation'
value = 1
self.metadata._set_exif_tag(key, value)
self.assert_(key in self.metadata.exif_keys)
- self.assert_(self.metadata._image.tags['exif'].has_key(key))
+ self.assert_(key in self.metadata._image._exifKeys())
tag = self.metadata._get_exif_tag(key)
self.assertEqual(tag.value, value)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['exif'], {key: tag})
- self.assertEqual(self.metadata._image.tags['exif'][key], tag.raw_value)
-
- def test_set_exif_tag_value_inexistent(self):
- self.metadata.read()
- self._set_exif_tags()
- key = 'Exif.Photo.ExposureTime'
- value = '1/500'
- self.failUnlessRaises(KeyError, self.metadata._set_exif_tag_value,
- key, value)
-
- def test_set_exif_tag_value_wrong_type(self):
- self.metadata.read()
- self._set_exif_tags()
- key = 'Exif.Image.DateTime'
- value = datetime.datetime(2009, 3, 24, 9, 37, 36)
- self.failUnlessRaises(TypeError, self.metadata._set_exif_tag_value,
- key, value)
-
- def test_set_exif_tag_value(self):
- self.metadata.read()
- self._set_exif_tags()
- key = 'Exif.Image.DateTime'
- tag = self.metadata._get_exif_tag(key)
- value = '2009:03:24 09:37:36'
- self.failIfEqual(self.metadata._image.tags['exif'][key], value)
- self.metadata._set_exif_tag_value(key, value)
- self.assertEqual(self.metadata._image.tags['exif'][key], value)
+ self.assertEqual(self.metadata._image._getExifTag(key)._getRawValue(),
+ tag.raw_value)
def test_delete_exif_tag_inexistent(self):
self.metadata.read()
- self._set_exif_tags()
key = 'Exif.Image.Artist'
self.failUnlessRaises(KeyError, self.metadata._delete_exif_tag, key)
def test_delete_exif_tag_not_cached(self):
self.metadata.read()
- self._set_exif_tags()
key = 'Exif.Image.DateTime'
self.assertEqual(self.metadata._tags['exif'], {})
- self.assert_(self.metadata._image.tags['exif'].has_key(key))
+ self.assert_(key in self.metadata._image._exifKeys())
self.metadata._delete_exif_tag(key)
self.assertEqual(self.metadata._tags['exif'], {})
- self.failIf(self.metadata._image.tags['exif'].has_key(key))
+ self.failIf(key in self.metadata._image._exifKeys())
def test_delete_exif_tag_cached(self):
self.metadata.read()
- self._set_exif_tags()
key = 'Exif.Image.DateTime'
- self.assert_(self.metadata._image.tags['exif'].has_key(key))
+ self.assert_(key in self.metadata._image._exifKeys())
tag = self.metadata._get_exif_tag(key)
self.assertEqual(self.metadata._tags['exif'][key], tag)
self.metadata._delete_exif_tag(key)
self.assertEqual(self.metadata._tags['exif'], {})
- self.failIf(self.metadata._image.tags['exif'].has_key(key))
+ self.failIf(key in self.metadata._image._exifKeys())
###########################
# Test IPTC-related methods
@@ -375,7 +191,6 @@ class TestImageMetadata(unittest.TestCase):
def test_iptc_keys(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._keys['iptc'], None)
keys = self.metadata.iptc_keys
self.assertEqual(len(keys), 2)
@@ -383,13 +198,11 @@ class TestImageMetadata(unittest.TestCase):
def test_get_iptc_tag(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Get an existing tag
key = 'Iptc.Application2.DateCreated'
tag = self.metadata._get_iptc_tag(key)
self.assertEqual(type(tag), IptcTag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['iptc'][key], tag)
# Try to get an nonexistent tag
key = 'Iptc.Application2.Copyright'
@@ -397,7 +210,6 @@ class TestImageMetadata(unittest.TestCase):
def test_set_iptc_tag_wrong(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Try to set a tag with wrong type
tag = 'Not an iptc tag'
@@ -406,122 +218,80 @@ class TestImageMetadata(unittest.TestCase):
def test_set_iptc_tag_create(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Create a new tag
tag = IptcTag('Iptc.Application2.Writer', ['Nobody'])
- self.assertEqual(tag.metadata, None)
self.assert_(tag.key not in self.metadata.iptc_keys)
self.metadata._set_iptc_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assert_(tag.key in self.metadata.iptc_keys)
self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['iptc'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['iptc'][tag.key],
+ self.assert_(tag.key in self.metadata._image._iptcKeys())
+ self.assertEqual(self.metadata._image._getIptcTag(tag.key)._getRawValues(),
tag.raw_values)
def test_set_iptc_tag_overwrite(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Overwrite an existing tag
tag = IptcTag('Iptc.Application2.Caption', ['A picture.'])
- self.assertEqual(tag.metadata, None)
self.metadata._set_iptc_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['iptc'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['iptc'][tag.key],
+ self.assert_(tag.key in self.metadata._image._iptcKeys())
+ self.assertEqual(self.metadata._image._getIptcTag(tag.key)._getRawValues(),
tag.raw_values)
def test_set_iptc_tag_overwrite_already_cached(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Overwrite an existing tag already cached
key = 'Iptc.Application2.Caption'
tag = self.metadata._get_iptc_tag(key)
self.assertEqual(self.metadata._tags['iptc'][key], tag)
new_tag = IptcTag(key, ['A picture.'])
- self.assertEqual(new_tag.metadata, None)
self.metadata._set_iptc_tag(key, new_tag)
- self.assertEqual(new_tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['iptc'], {key: new_tag})
- self.assert_(self.metadata._image.tags['iptc'].has_key(key))
- self.assertEqual(self.metadata._image.tags['iptc'][key],
+ self.assert_(key in self.metadata._image._iptcKeys())
+ self.assertEqual(self.metadata._image._getIptcTag(key)._getRawValues(),
new_tag.raw_values)
def test_set_iptc_tag_direct_value_assignment(self):
self.metadata.read()
- self._set_iptc_tags()
self.assertEqual(self.metadata._tags['iptc'], {})
# Direct value assignment: pass a value instead of a fully-formed tag
key = 'Iptc.Application2.Writer'
values = ['Nobody']
self.metadata._set_iptc_tag(key, values)
self.assert_(key in self.metadata.iptc_keys)
- self.assert_(self.metadata._image.tags['iptc'].has_key(key))
+ self.assert_(key in self.metadata._image._iptcKeys())
tag = self.metadata._get_iptc_tag(key)
self.assertEqual(tag.values, values)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['iptc'], {key: tag})
- self.assertEqual(self.metadata._image.tags['iptc'][key], tag.raw_values)
-
- def test_set_iptc_tag_values_inexistent(self):
- self.metadata.read()
- self._set_iptc_tags()
- key = 'Iptc.Application2.Urgency'
- values = ['1']
- self.failUnlessRaises(KeyError, self.metadata._set_iptc_tag_values,
- key, values)
-
- def test_set_iptc_tag_values_wrong_type(self):
- self.metadata.read()
- self._set_iptc_tags()
- key = 'Iptc.Application2.DateCreated'
- value = '20090324'
- self.failUnlessRaises(TypeError, self.metadata._set_iptc_tag_values,
- key, value)
- values = [datetime.date(2009, 3, 24)]
- self.failUnlessRaises(TypeError, self.metadata._set_iptc_tag_values,
- key, values)
-
- def test_set_iptc_tag_values(self):
- self.metadata.read()
- self._set_iptc_tags()
- key = 'Iptc.Application2.DateCreated'
- tag = self.metadata._get_iptc_tag(key)
- values = ['2009-04-07']
- self.failIfEqual(self.metadata._image.tags['iptc'][key], values)
- self.metadata._set_iptc_tag_values(key, values)
- self.assertEqual(self.metadata._image.tags['iptc'][key], values)
+ self.assertEqual(self.metadata._image._getIptcTag(key)._getRawValues(),
+ tag.raw_values)
def test_delete_iptc_tag_inexistent(self):
self.metadata.read()
- self._set_iptc_tags()
key = 'Iptc.Application2.LocationCode'
self.failUnlessRaises(KeyError, self.metadata._delete_iptc_tag, key)
def test_delete_iptc_tag_not_cached(self):
self.metadata.read()
- self._set_iptc_tags()
key = 'Iptc.Application2.Caption'
self.assertEqual(self.metadata._tags['iptc'], {})
- self.assert_(self.metadata._image.tags['iptc'].has_key(key))
+ self.assert_(key in self.metadata._image._iptcKeys())
self.metadata._delete_iptc_tag(key)
self.assertEqual(self.metadata._tags['iptc'], {})
- self.failIf(self.metadata._image.tags['iptc'].has_key(key))
+ self.failIf(key in self.metadata._image._iptcKeys())
def test_delete_iptc_tag_cached(self):
self.metadata.read()
- self._set_iptc_tags()
key = 'Iptc.Application2.Caption'
- self.assert_(self.metadata._image.tags['iptc'].has_key(key))
+ self.assert_(key in self.metadata._image._iptcKeys())
tag = self.metadata._get_iptc_tag(key)
self.assertEqual(self.metadata._tags['iptc'][key], tag)
self.metadata._delete_iptc_tag(key)
self.assertEqual(self.metadata._tags['iptc'], {})
- self.failIf(self.metadata._image.tags['iptc'].has_key(key))
+ self.failIf(key in self.metadata._image._iptcKeys())
##########################
# Test XMP-related methods
@@ -529,21 +299,18 @@ class TestImageMetadata(unittest.TestCase):
def test_xmp_keys(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._keys['xmp'], None)
keys = self.metadata.xmp_keys
- self.assertEqual(len(keys), 4)
+ self.assertEqual(len(keys), 2)
self.assertEqual(self.metadata._keys['xmp'], keys)
def test_get_xmp_tag(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Get an existing tag
key = 'Xmp.dc.subject'
tag = self.metadata._get_xmp_tag(key)
self.assertEqual(type(tag), XmpTag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['xmp'][key], tag)
# Try to get an nonexistent tag
key = 'Xmp.xmp.Label'
@@ -551,7 +318,6 @@ class TestImageMetadata(unittest.TestCase):
def test_set_xmp_tag_wrong(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Try to set a tag with wrong type
tag = 'Not an xmp tag'
@@ -560,55 +326,45 @@ class TestImageMetadata(unittest.TestCase):
def test_set_xmp_tag_create(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Create a new tag
tag = XmpTag('Xmp.dc.title', {'x-default': 'This is not a title',
'fr-FR': "Ceci n'est pas un titre"})
- self.assertEqual(tag.metadata, None)
self.assert_(tag.key not in self.metadata.xmp_keys)
self.metadata._set_xmp_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assert_(tag.key in self.metadata.xmp_keys)
self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['xmp'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['xmp'][tag.key],
+ self.assert_(tag.key in self.metadata._image._xmpKeys())
+ self.assertEqual(self.metadata._image._getXmpTag(tag.key)._getLangAltValue(),
tag.raw_value)
def test_set_xmp_tag_overwrite(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Overwrite an existing tag
tag = XmpTag('Xmp.dc.format', ('image', 'png'))
- self.assertEqual(tag.metadata, None)
self.metadata._set_xmp_tag(tag.key, tag)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag})
- self.assert_(self.metadata._image.tags['xmp'].has_key(tag.key))
- self.assertEqual(self.metadata._image.tags['xmp'][tag.key],
+ self.assert_(tag.key in self.metadata._image._xmpKeys())
+ self.assertEqual(self.metadata._image._getXmpTag(tag.key)._getTextValue(),
tag.raw_value)
def test_set_xmp_tag_overwrite_already_cached(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Overwrite an existing tag already cached
- key = 'Xmp.xmp.CreateDate'
+ key = 'Xmp.dc.subject'
tag = self.metadata._get_xmp_tag(key)
self.assertEqual(self.metadata._tags['xmp'][key], tag)
- new_tag = XmpTag(key, datetime.datetime(2009, 4, 21, 20, 7, 0, tzinfo=FixedOffset('+', 1, 0)))
- self.assertEqual(new_tag.metadata, None)
+ new_tag = XmpTag(key, ['hello', 'world'])
self.metadata._set_xmp_tag(key, new_tag)
- self.assertEqual(new_tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['xmp'], {key: new_tag})
- self.assert_(self.metadata._image.tags['xmp'].has_key(key))
- self.assertEqual(self.metadata._image.tags['xmp'][key],
+ self.assert_(key in self.metadata._image._xmpKeys())
+ self.assertEqual(self.metadata._image._getXmpTag(key)._getArrayValue(),
new_tag.raw_value)
def test_set_xmp_tag_direct_value_assignment(self):
self.metadata.read()
- self._set_xmp_tags()
self.assertEqual(self.metadata._tags['xmp'], {})
# Direct value assignment: pass a value instead of a fully-formed tag
key = 'Xmp.dc.title'
@@ -616,66 +372,35 @@ class TestImageMetadata(unittest.TestCase):
'fr-FR': "Ceci n'est pas un titre"}
self.metadata._set_xmp_tag(key, value)
self.assert_(key in self.metadata.xmp_keys)
- self.assert_(self.metadata._image.tags['xmp'].has_key(key))
+ self.assert_(key in self.metadata._image._xmpKeys())
tag = self.metadata._get_xmp_tag(key)
self.assertEqual(tag.value, value)
- self.assertEqual(tag.metadata, self.metadata)
self.assertEqual(self.metadata._tags['xmp'], {key: tag})
- self.assertEqual(self.metadata._image.tags['xmp'][key], tag.raw_value)
-
- def test_set_xmp_tag_value_inexistent(self):
- self.metadata.read()
- self._set_xmp_tags()
- key = 'Xmp.xmp.Nickname'
- value = 'oSoMoN'
- self.failUnlessRaises(KeyError, self.metadata._set_xmp_tag_value,
- key, value)
-
- def test_set_xmp_tag_value_wrong_type(self):
- self.metadata.read()
- self._set_xmp_tags()
- key = 'Xmp.xmp.CreateDate'
- tag = self.metadata[key]
- value = datetime.datetime(2009, 4, 21, 20, 11, 0)
- self.failUnlessRaises(TypeError, self.metadata._set_xmp_tag_value,
- key, value)
-
- def test_set_xmp_tag_value(self):
- self.metadata.read()
- self._set_xmp_tags()
- key = 'Xmp.xmp.CreateDate'
- tag = self.metadata._get_xmp_tag(key)
- value = '2009-04-21T20:12:47+01:00'
- self.failIfEqual(self.metadata._image.tags['xmp'][key], value)
- self.metadata._set_xmp_tag_value(key, value)
- self.assertEqual(self.metadata._image.tags['xmp'][key], value)
+ self.assertEqual(self.metadata._image._getXmpTag(key)._getLangAltValue(), tag.raw_value)
def test_delete_xmp_tag_inexistent(self):
self.metadata.read()
- self._set_xmp_tags()
key = 'Xmp.xmp.CreatorTool'
self.failUnlessRaises(KeyError, self.metadata._delete_xmp_tag, key)
def test_delete_xmp_tag_not_cached(self):
self.metadata.read()
- self._set_xmp_tags()
key = 'Xmp.dc.subject'
self.assertEqual(self.metadata._tags['xmp'], {})
- self.assert_(self.metadata._image.tags['xmp'].has_key(key))
+ self.assert_(key in self.metadata._image._xmpKeys())
self.metadata._delete_xmp_tag(key)
self.assertEqual(self.metadata._tags['xmp'], {})
- self.failIf(self.metadata._image.tags['xmp'].has_key(key))
+ self.failIf(key in self.metadata._image._xmpKeys())
def test_delete_xmp_tag_cached(self):
self.metadata.read()
- self._set_xmp_tags()
key = 'Xmp.dc.subject'
- self.assert_(self.metadata._image.tags['xmp'].has_key(key))
+ self.assert_(key in self.metadata._image._xmpKeys())
tag = self.metadata._get_xmp_tag(key)
self.assertEqual(self.metadata._tags['xmp'][key], tag)
self.metadata._delete_xmp_tag(key)
self.assertEqual(self.metadata._tags['xmp'], {})
- self.failIf(self.metadata._image.tags['xmp'].has_key(key))
+ self.failIf(key in self.metadata._image._xmpKeys())
###########################
# Test dictionary interface
@@ -683,17 +408,14 @@ class TestImageMetadata(unittest.TestCase):
def test_getitem(self):
self.metadata.read()
- self._set_exif_tags()
- self._set_iptc_tags()
- self._set_xmp_tags()
# Get existing tags
- key = 'Exif.Photo.ExifVersion'
+ key = 'Exif.Image.DateTime'
tag = self.metadata[key]
self.assertEqual(type(tag), ExifTag)
key = 'Iptc.Application2.Caption'
tag = self.metadata[key]
self.assertEqual(type(tag), IptcTag)
- key = 'Xmp.xmp.CreateDate'
+ key = 'Xmp.dc.format'
tag = self.metadata[key]
self.assertEqual(type(tag), XmpTag)
# Try to get nonexistent tags
@@ -704,9 +426,6 @@ class TestImageMetadata(unittest.TestCase):
def test_setitem(self):
self.metadata.read()
- self._set_exif_tags()
- self._set_iptc_tags()
- self._set_xmp_tags()
# Set new tags
key = 'Exif.Photo.ExposureBiasValue'
tag = ExifTag(key, Rational(0, 3))
@@ -742,17 +461,14 @@ class TestImageMetadata(unittest.TestCase):
def test_delitem(self):
self.metadata.read()
- self._set_exif_tags()
- self._set_iptc_tags()
- self._set_xmp_tags()
# Delete existing tags
- key = 'Exif.Photo.ExifVersion'
+ key = 'Exif.Image.Make'
del self.metadata[key]
self.failIf(key in self.metadata._tags['exif'])
key = 'Iptc.Application2.Caption'
del self.metadata[key]
self.failIf(key in self.metadata._tags['iptc'])
- key = 'Xmp.xmp.CreateDate'
+ key = 'Xmp.dc.subject'
del self.metadata[key]
self.failIf(key in self.metadata._tags['xmp'])
# Try to delete nonexistent tags
@@ -766,27 +482,32 @@ class TestImageMetadata(unittest.TestCase):
####################
def _set_up_other(self):
- self.other = ImageMetadata('nofile')
- self.other._instantiate_image = lambda filename: ImageMock(filename)
+ self.other = ImageMetadata.from_buffer(EMPTY_PNG_DATA)
def test_copy_metadata(self):
self.metadata.read()
- self._set_exif_tags()
- self._set_iptc_tags()
- self._set_xmp_tags()
self._set_up_other()
self.other.read()
- self.failUnlessEqual(self.other.exif_keys, [])
- self.failUnlessEqual(self.other.iptc_keys, [])
- self.failUnlessEqual(self.other.xmp_keys, [])
+ families = ('exif', 'iptc', 'xmp')
+
+ for family in families:
+ self.failUnlessEqual(getattr(self.other, '%s_keys' % family), [])
+
self.metadata.copy(self.other)
- # The actual test is here, the rest of the functionality being mocked...
for family in ('exif', 'iptc', 'xmp'):
self.failUnlessEqual(self.other._keys[family], None)
self.failUnlessEqual(self.other._tags[family], {})
+ keys = getattr(self.metadata, '%s_keys' % family)
+ self.failUnlessEqual(getattr(self.other._image, '_%sKeys' % family)(), keys)
+ self.failUnlessEqual(getattr(self.other, '%s_keys' % family), keys)
+
+ for key in self.metadata.exif_keys:
+ self.failUnlessEqual(self.metadata[key].value, self.other[key].value)
+
+ for key in self.metadata.iptc_keys:
+ self.failUnlessEqual(self.metadata[key].values, self.other[key].values)
- self.failUnlessEqual(self.other.exif_keys, self.metadata.exif_keys)
- self.failUnlessEqual(self.other.iptc_keys, self.metadata.iptc_keys)
- self.failUnlessEqual(self.other.xmp_keys, self.metadata.xmp_keys)
+ for key in self.metadata.xmp_keys:
+ self.failUnlessEqual(self.metadata[key].value, self.other[key].value)
diff --git a/test/xmp.py b/test/xmp.py
index a4a5832..10e03a8 100644
--- a/test/xmp.py
+++ b/test/xmp.py
@@ -32,14 +32,6 @@ from pyexiv2.utils import FixedOffset
import datetime
-class ImageMetadataMock(object):
-
- tags = {}
-
- def _set_xmp_tag_value(self, key, value):
- self.tags[key] = value
-
-
class TestXmpTag(unittest.TestCase):
def test_convert_to_python_bag(self):
@@ -292,19 +284,11 @@ class TestXmpTag(unittest.TestCase):
# TODO: other types
- def test_set_value_no_metadata(self):
- tag = XmpTag('Xmp.xmp.ModifyDate', datetime.datetime(2005, 9, 7, 15, 9, 51, tzinfo=FixedOffset('-', 7, 0)))
- old_value = tag.value
- tag.value = datetime.datetime(2009, 4, 22, 8, 30, 27, tzinfo=FixedOffset())
- self.failIfEqual(tag.value, old_value)
-
- def test_set_value_with_metadata(self):
+ def test_set_value(self):
tag = XmpTag('Xmp.xmp.ModifyDate', datetime.datetime(2005, 9, 7, 15, 9, 51, tzinfo=FixedOffset('-', 7, 0)))
- tag.metadata = ImageMetadataMock()
old_value = tag.value
tag.value = datetime.datetime(2009, 4, 22, 8, 30, 27, tzinfo=FixedOffset())
self.failIfEqual(tag.value, old_value)
- self.assertEqual(tag.metadata.tags[tag.key], '2009-04-22T08:30:27Z')
def test_set_value_empty(self):
tag = XmpTag('Xmp.dc.creator')