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
This commit is contained in:
Mirco Miranda
2026-07-29 07:42:42 +02:00
parent 516750e6fc
commit 2e199eea80
13 changed files with 122 additions and 168 deletions
+81 -13
View File
@@ -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"