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