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