libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

commit 1bb323cecd7d54a6912a6d952a874ed0e177c40b
parent ab1f81976d78aee8725ffd2994f7b398f0c6bd79
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 15 Dec 2009 15:50:01 +0000

exiv

Diffstat:
Msrc/include/extractor.h | 55++++++++++++++++++++++++++++---------------------------
Msrc/main/extractor_metatypes.c | 39++++++++++++++++++++++++++++++++++++---
Msrc/plugins/Makefile.am | 8++++++++
Dsrc/plugins/exiv2/exiv2extractor.cc | 620-------------------------------------------------------------------------------
Asrc/plugins/exiv2_extractor.cc | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 333 insertions(+), 650 deletions(-)

diff --git a/src/include/extractor.h b/src/include/extractor.h @@ -188,7 +188,7 @@ enum EXTRACTOR_MetaType EXTRACTOR_METATYPE_FINDER_FILE_TYPE = 67, EXTRACTOR_METATYPE_FINDER_FILE_CREATOR = 68, - /* software package specifics (deb, rpm, tgz) */ + /* software package specifics (deb, rpm, tgz, elf) */ EXTRACTOR_METATYPE_PACKAGE_NAME = 69, EXTRACTOR_METATYPE_PACKAGE_VERSION = 70, EXTRACTOR_METATYPE_PACKAGE_SECTION = 71, @@ -212,11 +212,37 @@ enum EXTRACTOR_MetaType EXTRACTOR_METATYPE_TARGET_OS = 89, EXTRACTOR_METATYPE_SOFTWARE_VERSION = 90, EXTRACTOR_METATYPE_TARGET_PLATFORM = 91, - EXTRACTOR_METATYPE_RESOURCE_TYPE = 92, EXTRACTOR_METATYPE_LIBRARY_SEARCH_PATH = 93, EXTRACTOR_METATYPE_LIBRARY_DEPENDENCY = 94, + /* photography specifics */ + EXTRACTOR_METATYPE_CAMERA_MAKE = 95, + EXTRACTOR_METATYPE_CAMERA_MODEL = 96, + EXTRACTOR_METATYPE_EXPOSURE = 97, + EXTRACTOR_METATYPE_APERTURE = 98, + EXTRACTOR_METATYPE_EXPOSURE_BIAS = 99, + EXTRACTOR_METATYPE_FLASH = 100, + EXTRACTOR_METATYPE_FLASH_BIAS = 101, + EXTRACTOR_METATYPE_FOCAL_LENGTH = 102, + EXTRACTOR_METATYPE_FOCAL_LENGTH_35MM = 103, + EXTRACTOR_METATYPE_ISO_SPEED = 104, + EXTRACTOR_METATYPE_EXPOSURE_MODE = 105, + EXTRACTOR_METATYPE_METERING_MODE = 106, + EXTRACTOR_METATYPE_MACRO_MODE = 107, + EXTRACTOR_METATYPE_IMAGE_QUALITY = 108, + EXTRACTOR_METATYPE_WHITE_BALANCE = 109, + EXTRACTOR_METATYPE_ORIENTATION = 110, + EXTRACTOR_METATYPE_MAGNIFICATION = 111, + + /* image specifics */ + EXTRACTOR_METATYPE_IMAGE_DIMENSIONS = 112, + + /* fixme: used up to here! */ + EXTRACTOR_METATYPE_THUMBNAIL_DATA = 70, + EXTRACTOR_METATYPE_RESOLUTION = 57, + EXTRACTOR_METATYPE_SCALE = 108, + /* FIXME: transcribe & renumber those below */ @@ -248,31 +274,6 @@ enum EXTRACTOR_MetaType EXTRACTOR_METATYPE_ARTIST = 5, EXTRACTOR_METATYPE_GENRE = 12, - /* image specifics */ - EXTRACTOR_METATYPE_THUMBNAIL_DATA = 70, - EXTRACTOR_METATYPE_RESOLUTION = 57, - EXTRACTOR_METATYPE_IMAGE_DIMENSIONS = 43, - EXTRACTOR_METATYPE_SCALE = 108, - - /* photography specifics */ - EXTRACTOR_METATYPE_CAMERA_MAKE = 72, - EXTRACTOR_METATYPE_CAMERA_MODEL = 73, - EXTRACTOR_METATYPE_EXPOSURE = 74, - EXTRACTOR_METATYPE_APERTURE = 75, - EXTRACTOR_METATYPE_EXPOSURE_BIAS = 76, - EXTRACTOR_METATYPE_FLASH = 77, - EXTRACTOR_METATYPE_FLASH_BIAS = 78, - EXTRACTOR_METATYPE_FOCAL_LENGTH = 79, - EXTRACTOR_METATYPE_FOCAL_LENGTH_35MM = 80, - EXTRACTOR_METATYPE_ISO_SPEED = 81, - EXTRACTOR_METATYPE_EXPOSURE_MODE = 82, - EXTRACTOR_METATYPE_METERING_MODE = 83, - EXTRACTOR_METATYPE_MACRO_MODE = 84, - EXTRACTOR_METATYPE_IMAGE_QUALITY = 85, - EXTRACTOR_METATYPE_WHITE_BALANCE = 86, - EXTRACTOR_METATYPE_ORIENTATION = 87, - EXTRACTOR_METATYPE_MAGNIFICATION = 40, - /* numeric metrics */ EXTRACTOR_METATYPE_POPULARITY_METER = 119, EXTRACTOR_METATYPE_RATING = 145, diff --git a/src/main/extractor_metatypes.c b/src/main/extractor_metatypes.c @@ -243,11 +243,44 @@ static const struct MetaTypeDescription meta_type_descriptions[] = { { gettext_noop ("library dependency"), gettext_noop ("name of a library that this file depends on") }, /* 95 */ - { gettext_noop (""), + { gettext_noop ("camera make"), gettext_noop ("") }, - { gettext_noop (""), + { gettext_noop ("camera model"), gettext_noop ("") }, - { gettext_noop (""), + { gettext_noop ("exposure"), + gettext_noop ("") }, + { gettext_noop ("aperture"), + gettext_noop ("") }, + { gettext_noop ("exposure bias"), + gettext_noop ("") }, + /* 100 */ + { gettext_noop ("flash"), + gettext_noop ("") }, + { gettext_noop ("flash bias"), + gettext_noop ("") }, + { gettext_noop ("focal length"), + gettext_noop ("") }, + { gettext_noop ("focal length 35mm"), + gettext_noop ("") }, + { gettext_noop ("iso speed"), + gettext_noop ("") }, + /* 105 */ + { gettext_noop ("exposure mode"), + gettext_noop ("") }, + { gettext_noop ("metering mode"), + gettext_noop ("") }, + { gettext_noop ("macro mode"), + gettext_noop ("") }, + { gettext_noop ("image quality"), + gettext_noop ("") }, + { gettext_noop ("white balance"), + gettext_noop ("") }, + /* 110 */ + { gettext_noop ("orientation"), + gettext_noop ("") }, + { gettext_noop ("magnification"), + gettext_noop ("") }, + { gettext_noop ("image dimensions"), gettext_noop ("") }, { gettext_noop (""), gettext_noop ("") }, diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am @@ -91,6 +91,7 @@ plugin_LTLIBRARIES = \ libextractor_deb.la \ libextractor_dvi.la \ libextractor_elf.la \ + libextractor_exiv2.la \ libextractor_html.la \ libextractor_it.la \ libextractor_mime.la \ @@ -128,6 +129,13 @@ libextractor_elf_la_LDFLAGS = \ libextractor_elf_la_LIBADD = \ $(top_builddir)/src/common/libextractor_common.la +libextractor_exiv2_la_SOURCES = \ + exiv2_extractor.cc +libextractor_exiv2_la_LDFLAGS = \ + $(XTRA_CPPLIBS) $(PLUGINFLAGS) +libextractor_exiv2_la_LIBADD = \ + -lexiv2 + libextractor_html_la_SOURCES = \ html_extractor.c libextractor_html_la_LDFLAGS = \ diff --git a/src/plugins/exiv2/exiv2extractor.cc b/src/plugins/exiv2/exiv2extractor.cc @@ -1,620 +0,0 @@ -// ***************************************************************** -*- 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) - <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> - @date 30-Jun-05, ahu: created -*/ - -#include <iostream> -#include <iomanip> -#include <cassert> -#include <cstring> -#include <math.h> - -#include "platform.h" -#include "extractor.h" - -#include "exiv2/exif.hpp" -#include "exiv2/image.hpp" -#include "exiv2/futils.hpp" - -#define WORKAROUND_905 1 -#if WORKAROUND_905 -#include <pthread.h> -#endif - -#define SUPPRESS_WARNINGS 1 - -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; - } - -} - - -static 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); - md++; - } - return result; -} - - - -static struct EXTRACTOR_Keywords * -addIptcData(const Exiv2::IptcData& iptcData, - const std::string& key, - EXTRACTOR_KeywordType type, - struct EXTRACTOR_Keywords * result) -{ - const char * str; - - Exiv2::IptcKey ek(key); - Exiv2::IptcData::const_iterator md = iptcData.findKey(ek); - while (md != iptcData.end()) - { - if (0 != strcmp (Exiv2::toString(md->key()).c_str(), key.c_str())) - break; - 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); - md++; - } - return result; -} - - - - -static struct EXTRACTOR_Keywords * -addXmpData(const Exiv2::XmpData& xmpData, - const std::string& key, - EXTRACTOR_KeywordType type, - struct EXTRACTOR_Keywords * result) -{ - const char * str; - - Exiv2::XmpKey ek(key); - Exiv2::XmpData::const_iterator md = xmpData.findKey(ek); - while (md != xmpData.end()) - { - if (0 != strcmp (Exiv2::toString(md->key()).c_str(), key.c_str())) - break; - 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); - md++; - } - 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; - struct EXTRACTOR_Keywords * newResult; - try - { - Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(data, size); - assert(image.get() != 0); - image->readMetadata(); - Exiv2::ExifData &exifData = image->exifData(); - if (!exifData.empty()) - { - // 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 - 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<long>(tmp) << " s"; - } - else { - os << static_cast<long>(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.CanonSi.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.CanonSi.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.CanonCs.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.CanonCs.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.CanonCs.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; - - - /* 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); - } - // White balance - // Todo: Implement this for other cameras - - newResult = addExiv2Tag(exifData, - "Exif.CanonSi.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); - // GPS - result = addExiv2Tag(exifData, - "Exif.GPSInfo.GPSLatitudeRef", - EXTRACTOR_GPS_LATITUDE_REF, - result); - result = addExiv2Tag(exifData, - "Exif.GPSInfo.GPSLatitude", - EXTRACTOR_GPS_LATITUDE, - result); - result = addExiv2Tag(exifData, - "Exif.GPSInfo.GPSLongitudeRef", - EXTRACTOR_GPS_LONGITUDE_REF, - result); - result = addExiv2Tag(exifData, - "Exif.GPSInfo.GPSLongitude", - EXTRACTOR_GPS_LONGITUDE, - result); - } - - Exiv2::IptcData &iptcData = image->iptcData(); - Exiv2::XmpData &xmpData = image->xmpData(); - - if (! iptcData.empty()) { - result = addIptcData (iptcData, - "Iptc.Application2.Keywords", - EXTRACTOR_KEYWORDS, - result); - newResult = addIptcData (iptcData, - "Iptc.Application2.City", - EXTRACTOR_LOCATION_CITY, - result); - if ((result == newResult) && (!xmpData.empty())) - result = addXmpData (xmpData, - "Xmp.photoshop.City", - EXTRACTOR_RATING, - result); - else - result = newResult; - - result = addIptcData (iptcData, - "Iptc.Application2.SubLocation", - EXTRACTOR_LOCATION_SUBLOCATION, - result); - newResult = addIptcData (iptcData, - "Iptc.Application2.CountryName", - EXTRACTOR_LOCATION_COUNTRY, - result); - if ( (result == newResult) && (!xmpData.empty())) - result = addXmpData (xmpData, - "Xmp.photoshop.Country", - EXTRACTOR_RATING, - result); - else - result = newResult; - - } - - if (! xmpData.empty()) { - newResult = addXmpData (xmpData, - "Xmp.xmp.Rating", - EXTRACTOR_RATING, - result); - if (result == newResult) - result = addXmpData (xmpData, - "Xmp.MicrosoftPhoto.Rating", - EXTRACTOR_RATING, - result); - else - result = newResult; - result = addXmpData (xmpData, - "Xmp.iptc.CountryCode", - EXTRACTOR_COUNTRY_CODE, - result); - result = addXmpData (xmpData, - "Xmp.xmp.CreatorTool", - EXTRACTOR_CREATED_BY_SOFTWARE, - result); - result = addXmpData (xmpData, - "Xmp.lr.hierarchicalSubject", - EXTRACTOR_SUBJECT, - 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 -} diff --git a/src/plugins/exiv2_extractor.cc b/src/plugins/exiv2_extractor.cc @@ -0,0 +1,261 @@ +// ***************************************************************** -*- 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 exiv2_extractor.cc + @brief libextractor plugin for Exif using exiv2 + @version $Rev$ + @author Andreas Huggel (ahu) + <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> + @date 30-Jun-05, ahu: created; 15-Dec-09, cg: updated +*/ + +#include <iostream> +#include <iomanip> +#include <cassert> +#include <cstring> +#include <math.h> + +#include "platform.h" +#include "extractor.h" + +#include <exiv2/exif.hpp> +#include <exiv2/image.hpp> +#include <exiv2/futils.hpp> + +#define SUPPRESS_WARNINGS 1 + +#define ADD(s, type) do { if (0!=proc(proc_cls, "exiv2", type, EXTRACTOR_METAFORMAT_UTF8, "text/plain", s, strlen(s)+1)) return 1; } while (0) + +static int +addExiv2Tag(const Exiv2::ExifData& exifData, + const std::string& key, + enum EXTRACTOR_MetaType type, + EXTRACTOR_MetaDataProcessor proc, + void *proc_cls) +{ + 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) + ADD (str, type); + md++; + } + return 0; +} + + +static int +addIptcData(const Exiv2::IptcData& iptcData, + const std::string& key, + enum EXTRACTOR_MetaType type, + EXTRACTOR_MetaDataProcessor proc, + void *proc_cls) +{ + const char * str; + + Exiv2::IptcKey ek(key); + Exiv2::IptcData::const_iterator md = iptcData.findKey(ek); + while (md != iptcData.end()) + { + if (0 != strcmp (Exiv2::toString(md->key()).c_str(), key.c_str())) + break; + std::string ccstr = Exiv2::toString(*md); + str = ccstr.c_str(); + while ( (strlen(str) > 0) && isspace(str[0])) str++; + if (strlen(str) > 0) + ADD (str, type); + md++; + } + return 0; +} + + +static int +addXmpData(const Exiv2::XmpData& xmpData, + const std::string& key, + enum EXTRACTOR_MetaType type, + EXTRACTOR_MetaDataProcessor proc, + void *proc_cls) +{ + const char * str; + + Exiv2::XmpKey ek(key); + Exiv2::XmpData::const_iterator md = xmpData.findKey(ek); + while (md != xmpData.end()) + { + if (0 != strcmp (Exiv2::toString(md->key()).c_str(), key.c_str())) + break; + std::string ccstr = Exiv2::toString(*md); + str = ccstr.c_str(); + while ( (strlen(str) > 0) && isspace(str[0])) str++; + if (strlen(str) > 0) + ADD (str, type); + md++; + } + return 0; +} + +#define ADDEXIV(s,t) do { if (0 != addExiv2Tag (exifData, s, t, proc, proc_cls)) return 1; } while (0) +#define ADDIPTC(s,t) do { if (0 != addIptcData (iptcData, s, t, proc, proc_cls)) return 1; } while (0) +#define ADDXMP(s,t) do { if (0 != addXmpData (xmpData, s, t, proc, proc_cls)) return 1; } while (0) + + +extern "C" { + + int + EXTRACTOR_exiv2_extract (const char *data, + size_t size, + EXTRACTOR_MetaDataProcessor proc, + void *proc_cls, + const char *options) + { + try + { + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open( (Exiv2::byte*) data, + size); + if (image.get() == 0) + return 0; + image->readMetadata(); + Exiv2::ExifData &exifData = image->exifData(); + if (!exifData.empty()) + { + Exiv2::ExifData::const_iterator md; + + /* FIXME: this should be a loop over data, + not a looooong block of code */ + ADDEXIV ("Exif.Image.Copyright", EXTRACTOR_METATYPE_COPYRIGHT); + ADDEXIV ("Exif.Photo.UserComment", EXTRACTOR_METATYPE_COMMENT); + ADDEXIV ("Exif.GPSInfo.GPSLatitudeRef", EXTRACTOR_METATYPE_GPS_LATITUDE_REF); + ADDEXIV ("Exif.GPSInfo.GPSLatitude", EXTRACTOR_METATYPE_GPS_LATITUDE); + ADDEXIV ("Exif.GPSInfo.GPSLongitudeRef", EXTRACTOR_METATYPE_GPS_LONGITUDE_REF); + ADDEXIV ("Exif.GPSInfo.GPSLongitude", EXTRACTOR_METATYPE_GPS_LONGITUDE); + ADDEXIV ("Exif.Image.Make", EXTRACTOR_METATYPE_CAMERA_MAKE); + ADDEXIV ("Exif.Image.Model", EXTRACTOR_METATYPE_CAMERA_MODEL); + ADDEXIV ("Exif.Image.Orientation", EXTRACTOR_METATYPE_ORIENTATION); + ADDEXIV ("Exif.Photo.DateTimeOriginal", EXTRACTOR_METATYPE_CREATION_DATE); + ADDEXIV ("Exif.Photo.ExposureBiasValue", EXTRACTOR_METATYPE_EXPOSURE_BIAS); + ADDEXIV ("Exif.Photo.Flash", EXTRACTOR_METATYPE_FLASH); + ADDEXIV ("Exif.CanonSi.FlashBias", EXTRACTOR_METATYPE_FLASH_BIAS); + ADDEXIV ("Exif.Panasonic.FlashBias", EXTRACTOR_METATYPE_FLASH_BIAS); + ADDEXIV ("Exif.Olympus.FlashBias", EXTRACTOR_METATYPE_FLASH_BIAS); + ADDEXIV ("Exif.Photo.FocalLength", EXTRACTOR_METATYPE_FOCAL_LENGTH); + ADDEXIV ("Exif.Photo.FocalLengthIn35mmFilm", EXTRACTOR_METATYPE_FOCAL_LENGTH_35MM); + ADDEXIV ("Exif.Photo.ISOSpeedRatings", EXTRACTOR_METATYPE_ISO_SPEED); + ADDEXIV ("Exif.CanonSi.ISOSpeed", EXTRACTOR_METATYPE_ISO_SPEED); + ADDEXIV ("Exif.Nikon1.ISOSpeed", EXTRACTOR_METATYPE_ISO_SPEED); + ADDEXIV ("Exif.Nikon2.ISOSpeed", EXTRACTOR_METATYPE_ISO_SPEED); + ADDEXIV ("Exif.Nikon3.ISOSpeed", EXTRACTOR_METATYPE_ISO_SPEED); + ADDEXIV ("Exif.Photo.ExposureProgram", EXTRACTOR_METATYPE_EXPOSURE_MODE); + ADDEXIV ("Exif.CanonCs.ExposureProgram", EXTRACTOR_METATYPE_EXPOSURE_MODE); + ADDEXIV ("Exif.Photo.MeteringMode", EXTRACTOR_METATYPE_METERING_MODE); + ADDEXIV ("Exif.CanonCs.Macro", EXTRACTOR_METATYPE_MACRO_MODE); + ADDEXIV ("Exif.Fujifilm.Macro", EXTRACTOR_METATYPE_MACRO_MODE); + ADDEXIV ("Exif.Olympus.Macro", EXTRACTOR_METATYPE_MACRO_MODE); + ADDEXIV ("Exif.Panasonic.Macro", EXTRACTOR_METATYPE_MACRO_MODE); + ADDEXIV ("Exif.CanonCs.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Fujifilm.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Sigma.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Nikon1.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Nikon2.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Nikon3.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Olympus.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.Panasonic.Quality", EXTRACTOR_METATYPE_IMAGE_QUALITY); + ADDEXIV ("Exif.CanonSi.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Fujifilm.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Sigma.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Nikon1.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Nikon2.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Nikon3.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Olympus.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + ADDEXIV ("Exif.Panasonic.WhiteBalance", EXTRACTOR_METATYPE_WHITE_BALANCE); + + ADDEXIV ("Exif.Photo.FNumber", EXTRACTOR_METATYPE_APERTURE); + 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); + ADD (os.str().c_str(), EXTRACTOR_METATYPE_APERTURE); + } + + ADDEXIV ("Exif.Photo.ExposureTime", EXTRACTOR_METATYPE_EXPOSURE); + 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<long>(tmp) << " s"; + } + else { + os << static_cast<long>(1/tmp) << " s"; + } + ADD (os.str().c_str(), EXTRACTOR_METATYPE_EXPOSURE); + } + + /* 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; + ADD (os.str().c_str(), EXTRACTOR_METATYPE_IMAGE_DIMENSIONS); + } + } + + Exiv2::IptcData &iptcData = image->iptcData(); + if (! iptcData.empty()) { + ADDIPTC ("Iptc.Application2.Keywords", EXTRACTOR_METATYPE_KEYWORDS); + ADDIPTC ("Iptc.Application2.City", EXTRACTOR_METATYPE_LOCATION_CITY); + ADDIPTC ("Iptc.Application2.SubLocation", EXTRACTOR_METATYPE_LOCATION_SUBLOCATION); + ADDIPTC ("Iptc.Application2.CountryName", EXTRACTOR_METATYPE_LOCATION_COUNTRY); + ADDIPTC ("Xmp.photoshop.Country", EXTRACTOR_METATYPE_RATING); + } + + Exiv2::XmpData &xmpData = image->xmpData(); + if (! xmpData.empty()) { + ADDXMP ("Xmp.photoshop.City", EXTRACTOR_METATYPE_LOCATION_CITY); + ADDXMP ("Xmp.xmp.Rating", EXTRACTOR_METATYPE_RATING); + ADDXMP ("Xmp.MicrosoftPhoto.Rating", EXTRACTOR_METATYPE_RATING); + ADDXMP ("Xmp.iptc.CountryCode", EXTRACTOR_METATYPE_LOCATION_COUNTRY_CODE); + ADDXMP ("Xmp.xmp.CreatorTool", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE); + ADDXMP ("Xmp.lr.hierarchicalSubject", EXTRACTOR_METATYPE_SUBJECT); + } + } + catch (const Exiv2::AnyError& e) { +#ifndef SUPPRESS_WARNINGS + std::cout << "Caught Exiv2 exception '" << e << "'\n"; +#endif + } + + return 0; + } + + +}