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