diff options
-rw-r--r-- | src/exiv2wrapper.cpp | 13 | ||||
-rw-r--r-- | src/exiv2wrapper.hpp | 3 | ||||
-rw-r--r-- | src/exiv2wrapper_python.cpp | 2 | ||||
-rw-r--r-- | src/pyexiv2/metadata.py | 28 | ||||
-rw-r--r-- | test/metadata.py | 37 |
5 files changed, 83 insertions, 0 deletions
diff --git a/src/exiv2wrapper.cpp b/src/exiv2wrapper.cpp index b6c85cf..ab52892 100644 --- a/src/exiv2wrapper.cpp +++ b/src/exiv2wrapper.cpp @@ -404,6 +404,19 @@ boost::python::list Image::previews() return previews; } +void Image::copyMetadata(Image& other, bool exif, bool iptc, bool xmp) const +{ + CHECK_METADATA_READ + if (!other._dataRead) throw Exiv2::Error(METADATA_NOT_READ); + + if (exif) + other._exifData = _exifData; + if (iptc) + other._iptcData = _iptcData; + if (xmp) + other._xmpData = _xmpData; +} + ExifTag::ExifTag(const std::string& key, Exiv2::Exifdatum* datum, Exiv2::ExifData* data): _key(key) { diff --git a/src/exiv2wrapper.hpp b/src/exiv2wrapper.hpp index f6c305a..f87d032 100644 --- a/src/exiv2wrapper.hpp +++ b/src/exiv2wrapper.hpp @@ -221,6 +221,9 @@ public: // Read access to the thumbnail embedded in the image. boost::python::list previews(); + // Copy the metadata to another image. + void copyMetadata(Image& other, bool exif=true, bool iptc=true, bool xmp=true) const; + private: std::string _filename; Exiv2::Image::AutoPtr _image; diff --git a/src/exiv2wrapper_python.cpp b/src/exiv2wrapper_python.cpp index 7398194..a73eedf 100644 --- a/src/exiv2wrapper_python.cpp +++ b/src/exiv2wrapper_python.cpp @@ -132,6 +132,8 @@ BOOST_PYTHON_MODULE(libexiv2python) .def("deleteXmpTag", &Image::deleteXmpTag) .def("previews", &Image::previews) + + .def("copyMetadata", &Image::copyMetadata) ; } diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py index 7396aff..92a3f80 100644 --- a/src/pyexiv2/metadata.py +++ b/src/pyexiv2/metadata.py @@ -315,3 +315,31 @@ class ImageMetadata(object): def previews(self): return self._image.previews() + def copy(self, other, exif=True, iptc=True, xmp=True): + """ + Copy the metadata to another image. + The metadata in the destination is overridden. In particular, if the + destination contains e.g. EXIF data and the source doesn't, it will be + erased in the destination, unless explicitely omitted. + + @param other: the destination metadata to copy to + @type other: L{pyexiv2.ImageMetadata} + @param exif: whether to copy the EXIF metadata (C{True} by default) + @type exif: C{bool} + @param iptc: whether to copy the IPTC metadata (C{True} by default) + @type iptc: C{bool} + @param xmp: whether to copy the XMP metadata (C{True} by default) + @type xmp: C{bool} + """ + self._image.copyMetadata(other._image, exif, iptc, xmp) + # Empty the cache where needed + if exif: + other._keys['exif'] = None + other._tags['exif'] = {} + if iptc: + other._keys['iptc'] = None + other._tags['iptc'] = {} + if xmp: + other._keys['xmp'] = None + other._tags['xmp'] = {} + diff --git a/test/metadata.py b/test/metadata.py index 235fafa..ac0eae9 100644 --- a/test/metadata.py +++ b/test/metadata.py @@ -161,6 +161,14 @@ class ImageMock(object): 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'] + class TestImageMetadata(unittest.TestCase): @@ -694,3 +702,32 @@ class TestImageMetadata(unittest.TestCase): for key in keys: self.failUnlessRaises(KeyError, self.metadata.__delitem__, key) + #################### + # Test metadata copy + #################### + + def _set_up_other(self): + self.other = ImageMetadata('nofile') + self.other._instantiate_image = lambda filename: ImageMock(filename) + + 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, []) + 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], {}) + + 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) + |