[frameworks/kimageformats] /: KRA/ORA: merged in a single plugin and added metadata support
Mirco Miranda <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 2e199eea806a7cf627b4ac54acbde1ff041fa383 by Mirco Miranda.
Committed on 29/07/2026 at 05:42.
Pushed by mircomir into branch 'master'.
KRA/ORA: merged in a single plugin and added metadata support
- ORA/KRA: merged in a single plugin (KRA)
- ORA: removed standalone plugin
- KRA: use of logging category
- KRA: add metadata support by processing the embedded `documentinfo.xml`
- Improved signature lookup (see MR !17) and added test case
M +3 -9 README.md
M +0 -1 autotests/ossfuzz/build_fuzzers.sh
M +2 -5 autotests/ossfuzz/kimgio_fuzzer.cc
A +19 -0 autotests/read/kra/src.kra.json
A +15 -0 autotests/read/kra/src443_32bit.json
A +- -- autotests/read/kra/src443_32bit.kra
M +0 -3 src/imageformats/CMakeLists.txt
M +81 -13 src/imageformats/kra.cpp
M +2 -2 src/imageformats/kra.json
R +0 -0 src/imageformats/kra_p.h [from: src/imageformats/kra.h - 100% similarity]
D +0 -98 src/imageformats/ora.cpp
D +0 -33 src/imageformats/ora.h
D +0 -4 src/imageformats/ora.json
https://invent.kde.org/frameworks/kimageformats/-/commit/2e199eea806a7cf627b4ac54acbde1ff041fa383
diff --git a/README.md b/README.md
index 2991da5c..dc5ed6fc 100644
--- a/README.md
+++ b/README.md
@@ -493,16 +493,10 @@ plugin:
this format an hack is activated to guarantee total compatibility of the
plugin with Windows.
-### The KRA plugin
+### The KRA and ORA plugin
-The KRA format is a ZIP archive containing image data. In particular, the
-rendered image in PNG format is saved in the root: the plugin reads this
-image.
-
-### The ORA plugin
-
-The ORA format is a ZIP archive containing image data. In particular, the
-rendered image in PNG format is saved in the root: the plugin reads this
+Both KRA and ORA formats are ZIP archives containing image data. Specifically,
+the rendered PNG image is saved in the root directory: the plugin reads this
image.
### The PSD plugin
diff --git a/autotests/ossfuzz/build_fuzzers.sh b/autotests/ossfuzz/build_fuzzers.sh
index ccb333a9..a26b4ae9 100755
--- a/autotests/ossfuzz/build_fuzzers.sh
+++ b/autotests/ossfuzz/build_fuzzers.sh
@@ -165,7 +165,6 @@ HANDLER_TYPES="ani
jxl
jxr
kra
- ora
pcx
pfm
pic
diff --git a/autotests/ossfuzz/kimgio_fuzzer.cc b/autotests/ossfuzz/kimgio_fuzzer.cc
index a3833c1d..e3ae96f7 100644
--- a/autotests/ossfuzz/kimgio_fuzzer.cc
+++ b/autotests/ossfuzz/kimgio_fuzzer.cc
@@ -23,7 +23,7 @@
Usage:
python infra/helper.py build_image kimageformats
python infra/helper.py build_fuzzers --sanitizer undefined|address|memory kimageformats
- python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|ff|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
+ python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|ff|hdr|heif|iff|jp2|jxl|jxr|kra|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
*/
#include <QBuffer>
@@ -65,11 +65,8 @@
#include "jxr_p.h"
#define HANDLER JXRHandler
#elif defined KIMG_FUZZER_kra
-#include "kra.h"
+#include "kra_p.h"
#define HANDLER KraHandler
-#elif defined KIMG_FUZZER_ora
-#include "ora.h"
-#define HANDLER OraHandler
#elif defined KIMG_FUZZER_pcx
#include "pcx_p.h"
#define HANDLER PCXHandler
diff --git a/autotests/read/kra/src.kra.json b/autotests/read/kra/src.kra.json
new file mode 100644
index 00000000..ffdce2e5
--- /dev/null
+++ b/autotests/read/kra/src.kra.json
@@ -0,0 +1,19 @@
+[
+ {
+ "fileName" : "src.png",
+ "metadata" : [
+ {
+ "key" : "CreationDate",
+ "value" : "2016-01-27T10:40:16"
+ },
+ {
+ "key" : "ModificationDate",
+ "value" : "2016-01-27T10:41:28"
+ },
+ {
+ "key" : "Author" ,
+ "value" : "Boudewijn Rempt"
+ }
+ ]
+ }
+]
diff --git a/autotests/read/kra/src443_32bit.json b/autotests/read/kra/src443_32bit.json
new file mode 100644
index 00000000..f2231580
--- /dev/null
+++ b/autotests/read/kra/src443_32bit.json
@@ -0,0 +1,15 @@
+[
+ {
+ "fileName" : "src.png",
+ "metadata" : [
+ {
+ "key" : "CreationDate",
+ "value" : "2016-01-27T10:40:16"
+ },
+ {
+ "key" : "ModificationDate",
+ "value" : "2026-07-16T07:00:36"
+ }
+ ]
+ }
+]
diff --git a/autotests/read/kra/src443_32bit.kra b/autotests/read/kra/src443_32bit.kra
new file mode 100644
index 00000000..2a475baa
Binary files /dev/null and b/autotests/read/kra/src443_32bit.kra differ
diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt
index 202681ec..2398f48e 100644
--- a/src/imageformats/CMakeLists.txt
+++ b/src/imageformats/CMakeLists.txt
@@ -182,7 +182,4 @@ if (KF6Archive_FOUND)
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
target_link_libraries(kimg_kra PRIVATE KF6::Archive)
- kimageformats_add_plugin(kimg_ora SOURCES ora.cpp)
- target_link_libraries(kimg_ora PRIVATE KF6::Archive)
-
endif()
diff --git a/src/imageformats/kra.cpp b/src/imageformats/kra.cpp
index ef587d7a..6dbfc244 100644
--- a/src/imageformats/kra.cpp
+++ b/src/imageformats/kra.cpp
@@ -1,6 +1,7 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2013 Boudewijn Rempt <[email protected]>
+ SPDX-FileCopyrightText: 2026 Mirco Miranda <[email protected]>
SPDX-License-Identifier: LGPL-2.0-or-later
@@ -8,16 +9,23 @@
on public domain. See: http://tulrich.com/geekstuff/
*/
-#include "kra.h"
+#include "kra_p.h"
+#include "util_p.h"
#include <kzip.h>
+#include <QByteArrayView>
#include <QFile>
#include <QIODevice>
#include <QImage>
+#include <QLoggingCategory>
+#include <QXmlStreamReader>
-static constexpr char s_magic[] = "application/x-krita";
-static constexpr int s_magic_size = sizeof(s_magic) - 1; // -1 to remove the last \0
+Q_DECLARE_LOGGING_CATEGORY(LOG_KRAPLUGIN)
+Q_LOGGING_CATEGORY(LOG_KRAPLUGIN, "kf.imageformats.plugins.kra", QtWarningMsg)
+
+#define ORA_MAGIC QByteArrayView("image/openraster")
+#define KRA_MAGIC QByteArrayView("application/x-krita")
KraHandler::KraHandler()
{
@@ -32,6 +40,55 @@ bool KraHandler::canRead() const
return false;
}
+static bool addMetadata(QImage *image, const QByteArray& rawXml)
+{
+ if (image == nullptr) {
+ return false;
+ }
+ QXmlStreamReader xml(rawXml);
+ for(QString key; !xml.atEnd();) {
+ auto tt = xml.readNext();
+ if (tt == QXmlStreamReader::StartElement) {
+ key = xml.name().toString().toLower();
+ }
+ else if (tt == QXmlStreamReader::EndElement) {
+ key.clear();
+ }
+ else if (tt == QXmlStreamReader::Characters) {
+ auto text = xml.text().toString().trimmed();
+ if (text.isEmpty() || key.isEmpty())
+ continue;
+ if (key == QStringLiteral("title")) {
+ image->setText(QStringLiteral(META_KEY_TITLE), text);
+ }
+ else if (key == QStringLiteral("abstract")) {
+ image->setText(QStringLiteral(META_KEY_DESCRIPTION), text);
+ }
+ else if (key == QStringLiteral("full-name")) {
+ image->setText(QStringLiteral(META_KEY_AUTHOR), text);
+ }
+ else if (key == QStringLiteral("date")) {
+ if (QDateTime::fromString(text, Qt::ISODate).isValid())
+ image->setText(QStringLiteral(META_KEY_MODIFICATIONDATE), text);
+ }
+ else if (key == QStringLiteral("creation-date")) {
+ if (QDateTime::fromString(text, Qt::ISODate).isValid())
+ image->setText(QStringLiteral(META_KEY_CREATIONDATE), text);
+ }
+ else if (key == QStringLiteral("keyword")) {
+ image->setText(QStringLiteral(META_KEY_KEYWORDS), text);
+ }
+ else if (key == QStringLiteral("license")) {
+ image->setText(QStringLiteral(META_KEY_COPYRIGHT), text);
+ }
+ else {
+ qCDebug(LOG_KRAPLUGIN) << "Unmanaged metadata:" << key << text;
+ }
+ }
+ }
+ return !xml.hasError();
+}
+
bool KraHandler::read(QImage *image)
{
KZip zip(device());
@@ -39,14 +96,26 @@ bool KraHandler::read(QImage *image)
return false;
}
+ // reading the image
const KArchiveEntry *entry = zip.directory()->entry(QStringLiteral("mergedimage.png"));
if (!entry || !entry->isFile()) {
return false;
}
-
const KZipFileEntry *fileZipEntry = static_cast<const KZipFileEntry *>(entry);
+ if (!image->loadFromData(fileZipEntry->data(), "PNG")) {
+ qCCritical(LOG_KRAPLUGIN) << "Invalid image.";
+ return false;
+ }
- image->loadFromData(fileZipEntry->data(), "PNG");
+ // reading metadata
+ const KArchiveEntry *metaEntry = zip.directory()->entry(QStringLiteral("documentinfo.xml"));
+ if (!metaEntry || !metaEntry->isFile()) {
+ return true; // the image is still valid
+ }
+ const KZipFileEntry *metaZipEntry = static_cast<const KZipFileEntry *>(metaEntry);
+ if (!addMetadata(image, metaZipEntry->data())) {
+ qCWarning(LOG_KRAPLUGIN) << "XML metadat seems invalid.";
+ }
return true;
}
@@ -54,24 +123,23 @@ bool KraHandler::read(QImage *image)
bool KraHandler::canRead(QIODevice *device)
{
if (!device) {
- qWarning("KraHandler::canRead() called with no device");
+ qCWarning(LOG_KRAPLUGIN) << "KraHandler::canRead() called with no device";
return false;
}
if (device->isSequential()) {
return false;
}
- char buff[57];
- if (device->peek(buff, sizeof(buff)) == sizeof(buff)) {
- return memcmp(buff + 0x26, s_magic, s_magic_size) == 0;
+ auto head = device->peek(100);
+ if (!head.startsWith(QByteArrayView("PK"))) {
+ return false;
}
-
- return false;
+ return head.contains(KRA_MAGIC) || head.contains(ORA_MAGIC);
}
QImageIOPlugin::Capabilities KraPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
- if (format == "kra" || format == "KRA") {
+ if (format == "kra" || format == "ora") {
return Capabilities(CanRead);
}
if (!format.isEmpty()) {
@@ -96,4 +164,4 @@ QImageIOHandler *KraPlugin::create(QIODevice *device, const QByteArray &format)
return handler;
}
-#include "moc_kra.cpp"
+#include "moc_kra_p.cpp"
diff --git a/src/imageformats/kra.json b/src/imageformats/kra.json
index 6307531f..eeefa990 100644
--- a/src/imageformats/kra.json
+++ b/src/imageformats/kra.json
@@ -1,4 +1,4 @@
{
- "Keys": [ "kra" ],
- "MimeTypes": [ "application/x-krita" ]
+ "Keys": [ "kra", "ora" ],
+ "MimeTypes": [ "application/x-krita", "image/openraster" ]
}
diff --git a/src/imageformats/kra.h b/src/imageformats/kra_p.h
similarity index 100%
rename from src/imageformats/kra.h
rename to src/imageformats/kra_p.h
diff --git a/src/imageformats/ora.cpp b/src/imageformats/ora.cpp
deleted file mode 100644
index 0b785b4f..00000000
--- a/src/imageformats/ora.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- This file is part of the KDE project
- SPDX-FileCopyrightText: 2013 Boudewijn Rempt <[email protected]>
-
- SPDX-License-Identifier: LGPL-2.0-or-later
-
- This code is based on Thacher Ulrich PSD loading code released
- on public domain. See: http://tulrich.com/geekstuff/
-*/
-
-#include "ora.h"
-
-#include <QImage>
-#include <QScopedPointer>
-
-#include <kzip.h>
-
-static constexpr char s_magic[] = "image/openraster";
-static constexpr int s_magic_size = sizeof(s_magic) - 1; // -1 to remove the last \0
-
-OraHandler::OraHandler()
-{
-}
-
-bool OraHandler::canRead() const
-{
- if (canRead(device())) {
- setFormat("ora");
- return true;
- }
- return false;
-}
-
-bool OraHandler::read(QImage *image)
-{
- KZip zip(device());
- if (!zip.open(QIODevice::ReadOnly)) {
- return false;
- }
-
- const KArchiveEntry *entry = zip.directory()->entry(QStringLiteral("mergedimage.png"));
- if (!entry || !entry->isFile()) {
- return false;
- }
-
- const KZipFileEntry *fileZipEntry = static_cast<const KZipFileEntry *>(entry);
-
- image->loadFromData(fileZipEntry->data(), "PNG");
-
- return true;
-}
-
-bool OraHandler::canRead(QIODevice *device)
-{
- if (!device) {
- qWarning("OraHandler::canRead() called with no device");
- return false;
- }
- if (device->isSequential()) {
- return false;
- }
-
- char buff[54];
- if (device->peek(buff, sizeof(buff)) == sizeof(buff)) {
- return memcmp(buff + 0x26, s_magic, s_magic_size) == 0;
- }
-
- return false;
-}
-
-QImageIOPlugin::Capabilities OraPlugin::capabilities(QIODevice *device, const QByteArray &format) const
-{
- if (format == "ora" || format == "ORA") {
- return Capabilities(CanRead);
- }
- if (!format.isEmpty()) {
- return {};
- }
- if (!device->isOpen()) {
- return {};
- }
-
- Capabilities cap;
- if (device->isReadable() && OraHandler::canRead(device)) {
- cap |= CanRead;
- }
- return cap;
-}
-
-QImageIOHandler *OraPlugin::create(QIODevice *device, const QByteArray &format) const
-{
- QImageIOHandler *handler = new OraHandler;
- handler->setDevice(device);
- handler->setFormat(format);
- return handler;
-}
-
-#include "moc_ora.cpp"
diff --git a/src/imageformats/ora.h b/src/imageformats/ora.h
deleted file mode 100644
index 58d134d3..00000000
--- a/src/imageformats/ora.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- This file is part of the KDE project
- SPDX-FileCopyrightText: 2013 Boudewijn Rempt <[email protected]>
-
- SPDX-License-Identifier: LGPL-2.0-or-later
-*/
-
-#ifndef KIMG_ORA_H
-#define KIMG_ORA_H
-
-#include <QImageIOPlugin>
-
-class OraHandler : public QImageIOHandler
-{
-public:
- OraHandler();
-
- bool canRead() const override;
- bool read(QImage *image) override;
-
- static bool canRead(QIODevice *device);
-};
-
-class OraPlugin : public QImageIOPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "ora.json")
-public:
- Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
- QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
-};
-
-#endif
diff --git a/src/imageformats/ora.json b/src/imageformats/ora.json
deleted file mode 100644
index c12d7f85..00000000
--- a/src/imageformats/ora.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": [ "ora" ],
- "MimeTypes": [ "image/openraster" ]
-}