diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/exiv2wrapper.cpp | 205 | ||||
-rw-r--r-- | src/exiv2wrapper.hpp | 22 | ||||
-rw-r--r-- | src/exiv2wrapper_python.cpp | 8 | ||||
-rw-r--r-- | src/pyexiv2/exif.py | 9 | ||||
-rw-r--r-- | src/pyexiv2/iptc.py | 9 | ||||
-rw-r--r-- | src/pyexiv2/metadata.py | 64 | ||||
-rw-r--r-- | src/pyexiv2/xmp.py | 9 |
7 files changed, 122 insertions, 204 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 |