diff --git a/autotests/exiv2extractortest.cpp b/autotests/exiv2extractortest.cpp index 9170e50..a537b67 100644 --- a/autotests/exiv2extractortest.cpp +++ b/autotests/exiv2extractortest.cpp @@ -1,55 +1,58 @@ /* * * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "exiv2extractortest.h" #include "simpleextractionresult.h" #include "indexerextractortestsconfig.h" #include "extractors/exiv2extractor.h" #include #include #include using namespace KFileMetaData; QString Exiv2ExtractorTest::testFilePath(const QString& fileName) const { return QLatin1String(INDEXER_TESTS_SAMPLE_FILES_PATH) + QLatin1Char('/') + fileName; } void Exiv2ExtractorTest::test() { Exiv2Extractor plugin{this}; SimpleExtractionResult result(testFilePath("test.jpg"), "image/jpeg"); plugin.extract(&result); QCOMPARE(result.types().size(), 1); QCOMPARE(result.types().constFirst(), Type::Image); using namespace KFileMetaData::Property; QCOMPARE(result.properties().value(PhotoGpsLatitude).toDouble(), 41.411); QCOMPARE(result.properties().value(PhotoGpsLongitude).toDouble(), 2.173); QVERIFY(qAbs(result.properties().value(PhotoGpsAltitude).toDouble() - 12.2) < 0.0001); - + QCOMPARE(result.properties().value(Artist).toString(), QStringLiteral("Artist")); + QCOMPARE(result.properties().value(Description).toString(), QStringLiteral("Description")); + QCOMPARE(result.properties().value(Copyright).toString(), QStringLiteral("Copyright")); + QCOMPARE(result.properties().value(Generator).toString(), QStringLiteral("digiKam-5.9.0")); } QTEST_GUILESS_MAIN(Exiv2ExtractorTest) diff --git a/src/extractors/exiv2extractor.cpp b/src/extractors/exiv2extractor.cpp index ce67e2b..8c88737 100644 --- a/src/extractors/exiv2extractor.cpp +++ b/src/extractors/exiv2extractor.cpp @@ -1,306 +1,310 @@ /* Copyright (C) 2012 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "exiv2extractor.h" #include using namespace KFileMetaData; Exiv2Extractor::Exiv2Extractor(QObject* parent) : ExtractorPlugin(parent) { } namespace { static const QStringList supportedMimeTypes = { QStringLiteral("image/jp2"), QStringLiteral("image/jpeg"), QStringLiteral("image/pgf"), QStringLiteral("image/png"), QStringLiteral("image/tiff"), QStringLiteral("image/x-exv"), QStringLiteral("image/x-canon-cr2"), QStringLiteral("image/x-canon-crw"), QStringLiteral("image/x-fuji-raf"), QStringLiteral("image/x-minolta-mrw"), QStringLiteral("image/x-nikon-nef"), QStringLiteral("image/x-olympus-orf"), QStringLiteral("image/x-panasonic-rw2"), QStringLiteral("image/x-pentax-pef"), QStringLiteral("image/x-photoshop"), QStringLiteral("image/x-samsung-srw"), }; QString toString(const Exiv2::Value& value) { const std::string str = value.toString(); return QString::fromUtf8(str.c_str(), str.length()); } QVariant toVariantDateTime(const Exiv2::Value& value) { if (value.typeId() == Exiv2::asciiString) { QDateTime val = ExtractorPlugin::dateTimeFromString(QString::fromLatin1(value.toString().c_str())); if (val.isValid()) { // Datetime is stored in exif as local time. val.setUtcOffset(0); return QVariant(val); } } return QVariant(); } QVariant toVariantLong(const Exiv2::Value& value) { if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) { qlonglong val = value.toLong(); return QVariant(val); } QString str(toString(value)); bool ok = false; int val = str.toInt(&ok); if (ok) return QVariant(val); return QVariant(); } QVariant toVariantDouble(const Exiv2::Value& value) { if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) { return QVariant(static_cast(value.toFloat())); } QString str(toString(value)); bool ok = false; double val = str.toDouble(&ok); if (ok) return QVariant(val); return QVariant(); } QVariant toVariantString(const Exiv2::Value& value) { QString str = toString(value); if (!str.isEmpty()) return QVariant(str); return QVariant(); } QVariant toVariant(const Exiv2::Value& value, QVariant::Type type) { if (value.count() == 0) { return QVariant(); } switch (type) { case QVariant::Int: return toVariantLong(value); case QVariant::DateTime: return toVariantDateTime(value); case QVariant::Double: return toVariantDouble(value); case QVariant::String: default: return toVariantString(value); } } } QStringList Exiv2Extractor::mimetypes() const { return supportedMimeTypes; } void Exiv2Extractor::extract(ExtractionResult* result) { QByteArray arr = result->inputUrl().toUtf8(); std::string fileString(arr.data(), arr.length()); Exiv2::Image::AutoPtr image; try { image = Exiv2::ImageFactory::open(fileString); } catch (const std::exception&) { return; } if (!image.get()) { return; } try { image->readMetadata(); } catch (const std::exception&) { return; } result->addType(Type::Image); if (image->pixelHeight()) { result->add(Property::Height, image->pixelHeight()); } if (image->pixelWidth()) { result->add(Property::Width, image->pixelWidth()); } std::string comment = image->comment(); if (!comment.empty()) { result->add(Property::Comment, QString::fromUtf8(comment.c_str(), comment.length())); } const Exiv2::ExifData& data = image->exifData(); add(result, data, Property::ImageMake, "Exif.Image.Make", QVariant::String); add(result, data, Property::ImageModel, "Exif.Image.Model", QVariant::String); + add(result, data, Property::Description, "Exif.Image.ImageDescription", QVariant::String); + add(result, data, Property::Artist, "Exif.Image.Artist", QVariant::String); + add(result, data, Property::Copyright, "Exif.Image.Copyright", QVariant::String); + add(result, data, Property::Generator, "Exif.Image.Software", QVariant::String); add(result, data, Property::ImageDateTime, "Exif.Image.DateTime", QVariant::DateTime); add(result, data, Property::ImageOrientation, "Exif.Image.Orientation", QVariant::Int); add(result, data, Property::PhotoFlash, "Exif.Photo.Flash", QVariant::Int); add(result, data, Property::PhotoPixelXDimension, "Exif.Photo.PixelXDimension", QVariant::Int); add(result, data, Property::PhotoPixelXDimension, "Exif.Photo.PixelYDimension", QVariant::Int); add(result, data, Property::PhotoDateTimeOriginal, "Exif.Photo.DateTimeOriginal", QVariant::DateTime); add(result, data, Property::PhotoFocalLength, "Exif.Photo.FocalLength", QVariant::Double); add(result, data, Property::PhotoFocalLengthIn35mmFilm, "Exif.Photo.FocalLengthIn35mmFilm", QVariant::Double); add(result, data, Property::PhotoExposureTime, "Exif.Photo.ExposureTime", QVariant::Double); add(result, data, Property::PhotoExposureBiasValue, "Exif.Photo.ExposureBiasValue", QVariant::Double); add(result, data, Property::PhotoFNumber, "Exif.Photo.FNumber", QVariant::Double); add(result, data, Property::PhotoApertureValue, "Exif.Photo.ApertureValue", QVariant::Double); add(result, data, Property::PhotoWhiteBalance, "Exif.Photo.WhiteBalance", QVariant::Int); add(result, data, Property::PhotoMeteringMode, "Exif.Photo.MeteringMode", QVariant::Int); add(result, data, Property::PhotoISOSpeedRatings, "Exif.Photo.ISOSpeedRatings", QVariant::Int); add(result, data, Property::PhotoSaturation, "Exif.Photo.Saturation", QVariant::Int); add(result, data, Property::PhotoSharpness, "Exif.Photo.Sharpness", QVariant::Int); double latitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLatitude"); double longitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLongitude"); double altitude = fetchGpsAltitude(data); QByteArray latRef = fetchByteArray(data, "Exif.GPSInfo.GPSLatitudeRef"); if (!latRef.isEmpty() && latRef[0] == 'S') latitude *= -1; QByteArray longRef = fetchByteArray(data, "Exif.GPSInfo.GPSLongitudeRef"); if (!longRef.isEmpty() && longRef[0] == 'W') longitude *= -1; if (latitude) { result->add(Property::PhotoGpsLatitude, latitude); } if (longitude) { result->add(Property::PhotoGpsLongitude, longitude); } if (altitude) { result->add(Property::PhotoGpsAltitude, altitude); } } void Exiv2Extractor::add(ExtractionResult* result, const Exiv2::ExifData& data, Property::Property prop, const char* name, QVariant::Type type) { Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); if (it != data.end()) { QVariant value = toVariant(it->value(), type); if (!value.isNull()) result->add(prop, value); } } double Exiv2Extractor::fetchGpsDouble(const Exiv2::ExifData& data, const char* name) { Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); if (it != data.end() && it->count() == 3) { double n = 0.0; double d = 0.0; n = (*it).toRational(0).first; d = (*it).toRational(0).second; if (d == 0) { return 0.0; } double deg = n / d; n = (*it).toRational(1).first; d = (*it).toRational(1).second; if (d == 0) { return deg; } double min = n / d; if (min != -1.0) { deg += min / 60.0; } n = (*it).toRational(2).first; d = (*it).toRational(2).second; if (d == 0) { return deg; } double sec = n / d; if (sec != -1.0) { deg += sec / 3600.0; } return deg; } return 0.0; } double Exiv2Extractor::fetchGpsAltitude(const Exiv2::ExifData& data) { double alt = 0.0; Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude")); if (it != data.end()) { alt = it->value().toFloat(); it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef")); if (it != data.end()) { auto altRef = it->value().toLong(); if (altRef) { alt = alt * -1; } } } return alt; } QByteArray Exiv2Extractor::fetchByteArray(const Exiv2::ExifData& data, const char* name) { Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); if (it != data.end()) { std::string str = it->value().toString(); return QByteArray(str.c_str(), str.size()); } return QByteArray(); }