KFileMetaData

kritaextractor.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7
8#include "kritaextractor.h"
9#include "kfilemetadata_debug.h"
10
11#include <KZip>
12#include <QXmlStreamReader>
13
14using namespace KFileMetaData;
15
16KritaExtractor::KritaExtractor(QObject* parent)
17 : ExtractorPlugin(parent)
18{
19}
20
21QStringList KritaExtractor::mimetypes() const
22{
23 return {QStringLiteral("application/x-krita")};
24}
25
26void KritaExtractor::extract(ExtractionResult* result)
27{
28 // Krita files are secretly zip files
29 KZip zip(result->inputUrl());
30 if (!zip.open(QIODevice::ReadOnly)) {
31 qCDebug(KFILEMETADATA_LOG) << "Failed to open" << zip.fileName() << "-" << zip.errorString();
32 return;
33 }
34
35 result->addType(Type::Image);
36
37 if (!result->inputFlags().testFlag(ExtractionResult::ExtractMetaData)) {
38 return;
39 }
40
41 // Read main image information, e.g width and height
42 {
43 const KArchiveFile *entry = zip.directory()->file(QLatin1String("maindoc.xml"));
44 if (!entry) {
45 return;
46 }
47
48 std::unique_ptr<QIODevice> fileDevice{entry->createDevice()};
49
50 // There is only one element of the maindoc we care about: the IMAGE element.
51 // The document width and height are stored as attributes.
52 QXmlStreamReader xml{fileDevice.get()};
53 while (xml.readNextStartElement()) {
54 if (xml.name() == QLatin1String("IMAGE")) {
55 bool ok = false;
56 const int width = xml.attributes().value(QLatin1String("width")).toInt(&ok);
57 if (ok && width != 0) {
58 result->add(Property::Width, width);
59 }
60
61 const int height = xml.attributes().value(QLatin1String("height")).toInt(&ok);
62 if (ok && height != 0) {
63 result->add(Property::Height, height);
64 }
65
66 break;
67 }
68 }
69 }
70
71 // Read extra metadata entered by the author, e.g. title and description
72 {
73 const KArchiveFile *entry = zip.directory()->file(QLatin1String("documentinfo.xml"));
74 if (!entry) {
75 return;
76 }
77
78 std::unique_ptr<QIODevice> fileDevice{entry->createDevice()};
79
80 // The documentinfo xml schema is very simple, every field in the GUI is exposed
81 // as an element of the same name. The one exception is "description" which seems
82 // to be stored under <abstract>.
83 static const QHash<QStringView, Property::Property> propertyMapping {{
84 {QStringLiteral("title"), Property::Title},
85 {QStringLiteral("license"), Property::License},
86 {QStringLiteral("keyword"), Property::Keywords},
87 {QStringLiteral("abstract"), Property::Description},
88 }};
89
90 QXmlStreamReader xml{fileDevice.get()};
91 if (!xml.readNextStartElement() || xml.name() != QStringLiteral("document-info")) {
92 return;
93 }
94
95 while (xml.readNextStartElement()) {
96 const QStringView elementName = xml.name();
97
98 if (elementName == QStringLiteral("about")) {
99 while (xml.readNextStartElement()) {
100 const QStringView childElementName = xml.name();
101
102 const Property::Property property = propertyMapping.value(childElementName);
103 if (property != Property::Empty) {
104 const QString value = xml.readElementText();
105 result->add(property, value);
106 } else if (childElementName == QStringLiteral("creation-date")) {
107 const QString value = xml.readElementText();
108
109 const QDateTime creationDate = QDateTime::fromString(value, Qt::ISODate);
110 if (creationDate.isValid()) {
111 result->add(Property::CreationDate, creationDate);
112 }
113 } else {
114 xml.skipCurrentElement();
115 }
116 }
117 } else if (elementName == QStringLiteral("author")) {
118 while (xml.readNextStartElement()) {
119 const QStringView childElementName = xml.name();
120
121 if (childElementName == QStringLiteral("full-name")) {
122 const QString value = xml.readElementText();
123 if (!value.isEmpty()) {
124 result->add(Property::Author, value);
125 }
126 } else {
127 xml.skipCurrentElement();
128 }
129 }
130 } else {
131 xml.skipCurrentElement();
132 }
133 }
134 }
135}
136
137#include "moc_kritaextractor.cpp"
virtual QIODevice * createDevice() const
The ExtractionResult class is where all the data extracted by the indexer is saved.
QString inputUrl() const
The input URL which the plugins will use to locate the file.
virtual void addType(Type::Type type)=0
This function is called by the plugins.
virtual void add(Property::Property property, const QVariant &value)=0
This function is called by the plugins when they wish to add a key value pair which should be indexed...
Flags inputFlags() const
The flags which the extraction plugin should considering following when extracting metadata from the ...
The ExtractorPlugin is the base class for all file metadata extractors.
Property
The Property enum contains all files property types that KFileMetaData manipulates.
Definition properties.h:26
@ Width
Represents the width of the Media in pixels.
Definition properties.h:189
@ Title
Refers to the Title of the content of the file.
Definition properties.h:121
@ Author
The Author field indicated the primary creator of a document.
Definition properties.h:114
@ Height
Represents the height of the Media in pixels.
Definition properties.h:195
@ Description
Represents the description stored in the file.
Definition properties.h:351
@ CreationDate
The date the content of the file was created.
Definition properties.h:177
@ License
Contains the license information of the file.
Definition properties.h:318
@ Keywords
The keywords used to represent the document.
Definition properties.h:183
@ Image
Any Image file.
Definition types.h:56
The KFileMetaData namespace.
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
bool testFlag(Enum flag) const const
QVariant property(const char *name) const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:55:34 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.