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