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