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