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