00001 // ***************************************************************** -*- C++ -*- 00002 // xmpsample.cpp, $Rev: 1479 $ 00003 // Sample/test for high level XMP classes. See also addmoddel.cpp 00004 00005 #include <exiv2/xmp.hpp> 00006 #include <exiv2/error.hpp> 00007 00008 #include <string> 00009 #include <iostream> 00010 #include <iomanip> 00011 #include <cassert> 00012 #include <cmath> 00013 00014 bool isEqual(float a, float b) 00015 { 00016 double d = std::fabs(a - b); 00017 return d < 0.00001; 00018 } 00019 00020 int main() 00021 try { 00022 // The XMP property container 00023 Exiv2::XmpData xmpData; 00024 00025 // ------------------------------------------------------------------------- 00026 // Teaser: Setting XMP properties doesn't get much easier than this: 00027 00028 xmpData["Xmp.dc.source"] = "xmpsample.cpp"; // a simple text value 00029 xmpData["Xmp.dc.subject"] = "Palmtree"; // an array item 00030 xmpData["Xmp.dc.subject"] = "Rubbertree"; // add a 2nd array item 00031 // a language alternative with two entries and without default 00032 xmpData["Xmp.dc.title"] = "lang=de-DE Sonnenuntergang am Strand"; 00033 xmpData["Xmp.dc.title"] = "lang=en-US Sunset on the beach"; 00034 00035 // ------------------------------------------------------------------------- 00036 // Any properties can be set provided the namespace is known. Values of any 00037 // type can be assigned to an Xmpdatum, if they have an output operator. The 00038 // default XMP value type for unknown properties is a simple text value. 00039 00040 xmpData["Xmp.dc.one"] = -1; 00041 xmpData["Xmp.dc.two"] = 3.1415; 00042 xmpData["Xmp.dc.three"] = Exiv2::Rational(5, 7); 00043 xmpData["Xmp.dc.four"] = uint16_t(255); 00044 xmpData["Xmp.dc.five"] = int32_t(256); 00045 xmpData["Xmp.dc.six"] = false; 00046 00047 // In addition, there is a dedicated assignment operator for Exiv2::Value 00048 Exiv2::XmpTextValue val("Seven"); 00049 xmpData["Xmp.dc.seven"] = val; 00050 xmpData["Xmp.dc.eight"] = true; 00051 00052 // Extracting values 00053 assert(xmpData["Xmp.dc.one"].toLong() == -1); 00054 assert(xmpData["Xmp.dc.one"].value().ok()); 00055 00056 const Exiv2::Value &getv1 = xmpData["Xmp.dc.one"].value(); 00057 assert(isEqual(getv1.toFloat(), -1)); 00058 assert(getv1.ok()); 00059 assert(getv1.toRational() == Exiv2::Rational(-1, 1)); 00060 assert(getv1.ok()); 00061 00062 const Exiv2::Value &getv2 = xmpData["Xmp.dc.two"].value(); 00063 assert(isEqual(getv2.toFloat(), 3.1415f)); 00064 assert(getv2.ok()); 00065 assert(getv2.toLong() == 3); 00066 assert(getv2.ok()); 00067 Exiv2::Rational R = getv2.toRational(); 00068 assert(getv2.ok()); 00069 assert(isEqual(static_cast<float>(R.first) / R.second, 3.1415f )); 00070 00071 const Exiv2::Value &getv3 = xmpData["Xmp.dc.three"].value(); 00072 assert(isEqual(getv3.toFloat(), 5.0f/7.0f)); 00073 assert(getv3.ok()); 00074 assert(getv3.toLong() == 0); // long(5.0 / 7.0) 00075 assert(getv3.ok()); 00076 assert(getv3.toRational() == Exiv2::Rational(5, 7)); 00077 assert(getv3.ok()); 00078 00079 const Exiv2::Value &getv6 = xmpData["Xmp.dc.six"].value(); 00080 assert(getv6.toLong() == 0); 00081 assert(getv6.ok()); 00082 assert(getv6.toFloat() == 0.0); 00083 assert(getv6.ok()); 00084 assert(getv6.toRational() == Exiv2::Rational(0, 1)); 00085 assert(getv6.ok()); 00086 00087 const Exiv2::Value &getv7 = xmpData["Xmp.dc.seven"].value(); 00088 getv7.toLong(); // this should fail 00089 assert(!getv7.ok()); 00090 00091 const Exiv2::Value &getv8 = xmpData["Xmp.dc.eight"].value(); 00092 assert(getv8.toLong() == 1); 00093 assert(getv8.ok()); 00094 assert(getv8.toFloat() == 1.0); 00095 assert(getv8.ok()); 00096 assert(getv8.toRational() == Exiv2::Rational(1, 1)); 00097 assert(getv8.ok()); 00098 00099 // Deleting an XMP property 00100 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.dc.eight")); 00101 if (pos == xmpData.end()) throw Exiv2::Error(1, "Key not found"); 00102 xmpData.erase(pos); 00103 00104 // ------------------------------------------------------------------------- 00105 // Exiv2 has specialized values for simple XMP properties, arrays of simple 00106 // properties and language alternatives. 00107 00108 // Add a simple XMP property in a known namespace 00109 Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpText); 00110 v->read("image/jpeg"); 00111 xmpData.add(Exiv2::XmpKey("Xmp.dc.format"), v.get()); 00112 00113 // Add an ordered array of text values. 00114 v = Exiv2::Value::create(Exiv2::xmpSeq); // or xmpBag or xmpAlt. 00115 v->read("1) The first creator"); // The sequence in which the array 00116 v->read("2) The second creator"); // elements are added is their 00117 v->read("3) And another one"); // order in the array. 00118 xmpData.add(Exiv2::XmpKey("Xmp.dc.creator"), v.get()); 00119 00120 // Add a language alternative property 00121 v = Exiv2::Value::create(Exiv2::langAlt); 00122 v->read("lang=de-DE Hallo, Welt"); // The default doesn't need a 00123 v->read("Hello, World"); // qualifier 00124 xmpData.add(Exiv2::XmpKey("Xmp.dc.description"), v.get()); 00125 00126 // According to the XMP specification, Xmp.tiff.ImageDescription is an 00127 // alias for Xmp.dc.description. Exiv2 treats an alias just like any 00128 // other property and leaves it to the application to implement specific 00129 // behaviour if desired. 00130 xmpData["Xmp.tiff.ImageDescription"] = "TIFF image description"; 00131 xmpData["Xmp.tiff.ImageDescription"] = "lang=de-DE TIFF Bildbeschreibung"; 00132 00133 // ------------------------------------------------------------------------- 00134 // Register a namespace which Exiv2 doesn't know yet. This is only needed 00135 // when properties are added manually. If the XMP metadata is read from an 00136 // image, namespaces are decoded and registered at the same time. 00137 Exiv2::XmpProperties::registerNs("myNamespace/", "ns"); 00138 00139 // ------------------------------------------------------------------------- 00140 // Add a property in the new custom namespace. 00141 xmpData["Xmp.ns.myProperty"] = "myValue"; 00142 00143 // ------------------------------------------------------------------------- 00144 // There are no specialized values for structures, qualifiers and nested 00145 // types. However, these can be added by using an XmpTextValue and a path as 00146 // the key. 00147 00148 // Add a structure 00149 Exiv2::XmpTextValue tv("16"); 00150 xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:w"), &tv); 00151 tv.read("9"); 00152 xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:h"), &tv); 00153 tv.read("inch"); 00154 xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:unit"), &tv); 00155 00156 // Add an element with a qualifier (using the namespace registered above) 00157 xmpData["Xmp.dc.publisher"] = "James Bond"; // creates an unordered array 00158 xmpData["Xmp.dc.publisher[1]/?ns:role"] = "secret agent"; 00159 00160 // Add a qualifer to an array element of Xmp.dc.creator (added above) 00161 tv.read("programmer"); 00162 xmpData.add(Exiv2::XmpKey("Xmp.dc.creator[2]/?ns:role"), &tv); 00163 00164 // Add an array of structures 00165 tv.read(""); // Clear the value 00166 tv.setXmpArrayType(Exiv2::XmpValue::xaBag); 00167 xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv); // Set the array type. 00168 00169 tv.setXmpArrayType(Exiv2::XmpValue::xaNone); 00170 tv.read("Birthday party"); 00171 xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:name"), &tv); 00172 tv.read("Photographer"); 00173 xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:role"), &tv); 00174 00175 tv.read("Wedding ceremony"); 00176 xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:name"), &tv); 00177 tv.read("Best man"); 00178 xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:role"), &tv); 00179 00180 // ------------------------------------------------------------------------- 00181 // Output XMP properties 00182 for (Exiv2::XmpData::const_iterator md = xmpData.begin(); 00183 md != xmpData.end(); ++md) { 00184 std::cout << std::setfill(' ') << std::left 00185 << std::setw(44) 00186 << md->key() << " " 00187 << std::setw(9) << std::setfill(' ') << std::left 00188 << md->typeName() << " " 00189 << std::dec << std::setw(3) 00190 << std::setfill(' ') << std::right 00191 << md->count() << " " 00192 << std::dec << md->value() 00193 << std::endl; 00194 } 00195 00196 // ------------------------------------------------------------------------- 00197 // Serialize the XMP data and output the XMP packet 00198 std::string xmpPacket; 00199 if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) { 00200 throw Exiv2::Error(1, "Failed to serialize XMP data"); 00201 } 00202 std::cout << xmpPacket << "\n"; 00203 00204 // Cleanup 00205 Exiv2::XmpParser::terminate(); 00206 00207 return 0; 00208 } 00209 catch (Exiv2::AnyError& e) { 00210 std::cout << "Caught Exiv2 exception '" << e << "'\n"; 00211 return -1; 00212 }