updated copyright in source files
[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                 return &ret;
500         }
501         catch (Exiv2::AnyError& e) {
502                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
503                 return NULL;
504         }
505 }
506
507 const gchar *exif_get_tag_description_by_key(const gchar *key)
508 {
509         try {
510                 Exiv2::ExifKey ekey(key);
511                 return Exiv2::ExifTags::tagLabel(ekey.tag(), ekey.ifdId ());
512         }
513         catch (Exiv2::AnyError& e) {
514                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
515                 return NULL;
516         }
517 }
518
519 int exif_item_set_string(ExifItem *item, const char *str)
520 {
521         try {
522                 if (!item) return 0;
523                 ((Exiv2::Metadatum *)item)->setValue(std::string(str));
524                 return 1;
525         }
526         catch (Exiv2::AnyError& e) {
527                 return 0;
528         }
529 }
530
531 int exif_item_delete(ExifData *exif, ExifItem *item)
532 {
533         try {
534                 if (!item) return 0;
535                 for (Exiv2::ExifData::iterator i = exif->exifData().begin(); i != exif->exifData().end(); ++i) {
536                         if (((Exiv2::Metadatum *)item) == &*i) {
537                                 i = exif->exifData().erase(i);
538                                 return 1;
539                         }
540                 }
541                 for (Exiv2::IptcData::iterator i = exif->iptcData().begin(); i != exif->iptcData().end(); ++i) {
542                         if (((Exiv2::Metadatum *)item) == &*i) {
543                                 i = exif->iptcData().erase(i);
544                                 return 1;
545                         }
546                 }
547 #if EXIV2_TEST_VERSION(0,16,0)
548                 for (Exiv2::XmpData::iterator i = exif->xmpData().begin(); i != exif->xmpData().end(); ++i) {
549                         if (((Exiv2::Metadatum *)item) == &*i) {
550                                 i = exif->xmpData().erase(i);
551                                 return 1;
552                         }
553                 }
554 #endif          
555                 return 0;
556         }
557         catch (Exiv2::AnyError& e) {
558                 return 0;
559         }
560 }
561
562 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
563 {
564         if (exif->cp_data) g_free(exif->cp_data);
565         exif->cp_data = cp_data;
566         exif->cp_length =cp_length;
567 }
568
569 unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len)
570 {
571         if (exif->cp_data)
572                 {
573                 if (data_len) *data_len = exif->cp_length;
574                 return (unsigned char *) g_memdup(exif->cp_data, exif->cp_length);
575                 }
576         ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
577         if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
578                 return (unsigned char *) exif_item_get_data(prof_item, data_len);
579         return NULL;
580 }
581
582
583
584 }
585
586 /* This is a dirty hack to support raw file preview, bassed on 
587 tiffparse.cpp from Exiv2 examples */
588
589 class RawFile {
590         public:
591     
592         RawFile(int fd);
593         ~RawFile();
594     
595         const Exiv2::Value *find(uint16_t tag, uint16_t group);
596     
597         unsigned long preview_offset();
598     
599         private:
600         int type;
601         Exiv2::TiffComponent::AutoPtr rootDir;
602         Exiv2::byte *map_data;
603         size_t map_len;
604         unsigned long offset;
605 };
606
607 using namespace Exiv2;
608
609 RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
610 {
611         struct stat st;
612         if (fstat(fd, &st) == -1)
613                 {
614                 throw Error(14);
615                 }
616         map_len = st.st_size;
617         map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
618         if (map_data == MAP_FAILED)
619                 {
620                 throw Error(14);
621                 }
622         type = Exiv2::ImageFactory::getType(map_data, map_len);
623
624 #if EXIV2_TEST_VERSION(0,16,0)
625         TiffHeaderBase *tiffHeader = NULL;
626 #else
627         TiffHeade2 *tiffHeader = NULL;
628 #endif
629         Cr2Header *cr2Header = NULL;
630
631         switch (type) {
632                 case Exiv2::ImageType::tiff:
633                         tiffHeader = new TiffHeade2();
634                         break;
635                 case Exiv2::ImageType::cr2:
636                         cr2Header = new Cr2Header();
637                         break;
638 #if EXIV2_TEST_VERSION(0,16,0)
639                 case Exiv2::ImageType::orf:
640                         tiffHeader = new OrfHeader();
641                         break;
642 #endif
643 #if EXIV2_TEST_VERSION(0,13,0)
644                 case Exiv2::ImageType::raf:
645                         if (map_len < 84 + 4) throw Error(14);
646                         offset = getULong(map_data + 84, bigEndian);
647                         return;
648 #endif
649                 case Exiv2::ImageType::crw:
650                         {
651                         // Parse the image, starting with a CIFF header component
652                         Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
653                         parseTree->read(map_data, map_len);
654                         CiffComponent *entry = parseTree->findComponent(0x2007, 0); 
655                         if (entry) offset =  entry->pData() - map_data;
656                         return;
657                         }
658
659                 default:
660                         throw Error(3, "RAW");
661         }
662
663         // process tiff-like formats
664
665         TiffCompFactoryFct createFct = TiffCreator::create;
666
667         rootDir = createFct(Tag::root, Group::none);
668         if (0 == rootDir.get()) {
669                 throw Error(1, "No root element defined in TIFF structure");
670         }
671         
672         if (tiffHeader)
673                 {
674                 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
675 #if EXIV2_TEST_VERSION(0,16,0)
676                 rootDir->setStart(map_data + tiffHeader->offset());
677 #else
678                 rootDir->setStart(map_data + tiffHeader->ifdOffset());
679 #endif
680                 }
681                 
682         if (cr2Header)
683                 {
684                 rootDir->setStart(map_data + cr2Header->offset());
685                 }
686         
687         TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct));
688
689         TiffReader reader(map_data,
690                       map_len,
691                       rootDir.get(),
692                       state);
693
694         rootDir->accept(reader);
695         
696         if (tiffHeader) 
697                 delete tiffHeader;
698         if (cr2Header) 
699                 delete cr2Header;
700 }
701
702 RawFile::~RawFile()
703 {
704         if (map_data && munmap(map_data, map_len) == -1)
705                 {
706                 printf("Failed to unmap file \n");
707                 }
708 }
709
710 const Value * RawFile::find(uint16_t tag, uint16_t group)
711 {
712         TiffFinder finder(tag, group);
713         rootDir->accept(finder);
714         TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
715         if (te)
716                 {
717                 if (debug) printf("(tag: %04x %04x) ", tag, group);
718                 return te->pValue();
719                 }
720         else
721                 return NULL;
722 }
723
724 unsigned long RawFile::preview_offset()
725 {
726         const Value *val;
727         if (offset) return offset;
728         
729         if (type == Exiv2::ImageType::cr2)
730                 {
731                 val = find(0x111, Group::ifd0);
732                 if (val) return val->toLong();
733     
734                 return 0;
735                 }
736         
737         val = find(0x201, Group::sub0_0);
738         if (val) return val->toLong();
739
740         val = find(0x201, Group::ifd0);
741         if (val) return val->toLong();
742     
743         val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
744         if (val) return val->toLong();
745
746         val = find(0x111, Group::sub0_1); // dng
747         if (val) return val->toLong();
748
749         return 0;
750 }
751
752
753 extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
754                                     unsigned char *header_data, const guint header_len,
755                                     guint *image_offset, guint *exif_offset)
756 {
757         int success;
758         unsigned long offset;
759
760         /* given image pathname, first do simple (and fast) file extension test */
761         if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return 0;
762
763         try {
764                 RawFile rf(fd);
765                 if (debug) printf("%s: offset ", path);
766                 offset = rf.preview_offset();
767                 if (debug) printf("%lu\n", offset);
768         }
769         catch (Exiv2::AnyError& e) {
770                 std::cout << "Caught Exiv2 exception '" << e << "'\n";
771                 return 0;
772         }
773
774         if (image_offset && offset > 0)
775                 {
776                 *image_offset = offset;
777                 if ((unsigned long) lseek(fd, *image_offset, SEEK_SET) != *image_offset)
778                         {
779                         printf("Failed to seek to embedded image\n");
780
781                         *image_offset = 0;
782                         if (*exif_offset) *exif_offset = 0;
783                         success = FALSE;
784                         }
785                 }
786
787         return offset > 0;
788 }
789
790
791 #endif 
792 /* HAVE_EXIV2 */