libextractor

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

commit 338b053601c74175b61f8c041d421a0ce1716eb8
parent 57cc152f582c88c60f4112d2bf29a7dc18e71421
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun,  3 Jul 2005 19:00:26 +0000

Huggel's patch -- verbatim, still needs work

Diffstat:
MAUTHORS | 1+
Msrc/include/extractor.h | 17+++++++++++++++++
Msrc/main/extractor.c | 19++++++++++++++++++-
Msrc/plugins/Makefile.am | 5+++++
Asrc/plugins/exiv2extractor.cc | 432+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 473 insertions(+), 1 deletion(-)

diff --git a/AUTHORS b/AUTHORS @@ -29,6 +29,7 @@ id3v2 - core team with Milan <milan@skoid.org> hash - core team using GNU coreutils/textutils and GPL'ed RMD160 code. translit - Nils Durner <n.durner@t-online.de> thumbnail - core team using ImageMagick +exiv2 - Andreas Huggel <ahuggel@gmx.net> General contributors: Yuri N. Sedunov <aris@altlinux.ru> diff --git a/src/include/extractor.h b/src/include/extractor.h @@ -122,6 +122,23 @@ typedef enum { EXTRACTOR_CONTACT = 69, EXTRACTOR_THUMBNAIL_DATA = 70, EXTRACTOR_PUBLICATION_DATE = 71, + EXTRACTOR_CAMERA_MAKE = 72, + EXTRACTOR_CAMERA_MODEL = 73, + EXTRACTOR_EXPOSURE = 74, + EXTRACTOR_APERTURE = 75, + EXTRACTOR_EXPOSURE_BIAS = 76, + EXTRACTOR_FLASH = 77, + EXTRACTOR_FLASH_BIAS = 78, + EXTRACTOR_FOCAL_LENGTH = 79, + EXTRACTOR_FOCAL_LENGTH_35MM = 80, + EXTRACTOR_ISO_SPEED = 81, + EXTRACTOR_EXPOSURE_MODE = 82, + EXTRACTOR_METERING_MODE = 83, + EXTRACTOR_MACRO_MODE = 84, + EXTRACTOR_IMAGE_QUALITY = 85, + EXTRACTOR_WHITE_BALANCE = 86, + EXTRACTOR_FILESIZE = 87, + EXTRACTOR_ORIENTATION = 88, } EXTRACTOR_KeywordType; /** diff --git a/src/main/extractor.c b/src/main/extractor.c @@ -106,11 +106,28 @@ static const char *keywordTypes[] = { gettext_noop("contact"), gettext_noop("binary thumbnail data"), gettext_noop("publication date"), + gettext_noop("camera make"), + gettext_noop("camera model"), + gettext_noop("exposure"), + gettext_noop("aperture"), + gettext_noop("exposure bias"), + gettext_noop("flash"), + gettext_noop("flash bias"), + gettext_noop("focal length"), + gettext_noop("focal length (35mm equivalent)"), + gettext_noop("iso speed"), + gettext_noop("exposure mode"), + gettext_noop("metering mode"), + gettext_noop("macro mode"), + gettext_noop("image quality"), + gettext_noop("white balance"), + gettext_noop("filesize"), + gettext_noop("orientation"), NULL, }; /* the number of keyword types (for bounds-checking) */ -#define HIGHEST_TYPE_NUMBER 72 +#define HIGHEST_TYPE_NUMBER 89 #ifdef HAVE_LIBOGG #if HAVE_VORBIS diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am @@ -62,6 +62,7 @@ plugin_LTLIBRARIES = \ libextractor_zip.la \ libextractor_lower.la \ libextractor_translit.la \ + libextractor_exiv2.la \ $(extraqt) \ $(extraogg) @@ -252,3 +253,7 @@ libextractor_translit_la_SOURCES = \ libextractor_translit_la_LDFLAGS = \ $(PLUGINFLAGS) +libextractor_exiv2_la_SOURCES = \ + exiv2extractor.cc +libextractor_exiv2_la_LDFLAGS = \ + -lexiv2 $(PLUGINFLAGS) diff --git a/src/plugins/exiv2extractor.cc b/src/plugins/exiv2extractor.cc @@ -0,0 +1,432 @@ +// ***************************************************************** -*- 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 "platform.h" +#include "extractor.h" +#include "convert.h" + +#include "exiv2/exif.hpp" +#include "exiv2/image.hpp" +#include "exiv2/futils.hpp" + +#include <iostream> +#include <iomanip> +#include <cassert> +#include <cstring> +#include <sys/types.h> // for stat() +#include <sys/stat.h> // for stat() +#ifdef HAVE_UNISTD_H +# include <unistd.h> // for stat() +#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) +{ + Exiv2::ExifKey ek(key); + Exiv2::ExifData::const_iterator md = exifData.findKey(ek); + if (md != exifData.end()) { + result = addKeyword(type, + strdup(Exiv2::toString(*md).c_str()), + result); + } + return result; +} + +extern "C" { + + struct EXTRACTOR_Keywords * libextractor_exiv2_extract(char * filename, + unsigned char * data, + size_t size, + struct EXTRACTOR_Keywords * prev) + { + struct EXTRACTOR_Keywords * result = 0; + + try { + if (!Exiv2::fileExists(filename, true)) return result; + + // Filename + result = addKeyword(EXTRACTOR_FILENAME, strdup(filename), result); + + // Filesize + struct stat buf; + if (0 == stat(filename, &buf)) { + result = addKeyword(EXTRACTOR_FILESIZE, + strdup(Exiv2::toString(buf.st_size).c_str()), + result); + } + + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename); + 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<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.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); + } + + // 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; + + // Exif Resolution + long xdim = 0; + 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.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) { + std::cout << "Caught Exiv2 exception '" << e << "'\n"; + } + + return result; + } + +}