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