added more IPTC tags
[geeqie.git] / src / exiv2.cc
1 /*
2  * Geeqie
3  * Copyright (C) 2008 - 2009 The Geeqie Team
4  *
5  * Author: Vladimir Nadvornik
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "config.h"
13
14 #ifdef HAVE_EXIV2
15
16 #include <exiv2/image.hpp>
17 #include <exiv2/exif.hpp>
18 #include <iostream>
19
20 // EXIV2_TEST_VERSION is defined in Exiv2 0.15 and newer.
21 #ifndef EXIV2_TEST_VERSION
22 # define EXIV2_TEST_VERSION(major,minor,patch) \
23         ( EXIV2_VERSION >= EXIV2_MAKE_VERSION(major,minor,patch) )
24 #endif
25
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/mman.h>
32
33 #if !EXIV2_TEST_VERSION(0,17,90)
34 #include <exiv2/tiffparser.hpp>
35 #include <exiv2/tiffcomposite.hpp>
36 #include <exiv2/tiffvisitor.hpp>
37 #include <exiv2/tiffimage.hpp>
38 #include <exiv2/cr2image.hpp>
39 #include <exiv2/crwimage.hpp>
40 #if EXIV2_TEST_VERSION(0,16,0)
41 #include <exiv2/orfimage.hpp>
42 #endif
43 #if EXIV2_TEST_VERSION(0,13,0)
44 #include <exiv2/rafimage.hpp>
45 #endif
46 #include <exiv2/futils.hpp>
47 #else
48 #include <exiv2/preview.hpp>
49 #endif
50
51 #if EXIV2_TEST_VERSION(0,17,0)
52 #include <exiv2/convert.hpp>
53 #include <exiv2/xmpsidecar.hpp>
54 #endif
55
56 extern "C" {
57 #include <glib.h>
58
59 #include "main.h"
60 #include "exif.h"
61
62 #include "filefilter.h"
63 #include "ui_fileops.h"
64
65 #include "misc.h"
66 }
67
68 typedef struct _AltKey AltKey;
69
70 struct _AltKey
71 {
72         const gchar *xmp_key;
73         const gchar *exif_key;
74         const gchar *iptc_key;
75 };
76
77 /* this is a list of keys that should be converted, even with the older Exiv2 which does not support it directly */
78 static const AltKey alt_keys[] = {
79         {"Xmp.tiff.Orientation",                "Exif.Image.Orientation",       NULL},
80         {"Xmp.dc.title",                        NULL,                           "Iptc.Application2.ObjectName"          },
81         {"Xmp.photoshop.Urgency",               NULL,                           "Iptc.Application2.Urgency"             },
82         {"Xmp.photoshop.Category",              NULL,                           "Iptc.Application2.Category"            },
83         {"Xmp.photoshop.SupplementalCategory",  NULL,                           "Iptc.Application2.SuppCategory"        },
84         {"Xmp.dc.subject",                      NULL,                           "Iptc.Application2.Keywords"            },
85         {"Xmp.iptc.Location",                   NULL,                           "Iptc.Application2.LocationName"        },
86         {"Xmp.photoshop.Instruction",           NULL,                           "Iptc.Application2.SpecialInstructions" },
87         {"Xmp.photoshop.DateCreated",           NULL,                           "Iptc.Application2.DateCreated"         },
88         {"Xmp.dc.creator",                      NULL,                           "Iptc.Application2.Byline"              },
89         {"Xmp.photoshop.AuthorsPosition",       NULL,                           "Iptc.Application2.BylineTitle"         },
90         {"Xmp.photoshop.City",                  NULL,                           "Iptc.Application2.City"                },
91         {"Xmp.photoshop.State",                 NULL,                           "Iptc.Application2.ProvinceState"       },
92         {"Xmp.iptc.CountryCode",                NULL,                           "Iptc.Application2.CountryCode"         },
93         {"Xmp.photoshop.Country",               NULL,                           "Iptc.Application2.CountryName"         },
94         {"Xmp.photoshop.TransmissionReference", NULL,                           "Iptc.Application2.TransmissionReference"},
95         {"Xmp.photoshop.Headline",              NULL,                           "Iptc.Application2.Headline"            },
96         {"Xmp.photoshop.Credit",                NULL,                           "Iptc.Application2.Credit"              },
97         {"Xmp.photoshop.Source",                NULL,                           "Iptc.Application2.Source"              },
98         {"Xmp.dc.rights",                       NULL,                           "Iptc.Application2.Copyright"           },
99         {"Xmp.dc.description",                  NULL,                           "Iptc.Application2.Caption"             },
100         {"Xmp.photoshop.CaptionWriter",         NULL,                           "Iptc.Application2.Writer"              },
101         {NULL, NULL, NULL}
102         };
103
104 struct _ExifData
105 {
106         Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
107         Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
108 #if EXIV2_TEST_VERSION(0,16,0)
109         Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
110 #endif
111
112         virtual ~_ExifData()
113         {
114         }
115         
116         virtual void writeMetadata(gchar *path = NULL)
117         {
118                 g_critical("Unsupported method of writing metadata");
119         }
120
121         virtual ExifData *original()
122         {
123                 return NULL;
124         }
125
126         virtual Exiv2::Image *image() = 0;
127         
128         virtual Exiv2::ExifData &exifData() = 0;
129
130         virtual Exiv2::IptcData &iptcData() = 0;
131
132 #if EXIV2_TEST_VERSION(0,16,0)
133         virtual Exiv2::XmpData &xmpData() = 0;
134 #endif
135
136         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0;
137
138         virtual guchar *get_jpeg_color_profile(guint *data_len) = 0;
139 };
140
141 // This allows read-only access to the original metadata
142 struct _ExifDataOriginal : public _ExifData
143 {
144 protected:
145         Exiv2::Image::AutoPtr image_;
146
147         /* the icc profile in jpeg is not technically exif - store it here */
148         unsigned char *cp_data_;
149         guint cp_length_;
150         gboolean valid_;
151
152         Exiv2::ExifData emptyExifData_;
153         Exiv2::IptcData emptyIptcData_;
154 #if EXIV2_TEST_VERSION(0,16,0)
155         Exiv2::XmpData emptyXmpData_;
156 #endif
157
158 public:
159         _ExifDataOriginal(Exiv2::Image::AutoPtr image)
160         {
161                 cp_data_ = NULL;
162                 cp_length_ = 0;
163                 image_ = image;
164                 valid_ = TRUE;
165         }
166
167         _ExifDataOriginal(gchar *path)
168         {
169                 cp_data_ = NULL;
170                 cp_length_ = 0;
171                 valid_ = TRUE;
172                 
173                 gchar *pathl = path_from_utf8(path);
174                 try 
175                         {
176                         image_ = Exiv2::ImageFactory::open(pathl);
177 //                      g_assert (image.get() != 0);
178                         image_->readMetadata();
179
180 #if EXIV2_TEST_VERSION(0,16,0)
181                         if (image_->mimeType() == "application/rdf+xml")
182                                 {
183                                 //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
184                                 image_->clearExifData();
185                                 image_->clearIptcData();
186                                 }
187 #endif
188
189 #if EXIV2_TEST_VERSION(0,14,0)
190                         if (image_->mimeType() == "image/jpeg")
191                                 {
192                                 /* try to get jpeg color profile */
193                                 Exiv2::BasicIo &io = image_->io();
194                                 gint open = io.isopen();
195                                 if (!open) io.open();
196                                 if (io.isopen())
197                                         {
198                                         unsigned char *mapped = (unsigned char*)io.mmap();
199                                         if (mapped) exif_jpeg_parse_color(this, mapped, io.size());
200                                         io.munmap();
201                                         }
202                                 if (!open) io.close();
203                                 }
204 #endif
205                         }
206                 catch (Exiv2::AnyError& e) 
207                         {
208                         valid_ = FALSE;
209                         }
210                 g_free(pathl);
211         }
212         
213         virtual ~_ExifDataOriginal()
214         {
215                 if (cp_data_) g_free(cp_data_);
216         }
217         
218         virtual Exiv2::Image *image()
219         {
220                 if (!valid_) return NULL;
221                 return image_.get();
222         }
223         
224         virtual Exiv2::ExifData &exifData ()
225         {
226                 if (!valid_) return emptyExifData_;
227                 return image_->exifData();
228         }
229
230         virtual Exiv2::IptcData &iptcData ()
231         {
232                 if (!valid_) return emptyIptcData_;
233                 return image_->iptcData();
234         }
235
236 #if EXIV2_TEST_VERSION(0,16,0)
237         virtual Exiv2::XmpData &xmpData ()
238         {
239                 if (!valid_) return emptyXmpData_;
240                 return image_->xmpData();
241         }
242 #endif
243
244         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
245         {
246                 if (cp_data_) g_free(cp_data_);
247                 cp_data_ = cp_data;
248                 cp_length_ = cp_length;
249         }
250
251         virtual guchar *get_jpeg_color_profile(guint *data_len)
252         {
253                 if (cp_data_)
254                 {
255                         if (data_len) *data_len = cp_length_;
256                         return (unsigned char *) g_memdup(cp_data_, cp_length_);
257                 }
258                 return NULL;
259         }
260 };
261
262 extern "C" {
263 static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data);
264 }
265
266 // This allows read-write access to the metadata
267 struct _ExifDataProcessed : public _ExifData
268 {
269 protected:
270         _ExifDataOriginal *imageData_;
271         _ExifDataOriginal *sidecarData_;
272
273         Exiv2::ExifData exifData_;
274         Exiv2::IptcData iptcData_;
275 #if EXIV2_TEST_VERSION(0,16,0)
276         Exiv2::XmpData xmpData_;
277 #endif
278
279 public:
280         _ExifDataProcessed(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp)
281         {
282                 imageData_ = new _ExifDataOriginal(path);
283                 sidecarData_ = NULL;
284 #if EXIV2_TEST_VERSION(0,16,0)
285                 if (sidecar_path)
286                         {
287                         sidecarData_ = new _ExifDataOriginal(sidecar_path);
288                         xmpData_ = sidecarData_->xmpData();
289                         }
290                 else
291                         {
292                         xmpData_ = imageData_->xmpData();
293                         }
294
295 #endif
296                 exifData_ = imageData_->exifData();
297                 iptcData_ = imageData_->iptcData();
298 #if EXIV2_TEST_VERSION(0,17,0)
299                 syncExifWithXmp(exifData_, xmpData_);
300 #endif
301                 if (modified_xmp)
302                         {
303                         g_hash_table_foreach(modified_xmp, _ExifDataProcessed_update_xmp, this);
304                         }
305         }
306
307         virtual ~_ExifDataProcessed()
308         {
309                 if (imageData_) delete imageData_;
310                 if (sidecarData_) delete sidecarData_;
311         }
312
313         virtual ExifData *original()
314         {
315                 return imageData_;
316         }
317
318         virtual void writeMetadata(gchar *path = NULL)
319         {
320                 if (!path)
321                         {
322 #if EXIV2_TEST_VERSION(0,17,0)
323                         if (options->metadata.save_legacy_IPTC) 
324                                 copyXmpToIptc(xmpData_, iptcData_);
325                         else
326                                 iptcData_.clear();
327
328                         copyXmpToExif(xmpData_, exifData_);
329 #endif
330                         Exiv2::Image *image = imageData_->image();
331                         
332                         if (!image) Exiv2::Error(21);
333                         image->setExifData(exifData_);
334                         image->setIptcData(iptcData_);
335 #if EXIV2_TEST_VERSION(0,16,0)
336                         image->setXmpData(xmpData_);
337 #endif
338                         image->writeMetadata();
339                         } 
340                 else
341                         {
342 #if EXIV2_TEST_VERSION(0,17,0)
343                         gchar *pathl = path_from_utf8(path);;
344
345                         Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl);
346                                 
347                         g_free(pathl);
348
349                         sidecar->setXmpData(xmpData_);
350                         sidecar->writeMetadata();
351 #else
352                         throw Exiv2::Error(3, "xmp");
353 #endif
354                         }
355         }
356         
357         virtual Exiv2::Image *image()
358         {
359                 return imageData_->image();
360         }
361         
362         virtual Exiv2::ExifData &exifData ()
363         {
364                 return exifData_;
365         }
366
367         virtual Exiv2::IptcData &iptcData ()
368         {
369                 return iptcData_;
370         }
371
372 #if EXIV2_TEST_VERSION(0,16,0)
373         virtual Exiv2::XmpData &xmpData ()
374         {
375                 return xmpData_;
376         }
377 #endif
378
379         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
380         {
381                 imageData_->add_jpeg_color_profile(cp_data, cp_length);
382         }
383
384         virtual guchar *get_jpeg_color_profile(guint *data_len)
385         {
386                 return imageData_->get_jpeg_color_profile(data_len);
387         }
388 };
389
390
391
392
393 extern "C" {
394
395
396 void exif_init(void)
397 {
398 #ifdef EXV_ENABLE_NLS
399         bind_textdomain_codeset (EXV_PACKAGE, "UTF-8");
400 #endif
401 }
402
403
404
405 static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data)
406 {
407         exif_update_metadata((ExifData *)data, (gchar *)key, (GList *)value);
408 }
409
410 ExifData *exif_read(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp)
411 {
412         DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-");
413         try {
414                 return new _ExifDataProcessed(path, sidecar_path, modified_xmp);
415         }
416         catch (Exiv2::AnyError& e) {
417                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
418                 return NULL;
419         }
420         
421 }
422
423 gboolean exif_write(ExifData *exif)
424 {
425         try {
426                 exif->writeMetadata();
427                 return TRUE;
428         }
429         catch (Exiv2::AnyError& e) {
430                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
431                 return FALSE;
432         }
433 }
434
435 gboolean exif_write_sidecar(ExifData *exif, gchar *path)
436 {
437         try {
438                 exif->writeMetadata(path);
439                 return TRUE;
440         }
441         catch (Exiv2::AnyError& e) {
442                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
443                 return FALSE;
444         }
445         
446 }
447
448
449 void exif_free(ExifData *exif)
450 {
451         if (!exif) return;
452         g_assert(dynamic_cast<_ExifDataProcessed *>(exif)); // this should not be called on ExifDataOriginal
453         delete exif;
454 }
455
456 ExifData *exif_get_original(ExifData *exif)
457 {
458         return exif->original();
459 }
460
461
462 ExifItem *exif_get_item(ExifData *exif, const gchar *key)
463 {
464         try {
465                 Exiv2::Metadatum *item = NULL;
466                 try {
467                         Exiv2::ExifKey ekey(key);
468                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
469                         if (pos == exif->exifData().end()) return NULL;
470                         item = &*pos;
471                 }
472                 catch (Exiv2::AnyError& e) {
473                         try {
474                                 Exiv2::IptcKey ekey(key);
475                                 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
476                                 if (pos == exif->iptcData().end()) return NULL;
477                                 item = &*pos;
478                         }
479                         catch (Exiv2::AnyError& e) {
480 #if EXIV2_TEST_VERSION(0,16,0)
481                                 Exiv2::XmpKey ekey(key);
482                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
483                                 if (pos == exif->xmpData().end()) return NULL;
484                                 item = &*pos;
485 #endif
486                         }
487                 }
488                 return (ExifItem *)item;
489         }
490         catch (Exiv2::AnyError& e) {
491                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
492                 return NULL;
493         }
494 }
495
496 ExifItem *exif_add_item(ExifData *exif, const gchar *key)
497 {
498         try {
499                 Exiv2::Metadatum *item = NULL;
500                 try {
501                         Exiv2::ExifKey ekey(key);
502                         exif->exifData().add(ekey, NULL);
503                         Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item
504                         pos--;
505                         item = &*pos;
506                 }
507                 catch (Exiv2::AnyError& e) {
508                         try {
509                                 Exiv2::IptcKey ekey(key);
510                                 exif->iptcData().add(ekey, NULL);
511                                 Exiv2::IptcData::iterator pos = exif->iptcData().end();
512                                 pos--;
513                                 item = &*pos;
514                         }
515                         catch (Exiv2::AnyError& e) {
516 #if EXIV2_TEST_VERSION(0,16,0)
517                                 Exiv2::XmpKey ekey(key);
518                                 exif->xmpData().add(ekey, NULL);
519                                 Exiv2::XmpData::iterator pos = exif->xmpData().end();
520                                 pos--;
521                                 item = &*pos;
522 #endif
523                         }
524                 }
525                 return (ExifItem *)item;
526         }
527         catch (Exiv2::AnyError& e) {
528                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
529                 return NULL;
530         }
531 }
532
533
534 ExifItem *exif_get_first_item(ExifData *exif)
535 {
536         try {
537                 exif->exifIter = exif->exifData().begin();
538                 exif->iptcIter = exif->iptcData().begin();
539 #if EXIV2_TEST_VERSION(0,16,0)
540                 exif->xmpIter = exif->xmpData().begin();
541 #endif
542                 if (exif->exifIter != exif->exifData().end())
543                         {
544                         const Exiv2::Metadatum *item = &*exif->exifIter;
545                         exif->exifIter++;
546                         return (ExifItem *)item;
547                         }
548                 if (exif->iptcIter != exif->iptcData().end())
549                         {
550                         const Exiv2::Metadatum *item = &*exif->iptcIter;
551                         exif->iptcIter++;
552                         return (ExifItem *)item;
553                         }
554 #if EXIV2_TEST_VERSION(0,16,0)
555                 if (exif->xmpIter != exif->xmpData().end())
556                         {
557                         const Exiv2::Metadatum *item = &*exif->xmpIter;
558                         exif->xmpIter++;
559                         return (ExifItem *)item;
560                         }
561 #endif
562                 return NULL;
563                         
564         }
565         catch (Exiv2::AnyError& e) {
566                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
567                 return NULL;
568         }
569 }
570
571 ExifItem *exif_get_next_item(ExifData *exif)
572 {
573         try {
574                 if (exif->exifIter != exif->exifData().end())
575                         {
576                         const Exiv2::Metadatum *item = &*exif->exifIter;
577                         exif->exifIter++;
578                         return (ExifItem *)item;
579                 }
580                 if (exif->iptcIter != exif->iptcData().end())
581                         {
582                         const Exiv2::Metadatum *item = &*exif->iptcIter;
583                         exif->iptcIter++;
584                         return (ExifItem *)item;
585                 }
586 #if EXIV2_TEST_VERSION(0,16,0)
587                 if (exif->xmpIter != exif->xmpData().end())
588                         {
589                         const Exiv2::Metadatum *item = &*exif->xmpIter;
590                         exif->xmpIter++;
591                         return (ExifItem *)item;
592                 }
593 #endif
594                 return NULL;
595         }
596         catch (Exiv2::AnyError& e) {
597                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
598                 return NULL;
599         }
600 }
601
602 char *exif_item_get_tag_name(ExifItem *item)
603 {
604         try {
605                 if (!item) return NULL;
606                 return g_strdup(((Exiv2::Metadatum *)item)->key().c_str());
607         }
608         catch (Exiv2::AnyError& e) {
609                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
610                 return NULL;
611         }
612 }
613
614 guint exif_item_get_tag_id(ExifItem *item)
615 {
616         try {
617                 if (!item) return 0;
618                 return ((Exiv2::Metadatum *)item)->tag();
619         }
620         catch (Exiv2::AnyError& e) {
621                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
622                 return 0;
623         }
624 }
625
626 guint exif_item_get_elements(ExifItem *item)
627 {
628         try {
629                 if (!item) return 0;
630                 return ((Exiv2::Metadatum *)item)->count();
631         }
632         catch (Exiv2::AnyError& e) {
633                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
634                 return 0;
635         }
636 }
637
638 char *exif_item_get_data(ExifItem *item, guint *data_len)
639 {
640         try {
641                 if (!item) return 0;
642                 Exiv2::Metadatum *md = (Exiv2::Metadatum *)item;
643                 if (data_len) *data_len = md->size();
644                 char *data = (char *)g_malloc(md->size());
645                 long res = md->copy((Exiv2::byte *)data, Exiv2::littleEndian /* should not matter */);
646                 g_assert(res == md->size());
647                 return data;
648         }
649         catch (Exiv2::AnyError& e) {
650                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
651                 return NULL;
652         }
653 }
654
655 char *exif_item_get_description(ExifItem *item)
656 {
657         try {
658                 if (!item) return NULL;
659                 return utf8_validate_or_convert(((Exiv2::Metadatum *)item)->tagLabel().c_str());
660         }
661         catch (std::exception& e) {
662 //              std::cout << "Caught Exiv2 exception '" << e << "'\n";
663                 return NULL;
664         }
665 }
666
667 /*
668 invalidTypeId, unsignedByte, asciiString, unsignedShort,
669   unsignedLong, unsignedRational, signedByte, undefined,
670   signedShort, signedLong, signedRational, string,
671   date, time, comment, directory,
672   xmpText, xmpAlt, xmpBag, xmpSeq,
673   langAlt, lastTypeId
674 */
675
676 static guint format_id_trans_tbl [] = {
677         EXIF_FORMAT_UNKNOWN,
678         EXIF_FORMAT_BYTE_UNSIGNED,
679         EXIF_FORMAT_STRING,
680         EXIF_FORMAT_SHORT_UNSIGNED,
681         EXIF_FORMAT_LONG_UNSIGNED,
682         EXIF_FORMAT_RATIONAL_UNSIGNED,
683         EXIF_FORMAT_BYTE,
684         EXIF_FORMAT_UNDEFINED,
685         EXIF_FORMAT_SHORT,
686         EXIF_FORMAT_LONG,
687         EXIF_FORMAT_RATIONAL,
688         EXIF_FORMAT_STRING,
689         EXIF_FORMAT_STRING,
690         EXIF_FORMAT_STRING,
691         EXIF_FORMAT_UNDEFINED,
692         EXIF_FORMAT_STRING,
693         EXIF_FORMAT_STRING,
694         EXIF_FORMAT_STRING,
695         EXIF_FORMAT_STRING
696         };
697         
698         
699
700 guint exif_item_get_format_id(ExifItem *item)
701 {
702         try {
703                 if (!item) return EXIF_FORMAT_UNKNOWN;
704                 guint id = ((Exiv2::Metadatum *)item)->typeId();
705                 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
706                 return format_id_trans_tbl[id];
707         }
708         catch (Exiv2::AnyError& e) {
709                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
710                 return EXIF_FORMAT_UNKNOWN;
711         }
712 }
713
714 const char *exif_item_get_format_name(ExifItem *item, gboolean brief)
715 {
716         try {
717                 if (!item) return NULL;
718                 return ((Exiv2::Metadatum *)item)->typeName();
719         }
720         catch (Exiv2::AnyError& e) {
721                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
722                 return NULL;
723         }
724 }
725
726
727 gchar *exif_item_get_data_as_text(ExifItem *item)
728 {
729         try {
730                 if (!item) return NULL;
731                 Exiv2::Metadatum *metadatum = (Exiv2::Metadatum *)item;
732 #if EXIV2_TEST_VERSION(0,17,0)
733                 return utf8_validate_or_convert(metadatum->print().c_str());
734 #else
735                 std::stringstream str;
736                 Exiv2::Exifdatum *exifdatum;
737                 Exiv2::Iptcdatum *iptcdatum;
738 #if EXIV2_TEST_VERSION(0,16,0)
739                 Exiv2::Xmpdatum *xmpdatum;
740 #endif
741                 if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum)))
742                         str << *exifdatum;
743                 else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum)))
744                         str << *iptcdatum;
745 #if EXIV2_TEST_VERSION(0,16,0)
746                 else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum)))
747                         str << *xmpdatum;
748 #endif
749
750                 return utf8_validate_or_convert(str.str().c_str());
751 #endif
752         }
753         catch (Exiv2::AnyError& e) {
754                 return NULL;
755         }
756 }
757
758 gchar *exif_item_get_string(ExifItem *item, int idx)
759 {
760         try {
761                 if (!item) return NULL;
762                 Exiv2::Metadatum *em = (Exiv2::Metadatum *)item;
763 #if EXIV2_TEST_VERSION(0,16,0)
764                 std::string str = em->toString(idx);
765 #else
766                 std::string str = em->toString(); // FIXME
767 #endif
768                 if (idx == 0 && str == "") str = em->toString();
769                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
770                         {
771                         std::string::size_type pos = str.find_first_of(' ');
772                         if (pos != std::string::npos) str = str.substr(pos+1);
773                         }
774
775                 return utf8_validate_or_convert(str.c_str());
776         }
777         catch (Exiv2::AnyError& e) {
778                 return NULL;
779         }
780 }
781
782
783 gint exif_item_get_integer(ExifItem *item, gint *value)
784 {
785         try {
786                 if (!item) return 0;
787                 *value = ((Exiv2::Metadatum *)item)->toLong();
788                 return 1;
789         }
790         catch (Exiv2::AnyError& e) {
791                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
792                 return 0;
793         }
794 }
795
796 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n)
797 {
798         try {
799                 if (!item) return NULL;
800                 if (n >= exif_item_get_elements(item)) return NULL;
801                 Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational(n);
802                 static ExifRational ret;
803                 ret.num = v.first;
804                 ret.den = v.second;
805                 if (sign) *sign = (((Exiv2::Metadatum *)item)->typeId() == Exiv2::signedRational);
806                 return &ret;
807         }
808         catch (Exiv2::AnyError& e) {
809                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
810                 return NULL;
811         }
812 }
813
814 gchar *exif_get_tag_description_by_key(const gchar *key)
815 {
816         try {
817                 Exiv2::ExifKey ekey(key);
818                 return utf8_validate_or_convert(ekey.tagLabel().c_str());
819         }
820         catch (Exiv2::AnyError& e) {
821                 try {
822                         Exiv2::IptcKey ikey(key);
823                         return utf8_validate_or_convert(ikey.tagLabel().c_str());
824                 }
825                 catch (Exiv2::AnyError& e) {
826                         try {
827 #if EXIV2_TEST_VERSION(0,16,0)
828                                 Exiv2::XmpKey xkey(key);
829                                 return utf8_validate_or_convert(xkey.tagLabel().c_str());
830 #endif
831                         }
832                         catch (Exiv2::AnyError& e) {
833                                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
834                                 return NULL;
835                         }
836                 }
837         }
838         return NULL;
839 }
840
841 static const AltKey *find_alt_key(const gchar *xmp_key)
842 {
843         gint i = 0;
844         
845         while (alt_keys[i].xmp_key)
846                 {
847                 if (strcmp(xmp_key, alt_keys[i].xmp_key) == 0) return &alt_keys[i];
848                 i++;
849                 }
850         return NULL;
851 }
852
853 static gint exif_update_metadata_simple(ExifData *exif, const gchar *key, const GList *values)
854 {
855         try {
856                 const GList *work = values;
857
858                 try {
859                         Exiv2::ExifKey ekey(key);
860                         
861                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
862                         while (pos != exif->exifData().end())
863                                 {
864                                 exif->exifData().erase(pos);
865                                 pos = exif->exifData().findKey(ekey);
866                                 }
867
868                         while (work)
869                                 {
870                                 exif->exifData()[key] = (gchar *)work->data;
871                                 work = work->next;
872                                 }
873                 }
874                 catch (Exiv2::AnyError& e) {
875 #if EXIV2_TEST_VERSION(0,16,0)
876                         try 
877 #endif
878                         {
879                                 Exiv2::IptcKey ekey(key);
880                                 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
881                                 while (pos != exif->iptcData().end())
882                                         {
883                                         exif->iptcData().erase(pos);
884                                         pos = exif->iptcData().findKey(ekey);
885                                         }
886
887                                 while (work)
888                                         {
889                                         exif->iptcData()[key] = (gchar *)work->data;
890                                         work = work->next;
891                                         }
892                         }
893 #if EXIV2_TEST_VERSION(0,16,0)
894                         catch (Exiv2::AnyError& e) {
895                                 Exiv2::XmpKey ekey(key);
896                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
897                                 while (pos != exif->xmpData().end())
898                                         {
899                                         exif->xmpData().erase(pos);
900                                         pos = exif->xmpData().findKey(ekey);
901                                         }
902
903                                 while (work)
904                                         {
905                                         exif->xmpData()[key] = (gchar *)work->data;
906                                         work = work->next;
907                                         }
908                         }
909 #endif
910                 }
911                 return 1;
912         }
913         catch (Exiv2::AnyError& e) {
914                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
915                 return 0;
916         }
917 }
918
919 gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
920 {
921         gint ret = exif_update_metadata_simple(exif, key, values);
922         
923         if (
924 #if !EXIV2_TEST_VERSION(0,17,0)
925             TRUE || /* no conversion support */
926 #endif
927             !values || /* deleting item */
928             !ret  /* writing to the explicitely given xmp tag failed */
929             )
930                 {
931                 /* deleted xmp metadatum can't be converted, we have to delete also the corresponding legacy tag */
932                 /* if we can't write xmp, update at least the legacy tag */
933                 const AltKey *alt_key = find_alt_key(key);
934                 if (alt_key && alt_key->iptc_key)
935                         ret = exif_update_metadata_simple(exif, alt_key->iptc_key, values);
936
937                 if (alt_key && alt_key->exif_key)
938                         ret = exif_update_metadata_simple(exif, alt_key->exif_key, values);
939                 }
940         return ret;
941 }
942
943
944 static GList *exif_add_value_to_glist(GList *list, Exiv2::Metadatum &item, MetadataFormat format, const Exiv2::ExifData *metadata)
945 {
946 #if EXIV2_TEST_VERSION(0,16,0)
947         Exiv2::TypeId id = item.typeId();
948         if (format == METADATA_FORMATTED ||
949             id == Exiv2::asciiString ||
950             id == Exiv2::undefined ||
951             id == Exiv2::string ||
952             id == Exiv2::date ||
953             id == Exiv2::time ||
954             id == Exiv2::xmpText ||
955             id == Exiv2::langAlt ||
956             id == Exiv2::comment
957             )
958                 {
959 #endif 
960                 /* read as a single entry */
961                 std::string str;
962                 
963                 if (format == METADATA_FORMATTED)
964                         {
965 #if EXIV2_TEST_VERSION(0,17,0)
966                         str = item.print(
967 #if EXIV2_TEST_VERSION(0,18,0)
968                                         metadata
969 #endif 
970                                         );
971 #else
972                         std::stringstream stream;
973                         Exiv2::Exifdatum *exifdatum;
974                         Exiv2::Iptcdatum *iptcdatum;
975 #if EXIV2_TEST_VERSION(0,16,0)
976                         Exiv2::Xmpdatum *xmpdatum;
977 #endif
978                         if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum)))
979                                 stream << *exifdatum;
980                         else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum)))
981                                 stream << *iptcdatum;
982 #if EXIV2_TEST_VERSION(0,16,0)
983                         else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum)))
984                                 stream << *xmpdatum;
985 #endif
986                         str = stream.str();
987 #endif
988                         if (str.length() > 1024)
989                                 {
990                                 /* truncate very long strings, they cause problems in gui */
991                                 str.erase(1024);
992                                 str.append("...");
993                                 }
994                         }
995                 else
996                         {
997                         str = item.toString();
998                         if (str.length() > 5 && str.substr(0, 5) == "lang=")
999                                 {
1000                                 std::string::size_type pos = str.find_first_of(' ');
1001                                 if (pos != std::string::npos) str = str.substr(pos+1);
1002                                 }
1003                         }
1004                 list = g_list_append(list, utf8_validate_or_convert(str.c_str())); 
1005 #if EXIV2_TEST_VERSION(0,16,0)
1006                 }
1007         else
1008                 {
1009                 /* read as a list */
1010                 gint i;
1011                 for (i = 0; i < item.count(); i++)
1012                         list = g_list_append(list, utf8_validate_or_convert(item.toString(i).c_str())); 
1013                 }
1014 #endif 
1015         return list;
1016 }
1017
1018 static GList *exif_get_metadata_simple(ExifData *exif, const gchar *key, MetadataFormat format)
1019 {
1020         GList *list = NULL;
1021         try {
1022                 try {
1023                         Exiv2::ExifKey ekey(key);
1024                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
1025                         if (pos != exif->exifData().end())
1026                                 list = exif_add_value_to_glist(list, *pos, format, &exif->exifData());
1027
1028                 }
1029                 catch (Exiv2::AnyError& e) {
1030                         try {
1031                                 Exiv2::IptcKey ekey(key);
1032                                 Exiv2::IptcData::iterator pos = exif->iptcData().begin();
1033                                 while (pos != exif->iptcData().end())
1034                                         {
1035                                         if (pos->key() == key)
1036                                                 list = exif_add_value_to_glist(list, *pos, format, NULL);
1037                                         ++pos;
1038                                         }
1039
1040                         }
1041                         catch (Exiv2::AnyError& e) {
1042 #if EXIV2_TEST_VERSION(0,16,0)
1043                                 Exiv2::XmpKey ekey(key);
1044                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
1045                                 if (pos != exif->xmpData().end())
1046                                         list = exif_add_value_to_glist(list, *pos, format, NULL);
1047 #endif
1048                         }
1049                 }
1050         }
1051         catch (Exiv2::AnyError& e) {
1052                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
1053         }
1054         return list;
1055 }
1056
1057 GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format)
1058 {
1059         GList *list = NULL;
1060
1061         if (!key) return NULL;
1062
1063         if (format == METADATA_FORMATTED)
1064                 {
1065                 gchar *text;
1066                 gint key_valid;
1067                 text = exif_get_formatted_by_key(exif, key, &key_valid);
1068                 if (key_valid) return g_list_append(NULL, text);
1069                 }
1070                 
1071         list = exif_get_metadata_simple(exif, key, format);
1072         
1073         /* the following code can be ifdefed out as soon as Exiv2 supports it */
1074         if (!list)
1075                 {
1076                 const AltKey *alt_key = find_alt_key(key);
1077                 if (alt_key && alt_key->iptc_key)
1078                         list = exif_get_metadata_simple(exif, alt_key->iptc_key, format);
1079
1080 #if !EXIV2_TEST_VERSION(0,17,0) 
1081                 /* with older Exiv2 versions exif is not synced */
1082                 if (!list && alt_key && alt_key->exif_key)
1083                         list = exif_get_metadata_simple(exif, alt_key->exif_key, format);
1084 #endif
1085                 }
1086         return list;
1087 }
1088
1089
1090 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
1091 {
1092         exif->add_jpeg_color_profile(cp_data, cp_length);
1093 }
1094
1095 guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
1096 {
1097         guchar *ret = exif->get_jpeg_color_profile(data_len);
1098         if (ret) return ret;
1099
1100         ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
1101         if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
1102                 ret = (guchar *)exif_item_get_data(prof_item, data_len);
1103         return ret;
1104 }
1105
1106 #if EXIV2_TEST_VERSION(0,17,90)
1107
1108 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1109 {
1110         if (!exif) return NULL;
1111
1112         if (!exif->image()) return NULL;
1113         
1114         const char* path = exif->image()->io().path().c_str();
1115         /* given image pathname, first do simple (and fast) file extension test */
1116         gboolean is_raw = filter_file_class(path, FORMAT_CLASS_RAWIMAGE);
1117         
1118         if (!is_raw && requested_width == 0) return NULL;
1119
1120         try {
1121
1122                 Exiv2::PreviewManager pm(*exif->image());
1123
1124                 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties();
1125
1126                 if (!list.empty())
1127                         {
1128                         Exiv2::PreviewPropertiesList::iterator pos;
1129                         Exiv2::PreviewPropertiesList::iterator last = --list.end();
1130                         
1131                         if (requested_width == 0)
1132                                 {
1133                                 pos = last; // the largest
1134                                 }
1135                         else
1136                                 {
1137                                 pos = list.begin();
1138                                 while (pos != last)
1139                                         {
1140                                         if (pos->width_ >= (uint32_t)requested_width &&
1141                                             pos->height_ >= (uint32_t)requested_height) break;
1142                                         ++pos;
1143                                         }
1144                                 
1145                                 // we are not interested in smaller thumbnails in normal image formats - we can use full image instead
1146                                 if (!is_raw) 
1147                                         {
1148                                         if (pos->width_ < (uint32_t)requested_width || pos->height_ < (uint32_t)requested_height) return NULL; 
1149                                         }
1150                                 }
1151
1152                         Exiv2::PreviewImage image = pm.getPreviewImage(*pos);
1153
1154                         Exiv2::DataBuf buf = image.copy();
1155                         std::pair<Exiv2::byte*, long> p = buf.release();
1156
1157                         *data_len = p.second;
1158                         return p.first;
1159                         }
1160                 return NULL;
1161         }
1162         catch (Exiv2::AnyError& e) {
1163                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
1164                 return NULL;
1165         }
1166 }
1167
1168 void exif_free_preview(guchar *buf)
1169 {
1170         delete[] (Exiv2::byte*)buf;
1171 }
1172 #endif
1173
1174 }
1175 #if !EXIV2_TEST_VERSION(0,17,90)
1176
1177 /* This is a dirty hack to support raw file preview, bassed on
1178 tiffparse.cpp from Exiv2 examples */
1179
1180 class RawFile {
1181         public:
1182
1183         RawFile(Exiv2::BasicIo &io);
1184         ~RawFile();
1185
1186         const Exiv2::Value *find(uint16_t tag, uint16_t group);
1187
1188         unsigned long preview_offset();
1189
1190         private:
1191         int type;
1192         Exiv2::TiffComponent::AutoPtr rootDir;
1193         Exiv2::BasicIo &io_;
1194         const Exiv2::byte *map_data;
1195         size_t map_len;
1196         unsigned long offset;
1197 };
1198
1199 typedef struct _UnmapData UnmapData;
1200 struct _UnmapData
1201 {
1202         guchar *ptr;
1203         guchar *map_data;
1204         size_t map_len;
1205 };
1206
1207 static GList *exif_unmap_list = 0;
1208
1209 extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1210 {
1211         unsigned long offset;
1212
1213         if (!exif) return NULL;
1214         if (!exif->image()) return NULL;
1215
1216         const char* path = exif->image()->io().path().c_str();
1217
1218         /* given image pathname, first do simple (and fast) file extension test */
1219         if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL;
1220
1221         try {
1222                 struct stat st;
1223                 guchar *map_data;
1224                 size_t map_len;
1225                 UnmapData *ud;
1226                 int fd;
1227                 
1228                 RawFile rf(exif->image()->io());
1229                 offset = rf.preview_offset();
1230                 DEBUG_1("%s: offset %lu", path, offset);
1231                 
1232                 fd = open(path, O_RDONLY);
1233                 if (fd == -1)
1234                         {
1235                         return NULL;
1236                         }
1237
1238                 if (fstat(fd, &st) == -1)
1239                         {
1240                         close(fd);
1241                         return NULL;
1242                         }
1243                 map_len = st.st_size;
1244                 map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1245                 close(fd);
1246                 if (map_data == MAP_FAILED)
1247                         {
1248                         return NULL;
1249                         }
1250                 *data_len = map_len - offset;
1251                 ud = g_new(UnmapData, 1);
1252                 ud->ptr = map_data + offset;
1253                 ud->map_data = map_data;
1254                 ud->map_len = map_len;
1255                 
1256                 exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
1257                 return ud->ptr;
1258                 
1259         }
1260         catch (Exiv2::AnyError& e) {
1261                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
1262         }
1263         return NULL;
1264
1265 }
1266
1267 void exif_free_preview(guchar *buf)
1268 {
1269         GList *work = exif_unmap_list;
1270         
1271         while (work)
1272                 {
1273                 UnmapData *ud = (UnmapData *)work->data;
1274                 if (ud->ptr == buf)
1275                         {
1276                         munmap(ud->map_data, ud->map_len);
1277                         exif_unmap_list = g_list_remove_link(exif_unmap_list, work);
1278                         g_free(ud);
1279                         return;
1280                         }
1281                 work = work->next;
1282                 }
1283         g_assert_not_reached();
1284 }
1285
1286 using namespace Exiv2;
1287
1288 RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0)
1289 {
1290 /*
1291         struct stat st;
1292         if (fstat(fd, &st) == -1)
1293                 {
1294                 throw Error(14);
1295                 }
1296         map_len = st.st_size;
1297         map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1298         if (map_data == MAP_FAILED)
1299                 {
1300                 throw Error(14);
1301                 }
1302 */
1303         if (io.open() != 0) {
1304             throw Error(9, io.path(), strError());
1305         }
1306         
1307         map_data = io.mmap();
1308         map_len = io.size();
1309
1310
1311         type = Exiv2::ImageFactory::getType(map_data, map_len);
1312
1313 #if EXIV2_TEST_VERSION(0,16,0)
1314         TiffHeaderBase *tiffHeader = NULL;
1315 #else
1316         TiffHeade2 *tiffHeader = NULL;
1317 #endif
1318         Cr2Header *cr2Header = NULL;
1319
1320         switch (type) {
1321                 case Exiv2::ImageType::tiff:
1322                         tiffHeader = new TiffHeade2();
1323                         break;
1324                 case Exiv2::ImageType::cr2:
1325                         cr2Header = new Cr2Header();
1326                         break;
1327 #if EXIV2_TEST_VERSION(0,16,0)
1328                 case Exiv2::ImageType::orf:
1329                         tiffHeader = new OrfHeader();
1330                         break;
1331 #endif
1332 #if EXIV2_TEST_VERSION(0,13,0)
1333                 case Exiv2::ImageType::raf:
1334                         if (map_len < 84 + 4) throw Error(14);
1335                         offset = getULong(map_data + 84, bigEndian);
1336                         return;
1337 #endif
1338                 case Exiv2::ImageType::crw:
1339                         {
1340                         // Parse the image, starting with a CIFF header component
1341                         Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
1342                         parseTree->read(map_data, map_len);
1343                         CiffComponent *entry = parseTree->findComponent(0x2007, 0);
1344                         if (entry) offset =  entry->pData() - map_data;
1345                         return;
1346                         }
1347
1348                 default:
1349                         throw Error(3, "RAW");
1350         }
1351
1352         // process tiff-like formats
1353
1354         TiffCompFactoryFct createFct = TiffCreator::create;
1355
1356         rootDir = createFct(Tag::root, Group::none);
1357         if (0 == rootDir.get()) {
1358                 throw Error(1, "No root element defined in TIFF structure");
1359         }
1360         
1361         if (tiffHeader)
1362                 {
1363                 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
1364 #if EXIV2_TEST_VERSION(0,16,0)
1365                 rootDir->setStart(map_data + tiffHeader->offset());
1366 #else
1367                 rootDir->setStart(map_data + tiffHeader->ifdOffset());
1368 #endif
1369                 }
1370                 
1371         if (cr2Header)
1372                 {
1373                 rootDir->setStart(map_data + cr2Header->offset());
1374                 }
1375         
1376         TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct));
1377
1378         TiffReader reader(map_data,
1379                           map_len,
1380                           rootDir.get(),
1381                           state);
1382
1383         rootDir->accept(reader);
1384         
1385         if (tiffHeader)
1386                 delete tiffHeader;
1387         if (cr2Header)
1388                 delete cr2Header;
1389 }
1390
1391 RawFile::~RawFile(void)
1392 {
1393         io_.munmap();
1394         io_.close();
1395 }
1396
1397 const Value * RawFile::find(uint16_t tag, uint16_t group)
1398 {
1399         TiffFinder finder(tag, group);
1400         rootDir->accept(finder);
1401         TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
1402         if (te)
1403                 {
1404                 DEBUG_1("(tag: %04x %04x) ", tag, group);
1405                 return te->pValue();
1406                 }
1407         else
1408                 return NULL;
1409 }
1410
1411 unsigned long RawFile::preview_offset(void)
1412 {
1413         const Value *val;
1414         if (offset) return offset;
1415         
1416         if (type == Exiv2::ImageType::cr2)
1417                 {
1418                 val = find(0x111, Group::ifd0);
1419                 if (val) return val->toLong();
1420
1421                 return 0;
1422                 }
1423         
1424         val = find(0x201, Group::sub0_0);
1425         if (val) return val->toLong();
1426
1427         val = find(0x201, Group::ifd0);
1428         if (val) return val->toLong();
1429
1430         val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
1431         if (val) return val->toLong();
1432
1433         val = find(0x111, Group::sub0_1); // dng
1434         if (val) return val->toLong();
1435
1436         return 0;
1437 }
1438
1439
1440 #endif
1441
1442
1443 #endif
1444 /* HAVE_EXIV2 */
1445 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */