manual loop conversions
[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 struct AltKey
71 {
72         const gchar *xmp_key;
73         const gchar *exif_key;
74         const gchar *iptc_key;
75 };
76
77 /* this is a list of keys that should be converted, even with the older Exiv2 which does not support it directly */
78 static constexpr AltKey alt_keys[] = {
79         {"Xmp.tiff.Orientation",                "Exif.Image.Orientation",       nullptr},
80         {"Xmp.dc.title",                        nullptr,                                "Iptc.Application2.ObjectName"          },
81         {"Xmp.photoshop.Urgency",               nullptr,                                "Iptc.Application2.Urgency"             },
82         {"Xmp.photoshop.Category",              nullptr,                                "Iptc.Application2.Category"            },
83         {"Xmp.photoshop.SupplementalCategory",  nullptr,                                "Iptc.Application2.SuppCategory"        },
84         {"Xmp.dc.subject",                      nullptr,                                "Iptc.Application2.Keywords"            },
85         {"Xmp.iptc.Location",                   nullptr,                                "Iptc.Application2.LocationName"        },
86         {"Xmp.photoshop.Instruction",           nullptr,                                "Iptc.Application2.SpecialInstructions" },
87         {"Xmp.photoshop.DateCreated",           nullptr,                                "Iptc.Application2.DateCreated"         },
88         {"Xmp.dc.creator",                      nullptr,                                "Iptc.Application2.Byline"              },
89         {"Xmp.photoshop.AuthorsPosition",       nullptr,                                "Iptc.Application2.BylineTitle"         },
90         {"Xmp.photoshop.City",                  nullptr,                                "Iptc.Application2.City"                },
91         {"Xmp.photoshop.State",                 nullptr,                                "Iptc.Application2.ProvinceState"       },
92         {"Xmp.iptc.CountryCode",                nullptr,                                "Iptc.Application2.CountryCode"         },
93         {"Xmp.photoshop.Country",               nullptr,                                "Iptc.Application2.CountryName"         },
94         {"Xmp.photoshop.TransmissionReference", nullptr,                                "Iptc.Application2.TransmissionReference"},
95         {"Xmp.photoshop.Headline",              nullptr,                                "Iptc.Application2.Headline"            },
96         {"Xmp.photoshop.Credit",                nullptr,                                "Iptc.Application2.Credit"              },
97         {"Xmp.photoshop.Source",                nullptr,                                "Iptc.Application2.Source"              },
98         {"Xmp.dc.rights",                       nullptr,                                "Iptc.Application2.Copyright"           },
99         {"Xmp.dc.description",                  nullptr,                                "Iptc.Application2.Caption"             },
100         {"Xmp.photoshop.CaptionWriter",         nullptr,                                "Iptc.Application2.Writer"              },
101         };
102
103 static void _debug_exception(const char* file,
104                              int line,
105                              const char* func,
106                              Exiv2::AnyError& e)
107 {
108         gchar *str = g_locale_from_utf8(e.what(), -1, nullptr, nullptr, nullptr);
109         DEBUG_1("%s:%d:%s:Exiv2: %s", file, line, func, str);
110         g_free(str);
111 }
112
113 #define debug_exception(e) _debug_exception(__FILE__, __LINE__, __func__, e)
114
115 struct ExifData
116 {
117         Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
118         Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
119 #if EXIV2_TEST_VERSION(0,16,0)
120         Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
121 #endif
122
123         virtual ~ExifData() = default;
124
125         virtual void writeMetadata(gchar *UNUSED(path) = nullptr)
126         {
127                 g_critical("Unsupported method of writing metadata");
128         }
129
130         virtual ExifData *original()
131         {
132                 return nullptr;
133         }
134
135         virtual Exiv2::Image *image() = 0;
136
137         virtual Exiv2::ExifData &exifData() = 0;
138
139         virtual Exiv2::IptcData &iptcData() = 0;
140
141 #if EXIV2_TEST_VERSION(0,16,0)
142         virtual Exiv2::XmpData &xmpData() = 0;
143 #endif
144
145         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0;
146
147         virtual guchar *get_jpeg_color_profile(guint *data_len) = 0;
148
149         virtual std::string image_comment() const = 0;
150
151         virtual void set_image_comment(const std::string& comment) = 0;
152 };
153
154 // This allows read-only access to the original metadata
155 struct ExifDataOriginal : public ExifData
156 {
157 protected:
158         Exiv2::Image::AutoPtr image_;
159
160         /* the icc profile in jpeg is not technically exif - store it here */
161         unsigned char *cp_data_;
162         guint cp_length_;
163         gboolean valid_;
164         gchar *pathl_;
165
166         Exiv2::ExifData emptyExifData_;
167         Exiv2::IptcData emptyIptcData_;
168 #if EXIV2_TEST_VERSION(0,16,0)
169         Exiv2::XmpData emptyXmpData_;
170 #endif
171
172 public:
173         ExifDataOriginal(Exiv2::Image::AutoPtr image)
174         {
175                 cp_data_ = nullptr;
176                 cp_length_ = 0;
177                 image_ = std::move(image);
178                 valid_ = TRUE;
179         }
180
181         ExifDataOriginal(gchar *path)
182         {
183                 cp_data_ = nullptr;
184                 cp_length_ = 0;
185                 valid_ = TRUE;
186
187                 pathl_ = path_from_utf8(path);
188                 try
189                         {
190                         image_ = Exiv2::ImageFactory::open(pathl_);
191 //                      g_assert (image.get() != 0);
192                         image_->readMetadata();
193
194 #if EXIV2_TEST_VERSION(0,16,0)
195                         if (image_->mimeType() == "application/rdf+xml")
196                                 {
197                                 //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
198                                 image_->clearExifData();
199                                 image_->clearIptcData();
200                                 }
201 #endif
202
203 #if EXIV2_TEST_VERSION(0,14,0)
204                         if (image_->mimeType() == "image/jpeg")
205                                 {
206                                 /* try to get jpeg color profile */
207                                 Exiv2::BasicIo &io = image_->io();
208                                 gint open = io.isopen();
209                                 if (!open) io.open();
210                                 if (io.isopen())
211                                         {
212                                         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 //              debug_exception(e);
716                 return nullptr;
717         }
718 }
719
720 /*
721 invalidTypeId, unsignedByte, asciiString, unsignedShort,
722   unsignedLong, unsignedRational, signedByte, undefined,
723   signedShort, signedLong, signedRational, string,
724   date, time, comment, directory,
725   xmpText, xmpAlt, xmpBag, xmpSeq,
726   langAlt, lastTypeId
727 */
728
729 static guint format_id_trans_tbl [] = {
730         EXIF_FORMAT_UNKNOWN,
731         EXIF_FORMAT_BYTE_UNSIGNED,
732         EXIF_FORMAT_STRING,
733         EXIF_FORMAT_SHORT_UNSIGNED,
734         EXIF_FORMAT_LONG_UNSIGNED,
735         EXIF_FORMAT_RATIONAL_UNSIGNED,
736         EXIF_FORMAT_BYTE,
737         EXIF_FORMAT_UNDEFINED,
738         EXIF_FORMAT_SHORT,
739         EXIF_FORMAT_LONG,
740         EXIF_FORMAT_RATIONAL,
741         EXIF_FORMAT_STRING,
742         EXIF_FORMAT_STRING,
743         EXIF_FORMAT_STRING,
744         EXIF_FORMAT_UNDEFINED,
745         EXIF_FORMAT_STRING,
746         EXIF_FORMAT_STRING,
747         EXIF_FORMAT_STRING,
748         EXIF_FORMAT_STRING
749         };
750
751
752
753 guint exif_item_get_format_id(ExifItem *item)
754 {
755         try {
756                 if (!item) return EXIF_FORMAT_UNKNOWN;
757                 guint id = (reinterpret_cast<Exiv2::Metadatum *>(item))->typeId();
758                 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
759                 return format_id_trans_tbl[id];
760         }
761         catch (Exiv2::AnyError& e) {
762                 debug_exception(e);
763                 return EXIF_FORMAT_UNKNOWN;
764         }
765 }
766
767 const char *exif_item_get_format_name(ExifItem *item, gboolean UNUSED(brief))
768 {
769         try {
770                 if (!item) return nullptr;
771                 return (reinterpret_cast<Exiv2::Metadatum *>(item))->typeName();
772         }
773         catch (Exiv2::AnyError& e) {
774                 debug_exception(e);
775                 return nullptr;
776         }
777 }
778
779
780 gchar *exif_item_get_data_as_text(ExifItem *item, ExifData *exif)
781 {
782         try {
783                 if (!item) return nullptr;
784                 auto metadatum = reinterpret_cast<Exiv2::Metadatum *>(item);
785 #if EXIV2_TEST_VERSION(0,17,0)
786                 return utf8_validate_or_convert(metadatum->print(&exif->exifData()).c_str());
787 #else
788                 std::stringstream str;
789                 Exiv2::Exifdatum *exifdatum;
790                 Exiv2::Iptcdatum *iptcdatum;
791 #if EXIV2_TEST_VERSION(0,16,0)
792                 Exiv2::Xmpdatum *xmpdatum;
793 #endif
794                 if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum)))
795                         str << *exifdatum;
796                 else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum)))
797                         str << *iptcdatum;
798 #if EXIV2_TEST_VERSION(0,16,0)
799                 else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum)))
800                         str << *xmpdatum;
801 #endif
802
803                 return utf8_validate_or_convert(str.str().c_str());
804 #endif
805         }
806         catch (Exiv2::AnyError& e) {
807                 return nullptr;
808         }
809 }
810
811 gchar *exif_item_get_string(ExifItem *item, int idx)
812 {
813         try {
814                 if (!item) return nullptr;
815                 auto em = reinterpret_cast<Exiv2::Metadatum *>(item);
816 #if EXIV2_TEST_VERSION(0,16,0)
817                 std::string str = em->toString(idx);
818 #else
819                 std::string str = em->toString(); /**< @FIXME ?? */
820 #endif
821                 if (idx == 0 && str.empty()) str = em->toString();
822                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
823                         {
824                         std::string::size_type pos = str.find_first_of(' ');
825                         if (pos != std::string::npos) str = str.substr(pos+1);
826                         }
827
828                 return utf8_validate_or_convert(str.c_str());
829         }
830         catch (Exiv2::AnyError& e) {
831                 return nullptr;
832         }
833 }
834
835
836 gint exif_item_get_integer(ExifItem *item, gint *value)
837 {
838         try {
839                 if (!item || exif_item_get_elements(item) == 0) return 0;
840
841 #if EXIV2_TEST_VERSION(0,28,0)
842         *value = ((Exiv2::Metadatum *)item)->toInt64();
843 #else
844                 *value = (reinterpret_cast<Exiv2::Metadatum *>(item))->toLong();
845 #endif
846                 return 1;
847         }
848         catch (Exiv2::AnyError& e) {
849                 debug_exception(e);
850                 return 0;
851         }
852 }
853
854 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n)
855 {
856         try {
857                 if (!item) return nullptr;
858                 if (n >= exif_item_get_elements(item)) return nullptr;
859                 Exiv2::Rational v = (reinterpret_cast<Exiv2::Metadatum *>(item))->toRational(n);
860                 static ExifRational ret;
861                 ret.num = v.first;
862                 ret.den = v.second;
863                 if (sign) *sign = ((reinterpret_cast<Exiv2::Metadatum *>(item))->typeId() == Exiv2::signedRational);
864                 return &ret;
865         }
866         catch (Exiv2::AnyError& e) {
867                 debug_exception(e);
868                 return nullptr;
869         }
870 }
871
872 gchar *exif_get_tag_description_by_key(const gchar *key)
873 {
874         try {
875                 Exiv2::ExifKey ekey(key);
876                 return utf8_validate_or_convert(ekey.tagLabel().c_str());
877         }
878         catch (Exiv2::AnyError& e) {
879                 try {
880                         Exiv2::IptcKey ikey(key);
881                         return utf8_validate_or_convert(ikey.tagLabel().c_str());
882                 }
883                 catch (Exiv2::AnyError& e) {
884                         try {
885 #if EXIV2_TEST_VERSION(0,16,0)
886                                 Exiv2::XmpKey xkey(key);
887                                 return utf8_validate_or_convert(xkey.tagLabel().c_str());
888 #endif
889                         }
890                         catch (Exiv2::AnyError& e) {
891                                 debug_exception(e);
892                                 return nullptr;
893                         }
894                 }
895         }
896         return nullptr;
897 }
898
899 static const AltKey *find_alt_key(const gchar *xmp_key)
900 {
901         for (const auto& k : alt_keys)
902                 if (strcmp(xmp_key, k.xmp_key) == 0) return &k;
903         return nullptr;
904 }
905
906 static gint exif_update_metadata_simple(ExifData *exif, const gchar *key, const GList *values)
907 {
908         try {
909                 const GList *work = values;
910
911                 try {
912                         Exiv2::ExifKey ekey(key);
913
914                         auto pos = exif->exifData().findKey(ekey);
915                         while (pos != exif->exifData().end())
916                                 {
917                                 exif->exifData().erase(pos);
918                                 pos = exif->exifData().findKey(ekey);
919                                 }
920
921                         while (work)
922                                 {
923                                 exif->exifData()[key] = static_cast<gchar *>(work->data);
924                                 work = work->next;
925                                 }
926                 }
927                 catch (Exiv2::AnyError& e) {
928 #if EXIV2_TEST_VERSION(0,16,0)
929                         try
930 #endif
931                         {
932                                 Exiv2::IptcKey ekey(key);
933                                 auto pos = exif->iptcData().findKey(ekey);
934                                 while (pos != exif->iptcData().end())
935                                         {
936                                         exif->iptcData().erase(pos);
937                                         pos = exif->iptcData().findKey(ekey);
938                                         }
939
940                                 while (work)
941                                         {
942                                         exif->iptcData()[key] = static_cast<gchar *>(work->data);
943                                         work = work->next;
944                                         }
945                         }
946 #if EXIV2_TEST_VERSION(0,16,0)
947                         catch (Exiv2::AnyError& e) {
948                                 Exiv2::XmpKey ekey(key);
949                                 auto pos = exif->xmpData().findKey(ekey);
950                                 while (pos != exif->xmpData().end())
951                                         {
952                                         exif->xmpData().erase(pos);
953                                         pos = exif->xmpData().findKey(ekey);
954                                         }
955
956                                 while (work)
957                                         {
958                                         exif->xmpData()[key] = static_cast<gchar *>(work->data);
959                                         work = work->next;
960                                         }
961                         }
962 #endif
963                 }
964                 return 1;
965         }
966         catch (Exiv2::AnyError& e) {
967                 debug_exception(e);
968                 return 0;
969         }
970 }
971
972 gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
973 {
974         gint ret = exif_update_metadata_simple(exif, key, values);
975
976         if (
977 #if !EXIV2_TEST_VERSION(0,17,0)
978             TRUE || /* no conversion support */
979 #endif
980             !values || /* deleting item */
981             !ret  /* writing to the explicitly given xmp tag failed */
982             )
983                 {
984                 /* deleted xmp metadatum can't be converted, we have to delete also the corresponding legacy tag */
985                 /* if we can't write xmp, update at least the legacy tag */
986                 const AltKey *alt_key = find_alt_key(key);
987                 if (alt_key && alt_key->iptc_key)
988                         ret = exif_update_metadata_simple(exif, alt_key->iptc_key, values);
989
990                 if (alt_key && alt_key->exif_key)
991                         ret = exif_update_metadata_simple(exif, alt_key->exif_key, values);
992                 }
993         return ret;
994 }
995
996
997 static GList *exif_add_value_to_glist(GList *list, Exiv2::Metadatum &item, MetadataFormat format, const Exiv2::ExifData *metadata)
998 {
999 #if EXIV2_TEST_VERSION(0,16,0)
1000         Exiv2::TypeId id = item.typeId();
1001         if (format == METADATA_FORMATTED ||
1002             id == Exiv2::asciiString ||
1003             id == Exiv2::undefined ||
1004             id == Exiv2::string ||
1005             id == Exiv2::date ||
1006             id == Exiv2::time ||
1007             id == Exiv2::xmpText ||
1008             id == Exiv2::langAlt ||
1009             id == Exiv2::comment
1010             )
1011                 {
1012 #endif
1013                 /* read as a single entry */
1014                 std::string str;
1015
1016                 if (format == METADATA_FORMATTED)
1017                         {
1018 #if EXIV2_TEST_VERSION(0,17,0)
1019                         str = item.print(
1020 #if EXIV2_TEST_VERSION(0,18,0)
1021                                         metadata
1022 #endif
1023                                         );
1024 #else
1025                         std::stringstream stream;
1026                         Exiv2::Exifdatum *exifdatum;
1027                         Exiv2::Iptcdatum *iptcdatum;
1028 #if EXIV2_TEST_VERSION(0,16,0)
1029                         Exiv2::Xmpdatum *xmpdatum;
1030 #endif
1031                         if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(&item)))
1032                                 stream << *exifdatum;
1033                         else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(&item)))
1034                                 stream << *iptcdatum;
1035 #if EXIV2_TEST_VERSION(0,16,0)
1036                         else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(&item)))
1037                                 stream << *xmpdatum;
1038 #endif
1039                         str = stream.str();
1040 #endif
1041                         if (str.length() > 1024)
1042                                 {
1043                                 /* truncate very long strings, they cause problems in gui */
1044                                 str.erase(1024);
1045                                 str.append("...");
1046                                 }
1047                         }
1048                 else
1049                         {
1050                         str = item.toString();
1051                         }
1052                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
1053                         {
1054                         std::string::size_type pos = str.find_first_of(' ');
1055                         if (pos != std::string::npos) str = str.substr(pos+1);
1056                         }
1057                 list = g_list_append(list, utf8_validate_or_convert(str.c_str()));
1058 #if EXIV2_TEST_VERSION(0,16,0)
1059                 }
1060         else
1061                 {
1062                 /* read as a list */
1063                 gint i;
1064                 for (i = 0; i < item.count(); i++)
1065                         list = g_list_append(list, utf8_validate_or_convert(item.toString(i).c_str()));
1066                 }
1067 #endif
1068         return list;
1069 }
1070
1071 static GList *exif_get_metadata_simple(ExifData *exif, const gchar *key, MetadataFormat format)
1072 {
1073         GList *list = nullptr;
1074         try {
1075                 try {
1076                         Exiv2::ExifKey ekey(key);
1077                         auto pos = exif->exifData().findKey(ekey);
1078                         if (pos != exif->exifData().end())
1079                                 list = exif_add_value_to_glist(list, *pos, format, &exif->exifData());
1080
1081                 }
1082                 catch (Exiv2::AnyError& e) {
1083                         try {
1084                                 Exiv2::IptcKey ekey(key);
1085                                 auto pos = exif->iptcData().begin();
1086                                 while (pos != exif->iptcData().end())
1087                                         {
1088                                         if (pos->key() == key)
1089                                                 list = exif_add_value_to_glist(list, *pos, format, nullptr);
1090                                         ++pos;
1091                                         }
1092
1093                         }
1094                         catch (Exiv2::AnyError& e) {
1095 #if EXIV2_TEST_VERSION(0,16,0)
1096                                 Exiv2::XmpKey ekey(key);
1097                                 auto pos = exif->xmpData().findKey(ekey);
1098                                 if (pos != exif->xmpData().end())
1099                                         list = exif_add_value_to_glist(list, *pos, format, nullptr);
1100 #endif
1101                         }
1102                 }
1103         }
1104         catch (Exiv2::AnyError& e) {
1105                 debug_exception(e);
1106         }
1107         return list;
1108 }
1109
1110 GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format)
1111 {
1112         GList *list = nullptr;
1113
1114         if (!key) return nullptr;
1115
1116         if (format == METADATA_FORMATTED)
1117                 {
1118                 gchar *text;
1119                 gint key_valid;
1120                 text = exif_get_formatted_by_key(exif, key, &key_valid);
1121                 if (key_valid) return g_list_append(nullptr, text);
1122                 }
1123
1124         list = exif_get_metadata_simple(exif, key, format);
1125
1126         /* the following code can be ifdefed out as soon as Exiv2 supports it */
1127         if (!list)
1128                 {
1129                 const AltKey *alt_key = find_alt_key(key);
1130                 if (alt_key && alt_key->iptc_key)
1131                         list = exif_get_metadata_simple(exif, alt_key->iptc_key, format);
1132
1133 #if !EXIV2_TEST_VERSION(0,17,0)
1134                 /* with older Exiv2 versions exif is not synced */
1135                 if (!list && alt_key && alt_key->exif_key)
1136                         list = exif_get_metadata_simple(exif, alt_key->exif_key, format);
1137 #endif
1138                 }
1139         return list;
1140 }
1141
1142
1143 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
1144 {
1145         exif->add_jpeg_color_profile(cp_data, cp_length);
1146 }
1147
1148 guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
1149 {
1150         guchar *ret = exif->get_jpeg_color_profile(data_len);
1151         if (ret) return ret;
1152
1153         ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
1154         if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
1155                 ret = reinterpret_cast<guchar *>(exif_item_get_data(prof_item, data_len));
1156         return ret;
1157 }
1158
1159 gchar* exif_get_image_comment(FileData* fd)
1160 {
1161         if (!fd || !fd->exif)
1162                 return g_strdup("");
1163
1164         return g_strdup(fd->exif->image_comment().c_str());
1165 }
1166
1167 void exif_set_image_comment(FileData* fd, const gchar* comment)
1168 {
1169         if (!fd || !fd->exif)
1170                 return;
1171
1172         fd->exif->set_image_comment(comment ? comment : "");
1173 }
1174
1175
1176 #if EXIV2_TEST_VERSION(0,17,90)
1177
1178 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1179 {
1180         if (!exif) return nullptr;
1181
1182         if (!exif->image()) return nullptr;
1183
1184         std::string const path = exif->image()->io().path();
1185         /* given image pathname, first do simple (and fast) file extension test */
1186         gboolean is_raw = filter_file_class(path.c_str(), FORMAT_CLASS_RAWIMAGE);
1187
1188         if (!is_raw && requested_width == 0) return nullptr;
1189
1190         try {
1191
1192                 Exiv2::PreviewManager pm(*exif->image());
1193
1194                 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties();
1195
1196                 if (!list.empty())
1197                         {
1198                         Exiv2::PreviewPropertiesList::iterator pos;
1199                         auto last = --list.end();
1200
1201                         if (requested_width == 0)
1202                                 {
1203                                 pos = last; // the largest
1204                                 }
1205                         else
1206                                 {
1207                                 pos = list.begin();
1208                                 while (pos != last)
1209                                         {
1210                                         if (pos->width_ >= static_cast<uint32_t>(requested_width) &&
1211                                             pos->height_ >= static_cast<uint32_t>(requested_height)) break;
1212                                         ++pos;
1213                                         }
1214
1215                                 // we are not interested in smaller thumbnails in normal image formats - we can use full image instead
1216                                 if (!is_raw)
1217                                         {
1218                                         if (pos->width_ < static_cast<uint32_t>(requested_width) || pos->height_ < static_cast<uint32_t>(requested_height)) return nullptr;
1219                                         }
1220                                 }
1221
1222                         Exiv2::PreviewImage image = pm.getPreviewImage(*pos);
1223
1224                         Exiv2::DataBuf buf = image.copy();
1225
1226 #if EXIV2_TEST_VERSION(0,28,0)
1227                        *data_len = buf.size();
1228                        auto b = buf.data();
1229                        buf.reset();
1230                        return b;
1231 #else
1232                         std::pair<Exiv2::byte*, long> p = buf.release();
1233
1234                         *data_len = p.second;
1235                         return p.first;
1236 #endif
1237                         }
1238                 return nullptr;
1239         }
1240         catch (Exiv2::AnyError& e) {
1241                 debug_exception(e);
1242                 return nullptr;
1243         }
1244 }
1245
1246 void exif_free_preview(guchar *buf)
1247 {
1248         delete[] static_cast<Exiv2::byte*>(buf);
1249 }
1250 #endif
1251
1252 #if !EXIV2_TEST_VERSION(0,17,90)
1253
1254 /* This is a dirty hack to support raw file preview, bassed on
1255 tiffparse.cpp from Exiv2 examples */
1256
1257 class RawFile {
1258         public:
1259
1260         RawFile(Exiv2::BasicIo &io);
1261         ~RawFile();
1262
1263         const Exiv2::Value *find(uint16_t tag, uint16_t group);
1264
1265         unsigned long preview_offset();
1266
1267         private:
1268         int type;
1269         Exiv2::TiffComponent::AutoPtr rootDir;
1270         Exiv2::BasicIo &io_;
1271         const Exiv2::byte *map_data;
1272         size_t map_len;
1273         unsigned long offset;
1274 };
1275
1276 struct UnmapData
1277 {
1278         guchar *ptr;
1279         guchar *map_data;
1280         size_t map_len;
1281 };
1282
1283 static GList *exif_unmap_list = 0;
1284
1285 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1286 {
1287         unsigned long offset;
1288
1289         if (!exif) return NULL;
1290         if (!exif->image()) return NULL;
1291
1292         std::string const path = exif->image()->io().path();
1293
1294         /* given image pathname, first do simple (and fast) file extension test */
1295         if (!filter_file_class(path.c_str(), FORMAT_CLASS_RAWIMAGE)) return NULL;
1296
1297         try {
1298                 struct stat st;
1299                 guchar *map_data;
1300                 size_t map_len;
1301                 UnmapData *ud;
1302                 int fd;
1303
1304                 RawFile rf(exif->image()->io());
1305                 offset = rf.preview_offset();
1306                 DEBUG_1("%s: offset %lu", path.c_str(), offset);
1307
1308                 fd = open(path.c_str(), O_RDONLY);
1309                 if (fd == -1)
1310                         {
1311                         return NULL;
1312                         }
1313
1314                 if (fstat(fd, &st) == -1)
1315                         {
1316                         close(fd);
1317                         return NULL;
1318                         }
1319                 map_len = st.st_size;
1320                 map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1321                 close(fd);
1322                 if (map_data == MAP_FAILED)
1323                         {
1324                         return NULL;
1325                         }
1326                 *data_len = map_len - offset;
1327                 ud = g_new(UnmapData, 1);
1328                 ud->ptr = map_data + offset;
1329                 ud->map_data = map_data;
1330                 ud->map_len = map_len;
1331
1332                 exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
1333                 return ud->ptr;
1334
1335         }
1336         catch (Exiv2::AnyError& e) {
1337                 debug_exception(e);
1338         }
1339         return NULL;
1340
1341 }
1342
1343 void exif_free_preview(guchar *buf)
1344 {
1345         GList *work = exif_unmap_list;
1346
1347         while (work)
1348                 {
1349                 UnmapData *ud = (UnmapData *)work->data;
1350                 if (ud->ptr == buf)
1351                         {
1352                         munmap(ud->map_data, ud->map_len);
1353                         exif_unmap_list = g_list_remove_link(exif_unmap_list, work);
1354                         g_free(ud);
1355                         return;
1356                         }
1357                 work = work->next;
1358                 }
1359         g_assert_not_reached();
1360 }
1361
1362 using namespace Exiv2;
1363
1364 RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0)
1365 {
1366 /*
1367         struct stat st;
1368         if (fstat(fd, &st) == -1)
1369                 {
1370                 throw Error(14);
1371                 }
1372         map_len = st.st_size;
1373         map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1374         if (map_data == MAP_FAILED)
1375                 {
1376                 throw Error(14);
1377                 }
1378 */
1379         if (io.open() != 0) {
1380             throw Error(9, io.path(), strError());
1381         }
1382
1383         map_data = io.mmap();
1384         map_len = io.size();
1385
1386
1387         type = Exiv2::ImageFactory::getType(map_data, map_len);
1388
1389 #if EXIV2_TEST_VERSION(0,16,0)
1390         std::unique_ptr<TiffHeaderBase> tiffHeader;
1391 #else
1392         std::unique_ptr<TiffHeade2> tiffHeader;
1393 #endif
1394         std::unique_ptr<Cr2Header> cr2Header;
1395
1396         switch (type) {
1397                 case Exiv2::ImageType::tiff:
1398                         tiffHeader = std::make_unique<TiffHeade2>();
1399                         break;
1400                 case Exiv2::ImageType::cr2:
1401                         cr2Header = std::make_unique<Cr2Header>();
1402                         break;
1403 #if EXIV2_TEST_VERSION(0,16,0)
1404                 case Exiv2::ImageType::orf:
1405                         tiffHeader = std::make_unique<OrfHeader>();
1406                         break;
1407 #endif
1408 #if EXIV2_TEST_VERSION(0,13,0)
1409                 case Exiv2::ImageType::raf:
1410                         if (map_len < 84 + 4) throw Error(14);
1411                         offset = getULong(map_data + 84, bigEndian);
1412                         return;
1413 #endif
1414                 case Exiv2::ImageType::crw:
1415                         {
1416                         // Parse the image, starting with a CIFF header component
1417                         auto parseTree = std::make_unique<Exiv2::CiffHeader>();
1418                         parseTree->read(map_data, map_len);
1419                         CiffComponent *entry = parseTree->findComponent(0x2007, 0);
1420                         if (entry) offset =  entry->pData() - map_data;
1421                         return;
1422                         }
1423
1424                 default:
1425                         throw Error(3, "RAW");
1426         }
1427
1428         // process tiff-like formats
1429
1430         TiffCompFactoryFct createFct = TiffCreator::create;
1431
1432         rootDir = createFct(Tag::root, Group::none);
1433         if (0 == rootDir.get()) {
1434                 throw Error(1, "No root element defined in TIFF structure");
1435         }
1436
1437         if (tiffHeader)
1438                 {
1439                 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
1440 #if EXIV2_TEST_VERSION(0,16,0)
1441                 rootDir->setStart(map_data + tiffHeader->offset());
1442 #else
1443                 rootDir->setStart(map_data + tiffHeader->ifdOffset());
1444 #endif
1445                 }
1446
1447         if (cr2Header)
1448                 {
1449                 rootDir->setStart(map_data + cr2Header->offset());
1450                 }
1451
1452         TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct));
1453
1454         TiffReader reader(map_data,
1455                           map_len,
1456                           rootDir.get(),
1457                           state);
1458
1459         rootDir->accept(reader);
1460 }
1461
1462 RawFile::~RawFile(void)
1463 {
1464         io_.munmap();
1465         io_.close();
1466 }
1467
1468 const Value * RawFile::find(uint16_t tag, uint16_t group)
1469 {
1470         TiffFinder finder(tag, group);
1471         rootDir->accept(finder);
1472         TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
1473         if (te)
1474                 {
1475                 DEBUG_1("(tag: %04x %04x) ", tag, group);
1476                 return te->pValue();
1477                 }
1478         else
1479                 return NULL;
1480 }
1481
1482 unsigned long RawFile::preview_offset(void)
1483 {
1484         const Value *val;
1485         if (offset) return offset;
1486
1487         if (type == Exiv2::ImageType::cr2)
1488                 {
1489                 val = find(0x111, Group::ifd0);
1490 #if EXIV2_TEST_VERSION(0,28,0)
1491                 if (val) return val->toInt64();
1492 #else
1493                 if (val) return val->tolong();
1494 #endif
1495                 return 0;
1496                 }
1497
1498         val = find(0x201, Group::sub0_0);
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::ifd0);
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(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
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         val = find(0x111, Group::sub0_1); // dng
1520 #if EXIV2_TEST_VERSION(0,28,0)
1521         if (val) return val->toInt64();
1522 #else
1523         if (val) return val->tolong();
1524 #endif
1525
1526         return 0;
1527 }
1528
1529
1530 #endif
1531
1532
1533 #endif
1534 /* HAVE_EXIV2 */
1535 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */