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