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