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