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