File indexing completed on 2025-03-16 12:49:35
0001 /* 0002 SPDX-FileCopyrightText: 2012 Vishesh Handa <me@vhanda.in> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 0008 #include "exiv2extractor.h" 0009 #include <cmath> 0010 0011 using namespace KFileMetaData; 0012 0013 0014 Exiv2Extractor::Exiv2Extractor(QObject* parent) 0015 : ExtractorPlugin(parent) 0016 { 0017 #ifdef EXV_ENABLE_BMFF 0018 Exiv2::enableBMFF(true); 0019 #endif 0020 } 0021 0022 namespace 0023 { 0024 static const QStringList supportedMimeTypes = { 0025 QStringLiteral("image/bmp"), 0026 QStringLiteral("image/gif"), 0027 QStringLiteral("image/jp2"), 0028 QStringLiteral("image/jpeg"), 0029 QStringLiteral("image/pgf"), 0030 QStringLiteral("image/png"), 0031 QStringLiteral("image/tiff"), 0032 QStringLiteral("image/webp"), 0033 #ifdef EXV_ENABLE_BMFF 0034 QStringLiteral("image/avif"), 0035 QStringLiteral("image/heif"), 0036 QStringLiteral("image/jxl"), 0037 QStringLiteral("image/x-canon-cr3"), 0038 #endif 0039 QStringLiteral("image/x-exv"), 0040 QStringLiteral("image/x-canon-cr2"), 0041 QStringLiteral("image/x-canon-crw"), 0042 QStringLiteral("image/x-fuji-raf"), 0043 QStringLiteral("image/x-minolta-mrw"), 0044 QStringLiteral("image/x-nikon-nef"), 0045 QStringLiteral("image/x-olympus-orf"), 0046 QStringLiteral("image/x-panasonic-rw2"), 0047 QStringLiteral("image/x-pentax-pef"), 0048 QStringLiteral("image/x-photoshop"), 0049 QStringLiteral("image/x-samsung-srw"), 0050 QStringLiteral("image/x-tga"), 0051 }; 0052 0053 QString toString(const Exiv2::Value& value) 0054 { 0055 const std::string str = value.toString(); 0056 return QString::fromUtf8(str.c_str(), str.length()); 0057 } 0058 0059 QVariant toVariantDateTime(const Exiv2::Value& value) 0060 { 0061 if (value.typeId() == Exiv2::asciiString) { 0062 QDateTime val = ExtractorPlugin::dateTimeFromString(QString::fromLatin1(value.toString().c_str())); 0063 if (val.isValid()) { 0064 // Datetime is stored in exif as local time. 0065 val.setOffsetFromUtc(0); 0066 return QVariant(val); 0067 } 0068 } 0069 0070 return QVariant(); 0071 } 0072 0073 QVariant toVariantLong(const Exiv2::Value& value) 0074 { 0075 if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) { 0076 #if EXIV2_TEST_VERSION(0,28,0) 0077 qlonglong val = value.toInt64(); 0078 #else 0079 qlonglong val = value.toLong(); 0080 #endif 0081 return QVariant(val); 0082 } 0083 0084 QString str(toString(value)); 0085 bool ok = false; 0086 int val = str.toInt(&ok); 0087 if (ok) { 0088 return QVariant(val); 0089 } 0090 0091 return QVariant(); 0092 } 0093 0094 QVariant toVariantDouble(const Exiv2::Value& value) 0095 { 0096 if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble 0097 || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) { 0098 return QVariant(static_cast<double>(value.toFloat())); 0099 } 0100 0101 QString str(toString(value)); 0102 bool ok = false; 0103 double val = str.toDouble(&ok); 0104 if (ok) { 0105 return QVariant(val); 0106 } 0107 0108 return QVariant(); 0109 } 0110 0111 QVariant toVariantString(const Exiv2::Value& value) 0112 { 0113 QString str = toString(value); 0114 if (!str.isEmpty()) { 0115 return QVariant(str); 0116 } 0117 0118 return QVariant(); 0119 } 0120 0121 QVariant toVariant(const Exiv2::Value& value, QVariant::Type type) { 0122 if (value.count() == 0) { 0123 return QVariant(); 0124 } 0125 switch (type) { 0126 case QVariant::Int: 0127 return toVariantLong(value); 0128 0129 case QVariant::DateTime: 0130 return toVariantDateTime(value); 0131 0132 case QVariant::Double: 0133 return toVariantDouble(value); 0134 0135 case QVariant::String: 0136 default: 0137 return toVariantString(value); 0138 } 0139 } 0140 } 0141 0142 QStringList Exiv2Extractor::mimetypes() const 0143 { 0144 return supportedMimeTypes; 0145 } 0146 0147 void Exiv2Extractor::extract(ExtractionResult* result) 0148 { 0149 QByteArray arr = result->inputUrl().toUtf8(); 0150 std::string fileString(arr.data(), arr.length()); 0151 0152 #if EXIV2_TEST_VERSION(0, 28, 0) 0153 Exiv2::Image::UniquePtr image; 0154 #else 0155 Exiv2::Image::AutoPtr image; 0156 #endif 0157 try { 0158 image = Exiv2::ImageFactory::open(fileString); 0159 } catch (const std::exception&) { 0160 return; 0161 } 0162 if (!image.get()) { 0163 return; 0164 } 0165 0166 try { 0167 image->readMetadata(); 0168 } catch (const std::exception&) { 0169 return; 0170 } 0171 result->addType(Type::Image); 0172 0173 if (!(result->inputFlags() & ExtractionResult::ExtractMetaData)) { 0174 return; 0175 } 0176 0177 if (image->pixelHeight()) { 0178 result->add(Property::Height, image->pixelHeight()); 0179 } 0180 0181 if (image->pixelWidth()) { 0182 result->add(Property::Width, image->pixelWidth()); 0183 } 0184 0185 std::string comment = image->comment(); 0186 if (!comment.empty()) { 0187 result->add(Property::Comment, QString::fromUtf8(comment.c_str(), comment.length())); 0188 } 0189 0190 const Exiv2::ExifData& data = image->exifData(); 0191 0192 add(result, data, Property::Manufacturer, "Exif.Image.Make", QVariant::String); 0193 add(result, data, Property::Model, "Exif.Image.Model", QVariant::String); 0194 add(result, data, Property::Description, "Exif.Image.ImageDescription", QVariant::String); 0195 add(result, data, Property::Artist, "Exif.Image.Artist", QVariant::String); 0196 add(result, data, Property::Copyright, "Exif.Image.Copyright", QVariant::String); 0197 add(result, data, Property::Generator, "Exif.Image.Software", QVariant::String); 0198 add(result, data, Property::ImageDateTime, "Exif.Image.DateTime", QVariant::DateTime); 0199 add(result, data, Property::ImageOrientation, "Exif.Image.Orientation", QVariant::Int); 0200 add(result, data, Property::PhotoFlash, "Exif.Photo.Flash", QVariant::Int); 0201 add(result, data, Property::PhotoPixelXDimension, "Exif.Photo.PixelXDimension", QVariant::Int); 0202 add(result, data, Property::PhotoPixelYDimension, "Exif.Photo.PixelYDimension", QVariant::Int); 0203 add(result, data, Property::PhotoDateTimeOriginal, "Exif.Photo.DateTimeOriginal", QVariant::DateTime); 0204 add(result, data, Property::PhotoFocalLength, "Exif.Photo.FocalLength", QVariant::Double); 0205 add(result, data, Property::PhotoFocalLengthIn35mmFilm, "Exif.Photo.FocalLengthIn35mmFilm", QVariant::Double); 0206 add(result, data, Property::PhotoExposureTime, "Exif.Photo.ExposureTime", QVariant::Double); 0207 add(result, data, Property::PhotoExposureBiasValue, "Exif.Photo.ExposureBiasValue", QVariant::Double); 0208 add(result, data, Property::PhotoFNumber, "Exif.Photo.FNumber", QVariant::Double); 0209 add(result, data, Property::PhotoApertureValue, "Exif.Photo.ApertureValue", QVariant::Double); 0210 add(result, data, Property::PhotoWhiteBalance, "Exif.Photo.WhiteBalance", QVariant::Int); 0211 add(result, data, Property::PhotoMeteringMode, "Exif.Photo.MeteringMode", QVariant::Int); 0212 add(result, data, Property::PhotoISOSpeedRatings, "Exif.Photo.ISOSpeedRatings", QVariant::Int); 0213 add(result, data, Property::PhotoSaturation, "Exif.Photo.Saturation", QVariant::Int); 0214 add(result, data, Property::PhotoSharpness, "Exif.Photo.Sharpness", QVariant::Int); 0215 0216 double latitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLatitude"); 0217 double longitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLongitude"); 0218 double altitude = fetchGpsAltitude(data); 0219 0220 QByteArray latRef = fetchByteArray(data, "Exif.GPSInfo.GPSLatitudeRef"); 0221 if (!latRef.isEmpty() && latRef[0] == 'S') { 0222 latitude *= -1; 0223 } 0224 0225 QByteArray longRef = fetchByteArray(data, "Exif.GPSInfo.GPSLongitudeRef"); 0226 if (!longRef.isEmpty() && longRef[0] == 'W') { 0227 longitude *= -1; 0228 } 0229 0230 if (!std::isnan(latitude)) { 0231 result->add(Property::PhotoGpsLatitude, latitude); 0232 } 0233 0234 if (!std::isnan(longitude)) { 0235 result->add(Property::PhotoGpsLongitude, longitude); 0236 } 0237 0238 if (!std::isnan(altitude)) { 0239 result->add(Property::PhotoGpsAltitude, altitude); 0240 } 0241 } 0242 0243 void Exiv2Extractor::add(ExtractionResult* result, const Exiv2::ExifData& data, 0244 Property::Property prop, const char* name, 0245 QVariant::Type type) 0246 { 0247 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); 0248 if (it != data.end()) { 0249 QVariant value = toVariant(it->value(), type); 0250 if (!value.isNull()) { 0251 result->add(prop, value); 0252 } 0253 } 0254 } 0255 0256 double Exiv2Extractor::fetchGpsDouble(const Exiv2::ExifData& data, const char* name) 0257 { 0258 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); 0259 if (it != data.end() && it->count() == 3) { 0260 double n = 0.0; 0261 double d = 0.0; 0262 0263 n = (*it).toRational(0).first; 0264 d = (*it).toRational(0).second; 0265 0266 if (d == 0.0) { 0267 return std::numeric_limits<double>::quiet_NaN(); 0268 } 0269 0270 double deg = n / d; 0271 0272 n = (*it).toRational(1).first; 0273 d = (*it).toRational(1).second; 0274 0275 if (d == 0.0) { 0276 return deg; 0277 } 0278 0279 double min = n / d; 0280 if (min != -1.0) { 0281 deg += min / 60.0; 0282 } 0283 0284 n = (*it).toRational(2).first; 0285 d = (*it).toRational(2).second; 0286 0287 if (d == 0.0) { 0288 return deg; 0289 } 0290 0291 double sec = n / d; 0292 if (sec != -1.0) { 0293 deg += sec / 3600.0; 0294 } 0295 0296 return deg; 0297 } 0298 0299 return std::numeric_limits<double>::quiet_NaN(); 0300 } 0301 0302 double Exiv2Extractor::fetchGpsAltitude(const Exiv2::ExifData& data) 0303 { 0304 double alt = std::numeric_limits<double>::quiet_NaN(); 0305 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude")); 0306 if (it != data.end() && it->count() > 0 && 0307 (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) { 0308 auto ratio = it->value().toRational(); 0309 if (ratio.second == 0) { 0310 return alt; 0311 } 0312 it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef")); 0313 if (it != data.end() && it->count() > 0 && 0314 (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) { 0315 #if EXIV2_TEST_VERSION(0,28,0) 0316 auto altRef = it->value().toInt64(); 0317 #else 0318 auto altRef = it->value().toLong(); 0319 #endif 0320 if (altRef) { 0321 alt = -1.0 * ratio.first / ratio.second; 0322 } else { 0323 alt = 1.0 * ratio.first / ratio.second; 0324 } 0325 } 0326 } 0327 return alt; 0328 } 0329 0330 QByteArray Exiv2Extractor::fetchByteArray(const Exiv2::ExifData& data, const char* name) 0331 { 0332 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name)); 0333 if (it != data.end() && it->count() > 0) { 0334 std::string str = it->value().toString(); 0335 return QByteArray(str.c_str(), str.size()); 0336 } 0337 0338 return QByteArray(); 0339 } 0340 0341 #include "moc_exiv2extractor.cpp"