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