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