Mercurial > geeqie
view src/exiv2.cc @ 1388:2496a345c452
Try to improve tab completion: when a key is pressed while tab completion menu is shown, the number of entries is reduced, dhowing all matching entries but the menu is no more closed after each key pressure. Number of possible entries in this menu was increased from 500 to 1000. Pressing TAB when path entry is empty now adds / (root directory).
author | zas_ |
---|---|
date | Fri, 06 Mar 2009 22:34:38 +0000 |
parents | 10073464e6aa |
children | 91bed0d66cf2 |
line wrap: on
line source
/* * Geeqie * Copyright (C) 2008 - 2009 The Geeqie Team * * Author: Vladimir Nadvornik * * This software is released under the GNU General Public License (GNU GPL). * Please read the included file COPYING for more information. * This software comes with no warranty of any kind, use at your own risk! */ #include "config.h" #ifdef HAVE_EXIV2 #include <exiv2/image.hpp> #include <exiv2/exif.hpp> #include <iostream> // EXIV2_TEST_VERSION is defined in Exiv2 0.15 and newer. #ifndef EXIV2_TEST_VERSION # define EXIV2_TEST_VERSION(major,minor,patch) \ ( EXIV2_VERSION >= EXIV2_MAKE_VERSION(major,minor,patch) ) #endif #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #if !EXIV2_TEST_VERSION(0,17,90) #include <exiv2/tiffparser.hpp> #include <exiv2/tiffcomposite.hpp> #include <exiv2/tiffvisitor.hpp> #include <exiv2/tiffimage.hpp> #include <exiv2/cr2image.hpp> #include <exiv2/crwimage.hpp> #if EXIV2_TEST_VERSION(0,16,0) #include <exiv2/orfimage.hpp> #endif #if EXIV2_TEST_VERSION(0,13,0) #include <exiv2/rafimage.hpp> #endif #include <exiv2/futils.hpp> #else #include <exiv2/preview.hpp> #endif #if EXIV2_TEST_VERSION(0,17,0) #include <exiv2/convert.hpp> #include <exiv2/xmpsidecar.hpp> #endif extern "C" { #include <glib.h> #include "main.h" #include "exif.h" #include "filefilter.h" #include "ui_fileops.h" #include "misc.h" } typedef struct _AltKey AltKey; struct _AltKey { const gchar *xmp_key; const gchar *exif_key; const gchar *iptc_key; }; /* this is a list of keys that should be converted, even with the older Exiv2 which does not support it directly */ static const AltKey alt_keys[] = { {"Xmp.tiff.Orientation", "Exif.Image.Orientation", NULL}, {"Xmp.dc.subject", NULL, "Iptc.Application2.Keywords"}, {"Xmp.dc.description", NULL, "Iptc.Application2.Caption"}, {NULL, NULL, NULL} }; struct _ExifData { Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */ Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */ #if EXIV2_TEST_VERSION(0,16,0) Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */ #endif virtual ~_ExifData() { } virtual void writeMetadata(gchar *path = NULL) { g_critical("Unsupported method of writing metadata"); } virtual ExifData *original() { return NULL; } virtual Exiv2::Image *image() = 0; virtual Exiv2::ExifData &exifData() = 0; virtual Exiv2::IptcData &iptcData() = 0; #if EXIV2_TEST_VERSION(0,16,0) virtual Exiv2::XmpData &xmpData() = 0; #endif virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0; virtual guchar *get_jpeg_color_profile(guint *data_len) = 0; }; // This allows read-only access to the original metadata struct _ExifDataOriginal : public _ExifData { protected: Exiv2::Image::AutoPtr image_; /* the icc profile in jpeg is not technically exif - store it here */ unsigned char *cp_data_; guint cp_length_; public: _ExifDataOriginal(Exiv2::Image::AutoPtr image) { cp_data_ = NULL; cp_length_ = 0; image_ = image; } _ExifDataOriginal(gchar *path) { cp_data_ = NULL; cp_length_ = 0; gchar *pathl = path_from_utf8(path); image_ = Exiv2::ImageFactory::open(pathl); g_free(pathl); // g_assert (image.get() != 0); image_->readMetadata(); #if EXIV2_TEST_VERSION(0,16,0) if (image_->mimeType() == "application/rdf+xml") { //Exiv2 sidecar converts xmp to exif and iptc, we don't want it. image_->clearExifData(); image_->clearIptcData(); } #endif #if EXIV2_TEST_VERSION(0,14,0) if (image_->mimeType() == "image/jpeg") { /* try to get jpeg color profile */ Exiv2::BasicIo &io = image_->io(); gint open = io.isopen(); if (!open) io.open(); if (io.isopen()) { unsigned char *mapped = (unsigned char*)io.mmap(); if (mapped) exif_jpeg_parse_color(this, mapped, io.size()); io.munmap(); } if (!open) io.close(); } #endif } virtual ~_ExifDataOriginal() { if (cp_data_) g_free(cp_data_); } virtual Exiv2::Image *image() { return image_.get(); } virtual Exiv2::ExifData &exifData () { return image_->exifData(); } virtual Exiv2::IptcData &iptcData () { return image_->iptcData(); } #if EXIV2_TEST_VERSION(0,16,0) virtual Exiv2::XmpData &xmpData () { return image_->xmpData(); } #endif virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) { if (cp_data_) g_free(cp_data_); cp_data_ = cp_data; cp_length_ = cp_length; } virtual guchar *get_jpeg_color_profile(guint *data_len) { if (cp_data_) { if (data_len) *data_len = cp_length_; return (unsigned char *) g_memdup(cp_data_, cp_length_); } return NULL; } }; extern "C" { static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data); } // This allows read-write access to the metadata struct _ExifDataProcessed : public _ExifData { protected: _ExifDataOriginal *imageData_; _ExifDataOriginal *sidecarData_; Exiv2::ExifData exifData_; Exiv2::IptcData iptcData_; #if EXIV2_TEST_VERSION(0,16,0) Exiv2::XmpData xmpData_; #endif public: _ExifDataProcessed(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp) { imageData_ = new _ExifDataOriginal(path); sidecarData_ = NULL; #if EXIV2_TEST_VERSION(0,16,0) if (sidecar_path) { sidecarData_ = new _ExifDataOriginal(sidecar_path); xmpData_ = sidecarData_->xmpData(); } else { xmpData_ = imageData_->xmpData(); } #endif exifData_ = imageData_->exifData(); iptcData_ = imageData_->iptcData(); #if EXIV2_TEST_VERSION(0,17,0) syncExifWithXmp(exifData_, xmpData_); #endif if (modified_xmp) { g_hash_table_foreach(modified_xmp, _ExifDataProcessed_update_xmp, this); } } virtual ~_ExifDataProcessed() { if (imageData_) delete imageData_; if (sidecarData_) delete sidecarData_; } virtual ExifData *original() { return imageData_; } virtual void writeMetadata(gchar *path = NULL) { if (!path) { #if EXIV2_TEST_VERSION(0,17,0) if (options->metadata.save_legacy_IPTC) copyXmpToIptc(xmpData_, iptcData_); else iptcData_.clear(); copyXmpToExif(xmpData_, exifData_); #endif imageData_->image()->setExifData(exifData_); imageData_->image()->setIptcData(iptcData_); #if EXIV2_TEST_VERSION(0,16,0) imageData_->image()->setXmpData(xmpData_); #endif imageData_->image()->writeMetadata(); } else { #if EXIV2_TEST_VERSION(0,17,0) gchar *pathl = path_from_utf8(path);; Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl); g_free(pathl); sidecar->setXmpData(xmpData_); sidecar->writeMetadata(); #else throw Exiv2::Error(3, "xmp"); #endif } } virtual Exiv2::Image *image() { return imageData_->image(); } virtual Exiv2::ExifData &exifData () { return exifData_; } virtual Exiv2::IptcData &iptcData () { return iptcData_; } #if EXIV2_TEST_VERSION(0,16,0) virtual Exiv2::XmpData &xmpData () { return xmpData_; } #endif virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) { imageData_->add_jpeg_color_profile(cp_data, cp_length); } virtual guchar *get_jpeg_color_profile(guint *data_len) { return imageData_->get_jpeg_color_profile(data_len); } }; extern "C" { void exif_init(void) { #ifdef EXV_ENABLE_NLS bind_textdomain_codeset (EXV_PACKAGE, "UTF-8"); #endif } static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data) { exif_update_metadata((ExifData *)data, (gchar *)key, (GList *)value); } ExifData *exif_read(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp) { DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-"); try { return new _ExifDataProcessed(path, sidecar_path, modified_xmp); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } gboolean exif_write(ExifData *exif) { try { exif->writeMetadata(); return TRUE; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return FALSE; } } gboolean exif_write_sidecar(ExifData *exif, gchar *path) { try { exif->writeMetadata(path); return TRUE; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return FALSE; } } void exif_free(ExifData *exif) { if (!exif) return; g_assert(dynamic_cast<_ExifDataProcessed *>(exif)); // this should not be called on ExifDataOriginal delete exif; } ExifData *exif_get_original(ExifData *exif) { return exif->original(); } ExifItem *exif_get_item(ExifData *exif, const gchar *key) { try { Exiv2::Metadatum *item = NULL; try { Exiv2::ExifKey ekey(key); Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey); if (pos == exif->exifData().end()) return NULL; item = &*pos; } catch (Exiv2::AnyError& e) { try { Exiv2::IptcKey ekey(key); Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey); if (pos == exif->iptcData().end()) return NULL; item = &*pos; } catch (Exiv2::AnyError& e) { #if EXIV2_TEST_VERSION(0,16,0) Exiv2::XmpKey ekey(key); Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey); if (pos == exif->xmpData().end()) return NULL; item = &*pos; #endif } } return (ExifItem *)item; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } ExifItem *exif_add_item(ExifData *exif, const gchar *key) { try { Exiv2::Metadatum *item = NULL; try { Exiv2::ExifKey ekey(key); exif->exifData().add(ekey, NULL); Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item pos--; item = &*pos; } catch (Exiv2::AnyError& e) { try { Exiv2::IptcKey ekey(key); exif->iptcData().add(ekey, NULL); Exiv2::IptcData::iterator pos = exif->iptcData().end(); pos--; item = &*pos; } catch (Exiv2::AnyError& e) { #if EXIV2_TEST_VERSION(0,16,0) Exiv2::XmpKey ekey(key); exif->xmpData().add(ekey, NULL); Exiv2::XmpData::iterator pos = exif->xmpData().end(); pos--; item = &*pos; #endif } } return (ExifItem *)item; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } ExifItem *exif_get_first_item(ExifData *exif) { try { exif->exifIter = exif->exifData().begin(); exif->iptcIter = exif->iptcData().begin(); #if EXIV2_TEST_VERSION(0,16,0) exif->xmpIter = exif->xmpData().begin(); #endif if (exif->exifIter != exif->exifData().end()) { const Exiv2::Metadatum *item = &*exif->exifIter; exif->exifIter++; return (ExifItem *)item; } if (exif->iptcIter != exif->iptcData().end()) { const Exiv2::Metadatum *item = &*exif->iptcIter; exif->iptcIter++; return (ExifItem *)item; } #if EXIV2_TEST_VERSION(0,16,0) if (exif->xmpIter != exif->xmpData().end()) { const Exiv2::Metadatum *item = &*exif->xmpIter; exif->xmpIter++; return (ExifItem *)item; } #endif return NULL; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } ExifItem *exif_get_next_item(ExifData *exif) { try { if (exif->exifIter != exif->exifData().end()) { const Exiv2::Metadatum *item = &*exif->exifIter; exif->exifIter++; return (ExifItem *)item; } if (exif->iptcIter != exif->iptcData().end()) { const Exiv2::Metadatum *item = &*exif->iptcIter; exif->iptcIter++; return (ExifItem *)item; } #if EXIV2_TEST_VERSION(0,16,0) if (exif->xmpIter != exif->xmpData().end()) { const Exiv2::Metadatum *item = &*exif->xmpIter; exif->xmpIter++; return (ExifItem *)item; } #endif return NULL; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } char *exif_item_get_tag_name(ExifItem *item) { try { if (!item) return NULL; return g_strdup(((Exiv2::Metadatum *)item)->key().c_str()); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } guint exif_item_get_tag_id(ExifItem *item) { try { if (!item) return 0; return ((Exiv2::Metadatum *)item)->tag(); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return 0; } } guint exif_item_get_elements(ExifItem *item) { try { if (!item) return 0; return ((Exiv2::Metadatum *)item)->count(); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return 0; } } char *exif_item_get_data(ExifItem *item, guint *data_len) { try { if (!item) return 0; Exiv2::Metadatum *md = (Exiv2::Metadatum *)item; if (data_len) *data_len = md->size(); char *data = (char *)g_malloc(md->size()); long res = md->copy((Exiv2::byte *)data, Exiv2::littleEndian /* should not matter */); g_assert(res == md->size()); return data; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } char *exif_item_get_description(ExifItem *item) { try { if (!item) return NULL; return utf8_validate_or_convert(((Exiv2::Metadatum *)item)->tagLabel().c_str()); } catch (std::exception& e) { // std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } /* invalidTypeId, unsignedByte, asciiString, unsignedShort, unsignedLong, unsignedRational, signedByte, undefined, signedShort, signedLong, signedRational, string, date, time, comment, directory, xmpText, xmpAlt, xmpBag, xmpSeq, langAlt, lastTypeId */ static guint format_id_trans_tbl [] = { EXIF_FORMAT_UNKNOWN, EXIF_FORMAT_BYTE_UNSIGNED, EXIF_FORMAT_STRING, EXIF_FORMAT_SHORT_UNSIGNED, EXIF_FORMAT_LONG_UNSIGNED, EXIF_FORMAT_RATIONAL_UNSIGNED, EXIF_FORMAT_BYTE, EXIF_FORMAT_UNDEFINED, EXIF_FORMAT_SHORT, EXIF_FORMAT_LONG, EXIF_FORMAT_RATIONAL, EXIF_FORMAT_STRING, EXIF_FORMAT_STRING, EXIF_FORMAT_STRING, EXIF_FORMAT_UNDEFINED, EXIF_FORMAT_STRING, EXIF_FORMAT_STRING, EXIF_FORMAT_STRING, EXIF_FORMAT_STRING }; guint exif_item_get_format_id(ExifItem *item) { try { if (!item) return EXIF_FORMAT_UNKNOWN; guint id = ((Exiv2::Metadatum *)item)->typeId(); if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN; return format_id_trans_tbl[id]; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return EXIF_FORMAT_UNKNOWN; } } const char *exif_item_get_format_name(ExifItem *item, gint brief) { try { if (!item) return NULL; return ((Exiv2::Metadatum *)item)->typeName(); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } gchar *exif_item_get_data_as_text(ExifItem *item) { try { if (!item) return NULL; Exiv2::Metadatum *metadatum = (Exiv2::Metadatum *)item; #if EXIV2_TEST_VERSION(0,17,0) return utf8_validate_or_convert(metadatum->print().c_str()); #else std::stringstream str; Exiv2::Exifdatum *exifdatum; Exiv2::Iptcdatum *iptcdatum; #if EXIV2_TEST_VERSION(0,16,0) Exiv2::Xmpdatum *xmpdatum; #endif if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum))) str << *exifdatum; else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum))) str << *iptcdatum; #if EXIV2_TEST_VERSION(0,16,0) else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum))) str << *xmpdatum; #endif return utf8_validate_or_convert(str.str().c_str()); #endif } catch (Exiv2::AnyError& e) { return NULL; } } gchar *exif_item_get_string(ExifItem *item, int idx) { try { if (!item) return NULL; Exiv2::Metadatum *em = (Exiv2::Metadatum *)item; #if EXIV2_TEST_VERSION(0,16,0) std::string str = em->toString(idx); #else std::string str = em->toString(); // FIXME #endif if (idx == 0 && str == "") str = em->toString(); if (str.length() > 5 && str.substr(0, 5) == "lang=") { std::string::size_type pos = str.find_first_of(' '); if (pos != std::string::npos) str = str.substr(pos+1); } return utf8_validate_or_convert(str.c_str()); } catch (Exiv2::AnyError& e) { return NULL; } } gint exif_item_get_integer(ExifItem *item, gint *value) { try { if (!item) return 0; *value = ((Exiv2::Metadatum *)item)->toLong(); return 1; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return 0; } } ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n) { try { if (!item) return NULL; if (n >= exif_item_get_elements(item)) return NULL; Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational(n); static ExifRational ret; ret.num = v.first; ret.den = v.second; if (sign) *sign = (((Exiv2::Metadatum *)item)->typeId() == Exiv2::signedRational); return &ret; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } gchar *exif_get_tag_description_by_key(const gchar *key) { try { Exiv2::ExifKey ekey(key); return utf8_validate_or_convert(Exiv2::ExifTags::tagLabel(ekey.tag(), ekey.ifdId ())); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } static const AltKey *find_alt_key(const gchar *xmp_key) { gint i = 0; while (alt_keys[i].xmp_key) { if (strcmp(xmp_key, alt_keys[i].xmp_key) == 0) return &alt_keys[i]; i++; } return NULL; } static gint exif_update_metadata_simple(ExifData *exif, const gchar *key, const GList *values) { try { const GList *work = values; try { Exiv2::ExifKey ekey(key); Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey); while (pos != exif->exifData().end()) { exif->exifData().erase(pos); pos = exif->exifData().findKey(ekey); } while (work) { exif->exifData()[key] = (gchar *)work->data; work = work->next; } } catch (Exiv2::AnyError& e) { #if EXIV2_TEST_VERSION(0,16,0) try #endif { Exiv2::IptcKey ekey(key); Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey); while (pos != exif->iptcData().end()) { exif->iptcData().erase(pos); pos = exif->iptcData().findKey(ekey); } while (work) { exif->iptcData()[key] = (gchar *)work->data; work = work->next; } } #if EXIV2_TEST_VERSION(0,16,0) catch (Exiv2::AnyError& e) { Exiv2::XmpKey ekey(key); Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey); while (pos != exif->xmpData().end()) { exif->xmpData().erase(pos); pos = exif->xmpData().findKey(ekey); } while (work) { exif->xmpData()[key] = (gchar *)work->data; work = work->next; } } #endif } return 1; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return 0; } } gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values) { gint ret = exif_update_metadata_simple(exif, key, values); if ( #if !EXIV2_TEST_VERSION(0,17,0) TRUE || /* no conversion support */ #endif !values || /* deleting item */ !ret /* writing to the explicitely given xmp tag failed */ ) { /* deleted xmp metadatum can't be converted, we have to delete also the corresponding legacy tag */ /* if we can't write xmp, update at least the legacy tag */ const AltKey *alt_key = find_alt_key(key); if (alt_key && alt_key->iptc_key) ret = exif_update_metadata_simple(exif, alt_key->iptc_key, values); if (alt_key && alt_key->exif_key) ret = exif_update_metadata_simple(exif, alt_key->exif_key, values); } return ret; } static GList *exif_add_value_to_glist(GList *list, Exiv2::Metadatum &item, MetadataFormat format, const Exiv2::ExifData *metadata) { #if EXIV2_TEST_VERSION(0,16,0) Exiv2::TypeId id = item.typeId(); if (format == METADATA_FORMATTED || id == Exiv2::asciiString || id == Exiv2::undefined || id == Exiv2::string || id == Exiv2::date || id == Exiv2::time || id == Exiv2::xmpText || id == Exiv2::langAlt || id == Exiv2::comment ) { #endif /* read as a single entry */ std::string str; if (format == METADATA_FORMATTED) { #if EXIV2_TEST_VERSION(0,17,0) str = item.print( #if EXIV2_TEST_VERSION(0,18,0) metadata #endif ); #else std::stringstream stream; Exiv2::Exifdatum *exifdatum; Exiv2::Iptcdatum *iptcdatum; #if EXIV2_TEST_VERSION(0,16,0) Exiv2::Xmpdatum *xmpdatum; #endif if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum))) stream << *exifdatum; else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum))) stream << *iptcdatum; #if EXIV2_TEST_VERSION(0,16,0) else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum))) stream << *xmpdatum; #endif str = stream.str(); #endif if (str.length() > 1024) { /* truncate very long strings, they cause problems in gui */ str.erase(1024); str.append("..."); } } else { str = item.toString(); if (str.length() > 5 && str.substr(0, 5) == "lang=") { std::string::size_type pos = str.find_first_of(' '); if (pos != std::string::npos) str = str.substr(pos+1); } } list = g_list_append(list, utf8_validate_or_convert(str.c_str())); #if EXIV2_TEST_VERSION(0,16,0) } else { /* read as a list */ gint i; for (i = 0; i < item.count(); i++) list = g_list_append(list, utf8_validate_or_convert(item.toString(i).c_str())); } #endif return list; } static GList *exif_get_metadata_simple(ExifData *exif, const gchar *key, MetadataFormat format) { GList *list = NULL; try { try { Exiv2::ExifKey ekey(key); Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey); if (pos != exif->exifData().end()) list = exif_add_value_to_glist(list, *pos, format, &exif->exifData()); } catch (Exiv2::AnyError& e) { try { Exiv2::IptcKey ekey(key); Exiv2::IptcData::iterator pos = exif->iptcData().begin(); while (pos != exif->iptcData().end()) { if (pos->key() == key) list = exif_add_value_to_glist(list, *pos, format, NULL); ++pos; } } catch (Exiv2::AnyError& e) { #if EXIV2_TEST_VERSION(0,16,0) Exiv2::XmpKey ekey(key); Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey); if (pos != exif->xmpData().end()) list = exif_add_value_to_glist(list, *pos, format, NULL); #endif } } } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; } return list; } GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format) { GList *list = NULL; if (!key) return NULL; if (format == METADATA_FORMATTED) { gchar *text; gint key_valid; text = exif_get_formatted_by_key(exif, key, &key_valid); if (key_valid) return g_list_append(NULL, text); } list = exif_get_metadata_simple(exif, key, format); /* the following code can be ifdefed out as soon as Exiv2 supports it */ if (!list) { const AltKey *alt_key = find_alt_key(key); if (alt_key && alt_key->iptc_key) list = exif_get_metadata_simple(exif, alt_key->iptc_key, format); #if !EXIV2_TEST_VERSION(0,17,0) /* with older Exiv2 versions exif is not synced */ if (!list && alt_key && alt_key->exif_key) list = exif_get_metadata_simple(exif, alt_key->exif_key, format); #endif } return list; } void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length) { exif->add_jpeg_color_profile(cp_data, cp_length); } guchar *exif_get_color_profile(ExifData *exif, guint *data_len) { guchar *ret = exif->get_jpeg_color_profile(data_len); if (ret) return ret; ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) ret = (guchar *)exif_item_get_data(prof_item, data_len); return ret; } #if EXIV2_TEST_VERSION(0,17,90) guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) { if (!exif) return NULL; const char* path = exif->image()->io().path().c_str(); /* given image pathname, first do simple (and fast) file extension test */ gboolean is_raw = filter_file_class(path, FORMAT_CLASS_RAWIMAGE); if (!is_raw && requested_width == 0) return NULL; try { Exiv2::PreviewManager pm(*exif->image()); Exiv2::PreviewPropertiesList list = pm.getPreviewProperties(); if (!list.empty()) { Exiv2::PreviewPropertiesList::iterator pos; Exiv2::PreviewPropertiesList::iterator last = --list.end(); if (requested_width == 0) { pos = last; // the largest } else { pos = list.begin(); while (pos != last) { if (pos->width_ >= (uint32_t)requested_width && pos->height_ >= (uint32_t)requested_height) break; ++pos; } // we are not interested in smaller thumbnails in normal image formats - we can use full image instead if (!is_raw) { if (pos->width_ < (uint32_t)requested_width || pos->height_ < (uint32_t)requested_height) return NULL; } } Exiv2::PreviewImage image = pm.getPreviewImage(*pos); Exiv2::DataBuf buf = image.copy(); std::pair<Exiv2::byte*, long> p = buf.release(); *data_len = p.second; return p.first; } return NULL; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return NULL; } } void exif_free_preview(guchar *buf) { delete[] (Exiv2::byte*)buf; } #endif } #if !EXIV2_TEST_VERSION(0,17,90) /* This is a dirty hack to support raw file preview, bassed on tiffparse.cpp from Exiv2 examples */ class RawFile { public: RawFile(Exiv2::BasicIo &io); ~RawFile(); const Exiv2::Value *find(uint16_t tag, uint16_t group); unsigned long preview_offset(); private: int type; Exiv2::TiffComponent::AutoPtr rootDir; Exiv2::BasicIo &io_; const Exiv2::byte *map_data; size_t map_len; unsigned long offset; }; typedef struct _UnmapData UnmapData; struct _UnmapData { guchar *ptr; guchar *map_data; size_t map_len; }; static GList *exif_unmap_list = 0; extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) { unsigned long offset; if (!exif) return NULL; const char* path = exif->image()->io().path().c_str(); /* given image pathname, first do simple (and fast) file extension test */ if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL; try { struct stat st; guchar *map_data; size_t map_len; UnmapData *ud; int fd; RawFile rf(exif->image()->io()); offset = rf.preview_offset(); DEBUG_1("%s: offset %lu", path, offset); fd = open(path, O_RDONLY); if (fd == -1) { return NULL; } if (fstat(fd, &st) == -1) { close(fd); return NULL; } map_len = st.st_size; map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (map_data == MAP_FAILED) { return NULL; } *data_len = map_len - offset; ud = g_new(UnmapData, 1); ud->ptr = map_data + offset; ud->map_data = map_data; ud->map_len = map_len; exif_unmap_list = g_list_prepend(exif_unmap_list, ud); return ud->ptr; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; } return NULL; } void exif_free_preview(guchar *buf) { GList *work = exif_unmap_list; while (work) { UnmapData *ud = (UnmapData *)work->data; if (ud->ptr == buf) { munmap(ud->map_data, ud->map_len); exif_unmap_list = g_list_remove_link(exif_unmap_list, work); g_free(ud); return; } work = work->next; } g_assert_not_reached(); } using namespace Exiv2; RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0) { /* struct stat st; if (fstat(fd, &st) == -1) { throw Error(14); } map_len = st.st_size; map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0); if (map_data == MAP_FAILED) { throw Error(14); } */ if (io.open() != 0) { throw Error(9, io.path(), strError()); } map_data = io.mmap(); map_len = io.size(); type = Exiv2::ImageFactory::getType(map_data, map_len); #if EXIV2_TEST_VERSION(0,16,0) TiffHeaderBase *tiffHeader = NULL; #else TiffHeade2 *tiffHeader = NULL; #endif Cr2Header *cr2Header = NULL; switch (type) { case Exiv2::ImageType::tiff: tiffHeader = new TiffHeade2(); break; case Exiv2::ImageType::cr2: cr2Header = new Cr2Header(); break; #if EXIV2_TEST_VERSION(0,16,0) case Exiv2::ImageType::orf: tiffHeader = new OrfHeader(); break; #endif #if EXIV2_TEST_VERSION(0,13,0) case Exiv2::ImageType::raf: if (map_len < 84 + 4) throw Error(14); offset = getULong(map_data + 84, bigEndian); return; #endif case Exiv2::ImageType::crw: { // Parse the image, starting with a CIFF header component Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader); parseTree->read(map_data, map_len); CiffComponent *entry = parseTree->findComponent(0x2007, 0); if (entry) offset = entry->pData() - map_data; return; } default: throw Error(3, "RAW"); } // process tiff-like formats TiffCompFactoryFct createFct = TiffCreator::create; rootDir = createFct(Tag::root, Group::none); if (0 == rootDir.get()) { throw Error(1, "No root element defined in TIFF structure"); } if (tiffHeader) { if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF"); #if EXIV2_TEST_VERSION(0,16,0) rootDir->setStart(map_data + tiffHeader->offset()); #else rootDir->setStart(map_data + tiffHeader->ifdOffset()); #endif } if (cr2Header) { rootDir->setStart(map_data + cr2Header->offset()); } TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct)); TiffReader reader(map_data, map_len, rootDir.get(), state); rootDir->accept(reader); if (tiffHeader) delete tiffHeader; if (cr2Header) delete cr2Header; } RawFile::~RawFile(void) { io_.munmap(); io_.close(); } const Value * RawFile::find(uint16_t tag, uint16_t group) { TiffFinder finder(tag, group); rootDir->accept(finder); TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result()); if (te) { DEBUG_1("(tag: %04x %04x) ", tag, group); return te->pValue(); } else return NULL; } unsigned long RawFile::preview_offset(void) { const Value *val; if (offset) return offset; if (type == Exiv2::ImageType::cr2) { val = find(0x111, Group::ifd0); if (val) return val->toLong(); return 0; } val = find(0x201, Group::sub0_0); if (val) return val->toLong(); val = find(0x201, Group::ifd0); if (val) return val->toLong(); val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2 if (val) return val->toLong(); val = find(0x111, Group::sub0_1); // dng if (val) return val->toLong(); return 0; } #endif #endif /* HAVE_EXIV2 */ /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */