8#include "datetimeparser_p.h"
9#include "exiv2extractor.h"
10#include "kfilemetadata_debug.h"
13#include <exiv2/exiv2.hpp>
22double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key);
23double fetchGpsAltitude(
const Exiv2::ExifData& data);
24QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key);
27Exiv2Extractor::Exiv2Extractor(
QObject* parent)
31#if !(EXIV2_TEST_VERSION(0, 28, 3))
33 Exiv2::enableBMFF(
true);
41 QStringLiteral(
"image/bmp"),
42 QStringLiteral(
"image/gif"),
43 QStringLiteral(
"image/jp2"),
44 QStringLiteral(
"image/jpeg"),
45 QStringLiteral(
"image/pgf"),
46 QStringLiteral(
"image/png"),
47 QStringLiteral(
"image/tiff"),
48 QStringLiteral(
"image/webp"),
50 QStringLiteral(
"image/avif"),
51 QStringLiteral(
"image/heif"),
52 QStringLiteral(
"image/jxl"),
53 QStringLiteral(
"image/x-canon-cr3"),
55 QStringLiteral(
"image/x-exv"),
56 QStringLiteral(
"image/x-canon-cr2"),
57 QStringLiteral(
"image/x-canon-crw"),
58 QStringLiteral(
"image/x-fuji-raf"),
59 QStringLiteral(
"image/x-minolta-mrw"),
60 QStringLiteral(
"image/x-nikon-nef"),
61 QStringLiteral(
"image/x-olympus-orf"),
62 QStringLiteral(
"image/x-panasonic-rw2"),
63 QStringLiteral(
"image/x-pentax-pef"),
64 QStringLiteral(
"image/x-photoshop"),
65 QStringLiteral(
"image/x-samsung-srw"),
66 QStringLiteral(
"image/x-tga"),
71 const std::string str = value.toString();
75QVariant toVariantDateTime(
const Exiv2::Value& value)
77 if (value.typeId() == Exiv2::asciiString) {
89QVariant toVariantLong(
const Exiv2::Value& value)
91 if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) {
92#if EXIV2_TEST_VERSION(0,28,0)
93 qlonglong val = value.toInt64();
95 qlonglong val = value.toLong();
102 int val = str.toInt(&ok);
110QVariant toVariantDouble(
const Exiv2::Value& value)
112 if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble
113 || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) {
114 return QVariant(
static_cast<double>(value.toFloat()));
119 double val = str.toDouble(&ok);
127QVariant toVariantString(
const Exiv2::Value& value)
138 if (value.count() == 0) {
143 return toVariantLong(value);
146 return toVariantDateTime(value);
149 return toVariantDouble(value);
153 return toVariantString(value);
160 return supportedMimeTypes;
165 using namespace std::string_literals;
168 std::string fileString(arr.
data(), arr.
length());
170#if EXIV2_TEST_VERSION(0, 28, 0)
171 Exiv2::Image::UniquePtr image;
173 Exiv2::Image::AutoPtr image;
176 image = Exiv2::ImageFactory::open(fileString);
177 }
catch (
const std::exception&) {
185 image->readMetadata();
186 }
catch (
const std::exception&) {
191 if (!(result->
inputFlags() & ExtractionResult::ExtractMetaData)) {
195 if (image->pixelHeight()) {
196 result->
add(Property::Height, image->pixelHeight());
199 if (image->pixelWidth()) {
200 result->
add(Property::Width, image->pixelWidth());
203 std::string comment = image->comment();
204 if (!comment.empty()) {
208 const Exiv2::ExifData& data = image->exifData();
210 using EK = Exiv2::ExifKey;
218 add(result, data, Property::ImageOrientation, EK{
"Exif.Image.Orientation"s},
QMetaType::Int);
219 add(result, data, Property::PhotoFlash, EK{
"Exif.Photo.Flash"s},
QMetaType::Int);
220 add(result, data, Property::PhotoPixelXDimension, EK{
"Exif.Photo.PixelXDimension"s},
QMetaType::Int);
221 add(result, data, Property::PhotoPixelYDimension, EK{
"Exif.Photo.PixelYDimension"s},
QMetaType::Int);
223 add(result, data, Property::PhotoFocalLength, EK{
"Exif.Photo.FocalLength"s},
QMetaType::Double);
224 add(result, data, Property::PhotoFocalLengthIn35mmFilm, EK{
"Exif.Photo.FocalLengthIn35mmFilm"s},
QMetaType::Double);
225 add(result, data, Property::PhotoExposureTime, EK{
"Exif.Photo.ExposureTime"s},
QMetaType::Double);
226 add(result, data, Property::PhotoExposureBiasValue, EK{
"Exif.Photo.ExposureBiasValue"s},
QMetaType::Double);
228 add(result, data, Property::PhotoApertureValue, EK{
"Exif.Photo.ApertureValue"s},
QMetaType::Double);
229 add(result, data, Property::PhotoWhiteBalance, EK{
"Exif.Photo.WhiteBalance"s},
QMetaType::Int);
230 add(result, data, Property::PhotoMeteringMode, EK{
"Exif.Photo.MeteringMode"s},
QMetaType::Int);
231 add(result, data, Property::PhotoISOSpeedRatings, EK{
"Exif.Photo.ISOSpeedRatings"s},
QMetaType::Int);
232 add(result, data, Property::PhotoSaturation, EK{
"Exif.Photo.Saturation"s},
QMetaType::Int);
233 add(result, data, Property::PhotoSharpness, EK{
"Exif.Photo.Sharpness"s},
QMetaType::Int);
237 double latitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLatitude"s});
238 double longitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLongitude"s});
239 double altitude = fetchGpsAltitude(data);
241 QByteArray latRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLatitudeRef"s});
242 if (!latRef.
isEmpty() && latRef[0] ==
'S') {
246 QByteArray longRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLongitudeRef"s});
247 if (!longRef.
isEmpty() && longRef[0] ==
'W') {
251 if (!std::isnan(latitude)) {
252 result->
add(Property::PhotoGpsLatitude, latitude);
255 if (!std::isnan(longitude)) {
256 result->
add(Property::PhotoGpsLongitude, longitude);
259 if (!std::isnan(altitude)) {
260 result->
add(Property::PhotoGpsAltitude, altitude);
263 const Exiv2::XmpData& xmpData = image->xmpData();
264 for (
const auto& entry : xmpData) {
265 if (entry.groupName() !=
"dc"s) {
269 std::map<std::string, Property::Property> propMap = {
270 {
"subject"s, Property::Subject },
271 {
"title"s, Property::Title},
272 {
"description"s, Property::Description},
274 if (
auto type = propMap.find(entry.tagName()); type != propMap.end()) {
275 auto xmpType = Exiv2::XmpValue::xmpArrayType(entry.value().typeId());
276 size_t limit = ((xmpType == Exiv2::XmpValue::xaBag) || (xmpType == Exiv2::XmpValue::xaSeq)) ? entry.count() : 1;
277 for (
size_t i = 0; i < limit; i++) {
279 if (!value.isEmpty()) {
280 result->
add(
type->second, value);
292 Exiv2::ExifData::const_iterator it = data.findKey(key);
293 if (it != data.end()) {
294 QVariant value = toVariant(it->value(), type);
296 result->
add(prop, value);
301double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
303 Exiv2::ExifData::const_iterator it = data.findKey(key);
304 if (it != data.end() && it->count() == 3) {
308 n = (*it).toRational(0).first;
309 d = (*it).toRational(0).second;
312 return std::numeric_limits<double>::quiet_NaN();
317 n = (*it).toRational(1).first;
318 d = (*it).toRational(1).second;
329 n = (*it).toRational(2).first;
330 d = (*it).toRational(2).second;
344 return std::numeric_limits<double>::quiet_NaN();
347double fetchGpsAltitude(
const Exiv2::ExifData& data)
349 using namespace std::string_literals;
351 double alt = std::numeric_limits<double>::quiet_NaN();
352 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitude"s));
353 if (it != data.end() && it->count() > 0 &&
354 (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) {
355 auto ratio = it->value().toRational();
356 if (ratio.second == 0) {
359 it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitudeRef"s));
360 if (it != data.end() && it->count() > 0 &&
361 (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) {
362#if EXIV2_TEST_VERSION(0,28,0)
363 auto altRef = it->value().toInt64();
365 auto altRef = it->value().toLong();
368 alt = -1.0 * ratio.first / ratio.second;
370 alt = 1.0 * ratio.first / ratio.second;
376QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
378 Exiv2::ExifData::const_iterator it = data.findKey(key);
379 if (it != data.end() && it->count() > 0) {
380 std::string str = it->value().toString();
388#include "moc_exiv2extractor.cpp"
char * toString(const EngineQuery &query)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
bool isEmpty() const const
qsizetype length() const const
bool isValid() const const
void setTimeZone(const QTimeZone &toZone)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toUtf8() const const
QTimeZone fromSecondsAheadOfUtc(int offset)
bool isNull() const const