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 Exiv2::enableBMFF(
true);
38 QStringLiteral(
"image/bmp"),
39 QStringLiteral(
"image/gif"),
40 QStringLiteral(
"image/jp2"),
41 QStringLiteral(
"image/jpeg"),
42 QStringLiteral(
"image/pgf"),
43 QStringLiteral(
"image/png"),
44 QStringLiteral(
"image/tiff"),
45 QStringLiteral(
"image/webp"),
47 QStringLiteral(
"image/avif"),
48 QStringLiteral(
"image/heif"),
49 QStringLiteral(
"image/jxl"),
50 QStringLiteral(
"image/x-canon-cr3"),
52 QStringLiteral(
"image/x-exv"),
53 QStringLiteral(
"image/x-canon-cr2"),
54 QStringLiteral(
"image/x-canon-crw"),
55 QStringLiteral(
"image/x-fuji-raf"),
56 QStringLiteral(
"image/x-minolta-mrw"),
57 QStringLiteral(
"image/x-nikon-nef"),
58 QStringLiteral(
"image/x-olympus-orf"),
59 QStringLiteral(
"image/x-panasonic-rw2"),
60 QStringLiteral(
"image/x-pentax-pef"),
61 QStringLiteral(
"image/x-photoshop"),
62 QStringLiteral(
"image/x-samsung-srw"),
63 QStringLiteral(
"image/x-tga"),
68 const std::string str = value.toString();
72QVariant toVariantDateTime(
const Exiv2::Value& value)
74 if (value.typeId() == Exiv2::asciiString) {
86QVariant toVariantLong(
const Exiv2::Value& value)
88 if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) {
89#if EXIV2_TEST_VERSION(0,28,0)
90 qlonglong val = value.toInt64();
92 qlonglong val = value.toLong();
99 int val = str.toInt(&ok);
107QVariant toVariantDouble(
const Exiv2::Value& value)
109 if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble
110 || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) {
111 return QVariant(
static_cast<double>(value.toFloat()));
116 double val = str.toDouble(&ok);
124QVariant toVariantString(
const Exiv2::Value& value)
135 if (value.count() == 0) {
140 return toVariantLong(value);
143 return toVariantDateTime(value);
146 return toVariantDouble(value);
150 return toVariantString(value);
157 return supportedMimeTypes;
162 using namespace std::string_literals;
165 std::string fileString(arr.
data(), arr.
length());
167#if EXIV2_TEST_VERSION(0, 28, 0)
168 Exiv2::Image::UniquePtr image;
170 Exiv2::Image::AutoPtr image;
173 image = Exiv2::ImageFactory::open(fileString);
174 }
catch (
const std::exception&) {
182 image->readMetadata();
183 }
catch (
const std::exception&) {
188 if (!(result->
inputFlags() & ExtractionResult::ExtractMetaData)) {
192 if (image->pixelHeight()) {
193 result->
add(Property::Height, image->pixelHeight());
196 if (image->pixelWidth()) {
197 result->
add(Property::Width, image->pixelWidth());
200 std::string comment = image->comment();
201 if (!comment.empty()) {
205 const Exiv2::ExifData& data = image->exifData();
207 using EK = Exiv2::ExifKey;
215 add(result, data, Property::ImageOrientation, EK{
"Exif.Image.Orientation"s},
QMetaType::Int);
216 add(result, data, Property::PhotoFlash, EK{
"Exif.Photo.Flash"s},
QMetaType::Int);
217 add(result, data, Property::PhotoPixelXDimension, EK{
"Exif.Photo.PixelXDimension"s},
QMetaType::Int);
218 add(result, data, Property::PhotoPixelYDimension, EK{
"Exif.Photo.PixelYDimension"s},
QMetaType::Int);
220 add(result, data, Property::PhotoFocalLength, EK{
"Exif.Photo.FocalLength"s},
QMetaType::Double);
221 add(result, data, Property::PhotoFocalLengthIn35mmFilm, EK{
"Exif.Photo.FocalLengthIn35mmFilm"s},
QMetaType::Double);
222 add(result, data, Property::PhotoExposureTime, EK{
"Exif.Photo.ExposureTime"s},
QMetaType::Double);
223 add(result, data, Property::PhotoExposureBiasValue, EK{
"Exif.Photo.ExposureBiasValue"s},
QMetaType::Double);
225 add(result, data, Property::PhotoApertureValue, EK{
"Exif.Photo.ApertureValue"s},
QMetaType::Double);
226 add(result, data, Property::PhotoWhiteBalance, EK{
"Exif.Photo.WhiteBalance"s},
QMetaType::Int);
227 add(result, data, Property::PhotoMeteringMode, EK{
"Exif.Photo.MeteringMode"s},
QMetaType::Int);
228 add(result, data, Property::PhotoISOSpeedRatings, EK{
"Exif.Photo.ISOSpeedRatings"s},
QMetaType::Int);
229 add(result, data, Property::PhotoSaturation, EK{
"Exif.Photo.Saturation"s},
QMetaType::Int);
230 add(result, data, Property::PhotoSharpness, EK{
"Exif.Photo.Sharpness"s},
QMetaType::Int);
234 double latitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLatitude"s});
235 double longitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLongitude"s});
236 double altitude = fetchGpsAltitude(data);
238 QByteArray latRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLatitudeRef"s});
239 if (!latRef.
isEmpty() && latRef[0] ==
'S') {
243 QByteArray longRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLongitudeRef"s});
244 if (!longRef.
isEmpty() && longRef[0] ==
'W') {
248 if (!std::isnan(latitude)) {
249 result->
add(Property::PhotoGpsLatitude, latitude);
252 if (!std::isnan(longitude)) {
253 result->
add(Property::PhotoGpsLongitude, longitude);
256 if (!std::isnan(altitude)) {
257 result->
add(Property::PhotoGpsAltitude, altitude);
260 const Exiv2::XmpData& xmpData = image->xmpData();
261 for (
const auto& entry : xmpData) {
262 if (entry.groupName() !=
"dc"s) {
266 std::map<std::string, Property::Property> propMap = {
267 {
"subject"s, Property::Subject },
268 {
"title"s, Property::Title},
269 {
"description"s, Property::Description},
271 if (
auto type = propMap.find(entry.tagName()); type != propMap.end()) {
272 auto xmpType = Exiv2::XmpValue::xmpArrayType(entry.value().typeId());
273 size_t limit = ((xmpType == Exiv2::XmpValue::xaBag) || (xmpType == Exiv2::XmpValue::xaSeq)) ? entry.count() : 1;
274 for (
size_t i = 0; i < limit; i++) {
276 if (!value.isEmpty()) {
277 result->
add(
type->second, value);
289 Exiv2::ExifData::const_iterator it = data.findKey(key);
290 if (it != data.end()) {
291 QVariant value = toVariant(it->value(), type);
293 result->
add(prop, value);
298double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
300 Exiv2::ExifData::const_iterator it = data.findKey(key);
301 if (it != data.end() && it->count() == 3) {
305 n = (*it).toRational(0).first;
306 d = (*it).toRational(0).second;
309 return std::numeric_limits<double>::quiet_NaN();
314 n = (*it).toRational(1).first;
315 d = (*it).toRational(1).second;
326 n = (*it).toRational(2).first;
327 d = (*it).toRational(2).second;
341 return std::numeric_limits<double>::quiet_NaN();
344double fetchGpsAltitude(
const Exiv2::ExifData& data)
346 using namespace std::string_literals;
348 double alt = std::numeric_limits<double>::quiet_NaN();
349 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitude"s));
350 if (it != data.end() && it->count() > 0 &&
351 (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) {
352 auto ratio = it->value().toRational();
353 if (ratio.second == 0) {
356 it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitudeRef"s));
357 if (it != data.end() && it->count() > 0 &&
358 (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) {
359#if EXIV2_TEST_VERSION(0,28,0)
360 auto altRef = it->value().toInt64();
362 auto altRef = it->value().toLong();
365 alt = -1.0 * ratio.first / ratio.second;
367 alt = 1.0 * ratio.first / ratio.second;
373QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
375 Exiv2::ExifData::const_iterator it = data.findKey(key);
376 if (it != data.end() && it->count() > 0) {
377 std::string str = it->value().toString();
385#include "moc_exiv2extractor.cpp"
Type type(const QSqlDatabase &db)
char * toString(const EngineQuery &query)
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