Krita Source Code Documentation
Loading...
Searching...
No Matches
KisXMPIO Class Reference

#include <kis_xmp_io.h>

+ Inheritance diagram for KisXMPIO:

Public Member Functions

bool canSaveAllEntries (KisMetaData::Store *) const override
 
QString id () const override
 
 KisXMPIO ()
 
bool loadFrom (KisMetaData::Store *store, QIODevice *ioDevice) const override
 
QString name () const override
 
bool saveTo (const KisMetaData::Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const override
 
bool supportLoading () const override
 
bool supportSaving () const override
 
BackendType type () const override
 
 ~KisXMPIO () override
 

Additional Inherited Members

Detailed Description

Definition at line 15 of file kis_xmp_io.h.

Constructor & Destructor Documentation

◆ KisXMPIO()

KisXMPIO::KisXMPIO ( )

◆ ~KisXMPIO()

KisXMPIO::~KisXMPIO ( )
override

Definition at line 32 of file kis_xmp_io.cpp.

33{
34}

Member Function Documentation

◆ canSaveAllEntries()

bool KisXMPIO::canSaveAllEntries ( KisMetaData::Store * store) const
inlineoverridevirtual
Parameters
storethe list of metadata
Returns
true if this backend is capable of saving all the metadata of the store

Implements KisMetaData::IOBackend.

Definition at line 37 of file kis_xmp_io.h.

38 {
39 return true;
40 }

◆ id()

QString KisXMPIO::id ( ) const
inlineoverridevirtual

Implements KisMetaData::IOBackend.

Definition at line 20 of file kis_xmp_io.h.

21 {
22 return "xmp";
23 }

◆ loadFrom()

bool KisXMPIO::loadFrom ( KisMetaData::Store * store,
QIODevice * ioDevice ) const
overridevirtual
Parameters
storethe list of metadata to load
ioDevicethe device from where the metadata will be loaded
Returns
true if the load was successful

Implements KisMetaData::IOBackend.

Definition at line 234 of file kis_xmp_io.cpp.

235{
236 ioDevice->open(QIODevice::ReadOnly);
237 dbgMetaData << "Load XMP Data";
238 std::string xmpPacket_;
239 QByteArray arr = ioDevice->readAll();
240 xmpPacket_.assign(arr.data(), arr.length());
241 dbgMetaData << xmpPacket_.length();
242 // dbgMetaData << xmpPacket_.c_str();
243 Exiv2::XmpData xmpData_;
244 if (Exiv2::XmpParser::decode(xmpData_, xmpPacket_) != 0) {
245 warnMetaData << "Failed to decode as XMP";
246 return false;
247 }
248 QMap<const KisMetaData::Schema *, QMap<QString, QMap<QString, KisMetaData::Value>>> structures;
249 QMap<const KisMetaData::Schema *, QMap<QString, QVector<QMap<QString, KisMetaData::Value>>>> arraysOfStructures;
250 for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) {
251 dbgMetaData << "Start iteration" << it->key().c_str();
252
253 Exiv2::XmpKey key(it->key());
254 dbgMetaData << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str();
255 if ((key.groupName() == "exif" || key.groupName() == "tiff")
256 && key.tagName() == "NativeDigest") { // TODO: someone who has time to lose can look in adding support for
257 // NativeDigest, it's undocumented use by the XMP SDK to check if exif
258 // data has been changed while XMP hasn't been updated
259 dbgMetaData << "dropped";
260 } else {
261 const KisMetaData::Schema *schema =
263 if (!schema) {
264 schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str());
265 if (!schema) {
266 schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str());
267 Q_ASSERT(schema);
268 }
269 }
270#if EXIV2_TEST_VERSION(0,28,0)
271 const Exiv2::Value::UniquePtr value = it->getValue();
272#else
273 const Exiv2::Value::AutoPtr value = it->getValue();
274#endif
275 QString structName;
276 int arrayIndex = -1;
277 QString tagName;
278 const KisMetaData::TypeInfo *typeInfo = 0;
279
280 if (!parseTagName(key.tagName().c_str(), structName, arrayIndex, tagName, &typeInfo, schema))
281 continue;
282
283 bool isStructureEntry = !structName.isEmpty() && arrayIndex == -1;
284 bool isStructureInArrayEntry = !structName.isEmpty() && arrayIndex != -1;
285 Q_ASSERT(isStructureEntry != isStructureInArrayEntry || !isStructureEntry);
286
288 bool ignoreValue = false;
289 // Compute the value
290 if (value->typeId() == Exiv2::xmpBag || value->typeId() == Exiv2::xmpSeq
291 || value->typeId() == Exiv2::xmpAlt) {
292 const KisMetaData::TypeInfo *embeddedTypeInfo = 0;
293 if (typeInfo) {
294 embeddedTypeInfo = typeInfo->embeddedPropertyType();
295 }
296 const KisMetaData::Parser *parser = 0;
297 if (embeddedTypeInfo) {
298 parser = embeddedTypeInfo->parser();
299 }
300 const Exiv2::XmpArrayValue *xav = dynamic_cast<const Exiv2::XmpArrayValue *>(value.get());
301 Q_ASSERT(xav);
303#if EXIV2_TEST_VERSION(0,28,0)
304 for (size_t i = 0; i < xav->count(); ++i) {
305#else
306 for (int i = 0; i < xav->count(); ++i) {
307#endif
308 QString value = QString::fromStdString(xav->toString(i));
309 if (parser) {
310 KisMetaData::Value parsed = parser->parse(value);
311 if (parsed.type() == KisMetaData::Value::Invalid) {
312 KisUsageLogger::log(QString("Cannot parse metadata value \"%1\" for %2. Assigning invalid value.").arg(value, tagName));
313 }
314 array.push_back(parsed);
315
316 } else {
317 dbgImage << "No parser " << tagName;
318 array.push_back(KisMetaData::Value(value));
319 }
320 }
322 switch (xav->xmpArrayType()) {
323 case Exiv2::XmpValue::xaNone:
324 warnKrita << "KisXMPIO: Unsupported array";
325 break;
326 case Exiv2::XmpValue::xaAlt:
328 break;
329 case Exiv2::XmpValue::xaBag:
331 break;
332 case Exiv2::XmpValue::xaSeq:
334 break;
335 }
336 v = KisMetaData::Value(array, vt);
337 } else if (value->typeId() == Exiv2::langAlt) {
338 const Exiv2::LangAltValue *xav = dynamic_cast<const Exiv2::LangAltValue *>(value.get());
339 KIS_ASSERT(xav);
340
342 for (std::map<std::string, std::string>::const_iterator it = xav->value_.begin();
343 it != xav->value_.end();
344 ++it) {
345 KisMetaData::Value valt(it->second.c_str());
346 valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str()));
347 alt.push_back(valt);
348 }
350 } else {
351 QString valTxt = value->toString().c_str();
352 if (typeInfo && typeInfo->parser()) {
353 v = typeInfo->parser()->parse(valTxt);
354 if (v.type() == KisMetaData::Value::Invalid) {
355 KisUsageLogger::log(QString("Cannot parse metadata value \"%1\" for %2. Assigning invalid value.").arg(valTxt, tagName));
356 }
357 } else {
358 dbgMetaData << "No parser " << tagName;
359 v = KisMetaData::Value(valTxt);
360 }
361 if (valTxt == "type=\"Struct\"") {
362 if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
363 ignoreValue = true;
364 }
365 }
366 }
367
368 // set the value
369 if (isStructureEntry) {
370 structures[schema][structName][tagName] = v;
371 } else if (isStructureInArrayEntry) {
372 if (arraysOfStructures[schema][structName].size() <= arrayIndex) {
373 arraysOfStructures[schema][structName].resize(arrayIndex + 1);
374 }
375
376 if (!arraysOfStructures[schema][structName][arrayIndex].contains(tagName)) {
377 arraysOfStructures[schema][structName][arrayIndex][tagName] = v;
378 } else {
379 warnKrita << "WARNING: trying to overwrite tag" << tagName << "in" << structName << arrayIndex;
380 }
381 } else {
382 if (!ignoreValue) {
383 store->addEntry(KisMetaData::Entry(schema, tagName, v));
384 } else {
385 dbgMetaData << "Ignoring value for " << tagName << " " << v;
386 }
387 }
388 }
389 }
390
391 for (QMap<const KisMetaData::Schema *, QMap<QString, QMap<QString, KisMetaData::Value>>>::iterator it =
392 structures.begin();
393 it != structures.end();
394 ++it) {
395 const KisMetaData::Schema *schema = it.key();
396 for (QMap<QString, QMap<QString, KisMetaData::Value>>::iterator it2 = it.value().begin();
397 it2 != it.value().end();
398 ++it2) {
399 store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value())));
400 }
401 }
402 for (QMap<const KisMetaData::Schema *, QMap<QString, QVector<QMap<QString, KisMetaData::Value>>>>::iterator it =
403 arraysOfStructures.begin();
404 it != arraysOfStructures.end();
405 ++it) {
406 const KisMetaData::Schema *schema = it.key();
407 for (QMap<QString, QVector<QMap<QString, KisMetaData::Value>>>::iterator it2 = it.value().begin();
408 it2 != it.value().end();
409 ++it2) {
411 QString entryName = it2.key();
412 if (schema->propertyType(entryName)) {
413 switch (schema->propertyType(entryName)->propertyType()) {
416 break;
419 break;
422 break;
423 default:
425 break;
426 }
427 } else if (store->containsEntry(schema, entryName)) {
428 KisMetaData::Value value = store->getEntry(schema, entryName).value();
429 if (value.isArray()) {
430 type = value.type();
431 }
432 }
433 store->removeEntry(schema, entryName);
436 for (int i = 0; i < it2.value().size(); ++i) {
437 valueList.append(it2.value()[i]);
438 }
439 store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type)));
440 }
441 }
442 }
443
444 return true;
445}
float value(const T *src, size_t ch)
qreal v
const KisMetaData::Value & value() const
virtual Value parse(const QString &) const =0
static KisMetaData::SchemaRegistry * instance()
const Schema * schemaFromPrefix(const QString &prefix) const
const Schema * schemaFromUri(const QString &uri) const
const KisMetaData::Schema * create(const QString &uri, const QString &prefix)
const TypeInfo * propertyType(const QString &_propertyName) const
void removeEntry(const QString &entryKey)
bool addEntry(const Entry &entry)
bool containsEntry(const QString &entryKey) const
Entry & getEntry(const QString &entryKey)
PropertyType propertyType() const
const Parser * parser() const
const TypeInfo * embeddedPropertyType() const
ValueType
Define the possible value type.
ValueType type() const
static void log(const QString &message)
Logs with date/time.
BackendType type() const override
Definition kis_xmp_io.h:28
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define warnMetaData
Definition kis_debug.h:103
#define warnKrita
Definition kis_debug.h:87
#define dbgMetaData
Definition kis_debug.h:61
#define dbgImage
Definition kis_debug.h:46
bool parseTagName(const QString &tagString, QString &structName, int &arrayIndex, QString &tagName, const KisMetaData::TypeInfo **typeInfo, const KisMetaData::Schema *schema)
int size(const Forest< T > &forest)
Definition KisForest.h:1232

References KisMetaData::Store::addEntry(), KisMetaData::Value::addPropertyQualifier(), KisMetaData::Value::AlternativeArray, KisMetaData::TypeInfo::AlternativeArrayType, KisMetaData::Store::containsEntry(), KisMetaData::SchemaRegistry::create(), dbgImage, dbgMetaData, KisMetaData::TypeInfo::embeddedPropertyType(), KisMetaData::Store::getEntry(), KisMetaData::SchemaRegistry::instance(), KisMetaData::Value::Invalid, KIS_ASSERT, KisMetaData::Value::LangArray, KisUsageLogger::log(), KisMetaData::Value::OrderedArray, KisMetaData::TypeInfo::OrderedArrayType, KisMetaData::Parser::parse(), KisMetaData::TypeInfo::parser(), parseTagName(), KisMetaData::TypeInfo::propertyType(), KisMetaData::Schema::propertyType(), KisMetaData::Store::removeEntry(), KisMetaData::SchemaRegistry::schemaFromPrefix(), KisMetaData::SchemaRegistry::schemaFromUri(), KisMetaData::TypeInfo::StructureType, KisMetaData::Value::type(), type(), KisMetaData::Value::UnorderedArray, KisMetaData::TypeInfo::UnorderedArrayType, v, KisMetaData::Entry::value(), value(), warnKrita, and warnMetaData.

◆ name()

QString KisXMPIO::name ( ) const
inlineoverridevirtual

Implements KisMetaData::IOBackend.

Definition at line 24 of file kis_xmp_io.h.

25 {
26 return i18n("XMP");
27 }

◆ saveTo()

bool KisXMPIO::saveTo ( const KisMetaData::Store * store,
QIODevice * ioDevice,
HeaderType headerType = NoHeader ) const
overridevirtual
Parameters
storethe list of metadata to save
ioDevicethe device to where the metadata will be saved
headerTypedetermine if an header must be prepend to the binary header, and if it does, which type of header
Returns
true if the save was successful (XXX: actually, all backends always return true...)

Implements KisMetaData::IOBackend.

Definition at line 70 of file kis_xmp_io.cpp.

71{
72 dbgMetaData << "Save XMP Data";
73 Exiv2::XmpData xmpData_;
74
75 for (const KisMetaData::Entry &entry : *store) {
76 // Check whether the prefix and namespace are know to exiv2
77 std::string prefix = exiv2Prefix(entry.schema());
78 dbgMetaData << "Saving " << entry.name();
79
80 const KisMetaData::Value &value = entry.value();
81
82 const KisMetaData::TypeInfo *typeInfo = entry.schema()->propertyType(entry.name());
84 QMap<QString, KisMetaData::Value> structure = value.asStructure();
85 const KisMetaData::Schema *structureSchema = 0;
86 if (typeInfo) {
87 structureSchema = typeInfo->structureSchema();
88 }
89 if (!structureSchema) {
90 dbgMetaData << "Unknown schema for " << entry.name();
91 structureSchema = entry.schema();
92 }
93 Q_ASSERT(structureSchema);
94 saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema);
95 } else {
96 Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData());
97 if (typeInfo
102 // Here is the bad part, again we need to do it by hand
103 Exiv2::XmpTextValue tv;
104 switch (typeInfo->propertyType()) {
106 tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
107 break;
109 tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
110 break;
112 tv.setXmpArrayType(Exiv2::XmpValue::xaAlt);
113 break;
114 default:
115 // Cannot happen
116 ;
117 }
118 xmpData_.add(key, &tv); // set the array type
119 const KisMetaData::TypeInfo *structureTypeInfo = typeInfo->embeddedPropertyType();
120 const KisMetaData::Schema *structureSchema = 0;
121 if (structureTypeInfo) {
122 structureSchema = structureTypeInfo->structureSchema();
123 }
124 if (!structureSchema) {
125 dbgMetaData << "Unknown schema for " << entry.name();
126 structureSchema = entry.schema();
127 }
128 Q_ASSERT(structureSchema);
129 QList<KisMetaData::Value> array = value.asArray();
130 for (int idx = 0; idx < array.size(); ++idx) {
131 saveStructure(xmpData_,
132 QString("%1[%2]").arg(entry.name()).arg(idx + 1),
133 prefix,
134 array[idx].asStructure(),
135 structureSchema);
136 }
137 } else {
138 dbgMetaData << ppVar(key.key().c_str());
139 Exiv2::Value *v = kmdValueToExivXmpValue(value);
140 if (v && v->typeId() != Exiv2::TypeId::invalidTypeId) {
141 xmpData_.add(key, v);
142 } else {
143 warnMetaData << "Invalid metadata value " << value << "for: " << entry.name();
144 KisUsageLogger::log(QString("Invalid metadata value \"%1\" for key %2. The metadata key is therefore skipped during saving.").arg(value.toString(), entry.name()));
145 }
146 }
147 }
148 // TODO property qualifier
149 }
150 // Serialize data
151 std::string xmpPacket_;
152 try {
153 Exiv2::XmpParser::encode(xmpPacket_, xmpData_);
154 } catch (std::exception &e) {
155 warnMetaData << "Couldn't encode the data, error =" << e.what();
156 return false;
157 }
158 // Save data into the IO device
159 ioDevice->open(QIODevice::WriteOnly);
160 if (headerType == KisMetaData::IOBackend::JpegHeader) {
161 xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_;
162 }
163 ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length());
164 return true;
165}
@ JpegHeader
Append Jpeg-style header.
#define ppVar(var)
Definition kis_debug.h:155
Exiv2::Value * kmdValueToExivXmpValue(const KisMetaData::Value &value)
std::string exiv2Prefix(const KisMetaData::Schema *_schema)

References KisMetaData::TypeInfo::AlternativeArrayType, dbgMetaData, KisMetaData::TypeInfo::embeddedPropertyType(), exiv2Prefix(), KisMetaData::IOBackend::JpegHeader, kmdValueToExivXmpValue(), KisUsageLogger::log(), KisMetaData::TypeInfo::OrderedArrayType, ppVar, KisMetaData::TypeInfo::propertyType(), KisMetaData::Value::Structure, KisMetaData::TypeInfo::structureSchema(), KisMetaData::TypeInfo::StructureType, KisMetaData::TypeInfo::UnorderedArrayType, v, value(), and warnMetaData.

◆ supportLoading()

bool KisXMPIO::supportLoading ( ) const
inlineoverridevirtual
Returns
true if this backend support loading

Implements KisMetaData::IOBackend.

Definition at line 41 of file kis_xmp_io.h.

42 {
43 return true;
44 }

◆ supportSaving()

bool KisXMPIO::supportSaving ( ) const
inlineoverridevirtual
Returns
tell if this backend support saving

Implements KisMetaData::IOBackend.

Definition at line 32 of file kis_xmp_io.h.

33 {
34 return true;
35 }

◆ type()

BackendType KisXMPIO::type ( ) const
inlineoverridevirtual
Returns
the type of the backend

Implements KisMetaData::IOBackend.

Definition at line 28 of file kis_xmp_io.h.

29 {
30 return Text;
31 }

References KisMetaData::IOBackend::Text.


The documentation for this class was generated from the following files: