diff --git a/kio/metadata/kfilemetadata_exiv2.cpp b/kio/metadata/kfilemetadata_exiv2.cpp index 3d968071..a2046635 100644 --- a/kio/metadata/kfilemetadata_exiv2.cpp +++ b/kio/metadata/kfilemetadata_exiv2.cpp @@ -102,207 +102,206 @@ QList KFileMetaDataExiv2Plugin::metaData(const KUrl &url, con Q_UNUSED(flags); QList result; const KExiv2 kexiv2(url.toLocalFile()); - const KExiv2::DataMap kexiv2metadata = kexiv2.data(); - foreach (const QByteArray &kexiv2key, kexiv2metadata.keys()) { - const QString kexiv2value = kexiv2metadata.value(kexiv2key); - // qDebug() << Q_FUNC_INFO << kexiv2key << kexiv2value; + const KExiv2PropertyList kexiv2metadata = kexiv2.metadata(); + foreach (const KExiv2Property &kexiv2property, kexiv2metadata) { + // qDebug() << Q_FUNC_INFO << kexiv2property.name << kexiv2property.value; // for reference: // https://exiv2.org/tags.html - if (kexiv2key == "Exif.Image.ImageWidth") { + if (kexiv2property.name == "Exif.Image.ImageWidth") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.ImageLength") { + } else if (kexiv2property.name == "Exif.Image.ImageLength") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.FrameRate") { + } else if (kexiv2property.name == "Exif.Image.FrameRate") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#frameRate"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Copyright") { + } else if (kexiv2property.name == "Exif.Image.Copyright") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#copyright"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.UserComment" || kexiv2key == "Exif.Photo.XPComment") { + } else if (kexiv2property.name == "Exif.Image.UserComment" || kexiv2property.name == "Exif.Photo.XPComment") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#comment"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.XPTitle") { + } else if (kexiv2property.name == "Exif.Photo.XPTitle") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#title"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.XPKeywords") { + } else if (kexiv2property.name == "Exif.Image.XPKeywords") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#keyword"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.XPSubject") { + } else if (kexiv2property.name == "Exif.Image.XPSubject") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#subject"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.ImageDescription") { + } else if (kexiv2property.name == "Exif.Image.ImageDescription") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#description"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Software") { + } else if (kexiv2property.name == "Exif.Image.Software") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#generator"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.DateTimeOriginal" || kexiv2key == "Exif.Photo.DateTimeOriginal") { + } else if (kexiv2property.name == "Exif.Image.DateTimeOriginal" || kexiv2property.name == "Exif.Photo.DateTimeOriginal") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#contentCreated"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Make") { + } else if (kexiv2property.name == "Exif.Image.Make") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#make"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Model") { + } else if (kexiv2property.name == "Exif.Image.Model") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#model"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Orientation") { + } else if (kexiv2property.name == "Exif.Image.Orientation") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#orientation"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Artist") { + } else if (kexiv2property.name == "Exif.Image.Artist") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#artist"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.GPSInfo.GPSLatitudeRef") { + } else if (kexiv2property.name == "Exif.GPSInfo.GPSLatitudeRef") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#gpsLatitudeRef"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.GPSInfo.GPSLongitudeRef") { + } else if (kexiv2property.name == "Exif.GPSInfo.GPSLongitudeRef") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#gpsLongitudeRef"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.Flash") { + } else if (kexiv2property.name == "Exif.Image.Flash") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#flash"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.ExposureTime") { + } else if (kexiv2property.name == "Exif.Photo.ExposureTime") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureTime"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.ExposureBiasValue") { + } else if (kexiv2property.name == "Exif.Image.ExposureBiasValue") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureBiasValue"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.ExposureMode") { + } else if (kexiv2property.name == "Exif.Photo.ExposureMode") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureMode"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.ApertureValue") { + } else if (kexiv2property.name == "Exif.Photo.ApertureValue") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#apertureValue"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.FocalLength") { + } else if (kexiv2property.name == "Exif.Image.FocalLength") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLength"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.FocalLengthIn35mmFilm") { + } else if (kexiv2property.name == "Exif.Photo.FocalLengthIn35mmFilm") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLengthIn35mmFilm"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Image.ISOSpeedRatings") { + } else if (kexiv2property.name == "Exif.Image.ISOSpeedRatings") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#isoSpeedRatings"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.MeteringMode") { + } else if (kexiv2property.name == "Exif.Photo.MeteringMode") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#meteringMode"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.WhiteBalance") { + } else if (kexiv2property.name == "Exif.Photo.WhiteBalance") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#whiteBalance"), - kexiv2value + kexiv2property.value ) ); - } else if (kexiv2key == "Exif.Photo.ImageUniqueID") { + } else if (kexiv2property.name == "Exif.Photo.ImageUniqueID") { result.append( KFileMetaInfoItem( QString::fromLatin1("http://www.semanticdesktop.org/ontologies/2007/05/10/nid3#uniqueFileIdentifier"), - kexiv2value + kexiv2property.value ) ); } diff --git a/kutils/kexiv2/kexiv2.cpp b/kutils/kexiv2/kexiv2.cpp index 9ee7728a..45abe057 100644 --- a/kutils/kexiv2/kexiv2.cpp +++ b/kutils/kexiv2/kexiv2.cpp @@ -101,6 +101,15 @@ KExiv2::KExiv2(const QString &path) { } +bool KExiv2::isSupported() +{ +#if defined(HAVE_EXIV2) + return true; +#else + return false; +#endif +} + QImage KExiv2::preview() const { QImage result; @@ -134,9 +143,32 @@ QImage KExiv2::preview() const bool KExiv2::rotateImage(QImage &image) const { +#if defined(HAVE_EXIV2) // for reference: // https://exiv2.org/tags-xmp-tiff.html - const int orientation = data().value("Exif.Image.Orientation").toInt(); + int orientation = 0; + if (d->m_exiv2image.get()) { + static const std::string s_orientationkey = std::string("Exif.Image.Orientation"); + try { + kDebug() << "Checking for orientation Exif data for" << d->m_path; + const Exiv2::ExifData exiv2data = d->m_exiv2image->exifData(); + for (Exiv2::ExifData::const_iterator it = exiv2data.begin(); it != exiv2data.end(); it++) { + const std::string key = (*it).key(); + if (key != s_orientationkey) { + continue; + } + orientation = (*it).value().toLong(); + kDebug() << "Found orientation Exif data" << orientation; + break; + } + } catch(Exiv2::Error &err) { + kWarning() << err.what() << err.code(); + } catch(std::exception &err) { + kWarning() << err.what(); + } catch (...) { + kWarning() << "Exception raised"; + } + } switch (orientation) { case 0: // not documented, nothing to do I guess case 1: { // normal orientation @@ -186,21 +218,30 @@ bool KExiv2::rotateImage(QImage &image) const } } Q_UNREACHABLE(); +#else + kWarning() << "KExiv2 is a stub"; + return false; +#endif // HAVE_EXIV2 } -KExiv2::DataMap KExiv2::data() const +KExiv2PropertyList KExiv2::metadata() const { - KExiv2::DataMap result; + KExiv2PropertyList result; #if defined(HAVE_EXIV2) if (d->m_exiv2image.get()) { try { + KExiv2Property kexiv2property; kDebug() << "Mapping Exif data for" << d->m_path; const Exiv2::ExifData exiv2data = d->m_exiv2image->exifData(); for (Exiv2::ExifData::const_iterator it = exiv2data.begin(); it != exiv2data.end(); it++) { const std::string key = (*it).key(); const std::string value = (*it).value().toString(); - kDebug() << "Key" << key.c_str() << "value" << value.c_str(); - result.insert(QByteArray(key.c_str(), key.size()), QString::fromStdString(value)); + const std::string taglabel = (*it).tagLabel(); + kDebug() << "Key" << key.c_str() << "value" << value.c_str() << "tag label" << taglabel.c_str(); + kexiv2property.name = QByteArray(key.c_str(), key.size()); + kexiv2property.value = QString::fromStdString(value); + kexiv2property.label = QString::fromStdString(taglabel); + result.append(kexiv2property); } kDebug() << "Mapping Iptc data for" << d->m_path; @@ -208,8 +249,12 @@ KExiv2::DataMap KExiv2::data() const for (Exiv2::IptcData::const_iterator it = iptcdata.begin(); it != iptcdata.end(); it++) { const std::string key = (*it).key(); const std::string value = (*it).value().toString(); - kDebug() << "Key" << key.c_str() << "value" << value.c_str(); - result.insert(QByteArray(key.c_str(), key.size()), QString::fromStdString(value)); + const std::string taglabel = (*it).tagLabel(); + kDebug() << "Key" << key.c_str() << "value" << value.c_str() << "tag label" << taglabel.c_str(); + kexiv2property.name = QByteArray(key.c_str(), key.size()); + kexiv2property.value = QString::fromStdString(value); + kexiv2property.label = QString::fromStdString(taglabel); + result.append(kexiv2property); } kDebug() << "Mapping Xmp data for" << d->m_path; @@ -217,8 +262,12 @@ KExiv2::DataMap KExiv2::data() const for (Exiv2::XmpData::const_iterator it = xmpdata.begin(); it != xmpdata.end(); it++) { const std::string key = (*it).key(); const std::string value = (*it).value().toString(); - kDebug() << "Key" << key.c_str() << "value" << value.c_str(); - result.insert(QByteArray(key.c_str(), key.size()), QString::fromStdString(value)); + const std::string taglabel = (*it).tagLabel(); + kDebug() << "Key" << key.c_str() << "value" << value.c_str() << "tag label" << taglabel.c_str(); + kexiv2property.name = QByteArray(key.c_str(), key.size()); + kexiv2property.value = QString::fromStdString(value); + kexiv2property.label = QString::fromStdString(taglabel); + result.append(kexiv2property); } } catch(Exiv2::Error &err) { kWarning() << err.what() << err.code(); @@ -232,11 +281,3 @@ KExiv2::DataMap KExiv2::data() const return result; } -QString KExiv2::label(const QByteArray &key) const -{ - const int lastdotindex = key.lastIndexOf('.'); - if (lastdotindex >= 0) { - return QString::fromLatin1(key.constData() + lastdotindex + 1, key.size() - lastdotindex - 1); - } - return QString::fromLatin1(key.constData(), key.size()); -} diff --git a/kutils/kexiv2/kexiv2.h b/kutils/kexiv2/kexiv2.h index 2b2806ca..47df197b 100644 --- a/kutils/kexiv2/kexiv2.h +++ b/kutils/kexiv2/kexiv2.h @@ -21,48 +21,61 @@ #include "kexiv2_export.h" -#include +#include #include #include class KExiv2Private; + /*! - Class to obtain EXIF metadata, preview and rotate images based on the metadata. + Structure that holds information about Exif, IPTC or XMP property. + + @since 4.22 + @see KExiv2 + @link https://exiv2.org/metadata.html +*/ +struct KExiv2Property +{ + QByteArray name; + QString value; + QString label; +}; + +typedef QList KExiv2PropertyList; + +/*! + Class to obtain Exif, IPTC and XMP metadata, preview and rotate images based on the metadata. @note Initialization and cleanup of the Exiv2 library resources is automatic @since 4.20 + @see KExiv2Property */ class KEXIV2_EXPORT KExiv2 { public: - typedef QMap DataMap; - /*! - @brief Contructs object from @p path and obtains EXIF data if possible + @brief Contructs object from @p path */ KExiv2(const QString &path); + static bool isSupported(); + /*! - @return Largest preview image if provided in the EXIF data, the image is not rotated + @return Largest preview image if provided in the metadata, the image is not rotated automatically and may be null */ QImage preview() const; /*! - @return Rotates @p image according to the EXIF orientation data + @return Rotates @p image according to the orientation metadata */ bool rotateImage(QImage &image) const; /*! - @return Map of all EXIF properties + @return List of all Exif, IPTC and XMP metadata properties */ - DataMap data() const; - - /*! - @return Tag label for EXIF property - */ - QString label(const QByteArray &key) const; + KExiv2PropertyList metadata() const; private: Q_DISABLE_COPY(KExiv2);