8 #include <exiv2/image.hpp>
9 #include <exiv2/exif.hpp>
12 #include <sys/types.h>
18 #include <exiv2/tiffparser.hpp>
19 #include <exiv2/tiffcomposite.hpp>
20 #include <exiv2/tiffvisitor.hpp>
21 #include <exiv2/tiffimage.hpp>
22 #include <exiv2/cr2image.hpp>
23 #include <exiv2/crwimage.hpp>
24 #include <exiv2/orfimage.hpp>
25 #include <exiv2/rafimage.hpp>
26 #include <exiv2/futils.hpp>
39 Exiv2::Image::AutoPtr image;
40 Exiv2::Image::AutoPtr sidecar;
41 Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
42 Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
43 Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
47 _ExifData(gchar *path, gchar *sidecar_path, gint parse_color_profile)
50 image = Exiv2::ImageFactory::open(path);
51 // g_assert (image.get() != 0);
52 image->readMetadata();
54 printf("xmp count %d\n", image->xmpData().count());
55 if (sidecar_path && image->xmpData().empty())
57 sidecar = Exiv2::ImageFactory::open(sidecar_path);
58 sidecar->readMetadata();
59 have_sidecar = sidecar->good();
60 printf("sidecar xmp count %d\n", sidecar->xmpData().count());
67 if (have_sidecar) sidecar->writeMetadata();
68 image->writeMetadata();
71 Exiv2::ExifData &exifData ()
73 return image->exifData();
76 Exiv2::IptcData &iptcData ()
78 return image->iptcData();
81 Exiv2::XmpData &xmpData ()
83 return have_sidecar ? sidecar->xmpData() : image->xmpData();
91 ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
93 printf("exif %s %s\n", path, sidecar_path ? sidecar_path : "-");
95 return new ExifData(path, sidecar_path, parse_color_profile);
97 catch (Exiv2::AnyError& e) {
98 std::cout << "Caught Exiv2 exception '" << e << "'\n";
104 int exif_write(ExifData *exif)
107 exif->writeMetadata();
110 catch (Exiv2::AnyError& e) {
111 std::cout << "Caught Exiv2 exception '" << e << "'\n";
118 void exif_free(ExifData *exif)
124 ExifItem *exif_get_item(ExifData *exif, const gchar *key)
127 Exiv2::Metadatum *item;
129 Exiv2::ExifKey ekey(key);
130 Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
131 if (pos == exif->exifData().end()) return NULL;
134 catch (Exiv2::AnyError& e) {
136 Exiv2::IptcKey ekey(key);
137 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
138 if (pos == exif->iptcData().end()) return NULL;
141 catch (Exiv2::AnyError& e) {
142 Exiv2::XmpKey ekey(key);
143 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
144 if (pos == exif->xmpData().end()) return NULL;
148 return (ExifItem *)item;
150 catch (Exiv2::AnyError& e) {
151 std::cout << "Caught Exiv2 exception '" << e << "'\n";
156 ExifItem *exif_add_item(ExifData *exif, const gchar *key)
159 Exiv2::Metadatum *item;
161 Exiv2::ExifKey ekey(key);
162 exif->exifData().add(ekey, NULL);
163 Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item
167 catch (Exiv2::AnyError& e) {
169 Exiv2::IptcKey ekey(key);
170 exif->iptcData().add(ekey, NULL);
171 Exiv2::IptcData::iterator pos = exif->iptcData().end();
175 catch (Exiv2::AnyError& e) {
176 Exiv2::XmpKey ekey(key);
177 exif->xmpData().add(ekey, NULL);
178 Exiv2::XmpData::iterator pos = exif->xmpData().end();
183 return (ExifItem *)item;
185 catch (Exiv2::AnyError& e) {
186 std::cout << "Caught Exiv2 exception '" << e << "'\n";
192 ExifItem *exif_get_first_item(ExifData *exif)
195 exif->exifIter = exif->exifData().begin();
196 exif->iptcIter = exif->iptcData().begin();
197 exif->xmpIter = exif->xmpData().begin();
198 if (exif->exifIter != exif->exifData().end())
200 const Exiv2::Metadatum *item = &*exif->exifIter;
202 return (ExifItem *)item;
204 if (exif->iptcIter != exif->iptcData().end())
206 const Exiv2::Metadatum *item = &*exif->iptcIter;
208 return (ExifItem *)item;
210 if (exif->xmpIter != exif->xmpData().end())
212 const Exiv2::Metadatum *item = &*exif->xmpIter;
214 return (ExifItem *)item;
219 catch (Exiv2::AnyError& e) {
220 std::cout << "Caught Exiv2 exception '" << e << "'\n";
225 ExifItem *exif_get_next_item(ExifData *exif)
228 if (exif->exifIter != exif->exifData().end())
230 const Exiv2::Metadatum *item = &*exif->exifIter;
232 return (ExifItem *)item;
234 if (exif->iptcIter != exif->iptcData().end())
236 const Exiv2::Metadatum *item = &*exif->iptcIter;
238 return (ExifItem *)item;
240 if (exif->xmpIter != exif->xmpData().end())
242 const Exiv2::Metadatum *item = &*exif->xmpIter;
244 return (ExifItem *)item;
248 catch (Exiv2::AnyError& e) {
249 std::cout << "Caught Exiv2 exception '" << e << "'\n";
254 char *exif_item_get_tag_name(ExifItem *item)
257 if (!item) return NULL;
258 return g_strdup(((Exiv2::Metadatum *)item)->key().c_str());
260 catch (Exiv2::AnyError& e) {
261 std::cout << "Caught Exiv2 exception '" << e << "'\n";
266 guint exif_item_get_tag_id(ExifItem *item)
270 return ((Exiv2::Metadatum *)item)->tag();
272 catch (Exiv2::AnyError& e) {
273 std::cout << "Caught Exiv2 exception '" << e << "'\n";
278 guint exif_item_get_elements(ExifItem *item)
282 return ((Exiv2::Metadatum *)item)->count();
284 catch (Exiv2::AnyError& e) {
285 std::cout << "Caught Exiv2 exception '" << e << "'\n";
290 char *exif_item_get_data(ExifItem *item, guint *data_len)
294 char *exif_item_get_description(ExifItem *item)
297 if (!item) return NULL;
298 return g_strdup(((Exiv2::Metadatum *)item)->tagLabel().c_str());
300 catch (std::exception& e) {
301 // std::cout << "Caught Exiv2 exception '" << e << "'\n";
307 invalidTypeId, unsignedByte, asciiString, unsignedShort,
308 unsignedLong, unsignedRational, signedByte, undefined,
309 signedShort, signedLong, signedRational, string,
310 date, time, comment, directory,
311 xmpText, xmpAlt, xmpBag, xmpSeq,
315 static guint format_id_trans_tbl [] = {
317 EXIF_FORMAT_BYTE_UNSIGNED,
319 EXIF_FORMAT_SHORT_UNSIGNED,
320 EXIF_FORMAT_LONG_UNSIGNED,
321 EXIF_FORMAT_RATIONAL_UNSIGNED,
323 EXIF_FORMAT_UNDEFINED,
326 EXIF_FORMAT_RATIONAL,
330 EXIF_FORMAT_UNDEFINED,
339 guint exif_item_get_format_id(ExifItem *item)
342 if (!item) return EXIF_FORMAT_UNKNOWN;
343 guint id = ((Exiv2::Metadatum *)item)->typeId();
344 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
345 return format_id_trans_tbl[id];
347 catch (Exiv2::AnyError& e) {
348 std::cout << "Caught Exiv2 exception '" << e << "'\n";
349 return EXIF_FORMAT_UNKNOWN;
353 const char *exif_item_get_format_name(ExifItem *item, gint brief)
356 if (!item) return NULL;
357 return ((Exiv2::Metadatum *)item)->typeName();
359 catch (Exiv2::AnyError& e) {
360 std::cout << "Caught Exiv2 exception '" << e << "'\n";
366 gchar *exif_item_get_data_as_text(ExifItem *item)
369 if (!item) return NULL;
370 // std::stringstream str; // does not work with Exiv2::Metadatum because operator<< is not virtual
371 // str << *((Exiv2::Metadatum *)item);
372 // return g_strdup(str.str().c_str());
373 return g_strdup(((Exiv2::Metadatum *)item)->toString().c_str());
375 catch (Exiv2::AnyError& e) {
380 gchar *exif_item_get_string(ExifItem *item, int idx)
383 if (!item) return NULL;
384 Exiv2::Metadatum *em = (Exiv2::Metadatum *)item;
385 std::string str = em->toString(idx);
386 if (idx == 0 && str == "") str = em->toString();
387 if (str.length() > 5 && str.substr(0, 5) == "lang=")
389 std::string::size_type pos = str.find_first_of(' ');
390 if (pos != std::string::npos) str = str.substr(pos+1);
393 return g_strdup(str.c_str());
395 catch (Exiv2::AnyError& e) {
401 gint exif_item_get_integer(ExifItem *item, gint *value)
405 *value = ((Exiv2::Metadatum *)item)->toLong();
408 catch (Exiv2::AnyError& e) {
409 std::cout << "Caught Exiv2 exception '" << e << "'\n";
414 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign)
417 if (!item) return NULL;
418 Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational();
419 static ExifRational ret;
424 catch (Exiv2::AnyError& e) {
425 std::cout << "Caught Exiv2 exception '" << e << "'\n";
430 const gchar *exif_get_tag_description_by_key(const gchar *key)
433 Exiv2::ExifKey ekey(key);
434 return Exiv2::ExifTags::tagLabel(ekey.tag(), ekey.ifdId ());
436 catch (Exiv2::AnyError& e) {
437 std::cout << "Caught Exiv2 exception '" << e << "'\n";
442 int exif_item_set_string(ExifItem *item, const char *str)
446 ((Exiv2::Metadatum *)item)->setValue(std::string(str));
449 catch (Exiv2::AnyError& e) {
454 int exif_item_delete(ExifData *exif, ExifItem *item)
458 for (Exiv2::ExifData::iterator i = exif->exifData().begin(); i != exif->exifData().end(); ++i) {
459 if (((Exiv2::Metadatum *)item) == &*i) {
460 i = exif->exifData().erase(i);
464 for (Exiv2::IptcData::iterator i = exif->iptcData().begin(); i != exif->iptcData().end(); ++i) {
465 if (((Exiv2::Metadatum *)item) == &*i) {
466 i = exif->iptcData().erase(i);
470 for (Exiv2::XmpData::iterator i = exif->xmpData().begin(); i != exif->xmpData().end(); ++i) {
471 if (((Exiv2::Metadatum *)item) == &*i) {
472 i = exif->xmpData().erase(i);
479 catch (Exiv2::AnyError& e) {
488 /* This is a dirty hack to support raw file preview, bassed on
489 tiffparse.cpp from Exiv2 examples */
497 const Exiv2::Value *find(uint16_t tag, uint16_t group);
499 unsigned long preview_offset();
503 Exiv2::TiffComponent::AutoPtr rootDir;
504 Exiv2::byte *map_data;
506 unsigned long offset;
509 using namespace Exiv2;
511 RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
514 if (fstat(fd, &st) == -1)
518 map_len = st.st_size;
519 map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
520 if (map_data == MAP_FAILED)
524 type = Exiv2::ImageFactory::getType(map_data, map_len);
526 TiffHeaderBase *tiffHeader;
528 case Exiv2::ImageType::tiff:
529 tiffHeader = new TiffHeade2();
531 case Exiv2::ImageType::cr2:
532 tiffHeader = new Cr2Header();
534 case Exiv2::ImageType::orf:
535 tiffHeader = new OrfHeader();
537 case Exiv2::ImageType::raf:
538 if (map_len < 84 + 4) throw Error(14);
539 offset = getULong(map_data + 84, bigEndian);
541 case Exiv2::ImageType::crw:
543 // Parse the image, starting with a CIFF header component
544 Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
545 parseTree->read(map_data, map_len);
546 CiffComponent *entry = parseTree->findComponent(0x2007, 0);
547 if (entry) offset = entry->pData() - map_data;
552 throw Error(3, "RAW");
555 // process tiff-like formats
556 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
558 TiffCompFactoryFct createFct = TiffCreator::create;
560 rootDir = createFct(Tag::root, Group::none);
561 if (0 == rootDir.get()) {
562 throw Error(1, "No root element defined in TIFF structure");
564 rootDir->setStart(map_data + tiffHeader->offset());
566 TiffRwState::AutoPtr state(
567 new TiffRwState(tiffHeader->byteOrder(), 0, createFct));
569 TiffReader reader(map_data,
574 rootDir->accept(reader);
581 if (map_data && munmap(map_data, map_len) == -1)
583 printf("Failed to unmap file \n");
587 const Value * RawFile::find(uint16_t tag, uint16_t group)
589 printf("%04x %04x\n", tag, group);
590 TiffFinder finder(tag, group);
591 rootDir->accept(finder);
592 TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
599 unsigned long RawFile::preview_offset()
602 if (offset) return offset;
604 if (type == Exiv2::ImageType::cr2)
606 val = find(0x111, Group::ifd0);
607 if (val) return val->toLong();
612 val = find(0x201, Group::sub0_0);
613 if (val) return val->toLong();
615 val = find(0x201, Group::ifd0);
616 if (val) return val->toLong();
618 val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
619 if (val) return val->toLong();
621 val = find(0x111, Group::sub0_1); // dng
622 if (val) return val->toLong();
628 const static char *raw_ext_list[] = { ".cr2", ".nef", ".pef", ".arw", NULL };
630 extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
631 unsigned char *header_data, const guint header_len,
632 guint *image_offset, guint *exif_offset)
635 unsigned long offset;
637 /* given image pathname, first do simple (and fast) file extension test */
644 ext = strrchr(path, '.');
645 if (!ext) return FALSE;
647 for (i = 0; raw_ext_list[i]; i++)
649 if (strcasecmp(raw_ext_list[i], ext) == 0)
656 if (!match) return FALSE;
662 offset = rf.preview_offset();
664 catch (Exiv2::AnyError& e) {
665 std::cout << "Caught Exiv2 exception '" << e << "'\n";
671 *image_offset = offset;
672 if (lseek(fd, *image_offset, SEEK_SET) != *image_offset)
674 printf("Failed to seek to embedded image\n");
677 if (*exif_offset) *exif_offset = 0;