// ***************************************************************** -*- C++ -*- /* * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*! @file exiv2extractor.cc @brief Prototype libextractor plugin for Exif using exiv2 @version $Rev$ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 30-Jun-05, ahu: created */ #include #include #include #include #include #include "platform.h" #include "extractor.h" #include "exif.hpp" #include "image.hpp" #include "futils.hpp" #define WORKAROUND_905 1 #if WORKAROUND_905 #include #endif extern "C" { static struct EXTRACTOR_Keywords * addKeyword(EXTRACTOR_KeywordType type, char * keyword, struct EXTRACTOR_Keywords * next) { EXTRACTOR_KeywordList * result; if (keyword == NULL) return next; result = (EXTRACTOR_KeywordList*) malloc(sizeof(EXTRACTOR_KeywordList)); result->next = next; result->keyword = keyword; result->keywordType = type; return result; } } struct EXTRACTOR_Keywords * addExiv2Tag(const Exiv2::ExifData& exifData, const std::string& key, EXTRACTOR_KeywordType type, struct EXTRACTOR_Keywords * result) { const char * str; Exiv2::ExifKey ek(key); Exiv2::ExifData::const_iterator md = exifData.findKey(ek); if (md != exifData.end()) { std::string ccstr = Exiv2::toString(*md); str = ccstr.c_str(); while ( (strlen(str) > 0) && isspace(str[0])) str++; if (strlen(str) > 0) result = addKeyword(type, strdup(str), result); } return result; } extern "C" { #if WORKAROUND_905 static struct EXTRACTOR_Keywords * extract(const char * filename, unsigned char * data, size_t size, struct EXTRACTOR_Keywords * prev) #else struct EXTRACTOR_Keywords * libextractor_exiv2_extract(const char * filename, unsigned char * data, size_t size, struct EXTRACTOR_Keywords * prev) #endif { struct EXTRACTOR_Keywords * result = prev; try { Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(data, size); assert(image.get() != 0); image->readMetadata(); Exiv2::ExifData &exifData = image->exifData(); if (exifData.empty()) return result; // Camera make result = addExiv2Tag(exifData, "Exif.Image.Make", EXTRACTOR_CAMERA_MAKE, result); // Camera model result = addExiv2Tag(exifData, "Exif.Image.Model", EXTRACTOR_CAMERA_MODEL, result); // Camera model result = addExiv2Tag(exifData, "Exif.Image.Orientation", EXTRACTOR_ORIENTATION, result); // Image Timestamp result = addExiv2Tag(exifData, "Exif.Photo.DateTimeOriginal", EXTRACTOR_DATE, result); // Exposure time // From ExposureTime, failing that, try ShutterSpeedValue struct EXTRACTOR_Keywords * newResult; newResult = addExiv2Tag(exifData, "Exif.Photo.ExposureTime", EXTRACTOR_EXPOSURE, result); Exiv2::ExifData::const_iterator md; if (newResult == result) { md = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue")); if (md != exifData.end()) { double tmp = exp(log(2.0) * md->toFloat()) + 0.5; std::ostringstream os; if (tmp > 1) { os << "1/" << static_cast(tmp) << " s"; } else { os << static_cast(1/tmp) << " s"; } newResult = addKeyword(EXTRACTOR_EXPOSURE, strdup(os.str().c_str()), result); } } result = newResult; // Aperture // Get if from FNumber and, failing that, try ApertureValue newResult = addExiv2Tag(exifData, "Exif.Photo.FNumber", EXTRACTOR_APERTURE, result); if (newResult == result) { md = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ApertureValue")); if (md != exifData.end()) { std::ostringstream os; os << std::fixed << std::setprecision(1) << "F" << exp(log(2.0) * md->toFloat() / 2); newResult = addKeyword(EXTRACTOR_APERTURE, strdup(os.str().c_str()), result); } } result = newResult; // Exposure bias result = addExiv2Tag(exifData, "Exif.Photo.ExposureBiasValue", EXTRACTOR_EXPOSURE_BIAS, result); // Flash result = addExiv2Tag(exifData, "Exif.Photo.Flash", EXTRACTOR_FLASH, result); // Flash bias // Todo: Implement this for other cameras newResult = addExiv2Tag(exifData, "Exif.CanonCs2.FlashBias", EXTRACTOR_FLASH_BIAS, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Panasonic.FlashBias", EXTRACTOR_FLASH_BIAS, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Olympus.FlashBias", EXTRACTOR_FLASH_BIAS, result); } result = newResult; // Actual focal length and 35 mm equivalent // Todo: Calculate 35 mm equivalent a la jhead result = addExiv2Tag(exifData, "Exif.Photo.FocalLength", EXTRACTOR_FOCAL_LENGTH, result); result = addExiv2Tag(exifData, "Exif.Photo.FocalLengthIn35mmFilm", EXTRACTOR_FOCAL_LENGTH_35MM, result); // ISO speed // from ISOSpeedRatings or the Makernote newResult = addExiv2Tag(exifData, "Exif.Photo.ISOSpeedRatings", EXTRACTOR_ISO_SPEED, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.CanonCs1.ISOSpeed", EXTRACTOR_ISO_SPEED, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon1.ISOSpeed", EXTRACTOR_ISO_SPEED, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon2.ISOSpeed", EXTRACTOR_ISO_SPEED, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon3.ISOSpeed", EXTRACTOR_ISO_SPEED, result); } result = newResult; // Exposure mode // From ExposureProgram or Canon Makernote newResult = addExiv2Tag(exifData, "Exif.Photo.ExposureProgram", EXTRACTOR_EXPOSURE_MODE, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.CanonCs1.ExposureProgram", EXTRACTOR_EXPOSURE_MODE, result); } result = newResult; // Metering mode result = addExiv2Tag(exifData, "Exif.Photo.MeteringMode", EXTRACTOR_METERING_MODE, result); // Macro mode // Todo: Implement this for other cameras newResult = addExiv2Tag(exifData, "Exif.CanonCs1.Macro", EXTRACTOR_MACRO_MODE, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Fujifilm.Macro", EXTRACTOR_MACRO_MODE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Olympus.Macro", EXTRACTOR_MACRO_MODE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Panasonic.Macro", EXTRACTOR_MACRO_MODE, result); } result = newResult; // Image quality setting (compression) // Todo: Implement this for other cameras newResult = addExiv2Tag(exifData, "Exif.CanonCs1.Quality", EXTRACTOR_IMAGE_QUALITY, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Fujifilm.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Sigma.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon1.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon2.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon3.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Olympus.Quality", EXTRACTOR_IMAGE_QUALITY, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Panasonic.Quality", EXTRACTOR_IMAGE_QUALITY, result); } result = newResult; #if 0 /* this can sometimes be wrong (corrupt exiv2 data?). Either way, we should get the data directly from the specific file format parser (i.e. jpeg, tiff). */ // Exif Resolution unsigned long xdim = 0; unsigned long ydim = 0; md = exifData.findKey(Exiv2::ExifKey("Exif.Photo.PixelXDimension")); if (md != exifData.end()) xdim = md->toLong(); md = exifData.findKey(Exiv2::ExifKey("Exif.Photo.PixelYDimension")); if (md != exifData.end()) ydim = md->toLong(); if (xdim != 0 && ydim != 0) { std::ostringstream os; os << xdim << "x" << ydim; result = addKeyword(EXTRACTOR_SIZE, strdup(os.str().c_str()), result); } #endif // White balance // Todo: Implement this for other cameras newResult = addExiv2Tag(exifData, "Exif.CanonCs2.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Fujifilm.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Sigma.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon1.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon2.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Nikon3.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Olympus.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } if (newResult == result) { newResult = addExiv2Tag(exifData, "Exif.Panasonic.WhiteBalance", EXTRACTOR_WHITE_BALANCE, result); } result = newResult; // Copyright result = addExiv2Tag(exifData, "Exif.Image.Copyright", EXTRACTOR_COPYRIGHT, result); // Exif Comment result = addExiv2Tag(exifData, "Exif.Photo.UserComment", EXTRACTOR_COMMENT, result); } catch (const Exiv2::AnyError& e) { #ifndef SUPPRESS_WARNINGS std::cout << "Caught Exiv2 exception '" << e << "'\n"; #endif } return result; } #if WORKAROUND_905 struct X { unsigned char * data; size_t size; struct EXTRACTOR_Keywords * prev; }; static void * run(void * arg) { struct X * x = (struct X*) arg; return extract(NULL, x->data, x->size, x->prev); } struct EXTRACTOR_Keywords * libextractor_exiv2_extract(const char * filename, unsigned char * data, size_t size, struct EXTRACTOR_Keywords * prev) { pthread_t pt; struct X cls; void * ret; cls.data = data; cls.size = size; cls.prev = prev; if (0 == pthread_create(&pt, NULL, &run, &cls)) if (0 == pthread_join(pt, &ret)) return (struct EXTRACTOR_Keywords*) ret; return prev; } #endif }