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