Example #1
0
KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
{
    QMap<QString, KisMetaData::Value> deviceSettingStructure;
    QByteArray array;

    const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
    if(dvalue)
    {
        array.resize(dvalue->count());
        dvalue->copy((Exiv2::byte*)array.data());
    } else {
        Q_ASSERT(value->typeId() == Exiv2::unsignedShort);
        array.resize(2 * value->count());
        value->copy((Exiv2::byte*)array.data(), Exiv2::littleEndian);
    }
    int columns = (reinterpret_cast<quint16*>(array.data()))[0];
    int rows = (reinterpret_cast<quint16*>(array.data()))[1];
    deviceSettingStructure["Columns"] = KisMetaData::Value(columns);
    deviceSettingStructure["Rows"] = KisMetaData::Value(rows);
    QList<KisMetaData::Value> settings;
    QByteArray null(2, 0);

    for (int index = 4; index < array.size(); )
    {
        int lastIndex = array.indexOf(null, index);
        QString setting = QString::fromUtf16((ushort*)(void*)( array.data() + index), lastIndex - index + 2);
        index = lastIndex + 2;
        dbgFile << "Setting << " << setting;
        settings.append(KisMetaData::Value(setting));
    }
    deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray);
    return KisMetaData::Value(deviceSettingStructure);
}
Example #2
0
bool KExiv2::setIptcTagsStringList(const char* iptcTagName, int maxSize,
                                   const QStringList& oldValues, const QStringList& newValues,
                                   bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldvals = oldValues;
        QStringList newvals = newValues;

        kDebug() << d->filePath.toAscii().constData() << " : " << iptcTagName
                      << " => " << newvals.join(",").toAscii().constData();

        // Remove all old values.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            // Also remove new values to avoid duplicates. They will be added again below.
            if ( key == QString(iptcTagName) &&
                 (oldvals.contains(val) || newvals.contains(val))
               )
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new values.

        Exiv2::IptcKey iptcTag(iptcTagName);

        for (QStringList::iterator it = newvals.begin(); it != newvals.end(); ++it)
        {
            QString key = *it;
            key.truncate(maxSize);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError(QString("Cannot set Iptc key '%1' into image using Exiv2 ")
                                    .arg(iptcTagName), e);
    }

    return false;
}
Example #3
0
bool KExiv2::setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords,
                             bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldkeys = oldKeywords;
        QStringList newkeys = newKeywords;

        kDebug() << d->filePath.toAscii().constData()
                      << " ==> Iptc Keywords: " << newkeys.join(",").toAscii().constData();

        // Remove all old keywords.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            // Also remove new keywords to avoid duplicates. They will be added again below.
            if ( key == QString("Iptc.Application2.Keywords") &&
                 (oldKeywords.contains(val) || newKeywords.contains(val)) )
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new keywords. Note that Keywords Iptc tag is limited to 64 char but can be redondant.

        Exiv2::IptcKey iptcTag("Iptc.Application2.Keywords");

        for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
        {
            QString key = *it;
            key.truncate(64);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Iptc Keywords into image using Exiv2 ", e);
    }

    return false;
}
Example #4
0
bool KExiv2::setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories,
                                  bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
        QStringList oldkeys = oldSubCategories;
        QStringList newkeys = newSubCategories;

        // Remove all old Sub Categories.
        Exiv2::IptcData iptcData(d->iptcMetadata());
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
            QString val(it->toString().c_str());

            if (key == QString("Iptc.Application2.SuppCategory") && oldSubCategories.contains(val))
                it = iptcData.erase(it);
            else
                ++it;
        };

        // Add new Sub Categories. Note that SubCategories Iptc tag is limited to 32
        // characters but can be redondant.

        Exiv2::IptcKey iptcTag("Iptc.Application2.SuppCategory");

        for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
        {
            QString key = *it;
            key.truncate(32);

            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
            val->read(key.toLatin1().constData());
            iptcData.add(iptcTag, val.get());
        }

        d->iptcMetadata() = iptcData;

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Iptc Sub Categories into image using Exiv2 ", e);
    }

    return false;
}
Example #5
0
// convert ExifVersion and FlashpixVersion to a KisMetaData value
KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
{
    const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
    if(dvalue)
    {
        Q_ASSERT(dvalue);
        QByteArray array(dvalue->count(), 0);
        dvalue->copy((Exiv2::byte*)array.data());
        return KisMetaData::Value(QString(array));
    } else {
        Q_ASSERT(value->typeId() == Exiv2::asciiString);
        return KisMetaData::Value(QString::fromLatin1(value->toString().c_str()));
    }
}
Example #6
0
bool ImageTags::writeTagsToImage(QString &imageFileName, QSet<QString> &newTags)
{
	QSet<QString> imageTags;
	Exiv2::Image::AutoPtr exifImage;

	try {
		exifImage = Exiv2::ImageFactory::open(imageFileName.toStdString());
		exifImage->readMetadata();

		Exiv2::IptcData newIptcData;

		/* copy existing data */
		Exiv2::IptcData &iptcData = exifImage->iptcData();
		if (!iptcData.empty()) {
			QString key;
			Exiv2::IptcData::iterator end = iptcData.end();
			for (Exiv2::IptcData::iterator iptcIt = iptcData.begin(); iptcIt != end; ++iptcIt) {
				if (iptcIt->tagName() != "Keywords") {
					newIptcData.add(*iptcIt);
				}
	        }
	    }

		/* add new tags */
		QSetIterator<QString> newTagsIt(newTags);
		while (newTagsIt.hasNext()) {
			QString tag = newTagsIt.next();
		    Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::string);
		    value->read(tag.toStdString());
		    Exiv2::IptcKey key("Iptc.Application2.Keywords");
		    newIptcData.add(key, value.get());
		}

	    exifImage->setIptcData(newIptcData);
	    exifImage->writeMetadata();
   	}
	catch (Exiv2::Error &error) {
		QMessageBox msgBox;
		msgBox.critical(this, tr("Error"), tr("Failed to save tags to ") + imageFileName);
		return false;
	}

	return true;
}
Example #7
0
// Convert an exif array of integer string to a KisMetaData array of integer
KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
{
    QList<KisMetaData::Value> v;
    const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
    if(dvalue)
    {
        QByteArray array(dvalue->count(), 0);
        dvalue->copy((Exiv2::byte*)array.data());
        for (int i = 0; i < array.size(); i++) {
            QChar c((char)array[i]);
            v.push_back(KisMetaData::Value(QString(c).toInt(0)));
        }
    } else {
        Q_ASSERT(value->typeId() == Exiv2::asciiString);
        QString str = QString::fromLatin1(value->toString().c_str());
        v.push_back(KisMetaData::Value(str.toInt()));
    }
    return KisMetaData::Value(v, KisMetaData::Value::OrderedArray);
}
Example #8
0
KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
{
    uint16_t v = value->toLong();
    QMap<QString, KisMetaData::Value> flashStructure;
    bool fired = (v & 0x01);  // bit 1 is whether flash was fired or not
    flashStructure["Fired"] = QVariant(fired);
    int ret = ((v >> 1) & 0x03);  // bit 2 and 3 are Return
    flashStructure["Return"] = QVariant(ret);
    int mode = ((v >> 3) & 0x03);  // bit 4 and 5 are Mode
    flashStructure["Mode"] = QVariant(mode);
    bool function = ((v >> 5) & 0x01);  // bit 6 if function
    flashStructure["Function"] = QVariant(function);
    bool redEye = ((v >> 6) & 0x01);  // bit 7 if function
    flashStructure["RedEyeMode"] = QVariant(redEye);
    return KisMetaData::Value(flashStructure);
}
Example #9
0
// Always creates a new metadata entry.
// Passing invalidTypeId causes the type to be guessed.
// Guessing types is accurate for IPTC, but not for EXIF.
// Returns 0 on success
EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllTypeId type)
{
    assert(img && key && val);
    if (img==0 || key==0 || val==0) return -1;
    ImageWrapper *imgWrap = (ImageWrapper*)img;
    int rc = 2;

    Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
    Exiv2::ExifData &exifData = imgWrap->image->exifData();

    std::string data(val);
    {
        size_t dataLen = data.length();
        // if data starts and ends with quotes, remove them
        if (dataLen > 1 && *(data.begin()) == '\"' && *(data.rbegin()) == '\"') {
            data = data.substr(1, dataLen-2);
        }
    }

    try {
        Exiv2::IptcKey iptcKey(key);
        rc = 1;

        if (type == invalidTypeId)
            type = (DllTypeId)Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record());
        Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
        value->read(data);

        rc = iptcData.add(iptcKey, value.get());
    }
    catch(const Exiv2::AnyError&) {
    }

    if (rc) {
        // Failed with iptc, so try exif
        try {
            Exiv2::ExifKey exifKey(key);
            rc = 1;

            // No way to get value type for exif... string is the most common
            if (type == invalidTypeId)
                type = asciiString;
            Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
            value->read(data);

            exifData.add(exifKey, value.get());
            rc = 0;
        }
        catch(const Exiv2::AnyError&) {
        }
    }

    return rc;
}
Example #10
0
// Convert an exiv value to a KisMetaData value
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType)
{
    switch (value->typeId()) {
    case Exiv2::signedByte:
    case Exiv2::invalidTypeId:
    case Exiv2::lastTypeId:
    case Exiv2::directory:
        dbgFile << "Invalid value :" << value->typeId() << " value =" << value->toString().c_str();
        return KisMetaData::Value();
    case Exiv2::undefined: {
        dbgFile << "Undefined value :" << value->typeId() << " value =" << value->toString().c_str();
        QByteArray array(value->count() , 0);
        value->copy((Exiv2::byte*)array.data(), Exiv2::invalidByteOrder);
        return KisMetaData::Value(QString(array.toBase64()));
    }
    case Exiv2::unsignedByte:
    case Exiv2::unsignedShort:
    case Exiv2::unsignedLong:
    case Exiv2::signedShort:
    case Exiv2::signedLong: {
        if (value->count() == 1 && !forceSeq) {
            return KisMetaData::Value((int)value->toLong());
        } else {
            QList<KisMetaData::Value> array;
            for (int i = 0; i < value->count(); i++)
                array.push_back(KisMetaData::Value((int)value->toLong(i)));
            return KisMetaData::Value(array, arrayType);
        }
    }
    case Exiv2::asciiString:
    case Exiv2::string:
    case Exiv2::comment: // look at kexiv2 for the problem about decoding correctly that tag
        return KisMetaData::Value(value->toString().c_str());
    case Exiv2::unsignedRational:
        if(value->size() < 2)
        {
          dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str();
          return KisMetaData::Value();
        }
        return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second));
    case Exiv2::signedRational:
        if(value->size() < 2)
        {
          dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str();
          return KisMetaData::Value();
        }
        return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second));
    case Exiv2::date:
    case Exiv2::time:
        return KisMetaData::Value(QDateTime::fromString(value->toString().c_str(), Qt::ISODate));
    case Exiv2::xmpText:
    case Exiv2::xmpAlt:
    case Exiv2::xmpBag:
    case Exiv2::xmpSeq:
    case Exiv2::langAlt:
    default: {
        dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str();
        //Q_ASSERT(false); // This point must never be reached !
        return KisMetaData::Value();
    }
    }
    dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str();
    //Q_ASSERT(false); // This point must never be reached !
    return KisMetaData::Value();
}
Example #11
0
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;
}
Example #12
0
bool KExiv2::setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized, bool setProgramName) const
{
    if(!dateTime.isValid())
        return false;

    if (!setProgramId(setProgramName))
        return false;

    try
    {
        // In first we write date & time into Exif.

        // DateTimeDigitized is set by slide scanners etc. when a picture is digitized.
        // DateTimeOriginal specifies the date/time when the picture was taken.
        // For digital cameras, these dates should be both set, and identical.
        // Reference: http://www.exif.org/Exif2-2.PDF, chapter 4.6.5, table 4, section F.

        const std::string &exifdatetime(dateTime.toString(QString("yyyy:MM:dd hh:mm:ss")).toAscii().constData());
        d->exifMetadata()["Exif.Image.DateTime"]         = exifdatetime;
        d->exifMetadata()["Exif.Photo.DateTimeOriginal"] = exifdatetime;
        if(setDateTimeDigitized)
            d->exifMetadata()["Exif.Photo.DateTimeDigitized"] = exifdatetime;

#ifdef _XMP_SUPPORT_

        // In second we write date & time into Xmp.

        const std::string &xmpdatetime(dateTime.toString(Qt::ISODate).toAscii().constData());

        Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
        xmpTxtVal->read(xmpdatetime);
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeOriginal"), xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.photoshop.DateCreated"), xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.tiff.DateTime"),         xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.CreateDate"),        xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.MetadataDate"),      xmpTxtVal.get());
        d->xmpMetadata().add(Exiv2::XmpKey("Xmp.xmp.ModifyDate"),        xmpTxtVal.get());
        if(setDateTimeDigitized)
            d->xmpMetadata().add(Exiv2::XmpKey("Xmp.exif.DateTimeDigitized"), xmpTxtVal.get());

        // Tag not updated:
        // "Xmp.dc.DateTime" is a sequence of date relevant of dublin core change.
        //                   This is not the picture date as well

#endif // _XMP_SUPPORT_

        // In third we write date & time into Iptc.

        const std::string &iptcdate(dateTime.date().toString(Qt::ISODate).toAscii().constData());
        const std::string &iptctime(dateTime.time().toString(Qt::ISODate).toAscii().constData());
        d->iptcMetadata()["Iptc.Application2.DateCreated"] = iptcdate;
        d->iptcMetadata()["Iptc.Application2.TimeCreated"] = iptctime;
        if(setDateTimeDigitized)
        {
            d->iptcMetadata()["Iptc.Application2.DigitizationDate"] = iptcdate;
            d->iptcMetadata()["Iptc.Application2.DigitizationTime"] = iptctime;
        }

        return true;
    }
    catch( Exiv2::Error& e )
    {
        d->printExiv2ExceptionError("Cannot set Date & Time into image using Exiv2 ", e);
    }
    catch(...)
    {
        kError() << "Default exception from Exiv2";
    }

    return false;
}
Example #13
0
// Overwrites existing value if found, otherwise creates a new one.
// Passing invalidTypeId causes the type to be guessed.
// Guessing types is accurate for IPTC, but not for EXIF.
// Returns 0 on success
EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllTypeId type)
{
    assert(img && key && val);
    if (img==0 || key==0 || val==0) return -1;
    ImageWrapper *imgWrap = (ImageWrapper*)img;
    int rc = 2;

    Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
    Exiv2::ExifData &exifData = imgWrap->image->exifData();

    std::string data(val);
    // if data starts and ends with quotes, remove them
    if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') {
        data = data.substr(1, data.size()-2);
    }

    try {
        Exiv2::IptcKey iptcKey(key);
        rc = 1;

        if (type == invalidTypeId)
            type = (DllTypeId)Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record());
        Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
        value->read(data);

        Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
        if (iter != iptcData.end()) {
            iter->setValue(value.get());
            rc = 0;
        }
        else {
            rc = iptcData.add(iptcKey, value.get());
        }
    } 
    catch(const Exiv2::AnyError&) {
    }

    if (rc) {
        // Failed with iptc, so try exif
        try {
            Exiv2::ExifKey exifKey(key);
            rc = 1;

            // No way to get value type for exif... string is the most common
            if (type == invalidTypeId)
                type = asciiString;
            Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
            value->read(data);

            Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
            if (iter != exifData.end()) {
                iter->setValue(value.get());
                rc = 0;
            }
            else {
                exifData.add(exifKey, value.get());
                rc = 0;
            }
        }
        catch(const Exiv2::AnyError&) {
        }
    }

    return rc;
}
Example #14
0
    void AsfVideo::metadataHandler(int meta)
    {
        DataBuf buf(5000);
        io_->read(buf.pData_, 2);
        int recordCount = Exiv2::getUShort(buf.pData_, littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
        byte guidBuf[16];   char fileID[37] = "";

        while(recordCount--) {
            std::memset(buf.pData_, 0x0, buf.size_);

            if(meta == 1 || meta == 3) {
                io_->read(buf.pData_, 4);
                io_->read(buf.pData_, 2);
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
                io_->read(buf.pData_, 2);
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);
                io_->read(buf.pData_, 4);
                dataLength = Exiv2::getULong(buf.pData_, littleEndian);

                if (nameLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, nameLength);

                v->read(toString16(buf));
                if(dataType == 6) {
                    io_->read(guidBuf, 16);
                    getGUID(guidBuf, fileID);
                }
                else
                    // Sanity check with an "unreasonably" large number
                    if (dataLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                        EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
                                  << " entries considered invalid; not read.\n";
#endif
                        io_->seek(io_->tell() + dataLength, BasicIo::beg);
                    }
                else
                        io_->read(buf.pData_, dataLength);
            }

            else if(meta == 2) {
                io_->read(buf.pData_, 2);
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);

                if (nameLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, nameLength);

                v->read(toString16(buf));

                io_->read(buf.pData_, 2);
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);

                io_->read(buf.pData_, 2);
                dataLength = Exiv2::getUShort(buf.pData_, littleEndian);

                // Sanity check with an "unreasonably" large number
                if (dataLength > 5000) {
#ifndef SUPPRESS_WARNINGS
                    EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
                              << " entries considered invalid; not read.\n";
#endif
                    io_->seek(io_->tell() + dataLength, BasicIo::beg);
                }
            else
                io_->read(buf.pData_, dataLength);
            }

            if(dataType == 0) {                       // Unicode String
                v->read(toString16(buf));
            }
            else if(dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer
                v->read( Exiv2::toString( Exiv2::getUShort(buf.pData_, littleEndian)));
            }
            else if(dataType == 3) {                  // 32-bit Unsigned Integer
                v->read( Exiv2::toString( Exiv2::getULong( buf.pData_, littleEndian)));
            }
            else if(dataType == 4) {                  // 64-bit Unsigned Integer
                v->read(Exiv2::toString(getUint64_t(buf)));
            }
            else if(dataType == 6) {                  // 128-bit GUID
                v->read(Exiv2::toString(fileID));
            }
            else {                                    // Byte array
                v->read( Exiv2::toString(buf.pData_));
            }
        }

        if(meta == 1) {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get());
        }
        else if(meta == 2) {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get());
        }
        else {
            xmpData_.add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get());
        }
    } // AsfVideo::metadataHandler
Example #15
0
    void AsfVideo::tagDecoder(const TagVocabulary *tv, uint64_t size)
    {
        uint64_t cur_pos = io_->tell();
        DataBuf buf(1000);
        unsigned long count = 0, tempLength = 0;
        buf.pData_[4] = '\0' ;
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);

        if(compareTag( exvGettext(tv->label_), "Header")) {
            localPosition_ = 0;
            io_->read(buf.pData_, 4);
            io_->read(buf.pData_, 2);

            while(localPosition_ < cur_pos + size) decodeBlock();
        }

        else if(compareTag( exvGettext(tv->label_), "File_Properties"))
            fileProperties();

        else if(compareTag( exvGettext(tv->label_), "Stream_Properties"))
            streamProperties();

        else if(compareTag( exvGettext(tv->label_), "Metadata"))
            metadataHandler(1);

        else if(compareTag( exvGettext(tv->label_), "Extended_Content_Description"))
            metadataHandler(2);

        else if(compareTag( exvGettext(tv->label_), "Metadata_Library"))
            metadataHandler(3);

        else if(compareTag( exvGettext(tv->label_), "Codec_List"))
            codecList();

        else if(compareTag( exvGettext(tv->label_), "Content_Description"))
            contentDescription(size);

        else if(compareTag( exvGettext(tv->label_), "Extended_Stream_Properties"))
            extendedStreamProperties(size);

        else if(compareTag( exvGettext(tv->label_), "Header_Extension")) {
            localPosition_ = 0;
            headerExtension(size);
        }

        else if(compareTag( exvGettext(tv->label_), "Language_List")) {
            std::memset(buf.pData_, 0x0, buf.size_);
            io_->read(buf.pData_, 2);
            count = Exiv2::getUShort(buf.pData_, littleEndian);

            while(count--) {
                std::memset(buf.pData_, 0x0, buf.size_);
                io_->read(buf.pData_, 1);   tempLength = (int)buf.pData_[0];

                io_->read(buf.pData_, tempLength);
                v->read(toString16(buf));
            }
            xmpData_.add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get());
        }

        io_->seek(cur_pos + size, BasicIo::beg);
        localPosition_ = io_->tell();
    } // AsfVideo::tagDecoder
Example #16
0
int WriteGPSData(const char* File, const struct GPSPoint* Point,
		 const char* Datum, int NoChangeMtime, int DegMinSecs)
{
	// Write the GPS data to the file...

	struct stat statbuf;
	struct stat statbuf2;
	struct utimbuf utb;
	if (NoChangeMtime)
		stat(File, &statbuf);
	Exiv2::Image::AutoPtr Image;

	try {
		Image = Exiv2::ImageFactory::open(File);
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to open file %s.\n", File);
		return 0;
	}
	Image->readMetadata();
	if (Image.get() == NULL)
	{
		// It failed if we got here.
		DEBUGLOG("Failed to read file %s %s.\n",
			 File, Exiv2::strError().c_str());
		return 0;
	}
	
	Exiv2::ExifData &ExifToWrite = Image->exifData();

	// Make sure we're starting from a clean GPS IFD.
	// There might be lots of GPS tags existing here, since only the
	// presence of the GPSLatitude tag causes correlation to stop with
	// "GPS Already Present" error.
	EraseGpsTags(ExifToWrite);

	char ScratchBuf[100];

	// Do all the easy constant ones first.
	// GPSVersionID tag: standard says it should be four bytes: 02 02 00 00
	//  (and, must be present).
	Exiv2::Value::AutoPtr Value = Exiv2::Value::create(Exiv2::unsignedByte);
	Value->read("2 2 0 0");
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), Value.get());
	// Datum: the datum of the measured data. The default is WGS-84.
	if (*Datum)
		ExifToWrite["Exif.GPSInfo.GPSMapDatum"] = Datum;
	
	// Now start adding data.
	// ALTITUDE.
	// If no altitude was found in the GPX file, ElevDecimals will be -1
	if (Point->ElevDecimals >= 0) {
		// Altitude reference: byte "00" meaning "sea level".
		// Or "01" if the altitude value is negative.
		Value = Exiv2::Value::create(Exiv2::unsignedByte);
		if (Point->Elev >= 0)
		{
			Value->read("0");
		} else {
			Value->read("1");
		}
		ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), Value.get());
		// And the actual altitude.
		Value = Exiv2::Value::create(Exiv2::unsignedRational);
		// 3 decimal points is beyond the limit of current GPS technology
		int Decimals = MIN(Point->ElevDecimals, 3);
		ConvertToRational(fabs(Point->Elev), Decimals, ScratchBuf, sizeof(ScratchBuf));

		/* printf("Altitude: %f -> %s\n", Point->Elev, ScratchBuf); */
		Value->read(ScratchBuf);
		ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude"), Value.get());
	}
	
	// LATITUDE
	// Latitude reference: "N" or "S".
	if (Point->Lat < 0)
	{
		// Less than Zero: ie, minus: means
		// Southern hemisphere. Where I live.
		ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "S";
	} else {
		// More than Zero: ie, plus: means
		// Northern hemisphere.
		ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "N";
	}
	// Now the actual latitude itself.
	// The original comment read:
	// This is done as three rationals.
	// I choose to do it as:
	//   dd/1 - degrees.
	//   mmmm/100 - minutes
	//   0/1 - seconds
	// Exif standard says you can do it with minutes
	// as mm/1 and then seconds as ss/1, but its
	// (slightly) more accurate to do it as
	//  mmmm/100 than to split it.
	// We also absolute the value (with fabs())
	// as the sign is encoded in LatRef.
	// Further note: original code did not translate between
	//   dd.dddddd to dd mm.mm - that's why we now multiply
	//   by 60*N - x60 to get minutes, xN to get to mmmm/N.
	//   N is 10^S where S is the number of significant decimal
	//   places.
	//
	// Rereading the EXIF standard, it's quite ok to do DD MM SS.SS
	// Which is much more accurate. This is the new default, unless otherwise
	// set.
	Value = Exiv2::Value::create(Exiv2::unsignedRational);

	if (DegMinSecs)
	{
		ConvertToLatLongRational(Point->Lat, Point->LatDecimals, ScratchBuf, sizeof(ScratchBuf));
	} else {
		ConvertToOldLatLongRational(Point->Lat, ScratchBuf, sizeof(ScratchBuf));
	}
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLatitude"), Value.get());
	
	// LONGITUDE
	// Longitude reference: "E" or "W".
	if (Point->Long < 0)
	{
		// Less than Zero: ie, minus: means
		// Western hemisphere.
		ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "W";
	} else {
		// More than Zero: ie, plus: means
		// Eastern hemisphere. Where I live.
		ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "E";
	}
	// Now the actual longitude itself, in the same way as latitude
	Value = Exiv2::Value::create(Exiv2::unsignedRational);

	if (DegMinSecs)
	{
		ConvertToLatLongRational(Point->Long, Point->LongDecimals, ScratchBuf, sizeof(ScratchBuf));
	} else {
		ConvertToOldLatLongRational(Point->Long, ScratchBuf, sizeof(ScratchBuf));
	}
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLongitude"), Value.get());

	// The timestamp.
	// Make up the timestamp...
	// The timestamp is taken as the UTC time of the photo.
	// If interpolation occurred, then this time is the time of the photo.
	struct tm TimeStamp = *gmtime(&(Point->Time));

	Value = Exiv2::Value::create(Exiv2::unsignedRational);
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%d/1 %d/1 %d/1",
			TimeStamp.tm_hour, TimeStamp.tm_min,
			TimeStamp.tm_sec);
	Value->read(ScratchBuf);
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp"), Value.get());

	// And we should also do a datestamp.
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%04d:%02d:%02d",
			TimeStamp.tm_year + 1900,
			TimeStamp.tm_mon + 1,
			TimeStamp.tm_mday);
	ExifToWrite["Exif.GPSInfo.GPSDateStamp"] = ScratchBuf;

	// Write the data to file.
	try {
		Image->writeMetadata();
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to write to file %s.\n", File);
		return 0;
	}

	if (NoChangeMtime)
	{
		stat(File, &statbuf2);
		utb.actime = statbuf2.st_atime;
		utb.modtime = statbuf.st_mtime;
		utime(File, &utb);
	}

	return 1;
	
}
Example #17
0
QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
{
    return QDateTime::fromString(value->toString().c_str(), Qt::ISODate);
}
Example #18
0
int WriteFixedDatestamp(const char* File, time_t Time)
{
	// Write the GPS data to the file...

	struct stat statbuf;
	struct stat statbuf2;
	struct utimbuf utb;
	stat(File, &statbuf);

	Exiv2::Image::AutoPtr Image;

	try {
		Image = Exiv2::ImageFactory::open(File);
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to open file %s.\n", File);
		return 0;
	}
	Image->readMetadata();
	if (Image.get() == NULL)
	{
		// It failed if we got here.
		DEBUGLOG("Failed to read file %s %s.\n",
			 File, Exiv2::strError().c_str());
		return 0;
	}
	
	Exiv2::ExifData &ExifToWrite = Image->exifData();
	
	const struct tm TimeStamp = *gmtime(&Time);
	char ScratchBuf[100];

	snprintf(ScratchBuf, sizeof(ScratchBuf), "%04d:%02d:%02d",
			TimeStamp.tm_year + 1900,
			TimeStamp.tm_mon + 1,
			TimeStamp.tm_mday);
	ExifToWrite.erase(ExifToWrite.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSDateStamp")));
	ExifToWrite["Exif.GPSInfo.GPSDateStamp"] = ScratchBuf;

	Exiv2::Value::AutoPtr Value = Exiv2::Value::create(Exiv2::unsignedRational);
	snprintf(ScratchBuf, sizeof(ScratchBuf), "%d/1 %d/1 %d/1",
			TimeStamp.tm_hour, TimeStamp.tm_min,
			TimeStamp.tm_sec);
	Value->read(ScratchBuf);
	ExifToWrite.erase(ExifToWrite.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp")));
	ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSTimeStamp"), Value.get());
	
	try {
		Image->writeMetadata();
	} catch (Exiv2::Error e) {
		DEBUGLOG("Failed to write to file %s.\n", File);
		return 0;
	}
	
	// Reset the mtime.
	stat(File, &statbuf2);
	utb.actime = statbuf2.st_atime;
	utb.modtime = statbuf.st_mtime;
	utime(File, &utb);

	return 1;
}
Example #19
0
bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
    ioDevice->open(QIODevice::ReadOnly);
    dbgFile << "Load XMP Data";
    std::string xmpPacket_;
    QByteArray arr = ioDevice->readAll();
    xmpPacket_.assign(arr.data(), arr.length());
    dbgFile << xmpPacket_.length();
//     dbgFile << xmpPacket_.c_str();
    Exiv2::XmpData xmpData_;
    Exiv2::XmpParser::decode(xmpData_, xmpPacket_);
    QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > > structures;
    QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > > arraysOfStructures;
    for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) {
        dbgFile << it->key().c_str();
        Exiv2::XmpKey key(it->key());
        dbgFile << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str();
        if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") {  // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated
            dbgFile << "dropped";
        } else {
            const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str());
            if (!schema) {
                schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str());
                if (!schema) {
                    schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str());
                    Q_ASSERT(schema);
                }
            }
            const Exiv2::Value::AutoPtr value = it->getValue();
            // Decrypt key
            QString structName = "";
            QString tagName = key.tagName().c_str();
            int arrayIndex = -1;
            const KisMetaData::TypeInfo* typeInfo = 0;
            bool isStructureEntry = false;
            bool isStructureInArrayEntry = false;
            if (tagName.contains("/")) {
                QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)");
                if (regexp.indexIn(tagName) != -1) {
                    structName = regexp.capturedTexts()[1];
                    tagName =  regexp.capturedTexts()[3];
                    typeInfo = schema->propertyType(structName);
                    Q_ASSERT(typeInfo == schema->propertyType(structName));
                    if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                        typeInfo = typeInfo->structureSchema()->propertyType(tagName);
                    }
                    isStructureEntry = true;
                } else {
                    QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)");
                    if (regexp2.indexIn(tagName) != -1) {
                        dbgFile << ppVar(tagName);
                        structName = regexp2.capturedTexts()[1];
                        arrayIndex = regexp2.capturedTexts()[2].toInt() - 1;
                        tagName = regexp2.capturedTexts()[4];
                        dbgFile << ppVar(structName) << ppVar(regexp2.capturedTexts()[3]);
                        //Q_ASSERT(schema->propertyType(structName));
                        if (schema->propertyType(structName)) {
                            typeInfo = schema->propertyType(structName)->embeddedPropertyType();
                            Q_ASSERT(typeInfo);
                            if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                                typeInfo = typeInfo->structureSchema()->propertyType(tagName);
                            }
                        }
                        isStructureInArrayEntry = true;
                    } else {
                        dbgFile << "Decoding structure name/entry failed: " << tagName;
                    }
                }
            } else {
                typeInfo = schema->propertyType(tagName);
            }
            KisMetaData::Value v;

            bool ignoreValue = false;
            // Compute the value
            if (value->typeId() == Exiv2::xmpBag
                    || value->typeId() == Exiv2::xmpSeq
                    || value->typeId() == Exiv2::xmpAlt) {
                const KisMetaData::TypeInfo* embeddedTypeInfo = 0;
                if (typeInfo) {
                    embeddedTypeInfo = typeInfo->embeddedPropertyType();
                }
                const KisMetaData::Parser* parser = 0;
                if (embeddedTypeInfo) {
                    parser = embeddedTypeInfo->parser();
                }
                const Exiv2::XmpArrayValue* xav = dynamic_cast<const Exiv2::XmpArrayValue*>(value.get());
                Q_ASSERT(xav);
                QList<KisMetaData::Value> array;
                for (std::vector< std::string >::const_iterator it = xav->value_.begin();
                        it != xav->value_.end(); ++it) {
                    QString value = it->c_str();
                    if (parser) {
                        array.push_back(parser->parse(value));
                    } else {
                        dbgImage << "No parser " << tagName;
                        array.push_back(KisMetaData::Value(value));
                    }
                }
                KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid;
                switch (xav->xmpArrayType()) {
                case Exiv2::XmpValue::xaNone:
                    warnKrita << "KisXMPIO: Unsupported array";
                    break;
                case Exiv2::XmpValue::xaAlt:
                    vt = KisMetaData::Value::AlternativeArray;
                    break;
                case Exiv2::XmpValue::xaBag:
                    vt = KisMetaData::Value::UnorderedArray;
                    break;
                case Exiv2::XmpValue::xaSeq:
                    vt = KisMetaData::Value::OrderedArray;
                    break;
                }
                v = KisMetaData::Value(array, vt);
            } else if (value->typeId() == Exiv2::langAlt) {
                const Exiv2::LangAltValue* xav = dynamic_cast<const Exiv2::LangAltValue*>(value.get());
                QList<KisMetaData::Value> alt;
                for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin();
                        it != xav->value_.end(); ++it) {
                    KisMetaData::Value valt(it->second.c_str());
                    valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str()));
                    alt.push_back(valt);
                }
                v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
            } else {
                QString valTxt = value->toString().c_str();
                if (typeInfo && typeInfo->parser()) {
                    v = typeInfo->parser()->parse(valTxt);
                } else {
                    dbgFile << "No parser " << tagName;
                    v = KisMetaData::Value(valTxt);
                }
                if (valTxt == "type=\"Struct\"") {
                    if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
                        ignoreValue = true;
                    }
                }
            }

            // set the value
            dbgFile << ppVar(tagName);
            if (isStructureEntry) {
                structures[schema][structName][tagName] = v;
            } else if (isStructureInArrayEntry) {
                if (arraysOfStructures[schema][structName].size() <= arrayIndex) {
                    arraysOfStructures[schema][structName].resize(arrayIndex + 1);
                }
                arraysOfStructures[schema][structName][arrayIndex][tagName] = v;
            } else {
                if (!ignoreValue) {
                    store->addEntry(KisMetaData::Entry(schema, tagName, v));
                } else {
                    dbgFile << "Ignoring value for " << tagName << " " << v;
                }
            }
        }
    }

    for (QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value>  > >::iterator it = structures.begin();
            it != structures.end(); ++it) {
        const KisMetaData::Schema* schema = it.key();
        for (QMap<QString, QMap<QString, KisMetaData::Value> >::iterator it2 = it.value().begin();
                it2 != it.value().end(); ++it2) {
            store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value())));
        }
    }
    for (QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) {
        const KisMetaData::Schema* schema = it.key();
        for (QMap<QString, QVector<QMap<QString, KisMetaData::Value> > >::iterator it2 = it.value().begin();
                it2 != it.value().end(); ++it2) {
            KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray;
            QString entryName = it2.key();
            if (schema->propertyType(entryName)) {
                switch (schema->propertyType(entryName)->propertyType()) {
                case KisMetaData::TypeInfo::OrderedArrayType:
                    type = KisMetaData::Value::OrderedArray;
                    break;
                case KisMetaData::TypeInfo::UnorderedArrayType:
                    type = KisMetaData::Value::OrderedArray;
                    break;
                case KisMetaData::TypeInfo::AlternativeArrayType:
                    type = KisMetaData::Value::AlternativeArray;
                    break;
                default:
                    type = KisMetaData::Value::Invalid;
                    break;
                }
            } else if (store->containsEntry(schema, entryName)) {
                KisMetaData::Value value = store->getEntry(schema, entryName).value();
                if (value.isArray()) {
                    type = value.type();
                }
            }
            store->removeEntry(schema, entryName);
            if (type != KisMetaData::Value::Invalid) {
                QList< KisMetaData::Value > valueList;
                for (int i = 0; i < it2.value().size(); ++i) {
                    valueList.append(it2.value()[i]);
                }
                store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type)));
            }
        }
    }

    return true;
}