fixed failed assertion in exif_free
[geeqie.git] / src / exiv2.cc
1 /*
2  * Geeqie
3  * Copyright (C) 2008 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
57 extern "C" {
58 #include <glib.h>
59
60 #include "main.h"
61 #include "exif.h"
62
63 #include "filefilter.h"
64 #include "ui_fileops.h"
65 }
66
67 struct _ExifData
68 {
69         Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
70         Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
71 #if EXIV2_TEST_VERSION(0,16,0)
72         Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
73 #endif
74
75         virtual ~_ExifData()
76         {
77         }
78         
79         virtual void writeMetadata()
80         {
81                 g_critical("Unsupported method of writing metadata");
82         }
83
84         virtual ExifData *original()
85         {
86                 return NULL;
87         }
88
89         virtual Exiv2::Image *image() = 0;
90         
91         virtual Exiv2::ExifData &exifData() = 0;
92
93         virtual Exiv2::IptcData &iptcData() = 0;
94
95 #if EXIV2_TEST_VERSION(0,16,0)
96         virtual Exiv2::XmpData &xmpData() = 0;
97 #endif
98
99         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0;
100
101         virtual guchar *get_jpeg_color_profile(guint *data_len) = 0;
102 };
103
104 // This allows read-only access to the original metadata
105 struct _ExifDataOriginal : public _ExifData
106 {
107 protected:
108         Exiv2::Image::AutoPtr image_;
109
110         /* the icc profile in jpeg is not technically exif - store it here */
111         unsigned char *cp_data_;
112         guint cp_length_;
113
114 public:
115         _ExifDataOriginal(Exiv2::Image::AutoPtr image)
116         {
117                 cp_data_ = NULL;
118                 cp_length_ = 0;
119                 image_ = image;
120         }
121
122         _ExifDataOriginal(gchar *path)
123         {
124                 cp_data_ = NULL;
125                 cp_length_ = 0;
126                 gchar *pathl = path_from_utf8(path);
127                 image_ = Exiv2::ImageFactory::open(pathl);
128                 g_free(pathl);
129 //              g_assert (image.get() != 0);
130                 image_->readMetadata();
131
132 #if EXIV2_TEST_VERSION(0,16,0)
133                 if (image_->mimeType() == "application/rdf+xml")
134                         {
135                         //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
136                         image_->clearExifData();
137                         image_->clearIptcData();
138                         }
139 #endif
140
141 #if EXIV2_TEST_VERSION(0,14,0)
142                 if (image_->mimeType() == "image/jpeg")
143                         {
144                         /* try to get jpeg color profile */
145                         Exiv2::BasicIo &io = image_->io();
146                         gint open = io.isopen();
147                         if (!open) io.open();
148                         unsigned char *mapped = (unsigned char*)io.mmap();
149                         if (mapped) exif_jpeg_parse_color(this, mapped, io.size());
150                         io.munmap();
151                         if (!open) io.close();
152                         }
153 #endif
154         }
155         
156         virtual ~_ExifDataOriginal()
157         {
158                 if (cp_data_) g_free(cp_data_);
159         }
160         
161         virtual Exiv2::Image *image()
162         {
163                 return image_.get();
164         }
165         
166         virtual Exiv2::ExifData &exifData ()
167         {
168                 return image_->exifData();
169         }
170
171         virtual Exiv2::IptcData &iptcData ()
172         {
173                 return image_->iptcData();
174         }
175
176 #if EXIV2_TEST_VERSION(0,16,0)
177         virtual Exiv2::XmpData &xmpData ()
178         {
179                 return image_->xmpData();
180         }
181 #endif
182
183         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
184         {
185                 if (cp_data_) g_free(cp_data_);
186                 cp_data_ = cp_data;
187                 cp_length_ = cp_length;
188         }
189
190         virtual guchar *get_jpeg_color_profile(guint *data_len)
191         {
192                 if (cp_data_)
193                 {
194                         if (data_len) *data_len = cp_length_;
195                         return (unsigned char *) g_memdup(cp_data_, cp_length_);
196                 }
197                 return NULL;
198         }
199 };
200
201 // This allows read-write access to the metadata
202 struct _ExifDataProcessed : public _ExifData
203 {
204 protected:
205         _ExifDataOriginal *imageData_;
206         _ExifDataOriginal *sidecarData_;
207
208         Exiv2::ExifData exifData_;
209         Exiv2::IptcData iptcData_;
210 #if EXIV2_TEST_VERSION(0,16,0)
211         Exiv2::XmpData xmpData_;
212 #endif
213
214 public:
215         _ExifDataProcessed(gchar *path, gchar *sidecar_path)
216         {
217                 imageData_ = new _ExifDataOriginal(path);
218                 sidecarData_ = NULL;
219 #if EXIV2_TEST_VERSION(0,16,0)
220                 xmpData_ = imageData_->xmpData();
221                 DEBUG_2("xmp count %li", xmpData_.count());
222                 if (sidecar_path && xmpData_.empty())
223                         {
224                         sidecarData_ = new _ExifDataOriginal(sidecar_path);
225                         xmpData_ = sidecarData_->xmpData();
226                         }
227 #endif
228                 exifData_ = imageData_->exifData();
229                 iptcData_ = imageData_->iptcData();
230 #if EXIV2_TEST_VERSION(0,17,0)
231                 syncExifWithXmp(exifData_, xmpData_);
232 #endif
233         }
234
235         virtual ~_ExifDataProcessed()
236         {
237                 if (imageData_) delete imageData_;
238                 if (sidecarData_) delete sidecarData_;
239         }
240
241         virtual ExifData *original()
242         {
243                 return imageData_;
244         }
245
246         virtual void writeMetadata()
247         {
248 #if EXIV2_TEST_VERSION(0,17,0)
249                 syncExifWithXmp(exifData_, xmpData_);
250                 copyXmpToIptc(xmpData_, iptcData_); //FIXME it should be configurable
251 #endif
252                 if (sidecarData_) 
253                         {
254                         sidecarData_->image()->setXmpData(xmpData_);
255                         //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
256                         sidecarData_->image()->clearExifData();
257                         sidecarData_->image()->clearIptcData();
258                         sidecarData_->image()->writeMetadata();
259                         }
260                 else
261                         {
262                         try     {
263                                 imageData_->image()->setExifData(exifData_);
264                                 imageData_->image()->setIptcData(iptcData_);
265                                 imageData_->image()->setXmpData(xmpData_);
266                                 imageData_->image()->writeMetadata();
267                                 } 
268                         catch (Exiv2::AnyError& e) 
269                                 {
270                                 // can't write into the image, create sidecar
271 #if EXIV2_TEST_VERSION(0,17,0)
272                                 gchar *base = remove_extension_from_path(imageData_->image()->io().path().c_str());
273                                 gchar *pathl = g_strconcat(base, ".xmp", NULL);
274
275                                 Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl);
276                                 
277                                 g_free(base);
278                                 g_free(pathl);
279                                 
280                                 sidecarData_ = new _ExifDataOriginal(sidecar);
281                                 sidecarData_->image()->setXmpData(xmpData_);
282                                 sidecarData_->image()->writeMetadata();
283 #endif
284                                 }
285                         }
286         }
287         
288         virtual Exiv2::Image *image()
289         {
290                 return imageData_->image();
291         }
292         
293         virtual Exiv2::ExifData &exifData ()
294         {
295                 return exifData_;
296         }
297
298         virtual Exiv2::IptcData &iptcData ()
299         {
300                 return iptcData_;
301         }
302
303 #if EXIV2_TEST_VERSION(0,16,0)
304         virtual Exiv2::XmpData &xmpData ()
305         {
306                 return xmpData_;
307         }
308 #endif
309
310         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
311         {
312                 imageData_->add_jpeg_color_profile(cp_data, cp_length);
313         }
314
315         virtual guchar *get_jpeg_color_profile(guint *data_len)
316         {
317                 return imageData_->get_jpeg_color_profile(data_len);
318         }
319 };
320
321
322
323
324 extern "C" {
325
326 ExifData *exif_read(gchar *path, gchar *sidecar_path)
327 {
328         DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-");
329         try {
330                 return new _ExifDataProcessed(path, sidecar_path);
331         }
332         catch (Exiv2::AnyError& e) {
333                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
334                 return NULL;
335         }
336         
337 }
338
339 int exif_write(ExifData *exif)
340 {
341         try {
342                 exif->writeMetadata();
343                 return 1;
344         }
345         catch (Exiv2::AnyError& e) {
346                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
347                 return 0;
348         }
349         
350 }
351
352
353 void exif_free(ExifData *exif)
354 {
355         if (!exif) return;
356         g_assert(dynamic_cast<_ExifDataProcessed *>(exif)); // this should not be called on ExifDataOriginal
357         delete exif;
358 }
359
360 ExifData *exif_get_original(ExifData *exif)
361 {
362         return exif->original();
363 }
364
365
366 ExifItem *exif_get_item(ExifData *exif, const gchar *key)
367 {
368         try {
369                 Exiv2::Metadatum *item = NULL;
370                 try {
371                         Exiv2::ExifKey ekey(key);
372                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
373                         if (pos == exif->exifData().end()) return NULL;
374                         item = &*pos;
375                 }
376                 catch (Exiv2::AnyError& e) {
377                         try {
378                                 Exiv2::IptcKey ekey(key);
379                                 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
380                                 if (pos == exif->iptcData().end()) return NULL;
381                                 item = &*pos;
382                         }
383                         catch (Exiv2::AnyError& e) {
384 #if EXIV2_TEST_VERSION(0,16,0)
385                                 Exiv2::XmpKey ekey(key);
386                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
387                                 if (pos == exif->xmpData().end()) return NULL;
388                                 item = &*pos;
389 #endif
390                         }
391                 }
392                 return (ExifItem *)item;
393         }
394         catch (Exiv2::AnyError& e) {
395                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
396                 return NULL;
397         }
398 }
399
400 ExifItem *exif_add_item(ExifData *exif, const gchar *key)
401 {
402         try {
403                 Exiv2::Metadatum *item = NULL;
404                 try {
405                         Exiv2::ExifKey ekey(key);
406                         exif->exifData().add(ekey, NULL);
407                         Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item
408                         pos--;
409                         item = &*pos;
410                 }
411                 catch (Exiv2::AnyError& e) {
412                         try {
413                                 Exiv2::IptcKey ekey(key);
414                                 exif->iptcData().add(ekey, NULL);
415                                 Exiv2::IptcData::iterator pos = exif->iptcData().end();
416                                 pos--;
417                                 item = &*pos;
418                         }
419                         catch (Exiv2::AnyError& e) {
420 #if EXIV2_TEST_VERSION(0,16,0)
421                                 Exiv2::XmpKey ekey(key);
422                                 exif->xmpData().add(ekey, NULL);
423                                 Exiv2::XmpData::iterator pos = exif->xmpData().end();
424                                 pos--;
425                                 item = &*pos;
426 #endif
427                         }
428                 }
429                 return (ExifItem *)item;
430         }
431         catch (Exiv2::AnyError& e) {
432                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
433                 return NULL;
434         }
435 }
436
437
438 ExifItem *exif_get_first_item(ExifData *exif)
439 {
440         try {
441                 exif->exifIter = exif->exifData().begin();
442                 exif->iptcIter = exif->iptcData().begin();
443 #if EXIV2_TEST_VERSION(0,16,0)
444                 exif->xmpIter = exif->xmpData().begin();
445 #endif
446                 if (exif->exifIter != exif->exifData().end())
447                         {
448                         const Exiv2::Metadatum *item = &*exif->exifIter;
449                         exif->exifIter++;
450                         return (ExifItem *)item;
451                         }
452                 if (exif->iptcIter != exif->iptcData().end())
453                         {
454                         const Exiv2::Metadatum *item = &*exif->iptcIter;
455                         exif->iptcIter++;
456                         return (ExifItem *)item;
457                         }
458 #if EXIV2_TEST_VERSION(0,16,0)
459                 if (exif->xmpIter != exif->xmpData().end())
460                         {
461                         const Exiv2::Metadatum *item = &*exif->xmpIter;
462                         exif->xmpIter++;
463                         return (ExifItem *)item;
464                         }
465 #endif
466                 return NULL;
467                         
468         }
469         catch (Exiv2::AnyError& e) {
470                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
471                 return NULL;
472         }
473 }
474
475 ExifItem *exif_get_next_item(ExifData *exif)
476 {
477         try {
478                 if (exif->exifIter != exif->exifData().end())
479                         {
480                         const Exiv2::Metadatum *item = &*exif->exifIter;
481                         exif->exifIter++;
482                         return (ExifItem *)item;
483                 }
484                 if (exif->iptcIter != exif->iptcData().end())
485                         {
486                         const Exiv2::Metadatum *item = &*exif->iptcIter;
487                         exif->iptcIter++;
488                         return (ExifItem *)item;
489                 }
490 #if EXIV2_TEST_VERSION(0,16,0)
491                 if (exif->xmpIter != exif->xmpData().end())
492                         {
493                         const Exiv2::Metadatum *item = &*exif->xmpIter;
494                         exif->xmpIter++;
495                         return (ExifItem *)item;
496                 }
497 #endif
498                 return NULL;
499         }
500         catch (Exiv2::AnyError& e) {
501                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
502                 return NULL;
503         }
504 }
505
506 char *exif_item_get_tag_name(ExifItem *item)
507 {
508         try {
509                 if (!item) return NULL;
510                 return g_strdup(((Exiv2::Metadatum *)item)->key().c_str());
511         }
512         catch (Exiv2::AnyError& e) {
513                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
514                 return NULL;
515         }
516 }
517
518 guint exif_item_get_tag_id(ExifItem *item)
519 {
520         try {
521                 if (!item) return 0;
522                 return ((Exiv2::Metadatum *)item)->tag();
523         }
524         catch (Exiv2::AnyError& e) {
525                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
526                 return 0;
527         }
528 }
529
530 guint exif_item_get_elements(ExifItem *item)
531 {
532         try {
533                 if (!item) return 0;
534                 return ((Exiv2::Metadatum *)item)->count();
535         }
536         catch (Exiv2::AnyError& e) {
537                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
538                 return 0;
539         }
540 }
541
542 char *exif_item_get_data(ExifItem *item, guint *data_len)
543 {
544         try {
545                 if (!item) return 0;
546                 Exiv2::Metadatum *md = (Exiv2::Metadatum *)item;
547                 if (data_len) *data_len = md->size();
548                 char *data = (char *)g_malloc(md->size());
549                 long res = md->copy((Exiv2::byte *)data, Exiv2::littleEndian /* should not matter */);
550                 g_assert(res == md->size());
551                 return data;
552         }
553         catch (Exiv2::AnyError& e) {
554                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
555                 return NULL;
556         }
557 }
558
559 char *exif_item_get_description(ExifItem *item)
560 {
561         try {
562                 if (!item) return NULL;
563                 return g_locale_to_utf8(((Exiv2::Metadatum *)item)->tagLabel().c_str(), -1, NULL, NULL, NULL);
564         }
565         catch (std::exception& e) {
566 //              std::cout << "Caught Exiv2 exception '" << e << "'\n";
567                 return NULL;
568         }
569 }
570
571 /*
572 invalidTypeId, unsignedByte, asciiString, unsignedShort,
573   unsignedLong, unsignedRational, signedByte, undefined,
574   signedShort, signedLong, signedRational, string,
575   date, time, comment, directory,
576   xmpText, xmpAlt, xmpBag, xmpSeq,
577   langAlt, lastTypeId
578 */
579
580 static guint format_id_trans_tbl [] = {
581         EXIF_FORMAT_UNKNOWN,
582         EXIF_FORMAT_BYTE_UNSIGNED,
583         EXIF_FORMAT_STRING,
584         EXIF_FORMAT_SHORT_UNSIGNED,
585         EXIF_FORMAT_LONG_UNSIGNED,
586         EXIF_FORMAT_RATIONAL_UNSIGNED,
587         EXIF_FORMAT_BYTE,
588         EXIF_FORMAT_UNDEFINED,
589         EXIF_FORMAT_SHORT,
590         EXIF_FORMAT_LONG,
591         EXIF_FORMAT_RATIONAL,
592         EXIF_FORMAT_STRING,
593         EXIF_FORMAT_STRING,
594         EXIF_FORMAT_STRING,
595         EXIF_FORMAT_UNDEFINED,
596         EXIF_FORMAT_STRING,
597         EXIF_FORMAT_STRING,
598         EXIF_FORMAT_STRING,
599         EXIF_FORMAT_STRING
600         };
601         
602         
603
604 guint exif_item_get_format_id(ExifItem *item)
605 {
606         try {
607                 if (!item) return EXIF_FORMAT_UNKNOWN;
608                 guint id = ((Exiv2::Metadatum *)item)->typeId();
609                 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
610                 return format_id_trans_tbl[id];
611         }
612         catch (Exiv2::AnyError& e) {
613                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
614                 return EXIF_FORMAT_UNKNOWN;
615         }
616 }
617
618 const char *exif_item_get_format_name(ExifItem *item, gint brief)
619 {
620         try {
621                 if (!item) return NULL;
622                 return ((Exiv2::Metadatum *)item)->typeName();
623         }
624         catch (Exiv2::AnyError& e) {
625                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
626                 return NULL;
627         }
628 }
629
630
631 gchar *exif_item_get_data_as_text(ExifItem *item)
632 {
633         try {
634                 if (!item) return NULL;
635                 Exiv2::Metadatum *metadatum = (Exiv2::Metadatum *)item;
636 #if EXIV2_TEST_VERSION(0,17,0)
637                 return g_locale_to_utf8(metadatum->print().c_str(), -1, NULL, NULL, NULL);
638 #else
639                 std::stringstream str;
640                 Exiv2::Exifdatum *exifdatum;
641                 Exiv2::Iptcdatum *iptcdatum;
642 #if EXIV2_TEST_VERSION(0,16,0)
643                 Exiv2::Xmpdatum *xmpdatum;
644 #endif
645                 if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum)))
646                         str << *exifdatum;
647                 else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum)))
648                         str << *iptcdatum;
649 #if EXIV2_TEST_VERSION(0,16,0)
650                 else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum)))
651                         str << *xmpdatum;
652 #endif
653
654                 return g_locale_to_utf8(str.str().c_str(), -1, NULL, NULL, NULL);
655 #endif
656         }
657         catch (Exiv2::AnyError& e) {
658                 return NULL;
659         }
660 }
661
662 gchar *exif_item_get_string(ExifItem *item, int idx)
663 {
664         try {
665                 if (!item) return NULL;
666                 Exiv2::Metadatum *em = (Exiv2::Metadatum *)item;
667 #if EXIV2_TEST_VERSION(0,16,0)
668                 std::string str = em->toString(idx);
669 #else
670                 std::string str = em->toString(); // FIXME
671 #endif
672                 if (idx == 0 && str == "") str = em->toString();
673                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
674                         {
675                         std::string::size_type pos = str.find_first_of(' ');
676                         if (pos != std::string::npos) str = str.substr(pos+1);
677                         }
678
679 //              return g_locale_to_utf8(str.c_str(), -1, NULL, NULL, NULL); // FIXME
680                 return g_strdup(str.c_str());
681         }
682         catch (Exiv2::AnyError& e) {
683                 return NULL;
684         }
685 }
686
687
688 gint exif_item_get_integer(ExifItem *item, gint *value)
689 {
690         try {
691                 if (!item) return 0;
692                 *value = ((Exiv2::Metadatum *)item)->toLong();
693                 return 1;
694         }
695         catch (Exiv2::AnyError& e) {
696                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
697                 return 0;
698         }
699 }
700
701 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n)
702 {
703         try {
704                 if (!item) return NULL;
705                 if (n >= exif_item_get_elements(item)) return NULL;
706                 Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational(n);
707                 static ExifRational ret;
708                 ret.num = v.first;
709                 ret.den = v.second;
710                 if (sign) *sign = (((Exiv2::Metadatum *)item)->typeId() == Exiv2::signedRational);
711                 return &ret;
712         }
713         catch (Exiv2::AnyError& e) {
714                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
715                 return NULL;
716         }
717 }
718
719 gchar *exif_get_tag_description_by_key(const gchar *key)
720 {
721         try {
722                 Exiv2::ExifKey ekey(key);
723                 return g_locale_to_utf8(Exiv2::ExifTags::tagLabel(ekey.tag(), ekey.ifdId ()), -1, NULL, NULL, NULL);
724         }
725         catch (Exiv2::AnyError& e) {
726                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
727                 return NULL;
728         }
729 }
730
731 int exif_item_set_string(ExifItem *item, const char *str)
732 {
733         try {
734                 if (!item) return 0;
735                 ((Exiv2::Metadatum *)item)->setValue(std::string(str));
736                 return 1;
737                 }
738         catch (Exiv2::AnyError& e) {
739                 return 0;
740         }
741 }
742
743 int exif_item_delete(ExifData *exif, ExifItem *item)
744 {
745         try {
746                 if (!item) return 0;
747                 for (Exiv2::ExifData::iterator i = exif->exifData().begin(); i != exif->exifData().end(); ++i) {
748                         if (((Exiv2::Metadatum *)item) == &*i) {
749                                 i = exif->exifData().erase(i);
750                                 return 1;
751                         }
752                 }
753                 for (Exiv2::IptcData::iterator i = exif->iptcData().begin(); i != exif->iptcData().end(); ++i) {
754                         if (((Exiv2::Metadatum *)item) == &*i) {
755                                 i = exif->iptcData().erase(i);
756                                 return 1;
757                         }
758                 }
759 #if EXIV2_TEST_VERSION(0,16,0)
760                 for (Exiv2::XmpData::iterator i = exif->xmpData().begin(); i != exif->xmpData().end(); ++i) {
761                         if (((Exiv2::Metadatum *)item) == &*i) {
762                                 i = exif->xmpData().erase(i);
763                                 return 1;
764                         }
765                 }
766 #endif
767                 return 0;
768         }
769         catch (Exiv2::AnyError& e) {
770                 return 0;
771         }
772 }
773
774 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
775 {
776         exif->add_jpeg_color_profile(cp_data, cp_length);
777 }
778
779 guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
780 {
781         guchar *ret = exif->get_jpeg_color_profile(data_len);
782         if (ret) return ret;
783
784         ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
785         if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
786                 ret = (guchar *)exif_item_get_data(prof_item, data_len);
787         return ret;
788 }
789
790 #if EXIV2_TEST_VERSION(0,17,90)
791
792 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
793 {
794         if (!exif) return NULL;
795
796         const char* path = exif->image()->io().path().c_str();
797         /* given image pathname, first do simple (and fast) file extension test */
798         gboolean is_raw = filter_file_class(path, FORMAT_CLASS_RAWIMAGE);
799         
800         if (!is_raw && requested_width == 0) return NULL;
801
802         try {
803
804                 Exiv2::PreviewManager pm(*exif->image());
805
806                 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties();
807
808                 if (!list.empty())
809                         {
810                         Exiv2::PreviewPropertiesList::iterator pos;
811                         Exiv2::PreviewPropertiesList::iterator last = --list.end();
812                         
813                         if (requested_width == 0)
814                                 {
815                                 pos = last; // the largest
816                                 }
817                         else
818                                 {
819                                 pos = list.begin();
820                                 while (pos != last)
821                                         {
822                                         if (pos->width_ >= (uint32_t)requested_width &&
823                                             pos->height_ >= (uint32_t)requested_height) break;
824                                         ++pos;
825                                         }
826                                 
827                                 // we are not interested in smaller thumbnails in normal image formats - we can use full image instead
828                                 if (!is_raw) 
829                                         {
830                                         if (pos->width_ < (uint32_t)requested_width || pos->height_ < (uint32_t)requested_height) return NULL; 
831                                         }
832                                 }
833
834                         Exiv2::PreviewImage image = pm.getPreviewImage(*pos);
835
836                         Exiv2::DataBuf buf = image.copy();
837                         std::pair<Exiv2::byte*, long> p = buf.release();
838
839                         *data_len = p.second;
840                         return p.first;
841                         }
842                 return NULL;
843         }
844         catch (Exiv2::AnyError& e) {
845                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
846                 return NULL;
847         }
848 }
849
850 void exif_free_preview(guchar *buf)
851 {
852         delete[] (Exiv2::byte*)buf;
853 }
854 #endif
855
856 }
857 #if !EXIV2_TEST_VERSION(0,17,90)
858
859 /* This is a dirty hack to support raw file preview, bassed on
860 tiffparse.cpp from Exiv2 examples */
861
862 class RawFile {
863         public:
864
865         RawFile(Exiv2::BasicIo &io);
866         ~RawFile();
867
868         const Exiv2::Value *find(uint16_t tag, uint16_t group);
869
870         unsigned long preview_offset();
871
872         private:
873         int type;
874         Exiv2::TiffComponent::AutoPtr rootDir;
875         Exiv2::BasicIo &io_;
876         const Exiv2::byte *map_data;
877         size_t map_len;
878         unsigned long offset;
879 };
880
881 typedef struct _UnmapData UnmapData;
882 struct _UnmapData
883 {
884         guchar *ptr;
885         guchar *map_data;
886         size_t map_len;
887 };
888
889 static GList *exif_unmap_list = 0;
890
891 extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
892 {
893         unsigned long offset;
894
895         if (!exif) return NULL;
896         const char* path = exif->image()->io().path().c_str();
897
898         /* given image pathname, first do simple (and fast) file extension test */
899         if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL;
900
901         try {
902                 struct stat st;
903                 guchar *map_data;
904                 size_t map_len;
905                 UnmapData *ud;
906                 int fd;
907                 
908                 RawFile rf(exif->image()->io());
909                 offset = rf.preview_offset();
910                 DEBUG_1("%s: offset %lu", path, offset);
911                 
912                 fd = open(path, O_RDONLY);
913                 if (fd == -1)
914                         {
915                         return NULL;
916                         }
917
918                 if (fstat(fd, &st) == -1)
919                         {
920                         close(fd);
921                         return NULL;
922                         }
923                 map_len = st.st_size;
924                 map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
925                 close(fd);
926                 if (map_data == MAP_FAILED)
927                         {
928                         return NULL;
929                         }
930                 *data_len = map_len - offset;
931                 ud = g_new(UnmapData, 1);
932                 ud->ptr = map_data + offset;
933                 ud->map_data = map_data;
934                 ud->map_len = map_len;
935                 
936                 exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
937                 return ud->ptr;
938                 
939         }
940         catch (Exiv2::AnyError& e) {
941                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
942         }
943         return NULL;
944
945 }
946
947 void exif_free_preview(guchar *buf)
948 {
949         GList *work = exif_unmap_list;
950         
951         while (work)
952                 {
953                 UnmapData *ud = (UnmapData *)work->data;
954                 if (ud->ptr == buf)
955                         {
956                         munmap(ud->map_data, ud->map_len);
957                         exif_unmap_list = g_list_remove_link(exif_unmap_list, work);
958                         g_free(ud);
959                         return;
960                         }
961                 work = work->next;
962                 }
963         g_assert_not_reached();
964 }
965
966 using namespace Exiv2;
967
968 RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0)
969 {
970 /*
971         struct stat st;
972         if (fstat(fd, &st) == -1)
973                 {
974                 throw Error(14);
975                 }
976         map_len = st.st_size;
977         map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
978         if (map_data == MAP_FAILED)
979                 {
980                 throw Error(14);
981                 }
982 */
983         if (io.open() != 0) {
984             throw Error(9, io.path(), strError());
985         }
986         
987         map_data = io.mmap();
988         map_len = io.size();
989
990
991         type = Exiv2::ImageFactory::getType(map_data, map_len);
992
993 #if EXIV2_TEST_VERSION(0,16,0)
994         TiffHeaderBase *tiffHeader = NULL;
995 #else
996         TiffHeade2 *tiffHeader = NULL;
997 #endif
998         Cr2Header *cr2Header = NULL;
999
1000         switch (type) {
1001                 case Exiv2::ImageType::tiff:
1002                         tiffHeader = new TiffHeade2();
1003                         break;
1004                 case Exiv2::ImageType::cr2:
1005                         cr2Header = new Cr2Header();
1006                         break;
1007 #if EXIV2_TEST_VERSION(0,16,0)
1008                 case Exiv2::ImageType::orf:
1009                         tiffHeader = new OrfHeader();
1010                         break;
1011 #endif
1012 #if EXIV2_TEST_VERSION(0,13,0)
1013                 case Exiv2::ImageType::raf:
1014                         if (map_len < 84 + 4) throw Error(14);
1015                         offset = getULong(map_data + 84, bigEndian);
1016                         return;
1017 #endif
1018                 case Exiv2::ImageType::crw:
1019                         {
1020                         // Parse the image, starting with a CIFF header component
1021                         Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
1022                         parseTree->read(map_data, map_len);
1023                         CiffComponent *entry = parseTree->findComponent(0x2007, 0);
1024                         if (entry) offset =  entry->pData() - map_data;
1025                         return;
1026                         }
1027
1028                 default:
1029                         throw Error(3, "RAW");
1030         }
1031
1032         // process tiff-like formats
1033
1034         TiffCompFactoryFct createFct = TiffCreator::create;
1035
1036         rootDir = createFct(Tag::root, Group::none);
1037         if (0 == rootDir.get()) {
1038                 throw Error(1, "No root element defined in TIFF structure");
1039         }
1040         
1041         if (tiffHeader)
1042                 {
1043                 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
1044 #if EXIV2_TEST_VERSION(0,16,0)
1045                 rootDir->setStart(map_data + tiffHeader->offset());
1046 #else
1047                 rootDir->setStart(map_data + tiffHeader->ifdOffset());
1048 #endif
1049                 }
1050                 
1051         if (cr2Header)
1052                 {
1053                 rootDir->setStart(map_data + cr2Header->offset());
1054                 }
1055         
1056         TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct));
1057
1058         TiffReader reader(map_data,
1059                           map_len,
1060                           rootDir.get(),
1061                           state);
1062
1063         rootDir->accept(reader);
1064         
1065         if (tiffHeader)
1066                 delete tiffHeader;
1067         if (cr2Header)
1068                 delete cr2Header;
1069 }
1070
1071 RawFile::~RawFile(void)
1072 {
1073         io_.munmap();
1074         io_.close();
1075 }
1076
1077 const Value * RawFile::find(uint16_t tag, uint16_t group)
1078 {
1079         TiffFinder finder(tag, group);
1080         rootDir->accept(finder);
1081         TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
1082         if (te)
1083                 {
1084                 DEBUG_1("(tag: %04x %04x) ", tag, group);
1085                 return te->pValue();
1086                 }
1087         else
1088                 return NULL;
1089 }
1090
1091 unsigned long RawFile::preview_offset(void)
1092 {
1093         const Value *val;
1094         if (offset) return offset;
1095         
1096         if (type == Exiv2::ImageType::cr2)
1097                 {
1098                 val = find(0x111, Group::ifd0);
1099                 if (val) return val->toLong();
1100
1101                 return 0;
1102                 }
1103         
1104         val = find(0x201, Group::sub0_0);
1105         if (val) return val->toLong();
1106
1107         val = find(0x201, Group::ifd0);
1108         if (val) return val->toLong();
1109
1110         val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
1111         if (val) return val->toLong();
1112
1113         val = find(0x111, Group::sub0_1); // dng
1114         if (val) return val->toLong();
1115
1116         return 0;
1117 }
1118
1119
1120 #endif
1121
1122
1123 #endif
1124 /* HAVE_EXIV2 */
1125 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */