aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOlivier Tilloy <olivier@tilloy.net>2010-11-23 20:49:04 +0100
committerOlivier Tilloy <olivier@tilloy.net>2010-11-23 20:49:04 +0100
commit2517ce2efcdea061e4a267f7ede70b15cee2a0f3 (patch)
treecd41b643afdae8e3e669cb7e23a14fff88ddec59 /src
parentcc86aa2c24b3c53444c108b82295aae2085904db (diff)
parent17689217344b7875fd2e701035e814e172af9630 (diff)
downloadpyexiv2-2517ce2efcdea061e4a267f7ede70b15cee2a0f3.tar.gz
Read/write access to the EXIF thumbnail.
Diffstat (limited to 'src')
-rw-r--r--src/exiv2wrapper.cpp65
-rw-r--r--src/exiv2wrapper.hpp11
-rw-r--r--src/exiv2wrapper_python.cpp8
-rw-r--r--src/pyexiv2/__init__.py2
-rw-r--r--src/pyexiv2/exif.py75
-rw-r--r--src/pyexiv2/metadata.py10
6 files changed, 169 insertions, 2 deletions
diff --git a/src/exiv2wrapper.cpp b/src/exiv2wrapper.cpp
index a5ae4aa..0d80e05 100644
--- a/src/exiv2wrapper.cpp
+++ b/src/exiv2wrapper.cpp
@@ -44,6 +44,8 @@ namespace exiv2wrapper
void Image::_instantiate_image()
{
+ _exifThumbnail = 0;
+
// If an exception is thrown, it has to be done outside of the
// Py_{BEGIN,END}_ALLOW_THREADS block.
Exiv2::Error error(0);
@@ -117,6 +119,10 @@ Image::~Image()
{
delete[] _data;
}
+ if (_exifThumbnail != 0)
+ {
+ delete _exifThumbnail;
+ }
}
void Image::readMetadata()
@@ -444,6 +450,65 @@ std::string Image::getDataBuffer() const
return buffer;
}
+Exiv2::ExifThumb* Image::_getExifThumbnail()
+{
+ CHECK_METADATA_READ
+ if (_exifThumbnail == 0)
+ {
+ _exifThumbnail = new Exiv2::ExifThumb(*_exifData);
+ }
+ return _exifThumbnail;
+}
+
+const std::string Image::getExifThumbnailMimeType()
+{
+ return std::string(_getExifThumbnail()->mimeType());
+}
+
+const std::string Image::getExifThumbnailExtension()
+{
+ return std::string(_getExifThumbnail()->extension());
+}
+
+void Image::writeExifThumbnailToFile(const std::string& path)
+{
+ _getExifThumbnail()->writeFile(path);
+}
+
+const std::string Image::getExifThumbnailData()
+{
+ Exiv2::DataBuf buffer = _getExifThumbnail()->copy();
+ // Copy the data buffer in a string. Since the data buffer can contain null
+ // characters ('\x00'), the string cannot be simply constructed like that:
+ // data = std::string((char*) buffer.pData_);
+ // because it would be truncated after the first occurence of a null
+ // character. Therefore, it has to be copied character by character.
+ // First allocate the memory for the whole string...
+ std::string data = std::string(buffer.size_, ' ');
+ // ... then fill it with the raw data.
+ for(unsigned int i = 0; i < buffer.size_; ++i)
+ {
+ data[i] = buffer.pData_[i];
+ }
+ return data;
+}
+
+void Image::eraseExifThumbnail()
+{
+ _getExifThumbnail()->erase();
+}
+
+void Image::setExifThumbnailFromFile(const std::string& path)
+{
+ _getExifThumbnail()->setJpegThumbnail(path);
+}
+
+void Image::setExifThumbnailFromData(const std::string& data)
+{
+ const Exiv2::byte* buffer = (const Exiv2::byte*) data.c_str();
+ _getExifThumbnail()->setJpegThumbnail(buffer, data.size());
+}
+
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 5abf07d..5058d56 100644
--- a/src/exiv2wrapper.hpp
+++ b/src/exiv2wrapper.hpp
@@ -230,6 +230,15 @@ public:
// Read access to the thumbnail embedded in the image.
boost::python::list previews();
+ // Manipulate the JPEG/TIFF thumbnail embedded in the EXIF data.
+ const std::string getExifThumbnailMimeType();
+ const std::string getExifThumbnailExtension();
+ void writeExifThumbnailToFile(const std::string& path);
+ const std::string getExifThumbnailData();
+ void eraseExifThumbnail();
+ void setExifThumbnailFromFile(const std::string& path);
+ void setExifThumbnailFromData(const std::string& data);
+
// Copy the metadata to another image.
void copyMetadata(Image& other, bool exif=true, bool iptc=true, bool xmp=true) const;
@@ -249,6 +258,8 @@ private:
Exiv2::ExifData* _exifData;
Exiv2::IptcData* _iptcData;
Exiv2::XmpData* _xmpData;
+ Exiv2::ExifThumb* _exifThumbnail;
+ Exiv2::ExifThumb* _getExifThumbnail();
// true if the image's internal metadata has already been read,
// false otherwise
diff --git a/src/exiv2wrapper_python.cpp b/src/exiv2wrapper_python.cpp
index d6a1425..5b793f7 100644
--- a/src/exiv2wrapper_python.cpp
+++ b/src/exiv2wrapper_python.cpp
@@ -144,6 +144,14 @@ BOOST_PYTHON_MODULE(libexiv2python)
.def("_copyMetadata", &Image::copyMetadata)
.def("_getDataBuffer", &Image::getDataBuffer)
+
+ .def("_getExifThumbnailMimeType", &Image::getExifThumbnailMimeType)
+ .def("_getExifThumbnailExtension", &Image::getExifThumbnailExtension)
+ .def("_writeExifThumbnailToFile", &Image::writeExifThumbnailToFile)
+ .def("_getExifThumbnailData", &Image::getExifThumbnailData)
+ .def("_eraseExifThumbnail", &Image::eraseExifThumbnail)
+ .def("_setExifThumbnailFromFile", &Image::setExifThumbnailFromFile)
+ .def("_setExifThumbnailFromData", &Image::setExifThumbnailFromData)
;
}
diff --git a/src/pyexiv2/__init__.py b/src/pyexiv2/__init__.py
index 395aaff..54d7ebe 100644
--- a/src/pyexiv2/__init__.py
+++ b/src/pyexiv2/__init__.py
@@ -60,7 +60,7 @@ A typical use of this binding would be:
import libexiv2python
from pyexiv2.metadata import ImageMetadata
-from pyexiv2.exif import ExifValueError, ExifTag
+from pyexiv2.exif import ExifValueError, ExifTag, ExifThumbnail
from pyexiv2.iptc import IptcValueError, IptcTag
from pyexiv2.xmp import XmpValueError, XmpTag
from pyexiv2.preview import Preview
diff --git a/src/pyexiv2/exif.py b/src/pyexiv2/exif.py
index b265d1f..0acb775 100644
--- a/src/pyexiv2/exif.py
+++ b/src/pyexiv2/exif.py
@@ -414,3 +414,78 @@ class ExifTag(ListenerInterface):
right = self._raw_value
return '<%s = %s>' % (left, right)
+
+class ExifThumbnail(object):
+
+ """
+ A thumbnail image optionally embedded in the IFD1 segment of the EXIF data.
+
+ The image is either a TIFF or a JPEG image.
+ """
+
+ def __init__(self, _metadata):
+ self._metadata = _metadata
+
+ @property
+ def mime_type(self):
+ """The mime type of the preview image (e.g. ``image/jpeg``)."""
+ return self._metadata._image._getExifThumbnailMimeType()
+
+ @property
+ def extension(self):
+ """The file extension of the preview image with a leading dot
+ (e.g. ``.jpg``)."""
+ return self._metadata._image._getExifThumbnailExtension()
+
+ def write_to_file(self, path):
+ """
+ Write the thumbnail image to a file on disk.
+ The file extension will be automatically appended to the path.
+
+ :param path: path to write the thumbnail to (without an extension)
+ :type path: string
+ """
+ self._metadata._image._writeExifThumbnailToFile(path)
+
+ def _update_exif_tags_cache(self):
+ # Update the cache of EXIF tags
+ keys = self._metadata._image._exifKeys()
+ self._metadata._keys['exif'] = keys
+ cached = self._metadata._tags['exif'].keys()
+ for key in cached:
+ if key not in keys:
+ del self._metadata._tags['exif'][key]
+
+ def erase(self):
+ """
+ Delete the thumbnail from the EXIF data.
+ Removes all Exif.Thumbnail.*, i.e. Exif IFD1 tags.
+ """
+ self._metadata._image._eraseExifThumbnail()
+ self._update_exif_tags_cache()
+
+ def set_from_file(self, path):
+ """
+ Set the EXIF thumbnail to the JPEG image path.
+ This sets only the ``Compression``, ``JPEGInterchangeFormat`` and
+ ``JPEGInterchangeFormatLength`` tags, which is not all the thumbnail
+ EXIF information mandatory according to the EXIF standard
+ (but it is enough to work with the thumbnail).
+
+ :param path: path to a JPEG file to set the thumbnail to
+ :type path: string
+ """
+ self._metadata._image._setExifThumbnailFromFile(path)
+ self._update_exif_tags_cache()
+
+ def _get_data(self):
+ return self._metadata._image._getExifThumbnailData()
+
+ def _set_data(self, data):
+ self._metadata._image._setExifThumbnailFromData(data)
+ self._update_exif_tags_cache()
+
+ data = property(fget=_get_data, fset=_set_data,
+ doc='The raw thumbnail data. Setting it is restricted to ' +
+ 'a buffer in the JPEG format.')
+
diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py
index 5ec296d..a121286 100644
--- a/src/pyexiv2/metadata.py
+++ b/src/pyexiv2/metadata.py
@@ -36,7 +36,7 @@ from itertools import chain
import libexiv2python
-from pyexiv2.exif import ExifTag
+from pyexiv2.exif import ExifTag, ExifThumbnail
from pyexiv2.iptc import IptcTag
from pyexiv2.xmp import XmpTag
from pyexiv2.preview import Preview
@@ -64,6 +64,7 @@ class ImageMetadata(MutableMapping):
self._image = None
self._keys = {'exif': None, 'iptc': None, 'xmp': None}
self._tags = {'exif': {}, 'iptc': {}, 'xmp': {}}
+ self._exif_thumbnail = None
def _instantiate_image(self, filename):
# This method is meant to be overridden in unit tests to easily replace
@@ -392,3 +393,10 @@ class ImageMetadata(MutableMapping):
"""
return self._image._getDataBuffer()
+ @property
+ def exif_thumbnail(self):
+ """A thumbnail image optionally embedded in the EXIF data."""
+ if self._exif_thumbnail is None:
+ self._exif_thumbnail = ExifThumbnail(self)
+ return self._exif_thumbnail
+