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