bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
    ioDevice->open(QIODevice::ReadOnly);
    if (!ioDevice->isOpen()) {
        return false;
    }
    QByteArray arr = ioDevice->readAll();
    Exiv2::ExifData exifData;
    Exiv2::ByteOrder byteOrder;
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
    exifData.load((const Exiv2::byte*)arr.data(), arr.size());
    byteOrder = exifData.byteOrder();
#else
    try {
        byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size());
    }
    catch (const std::exception& ex) {
        warnKrita << "Received exception trying to parse exiv data" << ex.what();
        return false;
    }
    catch (...) {
        dbgKrita << "Received unknown exception trying to parse exiv data";
        return false;
    }

#endif
    dbgFile << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
    dbgFile << "There are" << exifData.count() << " entries in the exif section";
    const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
    Q_ASSERT(tiffSchema);
    const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
    Q_ASSERT(exifSchema);
    const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
    Q_ASSERT(dcSchema);
    const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
    Q_ASSERT(xmpSchema);
    for (Exiv2::ExifMetadata::const_iterator it = exifData.begin();
            it != exifData.end(); ++it) {
        if (it->key() == "Exif.Photo.StripOffsets"
                || it->key() == "RowsPerStrip"
                || it->key() == "StripByteCounts"
                || it->key() == "JPEGInterchangeFormat"
                || it->key() == "JPEGInterchangeFormatLength"
                || it->tagName() == "0x0000" ) {
            dbgFile << it->key().c_str() << " is ignored";
        } else if (it->key() == "Exif.Photo.MakerNote") {
            const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
            store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate
            store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue()))));
        } else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description"
            store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool"
            store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Artist") { // load as dc:creator
            QList<KisMetaData::Value> creators;
            creators.push_back(exivValueToKMDValue(it->getValue(), false));
            store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray)));
        } else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights
            store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->groupName() == "Image") {
            // Tiff tags
            QString fixedTN = it->tagName().c_str();
            if (it->key() == "Exif.Image.ExifTag") {
                dbgFile << "Ignoring " << it->key().c_str();
            } else if (KisMetaData::Entry::isValidName(fixedTN)) {
                store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ;
            } else {
                dbgFile << "Invalid tag name: " << fixedTN;
            }
        } else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) {
            // Exif tags (and GPS tags)
            KisMetaData::Value v;
            if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") {
                v = exifVersionToKMDValue(it->getValue());
            } else if (it->key() == "Exif.Photo.FileSource") {
                v = KisMetaData::Value(3);
            } else if (it->key() == "Exif.Photo.SceneType") {
                v = KisMetaData::Value(1);
            } else if (it->key() == "Exif.Photo.ComponentsConfiguration") {
                v = exifArrayToKMDIntOrderedArray(it->getValue());
            } else if (it->key() == "Exif.Photo.OECF") {
                v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") {
                v = KisMetaData::Value(exivValueToDateTime(it->getValue()));
            } else if (it->key() == "Exif.Photo.DeviceSettingDescription") {
                v = deviceSettingDescriptionExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.CFAPattern") {
                v = cfaPatternExifToKMD(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.Flash") {
                v = flashExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.UserComment") {
                KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false);
                Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
                QVariant commentVar = vUC.asVariant();
                QString comment;
                if (commentVar.type() == QVariant::String) {
                    comment = commentVar.toString();
                } else if (commentVar.type() == QVariant::ByteArray) {
                    const QByteArray commentString = commentVar.toByteArray();
                    comment = QString::fromLatin1(commentString.constData(), commentString.size());
                } else {
                    warnKrita << "KisExifIO: Unhandled UserComment value type.";
                }
                KisMetaData::Value vcomment(comment);
                vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
                QList<KisMetaData::Value> alt;
                alt.append(vcomment);
                v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
            } else {
                bool forceSeq = false;
                KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
                if (it->key() == "Exif.Photo.ISOSpeedRatings") {
                    forceSeq = true;
                    arrayType = KisMetaData::Value::OrderedArray;
                }
                v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType);
            }
            if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag
                dbgFile << "Ignoring " << it->key().c_str();
            } else {
                store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v));
            }
        } else if (it->groupName() == "Thumbnail") {
            dbgFile << "Ignoring thumbnail tag :" << it->key().c_str();
        } else {
            dbgFile << "Unknown exif tag, cannot load:" << it->key().c_str();
        }
    }
    ioDevice->close();
    return true;
}
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;
}