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/orfimage.hpp>
24 #include <exiv2/rafimage.hpp>
25 #include <exiv2/futils.hpp>
38 Exiv2::Image::AutoPtr image;
39 Exiv2::Image::AutoPtr sidecar;
40 Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
41 Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
42 Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
46 _ExifData(gchar *path, gchar *sidecar_path, gint parse_color_profile)
49 image = Exiv2::ImageFactory::open(path);
50 // g_assert (image.get() != 0);
51 image->readMetadata();
53 printf("xmp count %d\n", image->xmpData().count());
54 if (sidecar_path && image->xmpData().empty())
56 sidecar = Exiv2::ImageFactory::open(sidecar_path);
57 sidecar->readMetadata();
58 have_sidecar = sidecar->good();
59 printf("sidecar xmp count %d\n", sidecar->xmpData().count());
66 if (have_sidecar) sidecar->writeMetadata();
67 image->writeMetadata();
70 Exiv2::ExifData &exifData ()
72 return image->exifData();
75 Exiv2::IptcData &iptcData ()
77 return image->iptcData();
80 Exiv2::XmpData &xmpData ()
82 return have_sidecar ? sidecar->xmpData() : image->xmpData();
90 ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
92 printf("exif %s %s\n", path, sidecar_path ? sidecar_path : "-");
94 return new ExifData(path, sidecar_path, parse_color_profile);
96 catch (Exiv2::AnyError& e) {
97 std::cout << "Caught Exiv2 exception '" << e << "'\n";
103 int exif_write(ExifData *exif)
106 exif->writeMetadata();
109 catch (Exiv2::AnyError& e) {
110 std::cout << "Caught Exiv2 exception '" << e << "'\n";
117 void exif_free(ExifData *exif)
123 ExifItem *exif_get_item(ExifData *exif, const gchar *key)
126 Exiv2::Metadatum *item;
128 Exiv2::ExifKey ekey(key);
129 Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
130 if (pos == exif->exifData().end()) return NULL;
133 catch (Exiv2::AnyError& e) {
135 Exiv2::IptcKey ekey(key);
136 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
137 if (pos == exif->iptcData().end()) return NULL;
140 catch (Exiv2::AnyError& e) {
141 Exiv2::XmpKey ekey(key);
142 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
143 if (pos == exif->xmpData().end()) return NULL;
147 return (ExifItem *)item;
149 catch (Exiv2::AnyError& e) {
150 std::cout << "Caught Exiv2 exception '" << e << "'\n";
155 ExifItem *exif_add_item(ExifData *exif, const gchar *key)
158 Exiv2::Metadatum *item;
160 Exiv2::ExifKey ekey(key);
161 exif->exifData().add(ekey, NULL);
162 Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item
166 catch (Exiv2::AnyError& e) {
168 Exiv2::IptcKey ekey(key);
169 exif->iptcData().add(ekey, NULL);
170 Exiv2::IptcData::iterator pos = exif->iptcData().end();
174 catch (Exiv2::AnyError& e) {
175 Exiv2::XmpKey ekey(key);
176 exif->xmpData().add(ekey, NULL);
177 Exiv2::XmpData::iterator pos = exif->xmpData().end();
182 return (ExifItem *)item;
184 catch (Exiv2::AnyError& e) {
185 std::cout << "Caught Exiv2 exception '" << e << "'\n";
191 ExifItem *exif_get_first_item(ExifData *exif)
194 exif->exifIter = exif->exifData().begin();
195 exif->iptcIter = exif->iptcData().begin();
196 exif->xmpIter = exif->xmpData().begin();
197 if (exif->exifIter != exif->exifData().end())
199 const Exiv2::Metadatum *item = &*exif->exifIter;
201 return (ExifItem *)item;
203 if (exif->iptcIter != exif->iptcData().end())
205 const Exiv2::Metadatum *item = &*exif->iptcIter;
207 return (ExifItem *)item;
209 if (exif->xmpIter != exif->xmpData().end())
211 const Exiv2::Metadatum *item = &*exif->xmpIter;
213 return (ExifItem *)item;
218 catch (Exiv2::AnyError& e) {
219 std::cout << "Caught Exiv2 exception '" << e << "'\n";
224 ExifItem *exif_get_next_item(ExifData *exif)
227 if (exif->exifIter != exif->exifData().end())
229 const Exiv2::Metadatum *item = &*exif->exifIter;
231 return (ExifItem *)item;
233 if (exif->iptcIter != exif->iptcData().end())
235 const Exiv2::Metadatum *item = &*exif->iptcIter;
237 return (ExifItem *)item;
239 if (exif->xmpIter != exif->xmpData().end())
241 const Exiv2::Metadatum *item = &*exif->xmpIter;
243 return (ExifItem *)item;
247 catch (Exiv2::AnyError& e) {
248 std::cout << "Caught Exiv2 exception '" << e << "'\n";
253 char *exif_item_get_tag_name(ExifItem *item)
256 if (!item) return NULL;
257 return g_strdup(((Exiv2::Metadatum *)item)->key().c_str());
259 catch (Exiv2::AnyError& e) {
260 std::cout << "Caught Exiv2 exception '" << e << "'\n";
265 guint exif_item_get_tag_id(ExifItem *item)
269 return ((Exiv2::Metadatum *)item)->tag();
271 catch (Exiv2::AnyError& e) {
272 std::cout << "Caught Exiv2 exception '" << e << "'\n";
277 guint exif_item_get_elements(ExifItem *item)
281 return ((Exiv2::Metadatum *)item)->count();
283 catch (Exiv2::AnyError& e) {
284 std::cout << "Caught Exiv2 exception '" << e << "'\n";
289 char *exif_item_get_data(ExifItem *item, guint *data_len)
293 char *exif_item_get_description(ExifItem *item)
296 if (!item) return NULL;
297 return g_strdup(((Exiv2::Metadatum *)item)->tagLabel().c_str());
299 catch (std::exception& e) {
300 // std::cout << "Caught Exiv2 exception '" << e << "'\n";
306 invalidTypeId, unsignedByte, asciiString, unsignedShort,
307 unsignedLong, unsignedRational, signedByte, undefined,
308 signedShort, signedLong, signedRational, string,
309 date, time, comment, directory,
310 xmpText, xmpAlt, xmpBag, xmpSeq,
314 static guint format_id_trans_tbl [] = {
316 EXIF_FORMAT_BYTE_UNSIGNED,
318 EXIF_FORMAT_SHORT_UNSIGNED,
319 EXIF_FORMAT_LONG_UNSIGNED,
320 EXIF_FORMAT_RATIONAL_UNSIGNED,
322 EXIF_FORMAT_UNDEFINED,
325 EXIF_FORMAT_RATIONAL,
329 EXIF_FORMAT_UNDEFINED,
338 guint exif_item_get_format_id(ExifItem *item)
341 if (!item) return EXIF_FORMAT_UNKNOWN;
342 guint id = ((Exiv2::Metadatum *)item)->typeId();
343 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
344 return format_id_trans_tbl[id];
346 catch (Exiv2::AnyError& e) {
347 std::cout << "Caught Exiv2 exception '" << e << "'\n";
348 return EXIF_FORMAT_UNKNOWN;
352 const char *exif_item_get_format_name(ExifItem *item, gint brief)
355 if (!item) return NULL;
356 return ((Exiv2::Metadatum *)item)->typeName();
358 catch (Exiv2::AnyError& e) {
359 std::cout << "Caught Exiv2 exception '" << e << "'\n";
365 gchar *exif_item_get_data_as_text(ExifItem *item)
368 if (!item) return NULL;
369 // std::stringstream str; // does not work with Exiv2::Metadatum because operator<< is not virtual
370 // str << *((Exiv2::Metadatum *)item);
371 // return g_strdup(str.str().c_str());
372 return g_strdup(((Exiv2::Metadatum *)item)->toString().c_str());
374 catch (Exiv2::AnyError& e) {
379 gchar *exif_item_get_string(ExifItem *item, int idx)
382 if (!item) return NULL;
383 Exiv2::Metadatum *em = (Exiv2::Metadatum *)item;
384 std::string str = em->toString(idx);
385 if (idx == 0 && str == "") str = em->toString();
386 if (str.length() > 5 && str.substr(0, 5) == "lang=")
388 std::string::size_type pos = str.find_first_of(' ');
389 if (pos != std::string::npos) str = str.substr(pos+1);
392 return g_strdup(str.c_str());
394 catch (Exiv2::AnyError& e) {
400 gint exif_item_get_integer(ExifItem *item, gint *value)
404 *value = ((Exiv2::Metadatum *)item)->toLong();
407 catch (Exiv2::AnyError& e) {
408 std::cout << "Caught Exiv2 exception '" << e << "'\n";
413 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign)
416 if (!item) return NULL;
417 Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational();
418 static ExifRational ret;
423 catch (Exiv2::AnyError& e) {
424 std::cout << "Caught Exiv2 exception '" << e << "'\n";
429 const gchar *exif_get_tag_description_by_key(const gchar *key)
432 Exiv2::ExifKey ekey(key);
433 return Exiv2::ExifTags::tagLabel(ekey.tag(), ekey.ifdId ());
435 catch (Exiv2::AnyError& e) {
436 std::cout << "Caught Exiv2 exception '" << e << "'\n";
441 int exif_item_set_string(ExifItem *item, const char *str)
445 ((Exiv2::Metadatum *)item)->setValue(std::string(str));
448 catch (Exiv2::AnyError& e) {
453 int exif_item_delete(ExifData *exif, ExifItem *item)
457 for (Exiv2::ExifData::iterator i = exif->exifData().begin(); i != exif->exifData().end(); ++i) {
458 if (((Exiv2::Metadatum *)item) == &*i) {
459 i = exif->exifData().erase(i);
463 for (Exiv2::IptcData::iterator i = exif->iptcData().begin(); i != exif->iptcData().end(); ++i) {
464 if (((Exiv2::Metadatum *)item) == &*i) {
465 i = exif->iptcData().erase(i);
469 for (Exiv2::XmpData::iterator i = exif->xmpData().begin(); i != exif->xmpData().end(); ++i) {
470 if (((Exiv2::Metadatum *)item) == &*i) {
471 i = exif->xmpData().erase(i);
478 catch (Exiv2::AnyError& e) {
487 /* This is a dirty hack to support raw file preview, bassed on
488 tiffparse.cpp from Exiv2 examples */
496 const Exiv2::Value *find(uint16_t tag, uint16_t group);
498 unsigned long preview_offset();
502 Exiv2::TiffComponent::AutoPtr rootDir;
503 Exiv2::byte *map_data;
505 unsigned long offset;
508 using namespace Exiv2;
510 RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
513 if (fstat(fd, &st) == -1)
517 map_len = st.st_size;
518 map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
519 if (map_data == MAP_FAILED)
523 type = Exiv2::ImageFactory::getType(map_data, map_len);
525 TiffHeaderBase *tiffHeader;
527 case Exiv2::ImageType::tiff:
528 tiffHeader = new TiffHeade2();
530 case Exiv2::ImageType::cr2:
531 tiffHeader = new Cr2Header();
533 case Exiv2::ImageType::orf:
534 tiffHeader = new OrfHeader();
536 case Exiv2::ImageType::raf:
537 if (map_len < 84 + 4) throw Error(14);
538 offset = getULong(map_data + 84, bigEndian);
542 throw Error(3, "RAW");
545 // process tiff-like formats
546 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
548 TiffCompFactoryFct createFct = TiffCreator::create;
550 rootDir = createFct(Tag::root, Group::none);
551 if (0 == rootDir.get()) {
552 throw Error(1, "No root element defined in TIFF structure");
554 rootDir->setStart(map_data + tiffHeader->offset());
556 TiffRwState::AutoPtr state(
557 new TiffRwState(tiffHeader->byteOrder(), 0, createFct));
559 TiffReader reader(map_data,
564 rootDir->accept(reader);
571 if (map_data && munmap(map_data, map_len) == -1)
573 printf("Failed to unmap file \n");
577 const Value * RawFile::find(uint16_t tag, uint16_t group)
579 printf("%04x %04x\n", tag, group);
580 TiffFinder finder(tag, group);
581 rootDir->accept(finder);
582 TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
589 unsigned long RawFile::preview_offset()
592 if (offset) return offset;
594 if (type == Exiv2::ImageType::cr2)
596 val = find(0x111, Group::ifd0);
597 if (val) return val->toLong();
602 val = find(0x201, Group::sub0_0);
603 if (val) return val->toLong();
605 val = find(0x201, Group::ifd0);
606 if (val) return val->toLong();
608 val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
609 if (val) return val->toLong();
611 val = find(0x111, Group::sub0_1); // dng
612 if (val) return val->toLong();
618 const static char *raw_ext_list[] = { ".cr2", ".nef", ".pef", ".arw", NULL };
620 extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
621 unsigned char *header_data, const guint header_len,
622 guint *image_offset, guint *exif_offset)
625 unsigned long offset;
627 /* given image pathname, first do simple (and fast) file extension test */
634 ext = strrchr(path, '.');
635 if (!ext) return FALSE;
637 for (i = 0; raw_ext_list[i]; i++)
639 if (strcasecmp(raw_ext_list[i], ext) == 0)
646 if (!match) return FALSE;
652 offset = rf.preview_offset();
654 catch (Exiv2::AnyError& e) {
655 std::cout << "Caught Exiv2 exception '" << e << "'\n";
661 *image_offset = offset;
662 if (lseek(fd, *image_offset, SEEK_SET) != *image_offset)
664 printf("Failed to seek to embedded image\n");
667 if (*exif_offset) *exif_offset = 0;