diff options
author | Olivier Tilloy <olivier@tilloy.net> | 2008-04-19 13:59:35 +0200 |
---|---|---|
committer | Olivier Tilloy <olivier@tilloy.net> | 2008-04-19 13:59:35 +0200 |
commit | a140f37deeea8ba2c2b4b52a9fc5a456c3d4b86a (patch) | |
tree | 65cdde3a20480f7c70c734d83e900975e06065a6 /src/exiv2wrapper.cpp | |
parent | a860ef0e6774d276d8851341151931b627eb9146 (diff) | |
download | pyexiv2-a140f37deeea8ba2c2b4b52a9fc5a456c3d4b86a.tar.gz |
Refactor the structure of the exiv2 wrapper, retrieve more data for an EXIF tag.
Diffstat (limited to 'src/exiv2wrapper.cpp')
-rw-r--r-- | src/exiv2wrapper.cpp | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/exiv2wrapper.cpp b/src/exiv2wrapper.cpp new file mode 100644 index 0000000..5b24472 --- /dev/null +++ b/src/exiv2wrapper.cpp @@ -0,0 +1,483 @@ +// ***************************************************************************** +/* + * Copyright (C) 2006-2008 Olivier Tilloy <olivier@tilloy.net> + * + * This file is part of the pyexiv2 distribution. + * + * pyexiv2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * pyexiv2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pyexiv2; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: exiv2wrapper.cpp + Author(s): Olivier Tilloy <olivier@tilloy.net> + */ +// ***************************************************************************** + +#include "exiv2wrapper.hpp" + +// Custom error codes for Exiv2 exceptions +#define METADATA_NOT_READ 101 +#define NON_REPEATABLE 102 +#define KEY_NOT_FOUND 103 +#define THUMB_ACCESS 104 +#define NO_THUMBNAIL 105 + +namespace exiv2wrapper +{ + +// Base constructor +Image::Image(const std::string& filename) +{ + _filename = filename; + _image = Exiv2::ImageFactory::open(filename); + assert(_image.get() != 0); + _dataRead = false; +} + +// Copy constructor +Image::Image(const Image& image) +{ + _filename = image._filename; + _image = Exiv2::ImageFactory::open(_filename); + assert(_image.get() != 0); + _dataRead = false; +} + +void Image::readMetadata() +{ + _image->readMetadata(); + _exifData = _image->exifData(); + //_iptcData = _image->iptcData(); + _dataRead = true; +} + +/*void Image::writeMetadata() +{ + if(_dataRead) + { + _image->setExifData(_exifData); + _image->setIptcData(_iptcData); + _image->writeMetadata(); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +}*/ + +boost::python::list Image::exifKeys() +{ + boost::python::list keys; + if(_dataRead) + { + for(Exiv2::ExifMetadata::iterator i = _exifData.begin(); + i != _exifData.end(); + ++i) + { + keys.append(i->key()); + } + return keys; + } + else + { + throw Exiv2::Error(METADATA_NOT_READ); + } +} + +boost::python::tuple Image::getExifTag(std::string key) +{ + if(_dataRead) + { + Exiv2::ExifKey exifKey = Exiv2::ExifKey(key); + if(_exifData.findKey(exifKey) != _exifData.end()) + { + Exiv2::Exifdatum exifDatum = _exifData[key]; + std::string sTagName = exifDatum.tagName(); + std::string sTagLabel = exifDatum.tagLabel(); + std::string sTagDesc(Exiv2::ExifTags::tagDesc(exifKey.tag(), exifKey.ifdId())); + std::string sTagType = exifDatum.typeName(); + std::string sTagValue = exifDatum.toString(); + std::ostringstream sTagStringValueBuffer; + sTagStringValueBuffer << exifDatum; + std::string sTagStringValue = sTagStringValueBuffer.str(); + return boost::python::make_tuple(sTagName, sTagLabel, sTagDesc, sTagType, sTagValue, sTagStringValue); + } + else + { + throw Exiv2::Error(KEY_NOT_FOUND, key); + } + } + else + { + throw Exiv2::Error(METADATA_NOT_READ); + } +} + +/*std::string Image::getExifTagToString(std::string key) +{ + if(_dataRead) + { + Exiv2::ExifKey exifKey = Exiv2::ExifKey(key); + Exiv2::ExifMetadata::iterator i = _exifData.findKey(exifKey); + if(i != _exifData.end()) + { + Exiv2::Exifdatum exifDatum = _exifData[key]; + std::ostringstream buffer; + buffer << exifDatum; + return buffer.str(); + } + else + throw Exiv2::Error(KEY_NOT_FOUND, key); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::tuple Image::setExifTag(std::string key, std::string value) +{ + boost::python::tuple returnValue; + if(_dataRead) + { + Exiv2::ExifKey exifKey = Exiv2::ExifKey(key); + Exiv2::ExifMetadata::iterator i = _exifData.findKey(exifKey); + if(i != _exifData.end()) + { + Exiv2::Exifdatum exifDatum = _exifData[key]; + returnValue = boost::python::make_tuple(std::string(exifDatum.typeName()), exifDatum.toString()); + // First erase the existing tag: in some case (and + // I don't know why), the new value won't replace + // the old one if not previously erased... + _exifData.erase(i); + } + else + { + // The key was not found + returnValue = boost::python::make_tuple(std::string(""), std::string("")); + } + _exifData[key] = value; + return returnValue; + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::tuple Image::deleteExifTag(std::string key) +{ + boost::python::tuple returnValue; + if(_dataRead) + { + Exiv2::ExifKey exifKey = Exiv2::ExifKey(key); + Exiv2::ExifMetadata::iterator i = _exifData.findKey(exifKey); + if(i != _exifData.end()) + { + Exiv2::Exifdatum exifDatum = _exifData[key]; + returnValue = boost::python::make_tuple(std::string(exifDatum.typeName()), exifDatum.toString()); + _exifData.erase(i); + return returnValue; + } + else + throw Exiv2::Error(KEY_NOT_FOUND, key); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::list Image::iptcKeys() +{ + boost::python::list list; + if(_dataRead) + { + for(Exiv2::IptcMetadata::iterator i = _iptcData.begin() ; i != _iptcData.end() ; i++) + { + // The key is appended to the list if and only if it is not + // already present. + if (list.count(i->key()) == 0) + list.append(i->key()); + } + return list; + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::list Image::getIptcTag(std::string key) +{ + if(_dataRead) + { + boost::python::list valuesList; + unsigned int valueOccurences = 0; + Exiv2::IptcKey iptcKey = Exiv2::IptcKey(key); + for (Exiv2::IptcMetadata::iterator dataIterator = _iptcData.begin(); + dataIterator != _iptcData.end(); ++dataIterator) + { + if (dataIterator->key() == key) + { + valuesList.append(boost::python::make_tuple(std::string(dataIterator->typeName()), dataIterator->toString())); + ++valueOccurences; + } + } + if (valueOccurences > 0) + return valuesList; + else + throw Exiv2::Error(KEY_NOT_FOUND, key); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::tuple Image::setIptcTag(std::string key, std::string value, unsigned int index=0) +{ + if(_dataRead) + { + boost::python::tuple returnValue; + unsigned int indexCounter = index; + Exiv2::IptcKey iptcKey = Exiv2::IptcKey(key); + Exiv2::IptcMetadata::iterator dataIterator = _iptcData.findKey(iptcKey); + while ((indexCounter > 0) && (dataIterator != _iptcData.end())) + { + dataIterator = std::find_if(++dataIterator, _iptcData.end(), + Exiv2::FindMetadatumById::FindMetadatumById(iptcKey.tag(), iptcKey.record())); + --indexCounter; + } + if (dataIterator != _iptcData.end()) + { + // The tag at given index already exists, override it + returnValue = boost::python::make_tuple(std::string(dataIterator->typeName()), dataIterator->toString()); + dataIterator->setValue(value); + } + else + { + // Either index is greater than the index of the last repetition + // of the tag, or the tag does not exist yet. + // In both cases, it is created. + returnValue = boost::python::make_tuple(std::string(""), std::string("")); + Exiv2::Iptcdatum iptcDatum(iptcKey); + iptcDatum.setValue(value); + int state = _iptcData.add(iptcDatum); + if (state == 6) + throw Exiv2::Error(NON_REPEATABLE); + } + return returnValue; + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::tuple Image::deleteIptcTag(std::string key, unsigned int index=0) +{ + if(_dataRead) + { + boost::python::tuple returnValue; + unsigned int indexCounter = index; + Exiv2::IptcKey iptcKey = Exiv2::IptcKey(key); + Exiv2::IptcMetadata::iterator dataIterator = _iptcData.findKey(iptcKey); + while ((indexCounter > 0) && (dataIterator != _iptcData.end())) + { + dataIterator = std::find_if(++dataIterator, _iptcData.end(), + Exiv2::FindMetadatumById::FindMetadatumById(iptcKey.tag(), iptcKey.record())); + --indexCounter; + } + if (dataIterator != _iptcData.end()) + { + // The tag at given index already exists, delete it + returnValue = boost::python::make_tuple(std::string(dataIterator->typeName()), dataIterator->toString()); + _iptcData.erase(dataIterator); + return returnValue; + } + else + throw Exiv2::Error(KEY_NOT_FOUND, key); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +boost::python::tuple Image::tagDetails(std::string key) +{ + std::string keyFamily = key.substr(0, 4); + if (keyFamily == "Exif") + { + Exiv2::ExifKey exifKey = Exiv2::ExifKey(key); + std::string tagLabel = exifKey.tagLabel(); + std::string tagDesc = std::string(Exiv2::ExifTags::tagDesc(exifKey.tag(), exifKey.ifdId())); + return boost::python::make_tuple(tagLabel, tagDesc); + } + else if (keyFamily == "Iptc") + { + Exiv2::IptcKey iptcKey = Exiv2::IptcKey(key); + std::string tagLabel = iptcKey.tagLabel(); + std::string tagDesc = std::string(Exiv2::IptcDataSets::dataSetDesc(iptcKey.tag(), iptcKey.record())); + return boost::python::make_tuple(tagLabel, tagDesc); + } +} + +boost::python::tuple Image::getThumbnailData() +{ + if(_dataRead) + { + Exiv2::Thumbnail::AutoPtr thumbnail = _exifData.getThumbnail(); + if (thumbnail.get() != 0) + { + std::string format(_exifData.thumbnailFormat()); + // Copy the data buffer in a string. Since the data buffer can + // contain null char ('\x00'), the string cannot be simply + // constructed like that: + // std::string data((char*) dataBuffer.pData_); + // because it would be truncated after the first occurence of a + // null char. Therefore, it has to be copied char by char. + Exiv2::DataBuf dataBuffer = _exifData.copyThumbnail(); + char* charData = (char*) dataBuffer.pData_; + long dataLen = dataBuffer.size_; + // First allocate the memory for the whole string... + std::string data(dataLen, ' '); + // ... then fill it with the raw jpeg data. + for(long i = 0; i < dataLen; ++i) + { + data[i] = charData[i]; + } + return boost::python::make_tuple(format, data); + } + else + throw Exiv2::Error(THUMB_ACCESS); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +void Image::setThumbnailData(std::string data) +{ + if(_dataRead) + { + const Exiv2::byte* dataBuf = (const Exiv2::byte*) data.c_str(); + _exifData.setJpegThumbnail(dataBuf, data.size()); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +void Image::deleteThumbnail() +{ + if(_dataRead) + _exifData.eraseThumbnail(); + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +void Image::dumpThumbnailToFile(const std::string path) +{ + if(_dataRead) + { + int result = _exifData.writeThumbnail(path); + if (result == 8) + throw Exiv2::Error(NO_THUMBNAIL); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +} + +void Image::setThumbnailFromJpegFile(const std::string path) +{ + if(_dataRead) + { + _exifData.setJpegThumbnail(path); + } + else + throw Exiv2::Error(METADATA_NOT_READ); +}*/ + +void translateExiv2Error(Exiv2::Error const& error) +{ + // Use the Python 'C' API to set up an exception object + + // Building a C++ string first allows this code to compile with all + // versions of libexiv2 (< 0.13 and >= 0.13), because the way exceptions + // are handled in libexiv2 was changed in 0.13. + const std::string sMessage(error.what()); + const char* message = sMessage.c_str(); + + // The type of the Python exception depends on the error code + // Warning: this piece of code should be updated in case the error codes + // defined by Exiv2 (file 'src/error.cpp') are changed + switch (error.code()) + { + case -2: + case -1: + case 1: + case 2: + PyErr_SetString(PyExc_RuntimeError, message); + break; + case 3: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 17: + case 18: + case 20: + case 21: + case 23: + case 31: + case 32: + case 33: + case 36: + case 37: + PyErr_SetString(PyExc_IOError, message); + break; + case 4: + case 5: + case 6: + case 7: + PyErr_SetString(PyExc_IndexError, message); + break; + case 8: + case 22: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 34: + PyErr_SetString(PyExc_ValueError, message); + break; + case 16: + case 19: + case 35: + PyErr_SetString(PyExc_MemoryError, message); + break; + + // custom error codes + case METADATA_NOT_READ: + PyErr_SetString(PyExc_IOError, "Image metadata has not been read yet"); + break; + case NON_REPEATABLE: + PyErr_SetString(PyExc_KeyError, "Tag is not repeatable"); + break; + case KEY_NOT_FOUND: + PyErr_SetString(PyExc_KeyError, "Tag not set"); + break; + case THUMB_ACCESS: + PyErr_SetString(PyExc_IOError, "Cannot access image thumbnail"); + break; + case NO_THUMBNAIL: + PyErr_SetString(PyExc_IOError, "The EXIF data does not contain a thumbnail"); + break; + + default: + PyErr_SetString(PyExc_RuntimeError, message); + } +} + +} // End of namespace exiv2wrapper |