gqview.h -> main.h
[geeqie.git] / src / exiv2.cc
index 406e6f3..3b40488 100644 (file)
@@ -9,6 +9,13 @@
 #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 <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>
 
 
 
 extern "C" {
 #include <glib.h> 
+#include "main.h"
 #include "exif.h"
+#include "filelist.h"
 
 }
 
 struct _ExifData
 {
        Exiv2::Image::AutoPtr image;
+       Exiv2::Image::AutoPtr sidecar;
        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
+       bool have_sidecar;
+
 
-       _ExifData(gchar *path, gint parse_color_profile)
+       _ExifData(gchar *path, gchar *sidecar_path, gint parse_color_profile)
        {
+               have_sidecar = false;
                image = Exiv2::ImageFactory::open(path);
 //             g_assert (image.get() != 0);
                image->readMetadata();
+
+#if EXIV2_TEST_VERSION(0,16,0)
+               printf("xmp count %d\n", image->xmpData().count());
+               if (sidecar_path && image->xmpData().empty())
+                       {
+                       sidecar = Exiv2::ImageFactory::open(sidecar_path);
+                       sidecar->readMetadata();
+                       have_sidecar = sidecar->good();
+                       printf("sidecar xmp count %d\n", sidecar->xmpData().count());
+                       }
+               
+#endif
        }
        
        void writeMetadata()
        {
+               if (have_sidecar) sidecar->writeMetadata();
                image->writeMetadata();
        }
        
+       Exiv2::ExifData &exifData ()
+       {
+               return image->exifData();
+       }
+
+       Exiv2::IptcData &iptcData ()
+       {
+               return image->iptcData();
+       }
+
+#if EXIV2_TEST_VERSION(0,16,0)
+       Exiv2::XmpData &xmpData ()
+       {
+               return have_sidecar ? sidecar->xmpData() : image->xmpData();
+       }
+#endif
 
 };
 
 extern "C" {
 
-ExifData *exif_read(gchar *path, gint parse_color_profile)
+ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
 {
-       printf("exif %s\n", path);
+       if (debug) printf("exif read %s,  sidecar: %s\n", path, sidecar_path ? sidecar_path : "-");
        try {
-               return new ExifData(path, parse_color_profile);
+               return new ExifData(path, sidecar_path, parse_color_profile);
        }
        catch (Exiv2::AnyError& e) {
                std::cout << "Caught Exiv2 exception '" << e << "'\n";
-               return 0;
+               return NULL;
        }
        
 }
@@ -95,22 +144,24 @@ ExifItem *exif_get_item(ExifData *exif, const gchar *key)
                Exiv2::Metadatum *item;
                try {
                        Exiv2::ExifKey ekey(key);
-                       Exiv2::ExifData::iterator pos = exif->image->exifData().findKey(ekey);
-                       if (pos == exif->image->exifData().end()) return NULL;
+                       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->image->iptcData().findKey(ekey);
-                               if (pos == exif->image->iptcData().end()) return NULL;
+                               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->image->xmpData().findKey(ekey);
-                               if (pos == exif->image->xmpData().end()) return NULL;
+                               Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
+                               if (pos == exif->xmpData().end()) return NULL;
                                item = &*pos;
+#endif
                        }
                }
                return (ExifItem *)item;
@@ -127,25 +178,27 @@ ExifItem *exif_add_item(ExifData *exif, const gchar *key)
                Exiv2::Metadatum *item;
                try {
                        Exiv2::ExifKey ekey(key);
-                       exif->image->exifData().add(ekey, NULL);
-                       Exiv2::ExifData::iterator pos = exif->image->exifData().end(); // a hack, there should be a better way to get the currently added item
+                       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->image->iptcData().add(ekey, NULL);
-                               Exiv2::IptcData::iterator pos = exif->image->iptcData().end();
+                               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->image->xmpData().add(ekey, NULL);
-                               Exiv2::XmpData::iterator pos = exif->image->xmpData().end();
+                               exif->xmpData().add(ekey, NULL);
+                               Exiv2::XmpData::iterator pos = exif->xmpData().end();
                                pos--;
                                item = &*pos;
+#endif
                        }
                }
                return (ExifItem *)item;
@@ -160,27 +213,31 @@ ExifItem *exif_add_item(ExifData *exif, const gchar *key)
 ExifItem *exif_get_first_item(ExifData *exif)
 {
        try {
-               exif->exifIter = exif->image->exifData().begin();
-               exif->iptcIter = exif->image->iptcData().begin();
-               exif->xmpIter = exif->image->xmpData().begin();
-               if (exif->exifIter != exif->image->exifData().end()) 
+               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->image->iptcData().end()) 
+               if (exif->iptcIter != exif->iptcData().end()) 
                        {
                        const Exiv2::Metadatum *item = &*exif->iptcIter;
                        exif->iptcIter++;
                        return (ExifItem *)item;
                        }
-               if (exif->xmpIter != exif->image->xmpData().end()) 
+#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;
                        
        }
@@ -193,24 +250,26 @@ ExifItem *exif_get_first_item(ExifData *exif)
 ExifItem *exif_get_next_item(ExifData *exif)
 {
        try {
-               if (exif->exifIter != exif->image->exifData().end())
+               if (exif->exifIter != exif->exifData().end())
                        {
                        const Exiv2::Metadatum *item = &*exif->exifIter;
                        exif->exifIter++;
                        return (ExifItem *)item;
                }
-               if (exif->iptcIter != exif->image->iptcData().end())
+               if (exif->iptcIter != exif->iptcData().end())
                        {
                        const Exiv2::Metadatum *item = &*exif->iptcIter;
                        exif->iptcIter++;
                        return (ExifItem *)item;
                }
-               if (exif->xmpIter != exif->image->xmpData().end())
+#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) {
@@ -345,6 +404,30 @@ gchar *exif_item_get_data_as_text(ExifItem *item)
        }
 }
 
+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 g_strdup(str.c_str());
+       }
+       catch (Exiv2::AnyError& e) {
+               return NULL;
+       }
+}
+
 
 gint exif_item_get_integer(ExifItem *item, gint *value)
 {
@@ -403,25 +486,26 @@ int exif_item_delete(ExifData *exif, ExifItem *item)
 {
        try {
                if (!item) return 0;
-               for (Exiv2::ExifData::iterator i = exif->image->exifData().begin(); i != exif->image->exifData().end(); ++i) {
+               for (Exiv2::ExifData::iterator i = exif->exifData().begin(); i != exif->exifData().end(); ++i) {
                        if (((Exiv2::Metadatum *)item) == &*i) {
-                               i = exif->image->exifData().erase(i);
+                               i = exif->exifData().erase(i);
                                return 1;
                        }
                }
-               for (Exiv2::IptcData::iterator i = exif->image->iptcData().begin(); i != exif->image->iptcData().end(); ++i) {
+               for (Exiv2::IptcData::iterator i = exif->iptcData().begin(); i != exif->iptcData().end(); ++i) {
                        if (((Exiv2::Metadatum *)item) == &*i) {
-                               i = exif->image->iptcData().erase(i);
+                               i = exif->iptcData().erase(i);
                                return 1;
                        }
                }
-               for (Exiv2::XmpData::iterator i = exif->image->xmpData().begin(); i != exif->image->xmpData().end(); ++i) {
+#if EXIV2_TEST_VERSION(0,16,0)
+               for (Exiv2::XmpData::iterator i = exif->xmpData().begin(); i != exif->xmpData().end(); ++i) {
                        if (((Exiv2::Metadatum *)item) == &*i) {
-                               i = exif->image->xmpData().erase(i);
+                               i = exif->xmpData().erase(i);
                                return 1;
                        }
                }
-               
+#endif         
                return 0;
        }
        catch (Exiv2::AnyError& e) {
@@ -471,28 +555,46 @@ RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
                }
        type = Exiv2::ImageFactory::getType(map_data, map_len);
 
-       TiffHeaderBase *tiffHeader;
+#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:
-                       tiffHeader = new Cr2Header();
+                       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
-       if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
 
        TiffCompFactoryFct createFct = TiffCreator::create;
 
@@ -500,10 +602,23 @@ RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
        if (0 == rootDir.get()) {
                throw Error(1, "No root element defined in TIFF structure");
        }
-       rootDir->setStart(map_data + tiffHeader->offset());
-
-       TiffRwState::AutoPtr state(
-               new TiffRwState(tiffHeader->byteOrder(), 0, createFct));
+       
+       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,
@@ -512,7 +627,10 @@ RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
 
        rootDir->accept(reader);
        
-       delete tiffHeader;
+       if (tiffHeader) 
+               delete tiffHeader;
+       if (cr2Header) 
+               delete cr2Header;
 }
 
 RawFile::~RawFile()
@@ -525,12 +643,14 @@ RawFile::~RawFile()
 
 const Value * RawFile::find(uint16_t tag, uint16_t group)
 {
-       printf("%04x %04x\n", tag, group);
        TiffFinder finder(tag, group);
        rootDir->accept(finder);
        TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
        if (te)
+               {
+               if (debug) printf("(tag: %04x %04x) ", tag, group);
                return te->pValue();
+               }
        else
                return NULL;
 }
@@ -564,8 +684,6 @@ unsigned long RawFile::preview_offset()
 }
 
 
-const static char *raw_ext_list[] = { ".cr2", ".nef", ".pef", ".arw", NULL };
-
 extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
                                    unsigned char *header_data, const guint header_len,
                                    guint *image_offset, guint *exif_offset)
@@ -574,38 +692,20 @@ extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
        unsigned long offset;
 
        /* given image pathname, first do simple (and fast) file extension test */
-/*     if (path)
-               {
-               const gchar *ext;
-               gint match = FALSE;
-               gint i;
-
-               ext = strrchr(path, '.');
-               if (!ext) return FALSE;
-               
-               for (i = 0; raw_ext_list[i]; i++) 
-                       {
-                       if (strcasecmp(raw_ext_list[i], ext) == 0)
-                               {
-                               match = TRUE;
-                               break;
-                               }
-                       }
-
-               if (!match) return FALSE;
+       if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return 0;
 
-               }
-*/
        try {
                RawFile rf(fd);
+               if (debug) printf("%s: offset ", path);
                offset = rf.preview_offset();
+               if (debug) printf("%d\n", offset);
        }
        catch (Exiv2::AnyError& e) {
                std::cout << "Caught Exiv2 exception '" << e << "'\n";
                return 0;
        }
 
-       if (image_offset)
+       if (image_offset && offset > 0)
                {
                *image_offset = offset;
                if (lseek(fd, *image_offset, SEEK_SET) != *image_offset)