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