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