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