Blame samples/xmpsample.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
// xmpsample.cpp
Packit 01d647
// Sample/test for high level XMP classes. See also addmoddel.cpp
Packit 01d647
Packit 01d647
#include <exiv2/exiv2.hpp>
Packit 01d647
#include "unused.h"
Packit 01d647
Packit 01d647
#include <string>
Packit 01d647
#include <iostream>
Packit 01d647
#include <iomanip>
Packit 01d647
#include <cassert>
Packit 01d647
#include <cmath>
Packit 01d647
Packit 01d647
bool isEqual(float a, float b)
Packit 01d647
{
Packit 01d647
    double d = std::fabs(a - b);
Packit 01d647
    return d < 0.00001;
Packit 01d647
}
Packit 01d647
Packit 01d647
int main()
Packit 01d647
try {
Packit Service fb147c
    Exiv2::XmpParser::initialize();
Packit Service fb147c
    ::atexit(Exiv2::XmpParser::terminate);
Packit Service fb147c
Packit 01d647
    // The XMP property container
Packit 01d647
    Exiv2::XmpData xmpData;
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Teaser: Setting XMP properties doesn't get much easier than this:
Packit 01d647
Packit 01d647
    xmpData["Xmp.dc.source"]  = "xmpsample.cpp";    // a simple text value
Packit 01d647
    xmpData["Xmp.dc.subject"] = "Palmtree";         // an array item
Packit 01d647
    xmpData["Xmp.dc.subject"] = "Rubbertree";       // add a 2nd array item
Packit 01d647
    // a language alternative with two entries and without default
Packit 01d647
    xmpData["Xmp.dc.title"]   = "lang=de-DE Sonnenuntergang am Strand";
Packit 01d647
    xmpData["Xmp.dc.title"]   = "lang=en-US Sunset on the beach";
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Any properties can be set provided the namespace is known. Values of any
Packit 01d647
    // type can be assigned to an Xmpdatum, if they have an output operator. The
Packit 01d647
    // default XMP value type for unknown properties is a simple text value.
Packit 01d647
Packit 01d647
    xmpData["Xmp.dc.one"]     = -1;
Packit 01d647
    xmpData["Xmp.dc.two"]     = 3.1415;
Packit 01d647
    xmpData["Xmp.dc.three"]   = Exiv2::Rational(5, 7);
Packit 01d647
    xmpData["Xmp.dc.four"]    = uint16_t(255);
Packit 01d647
    xmpData["Xmp.dc.five"]    = 256;
Packit 01d647
    xmpData["Xmp.dc.six"]     = false;
Packit 01d647
Packit 01d647
    // In addition, there is a dedicated assignment operator for Exiv2::Value
Packit 01d647
    Exiv2::XmpTextValue val("Seven");
Packit 01d647
    xmpData["Xmp.dc.seven"]   = val;
Packit 01d647
    xmpData["Xmp.dc.eight"]   = true;
Packit 01d647
Packit 01d647
    // Extracting values
Packit 01d647
    assert(xmpData["Xmp.dc.one"].toLong() == -1);
Packit 01d647
    assert(xmpData["Xmp.dc.one"].value().ok());
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv1 = xmpData["Xmp.dc.one"].value();
Packit 01d647
    UNUSED(getv1);
Packit 01d647
    assert(isEqual(getv1.toFloat(), -1));
Packit 01d647
    assert(getv1.ok());
Packit 01d647
    assert(getv1.toRational() == Exiv2::Rational(-1, 1));
Packit 01d647
    assert(getv1.ok());
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv2 = xmpData["Xmp.dc.two"].value();
Packit 01d647
    UNUSED(getv2);
Packit 01d647
    assert(isEqual(getv2.toFloat(), 3.1415f));
Packit 01d647
    assert(getv2.ok());
Packit 01d647
    assert(getv2.toLong() == 3);
Packit 01d647
    assert(getv2.ok());
Packit 01d647
    Exiv2::Rational R = getv2.toRational();
Packit 01d647
    UNUSED(R);
Packit 01d647
    assert(getv2.ok());
Packit 01d647
    assert(isEqual(static_cast<float>(R.first) / R.second, 3.1415f ));
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv3 = xmpData["Xmp.dc.three"].value();
Packit 01d647
    UNUSED(getv3);
Packit 01d647
    assert(isEqual(getv3.toFloat(), 5.0f/7.0f));
Packit 01d647
    assert(getv3.ok());
Packit 01d647
    assert(getv3.toLong() == 0);  // long(5.0 / 7.0)
Packit 01d647
    assert(getv3.ok());
Packit 01d647
    assert(getv3.toRational() == Exiv2::Rational(5, 7));
Packit 01d647
    assert(getv3.ok());
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv6 = xmpData["Xmp.dc.six"].value();
Packit 01d647
    UNUSED(getv6);
Packit 01d647
    assert(getv6.toLong() == 0);
Packit 01d647
    assert(getv6.ok());
Packit 01d647
    assert(getv6.toFloat() == 0.0f);
Packit 01d647
    assert(getv6.ok());
Packit 01d647
    assert(getv6.toRational() == Exiv2::Rational(0, 1));
Packit 01d647
    assert(getv6.ok());
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv7 = xmpData["Xmp.dc.seven"].value();
Packit 01d647
    getv7.toLong(); // this should fail
Packit 01d647
    assert(!getv7.ok());
Packit 01d647
Packit 01d647
    const Exiv2::Value &getv8 = xmpData["Xmp.dc.eight"].value();
Packit 01d647
    UNUSED(getv8);
Packit 01d647
    assert(getv8.toLong() == 1);
Packit 01d647
    assert(getv8.ok());
Packit 01d647
    assert(getv8.toFloat() == 1.0f);
Packit 01d647
    assert(getv8.ok());
Packit 01d647
    assert(getv8.toRational() == Exiv2::Rational(1, 1));
Packit 01d647
    assert(getv8.ok());
Packit 01d647
Packit 01d647
    // Deleting an XMP property
Packit 01d647
    Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.dc.eight"));
Packit 01d647
    if (pos == xmpData.end()) throw Exiv2::Error(Exiv2::kerErrorMessage, "Key not found");
Packit 01d647
    xmpData.erase(pos);
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Exiv2 has specialized values for simple XMP properties, arrays of simple
Packit 01d647
    // properties and language alternatives.
Packit 01d647
Packit 01d647
    // Add a simple XMP property in a known namespace
Packit 01d647
    Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpText);
Packit 01d647
    v->read("image/jpeg");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.dc.format"), v.get());
Packit 01d647
Packit 01d647
    // Add an ordered array of text values.
Packit 01d647
    v = Exiv2::Value::create(Exiv2::xmpSeq); // or xmpBag or xmpAlt.
Packit 01d647
    v->read("1) The first creator");         // The sequence in which the array
Packit 01d647
    v->read("2) The second creator");        // elements are added is their
Packit 01d647
    v->read("3) And another one");           // order in the array.
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.dc.creator"), v.get());
Packit 01d647
Packit 01d647
    // Add a language alternative property
Packit 01d647
    v = Exiv2::Value::create(Exiv2::langAlt);
Packit 01d647
    v->read("lang=de-DE Hallo, Welt");       // The default doesn't need a
Packit 01d647
    v->read("Hello, World");                 // qualifier
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.dc.description"), v.get());
Packit 01d647
Packit 01d647
    // According to the XMP specification, Xmp.tiff.ImageDescription is an
Packit 01d647
    // alias for Xmp.dc.description. Exiv2 treats an alias just like any
Packit 01d647
    // other property and leaves it to the application to implement specific
Packit 01d647
    // behaviour if desired.
Packit 01d647
    xmpData["Xmp.tiff.ImageDescription"] = "TIFF image description";
Packit 01d647
    xmpData["Xmp.tiff.ImageDescription"] = "lang=de-DE TIFF Bildbeschreibung";
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Register a namespace which Exiv2 doesn't know yet. This is only needed
Packit 01d647
    // when properties are added manually. If the XMP metadata is read from an
Packit 01d647
    // image, namespaces are decoded and registered at the same time.
Packit 01d647
    Exiv2::XmpProperties::registerNs("myNamespace/", "ns");
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Add a property in the new custom namespace.
Packit 01d647
    xmpData["Xmp.ns.myProperty"] = "myValue";
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // There are no specialized values for structures, qualifiers and nested
Packit 01d647
    // types. However, these can be added by using an XmpTextValue and a path as
Packit 01d647
    // the key.
Packit 01d647
Packit 01d647
    // Add a structure
Packit 01d647
    Exiv2::XmpTextValue tv("16");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:w"), &tv;;
Packit 01d647
    tv.read("9");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:h"), &tv;;
Packit 01d647
    tv.read("inch");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpDM.videoFrameSize/stDim:unit"), &tv;;
Packit 01d647
Packit 01d647
    // Add an element with a qualifier (using the namespace registered above)
Packit 01d647
    xmpData["Xmp.dc.publisher"] = "James Bond";  // creates an unordered array
Packit 01d647
    xmpData["Xmp.dc.publisher[1]/?ns:role"] = "secret agent";
Packit 01d647
Packit 01d647
    // Add a qualifer to an array element of Xmp.dc.creator (added above)
Packit 01d647
    tv.read("programmer");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.dc.creator[2]/?ns:role"), &tv;;
Packit 01d647
Packit 01d647
    // Add an array of structures
Packit 01d647
    tv.read("");                                         // Clear the value
Packit 01d647
    tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef"), &tv;; // Set the array type.
Packit 01d647
Packit 01d647
    tv.setXmpArrayType(Exiv2::XmpValue::xaNone);
Packit 01d647
    tv.read("Birthday party");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:name"), &tv;;
Packit 01d647
    tv.read("Photographer");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[1]/stJob:role"), &tv;;
Packit 01d647
Packit 01d647
    tv.read("Wedding ceremony");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:name"), &tv;;
Packit 01d647
    tv.read("Best man");
Packit 01d647
    xmpData.add(Exiv2::XmpKey("Xmp.xmpBJ.JobRef[2]/stJob:role"), &tv;;
Packit 01d647
Packit 01d647
    // Add a creator contact info structure
Packit 01d647
    xmpData["Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity"] = "Kuala Lumpur";
Packit 01d647
    xmpData["Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry"] = "Malaysia";
Packit 01d647
    xmpData["Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork"] = "http://www.exiv2.org";
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Output XMP properties
Packit 01d647
    for (Exiv2::XmpData::const_iterator md = xmpData.begin();
Packit 01d647
        md != xmpData.end(); ++md) {
Packit 01d647
        std::cout << std::setfill(' ') << std::left
Packit 01d647
                  << std::setw(44)
Packit 01d647
                  << md->key() << " "
Packit 01d647
                  << std::setw(9) << std::setfill(' ') << std::left
Packit 01d647
                  << md->typeName() << " "
Packit 01d647
                  << std::dec << std::setw(3)
Packit 01d647
                  << std::setfill(' ') << std::right
Packit 01d647
                  << md->count() << "  "
Packit 01d647
                  << std::dec << md->value()
Packit 01d647
                  << std::endl;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    // -------------------------------------------------------------------------
Packit 01d647
    // Serialize the XMP data and output the XMP packet
Packit 01d647
    std::string xmpPacket;
Packit 01d647
    if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) {
Packit 01d647
        throw Exiv2::Error(Exiv2::kerErrorMessage, "Failed to serialize XMP data");
Packit 01d647
    }
Packit 01d647
    std::cout << xmpPacket << "\n";
Packit 01d647
Packit 01d647
    // Cleanup
Packit 01d647
    Exiv2::XmpParser::terminate();
Packit 01d647
Packit 01d647
    return 0;
Packit 01d647
}
Packit 01d647
catch (Exiv2::AnyError& e) {
Packit 01d647
    std::cout << "Caught Exiv2 exception '" << e << "'\n";
Packit 01d647
    return -1;
Packit 01d647
}