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