bool getXmpDateTime(Exiv2::XmpData &xmpData, QDateTime &dateTime) { bool anyFound = false; try { Exiv2::XmpKey psKey(XMP_PS_DATECREATED); Exiv2::XmpData::iterator xmpIt = xmpData.findKey(psKey); if (xmpIt != xmpData.end()) { dateTime = QDateTime::fromString(QString::fromLatin1(xmpIt->toString().c_str()), Qt::ISODate); anyFound = dateTime.isValid(); } } catch (Exiv2::Error &e) { LOG_WARNING << "Exiv2 error:" << e.what(); anyFound = false; } catch (...) { LOG_WARNING << "Exception"; anyFound = false; #ifdef QT_DEBUG throw; #endif } return anyFound; }
bool getXmpTagStringBag(Exiv2::XmpData &xmpData, const char *propertyName, QStringList &bag) { bool found = false; Exiv2::XmpKey key(propertyName); Exiv2::XmpData::iterator it = xmpData.findKey(key); if ((it != xmpData.end()) && (it->typeId() == Exiv2::xmpBag)) { found = true; int count = it->count(); bag.reserve(count); if (count == 1) { QString bagValue = QString::fromUtf8(it->toString(0).c_str()); if (bagValue.contains(',')) { LOG_DEBUG << "processing legacy saved keywords"; bag += decomposeKeyword(bagValue); } else { bag.append(bagValue); } } else { for (int i = 0; i < count; i++) { QString bagValue = QString::fromUtf8(it->toString(i).c_str()); bag.append(bagValue); } } } return found; }
bool getXmpDescription(Exiv2::XmpData &xmpData, const QString &langAlt, QString &description) { bool anyFound = false; try { anyFound = getXmpLangAltValue(xmpData, XMP_DESCRIPTION, langAlt, description); if (!anyFound || description.isEmpty()) { Exiv2::XmpKey psKey(XMP_PS_HEADLINE); Exiv2::XmpData::iterator xmpIt = xmpData.findKey(psKey); if (xmpIt != xmpData.end()) { const Exiv2::XmpTextValue &value = static_cast<const Exiv2::XmpTextValue &>(xmpIt->value()); QString headline = QString::fromUtf8(value.value_.c_str()).trimmed(); if (!headline.isEmpty()) { anyFound = true; description = headline; } } } } catch (Exiv2::Error &e) { LOG_WARNING << "Exiv2 error:" << e.what(); anyFound = false; } catch (...) { LOG_WARNING << "Exception"; anyFound = false; #ifdef QT_DEBUG throw; #endif } return anyFound; }
void KExiv2::Private::mergeXmpData(const Exiv2::XmpData& src, Exiv2::XmpData& dest) { for (Exiv2::XmpData::const_iterator it = src.begin(); it != src.end(); ++it) { Exiv2::XmpData::iterator destIt = dest.findKey(Exiv2::XmpKey(it->key())); if (destIt == dest.end()) { dest.add(*it); } else { *destIt = *it; } } }
bool getXmpLangAltValue(Exiv2::XmpData &xmpData, const char *propertyName, const QString &langAlt, QString &resultValue) { bool anyFound = false; Exiv2::XmpKey key(propertyName); Exiv2::XmpData::iterator it = xmpData.findKey(key); if ((it != xmpData.end()) && (it->typeId() == Exiv2::langAlt)) { const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value()); QString anyValue; Exiv2::LangAltValue::ValueType::const_iterator it2 = value.value_.begin(); Exiv2::LangAltValue::ValueType::const_iterator end = value.value_.end(); for (; it2 != end; ++it2) { QString lang = QString::fromUtf8(it2->first.c_str()); if (langAlt == lang) { QString text = QString::fromUtf8(it2->second.c_str()).trimmed(); if (!text.isEmpty()) { anyFound = true; resultValue = text.trimmed(); break; } } if (anyValue.isEmpty()) { QString text = QString::fromUtf8(it2->second.c_str()); anyValue = text.trimmed(); } } if (!anyFound && !anyValue.isEmpty()) { anyFound = true; resultValue = anyValue; } } return anyFound; }
int main() try { // The XMP property container Exiv2::XmpData xmpData; // ------------------------------------------------------------------------- // Teaser: Setting XMP properties doesn't get much easier than this: xmpData["Xmp.dc.source"] = "xmpsample.cpp"; // a simple text value xmpData["Xmp.dc.subject"] = "Palmtree"; // an array item xmpData["Xmp.dc.subject"] = "Rubbertree"; // add a 2nd array item // a language alternative with two entries and without default xmpData["Xmp.dc.title"] = "lang=de-DE Sonnenuntergang am Strand"; xmpData["Xmp.dc.title"] = "lang=en-US Sunset on the beach"; // ------------------------------------------------------------------------- // Any properties can be set provided the namespace is known. Values of any // type can be assigned to an Xmpdatum, if they have an output operator. The // default XMP value type for unknown properties is a simple text value. xmpData["Xmp.dc.one"] = -1; xmpData["Xmp.dc.two"] = 3.1415; xmpData["Xmp.dc.three"] = Exiv2::Rational(5, 7); xmpData["Xmp.dc.four"] = uint16_t(255); xmpData["Xmp.dc.five"] = int32_t(256); xmpData["Xmp.dc.six"] = false; // In addition, there is a dedicated assignment operator for Exiv2::Value Exiv2::XmpTextValue val("Seven"); xmpData["Xmp.dc.seven"] = val; xmpData["Xmp.dc.eight"] = true; // Extracting values assert(xmpData["Xmp.dc.one"].toLong() == -1); assert(xmpData["Xmp.dc.one"].value().ok()); const Exiv2::Value &getv1 = xmpData["Xmp.dc.one"].value(); assert(isEqual(getv1.toFloat(), -1)); assert(getv1.ok()); assert(getv1.toRational() == Exiv2::Rational(-1, 1)); assert(getv1.ok()); const Exiv2::Value &getv2 = xmpData["Xmp.dc.two"].value(); assert(isEqual(getv2.toFloat(), 3.1415f)); assert(getv2.ok()); assert(getv2.toLong() == 3); assert(getv2.ok()); Exiv2::Rational R = getv2.toRational(); assert(getv2.ok()); assert(isEqual(static_cast<float>(R.first) / R.second, 3.1415f )); const Exiv2::Value &getv3 = xmpData["Xmp.dc.three"].value(); assert(isEqual(getv3.toFloat(), 5.0f/7.0f)); assert(getv3.ok()); assert(getv3.toLong() == 0); // long(5.0 / 7.0) assert(getv3.ok()); assert(getv3.toRational() == Exiv2::Rational(5, 7)); assert(getv3.ok()); const Exiv2::Value &getv6 = xmpData["Xmp.dc.six"].value(); assert(getv6.toLong() == 0); assert(getv6.ok()); assert(getv6.toFloat() == 0.0); assert(getv6.ok()); assert(getv6.toRational() == Exiv2::Rational(0, 1)); assert(getv6.ok()); const Exiv2::Value &getv7 = xmpData["Xmp.dc.seven"].value(); getv7.toLong(); // this should fail assert(!getv7.ok()); const Exiv2::Value &getv8 = xmpData["Xmp.dc.eight"].value(); assert(getv8.toLong() == 1); assert(getv8.ok()); assert(getv8.toFloat() == 1.0); assert(getv8.ok()); assert(getv8.toRational() == Exiv2::Rational(1, 1)); assert(getv8.ok()); // Deleting an XMP property Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.dc.eight")); if (pos == xmpData.end()) throw Exiv2::Error(1, "Key not found"); xmpData.erase(pos); // ------------------------------------------------------------------------- // Exiv2 has specialized values for simple XMP properties, arrays of simple // properties and language alternatives. // Add a simple XMP property in a known namespace Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpText); v->read("image/jpeg"); xmpData.add(Exiv2::XmpKey("Xmp.dc.format"), v.get()); // Add an ordered array of text values. v = Exiv2::Value::create(Exiv2::xmpSeq); // or xmpBag or xmpAlt. v->read("1) The first creator"); // The sequence in which the array v->read("2) The second creator"); // elements are added is their v->read("3) And another one"); // order in the array. xmpData.add(Exiv2::XmpKey("Xmp.dc.creator"), v.get()); // Add a language alternative property v = Exiv2::Value::create(Exiv2::langAlt); v->read("lang=de-DE Hallo, Welt"); // The default doesn't need a v->read("Hello, World"); // qualifier xmpData.add(Exiv2::XmpKey("Xmp.dc.description"), v.get()); // According to the XMP specification, Xmp.tiff.ImageDescription is an // alias for Xmp.dc.description. Exiv2 treats an alias just like any // other property and leaves it to the application to implement specific // behaviour if desired. xmpData["Xmp.tiff.ImageDescription"] = "TIFF image description"; xmpData["Xmp.tiff.ImageDescription"] = "lang=de-DE TIFF Bildbeschreibung"; // ------------------------------------------------------------------------- // Register a namespace which Exiv2 doesn't know yet. This is only needed // when properties are added manually. If the XMP metadata is read from an // image, namespaces are decoded and registered at the same time. Exiv2::XmpProperties::registerNs("myNamespace/", "ns"); // ------------------------------------------------------------------------- // Add a property in the new custom namespace. xmpData["Xmp.ns.myProperty"] = "myValue"; // ------------------------------------------------------------------------- // There are no specialized values for structures, qualifiers and nested // types. However, these can be added by using an XmpTextValue and a path as // the key. // Add a structure Exiv2::XmpTextValue tv("16"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:w"), &tv); tv.read("9"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:h"), &tv); tv.read("inch"); xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:unit"), &tv); // Add an element with a qualifier (using the namespace registered above) xmpData["Xmp.dc.publisher"] = "James Bond"; // creates an unordered array xmpData["Xmp.dc.publisher[1]/?ns:role"] = "secret agent"; // Add a qualifer to an array element of Xmp.dc.creator (added above) tv.read("programmer"); xmpData.add(Exiv2::XmpKey("Xmp.dc.creator[2]/?ns:role"), &tv); // Add an array of structures tv.read(""); // Clear the value tv.setXmpArrayType(Exiv2::XmpValue::xaBag); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv); // Set the array type. tv.setXmpArrayType(Exiv2::XmpValue::xaNone); tv.read("Birthday party"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:name"), &tv); tv.read("Photographer"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:role"), &tv); tv.read("Wedding ceremony"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:name"), &tv); tv.read("Best man"); xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:role"), &tv); // ------------------------------------------------------------------------- // Output XMP properties for (Exiv2::XmpData::const_iterator md = xmpData.begin(); md != xmpData.end(); ++md) { std::cout << std::setfill(' ') << std::left << std::setw(44) << md->key() << " " << std::setw(9) << std::setfill(' ') << std::left << md->typeName() << " " << std::dec << std::setw(3) << std::setfill(' ') << std::right << md->count() << " " << std::dec << md->value() << std::endl; } // ------------------------------------------------------------------------- // Serialize the XMP data and output the XMP packet std::string xmpPacket; if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) { throw Exiv2::Error(1, "Failed to serialize XMP data"); } std::cout << xmpPacket << "\n"; // Cleanup Exiv2::XmpParser::terminate(); return 0; } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; return -1; }