From 138880a23989cf2a8769e206a5bdf4c3c2defffb Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Thu, 7 Jul 2022 22:25:25 +0200 Subject: [PATCH] Add PNG extractor Textual information, such as author information, creator software, and a description can be stored in a PNG's tEXt, zTXt, and iTXt chunks, [1]. libexiv2 can extract EXIF information out of PNGs but does not support PNG Textual Information, see [2], however Qt's PNG handler makes this information readily available as textKeys. Unfortunately, the "Description" key is used by QImage to keep track of the QImageIOHandler's Description and overrides the actual PNG "Description" text, making it useless. [1] https://www.w3.org/TR/PNG/#11keywords [2] https://github.com/Exiv2/exiv2/issues/1343 --- src/extractors/CMakeLists.txt | 11 ++++ src/extractors/pngextractor.cpp | 90 ++++++++++++++++++++++++++++++++ src/extractors/pngextractor.h | 31 +++++++++++ src/extractors/pngextractor.json | 7 +++ 4 files changed, 139 insertions(+) create mode 100644 src/extractors/pngextractor.cpp create mode 100644 src/extractors/pngextractor.h create mode 100644 src/extractors/pngextractor.json diff --git a/src/extractors/CMakeLists.txt b/src/extractors/CMakeLists.txt index dcdb36b5..4f7c4781 100644 --- a/src/extractors/CMakeLists.txt +++ b/src/extractors/CMakeLists.txt @@ -255,3 +255,14 @@ if(libappimage_FOUND AND KF5Config_FOUND AND Qt${QT_MAJOR_VERSION}Gui_FOUND) TARGETS kfilemetadata_appimageextractor DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kfilemetadata) endif() + +add_library(kfilemetadata_pngextractor MODULE pngextractor.cpp ) +target_link_libraries(kfilemetadata_pngextractor + KF5::FileMetaData + Qt${QT_MAJOR_VERSION}::Gui +) + +set_target_properties(kfilemetadata_pngextractor PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kfilemetadata") +install( +TARGETS kfilemetadata_pngextractor +DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kfilemetadata) diff --git a/src/extractors/pngextractor.cpp b/src/extractors/pngextractor.cpp new file mode 100644 index 00000000..21db6cf7 --- /dev/null +++ b/src/extractors/pngextractor.cpp @@ -0,0 +1,90 @@ +/* + SPDX-FileCopyrightText: 2022 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-or-later +*/ + +#include "pngextractor.h" +#include "propertyinfo.h" + +#include + +using namespace KFileMetaData; + +// Keywords specified in https://www.w3.org/TR/PNG/#11keywords +static const struct { + QString key; + Property::Property property; +} s_textMapping[] = { + // Short (one line) title or caption for image + {QStringLiteral("Title"), Property::Title}, + // Name of image's creator + {QStringLiteral("Author"), Property::Author}, + // Description of image (possibly long) + // Unfortunately, QImage puts all text keys with spaces, such as + // "Raw profile type exif", into the "Description" key, + // (cf. qt_getImageTextFromDescription), overriding the actual + // PNG description, and making it useless. + //{QStringLiteral("Description"), Property::Description}, + // Copyright notice + {QStringLiteral("Copyright"), Property::Copyright}, + // Time of original image creation + {QStringLiteral("Creation Time"), Property::CreationDate}, + // Software used to create the image + {QStringLiteral("Software"), Property::Generator}, + // Disclaimer - Legal disclaimer + // Warning - Warning of nature of content + // Source - Device used to create the image + // Miscellaneous comment + {QStringLiteral("Comment"), Property::Comment}, +}; + +PngExtractor::PngExtractor(QObject* parent) + : ExtractorPlugin(parent) +{ +} + +QStringList PngExtractor::mimetypes() const +{ + return { + QStringLiteral("image/png") + }; +} + +void PngExtractor::extract(ExtractionResult* result) +{ + QImageReader reader(result->inputUrl(), "png"); + if (!reader.canRead()) { + return; + } + + result->addType(Type::Image); + + for (const auto &mapping : s_textMapping) { + QString text = reader.text(mapping.key); + if (text.isEmpty()) { + // Spec says, keywords are case-sensitive but of course the real world looks different. + text = reader.text(mapping.key.toLower()); + } + if (text.isEmpty()) { + continue; + } + + const auto propertyInfo = PropertyInfo(mapping.property); + + if (propertyInfo.valueType() == QVariant::DateTime) { + // "For the Creation Time keyword, the date format defined in section 5.2.14 of RFC 1123 is suggested" + // which in turn references RFC822... + const QDateTime dt = QDateTime::fromString(text, Qt::RFC2822Date); + + if (!dt.isValid()) { + continue; + } + + result->add(mapping.property, dt); + continue; + } + + result->add(mapping.property, text); + } +} diff --git a/src/extractors/pngextractor.h b/src/extractors/pngextractor.h new file mode 100644 index 00000000..694644d9 --- /dev/null +++ b/src/extractors/pngextractor.h @@ -0,0 +1,31 @@ +/* + SPDX-FileCopyrightText: 2022 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-or-later +*/ + +#ifndef PNGEXTRACTOR_H +#define PNGEXTRACTOR_H + +#include "extractorplugin.h" + +namespace KFileMetaData +{ + +class PngExtractor : public ExtractorPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.kde.kf5.kfilemetadata.ExtractorPlugin" + FILE "pngextractor.json") + Q_INTERFACES(KFileMetaData::ExtractorPlugin) + +public: + explicit PngExtractor(QObject *parent = nullptr); + + void extract(ExtractionResult *result) override; + QStringList mimetypes() const override; +}; + +} // namespace KFileMetaData + +#endif // PNGEXTRACTOR_H diff --git a/src/extractors/pngextractor.json b/src/extractors/pngextractor.json new file mode 100644 index 00000000..35b8b5e5 --- /dev/null +++ b/src/extractors/pngextractor.json @@ -0,0 +1,7 @@ +{ + "Name" : "PngExtractor", + "Id" : "org.kde.pngextractor", + "MimeTypes" : { + "image/png" : { "version" : "0.0" } + } +}