File indexing completed on 2024-05-19 03:56:44
0001 /* 0002 SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 0008 #include "kritaextractor.h" 0009 #include "kfilemetadata_debug.h" 0010 0011 #include <KZip> 0012 #include <QXmlStreamReader> 0013 0014 using namespace KFileMetaData; 0015 0016 KritaExtractor::KritaExtractor(QObject* parent) 0017 : ExtractorPlugin(parent) 0018 { 0019 } 0020 0021 QStringList KritaExtractor::mimetypes() const 0022 { 0023 return {QStringLiteral("application/x-krita")}; 0024 } 0025 0026 void KritaExtractor::extract(ExtractionResult* result) 0027 { 0028 // Krita files are secretly zip files 0029 KZip zip(result->inputUrl()); 0030 if (!zip.open(QIODevice::ReadOnly)) { 0031 return; 0032 } 0033 0034 result->addType(Type::Image); 0035 0036 if (!result->inputFlags().testFlag(ExtractionResult::ExtractMetaData)) { 0037 return; 0038 } 0039 0040 // Read main image information, e.g width and height 0041 { 0042 const KArchiveFile *entry = zip.directory()->file(QLatin1String("maindoc.xml")); 0043 if (!entry) { 0044 return; 0045 } 0046 0047 std::unique_ptr<QIODevice> fileDevice{entry->createDevice()}; 0048 0049 // There is only one element of the maindoc we care about: the IMAGE element. 0050 // The document width and height are stored as attributes. 0051 QXmlStreamReader xml{fileDevice.get()}; 0052 while (xml.readNextStartElement()) { 0053 if (xml.name() == QLatin1String("IMAGE")) { 0054 bool ok = false; 0055 const int width = xml.attributes().value(QLatin1String("width")).toInt(&ok); 0056 if (ok && width != 0) { 0057 result->add(Property::Width, width); 0058 } 0059 0060 const int height = xml.attributes().value(QLatin1String("height")).toInt(&ok); 0061 if (ok && height != 0) { 0062 result->add(Property::Height, height); 0063 } 0064 0065 break; 0066 } 0067 } 0068 } 0069 0070 // Read extra metadata entered by the author, e.g. title and description 0071 { 0072 const KArchiveFile *entry = zip.directory()->file(QLatin1String("documentinfo.xml")); 0073 if (!entry) { 0074 return; 0075 } 0076 0077 std::unique_ptr<QIODevice> fileDevice{entry->createDevice()}; 0078 0079 // The documentinfo xml schema is very simple, every field in the GUI is exposed 0080 // as an element of the same name. The one exception is "description" which seems 0081 // to be stored under <abstract>. 0082 static const QHash<QStringView, Property::Property> propertyMapping {{ 0083 {QStringLiteral("title"), Property::Title}, 0084 {QStringLiteral("license"), Property::License}, 0085 {QStringLiteral("keyword"), Property::Keywords}, 0086 {QStringLiteral("abstract"), Property::Description}, 0087 }}; 0088 0089 QXmlStreamReader xml{fileDevice.get()}; 0090 if (!xml.readNextStartElement() || xml.name() != QStringLiteral("document-info")) { 0091 return; 0092 } 0093 0094 while (xml.readNextStartElement()) { 0095 const QStringView elementName = xml.name(); 0096 0097 if (elementName == QStringLiteral("about")) { 0098 while (xml.readNextStartElement()) { 0099 const QStringView childElementName = xml.name(); 0100 0101 const Property::Property property = propertyMapping.value(childElementName); 0102 if (property != Property::Empty) { 0103 const QString value = xml.readElementText(); 0104 result->add(property, value); 0105 } else if (childElementName == QStringLiteral("creation-date")) { 0106 const QString value = xml.readElementText(); 0107 0108 const QDateTime creationDate = QDateTime::fromString(value, Qt::ISODate); 0109 if (creationDate.isValid()) { 0110 result->add(Property::CreationDate, creationDate); 0111 } 0112 } else { 0113 xml.skipCurrentElement(); 0114 } 0115 } 0116 } else if (elementName == QStringLiteral("author")) { 0117 while (xml.readNextStartElement()) { 0118 const QStringView childElementName = xml.name(); 0119 0120 if (childElementName == QStringLiteral("full-name")) { 0121 const QString value = xml.readElementText(); 0122 if (!value.isEmpty()) { 0123 result->add(Property::Author, value); 0124 } 0125 } else { 0126 xml.skipCurrentElement(); 0127 } 0128 } 0129 } else { 0130 xml.skipCurrentElement(); 0131 } 0132 } 0133 } 0134 } 0135 0136 #include "moc_kritaextractor.cpp"