aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/exiv2wrapper.cpp13
-rw-r--r--src/exiv2wrapper.hpp3
-rw-r--r--src/exiv2wrapper_python.cpp2
-rw-r--r--src/pyexiv2/metadata.py28
-rw-r--r--test/metadata.py37
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)
+