8#include "datetimeparser_p.h"
9#include "exiv2extractor.h"
12#include <exiv2/exiv2.hpp>
21double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key);
22double fetchGpsAltitude(
const Exiv2::ExifData& data);
23QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key);
26Exiv2Extractor::Exiv2Extractor(
QObject* parent)
30#if !(EXIV2_TEST_VERSION(0, 28, 3))
32 Exiv2::enableBMFF(
true);
40 QStringLiteral(
"image/bmp"),
41 QStringLiteral(
"image/gif"),
42 QStringLiteral(
"image/jp2"),
43 QStringLiteral(
"image/jpeg"),
44 QStringLiteral(
"image/pgf"),
45 QStringLiteral(
"image/png"),
46 QStringLiteral(
"image/tiff"),
47 QStringLiteral(
"image/webp"),
49 QStringLiteral(
"image/avif"),
50 QStringLiteral(
"image/heif"),
51 QStringLiteral(
"image/jxl"),
52 QStringLiteral(
"image/x-canon-cr3"),
54 QStringLiteral(
"image/x-exv"),
55 QStringLiteral(
"image/x-canon-cr2"),
56 QStringLiteral(
"image/x-canon-crw"),
57 QStringLiteral(
"image/x-fuji-raf"),
58 QStringLiteral(
"image/x-minolta-mrw"),
59 QStringLiteral(
"image/x-nikon-nef"),
60 QStringLiteral(
"image/x-olympus-orf"),
61 QStringLiteral(
"image/x-panasonic-rw2"),
62 QStringLiteral(
"image/x-pentax-pef"),
63 QStringLiteral(
"image/x-photoshop"),
64 QStringLiteral(
"image/x-samsung-srw"),
65 QStringLiteral(
"image/x-tga"),
70 const std::string str = value.toString();
74QVariant toVariantDateTime(
const Exiv2::Value& value)
76 if (value.typeId() == Exiv2::asciiString) {
88QVariant toVariantLong(
const Exiv2::Value& value)
90 if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) {
91#if EXIV2_TEST_VERSION(0,28,0)
92 qlonglong val = value.toInt64();
94 qlonglong val = value.toLong();
101 int val = str.toInt(&ok);
109QVariant toVariantDouble(
const Exiv2::Value& value)
111 if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble
112 || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) {
113 return QVariant(
static_cast<double>(value.toFloat()));
118 double val = str.toDouble(&ok);
126QVariant toVariantString(
const Exiv2::Value& value)
137 if (value.count() == 0) {
142 return toVariantLong(value);
145 return toVariantDateTime(value);
148 return toVariantDouble(value);
152 return toVariantString(value);
159 return supportedMimeTypes;
164 using namespace std::string_literals;
167 std::string fileString(arr.
data(), arr.
length());
169#if EXIV2_TEST_VERSION(0, 28, 0)
170 Exiv2::Image::UniquePtr image;
172 Exiv2::Image::AutoPtr image;
175 image = Exiv2::ImageFactory::open(fileString);
176 }
catch (
const std::exception&) {
184 image->readMetadata();
185 }
catch (
const std::exception&) {
190 if (!(result->
inputFlags() & ExtractionResult::ExtractMetaData)) {
194 if (image->pixelHeight()) {
195 result->
add(Property::Height, image->pixelHeight());
198 if (image->pixelWidth()) {
199 result->
add(Property::Width, image->pixelWidth());
202 std::string comment = image->comment();
203 if (!comment.empty()) {
207 const Exiv2::ExifData& data = image->exifData();
209 using EK = Exiv2::ExifKey;
217 add(result, data, Property::ImageOrientation, EK{
"Exif.Image.Orientation"s},
QMetaType::Int);
218 add(result, data, Property::PhotoFlash, EK{
"Exif.Photo.Flash"s},
QMetaType::Int);
219 add(result, data, Property::PhotoPixelXDimension, EK{
"Exif.Photo.PixelXDimension"s},
QMetaType::Int);
220 add(result, data, Property::PhotoPixelYDimension, EK{
"Exif.Photo.PixelYDimension"s},
QMetaType::Int);
222 add(result, data, Property::PhotoFocalLength, EK{
"Exif.Photo.FocalLength"s},
QMetaType::Double);
223 add(result, data, Property::PhotoFocalLengthIn35mmFilm, EK{
"Exif.Photo.FocalLengthIn35mmFilm"s},
QMetaType::Double);
224 add(result, data, Property::PhotoExposureTime, EK{
"Exif.Photo.ExposureTime"s},
QMetaType::Double);
225 add(result, data, Property::PhotoExposureBiasValue, EK{
"Exif.Photo.ExposureBiasValue"s},
QMetaType::Double);
227 add(result, data, Property::PhotoApertureValue, EK{
"Exif.Photo.ApertureValue"s},
QMetaType::Double);
228 add(result, data, Property::PhotoWhiteBalance, EK{
"Exif.Photo.WhiteBalance"s},
QMetaType::Int);
229 add(result, data, Property::PhotoMeteringMode, EK{
"Exif.Photo.MeteringMode"s},
QMetaType::Int);
230 add(result, data, Property::PhotoISOSpeedRatings, EK{
"Exif.Photo.ISOSpeedRatings"s},
QMetaType::Int);
231 add(result, data, Property::PhotoSaturation, EK{
"Exif.Photo.Saturation"s},
QMetaType::Int);
232 add(result, data, Property::PhotoSharpness, EK{
"Exif.Photo.Sharpness"s},
QMetaType::Int);
236 double latitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLatitude"s});
237 double longitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLongitude"s});
238 double altitude = fetchGpsAltitude(data);
240 QByteArray latRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLatitudeRef"s});
241 if (!latRef.
isEmpty() && latRef[0] ==
'S') {
245 QByteArray longRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLongitudeRef"s});
246 if (!longRef.
isEmpty() && longRef[0] ==
'W') {
250 if (!std::isnan(latitude)) {
251 result->
add(Property::PhotoGpsLatitude, latitude);
254 if (!std::isnan(longitude)) {
255 result->
add(Property::PhotoGpsLongitude, longitude);
258 if (!std::isnan(altitude)) {
259 result->
add(Property::PhotoGpsAltitude, altitude);
262 const Exiv2::XmpData& xmpData = image->xmpData();
263 for (
const auto& entry : xmpData) {
264 if (entry.groupName() !=
"dc"s) {
268 std::map<std::string, Property::Property> propMap = {
269 {
"subject"s, Property::Subject },
270 {
"title"s, Property::Title},
271 {
"description"s, Property::Description},
273 if (
auto type = propMap.find(entry.tagName()); type != propMap.end()) {
274 auto xmpType = Exiv2::XmpValue::xmpArrayType(entry.value().typeId());
275 size_t limit = ((xmpType == Exiv2::XmpValue::xaBag) || (xmpType == Exiv2::XmpValue::xaSeq)) ? entry.count() : 1;
276 for (
size_t i = 0; i < limit; i++) {
278 if (!value.isEmpty()) {
279 result->
add(
type->second, value);
291 Exiv2::ExifData::const_iterator it = data.findKey(key);
292 if (it != data.end()) {
293 QVariant value = toVariant(it->value(), type);
295 result->
add(prop, value);
300double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
302 Exiv2::ExifData::const_iterator it = data.findKey(key);
303 if (it != data.end() && it->count() == 3) {
307 n = (*it).toRational(0).first;
308 d = (*it).toRational(0).second;
311 return std::numeric_limits<double>::quiet_NaN();
316 n = (*it).toRational(1).first;
317 d = (*it).toRational(1).second;
328 n = (*it).toRational(2).first;
329 d = (*it).toRational(2).second;
343 return std::numeric_limits<double>::quiet_NaN();
346double fetchGpsAltitude(
const Exiv2::ExifData& data)
348 using namespace std::string_literals;
350 double alt = std::numeric_limits<double>::quiet_NaN();
351 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitude"s));
352 if (it != data.end() && it->count() > 0 &&
353 (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) {
354 auto ratio = it->value().toRational();
355 if (ratio.second == 0) {
358 it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitudeRef"s));
359 if (it != data.end() && it->count() > 0 &&
360 (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) {
361#if EXIV2_TEST_VERSION(0,28,0)
362 auto altRef = it->value().toInt64();
364 auto altRef = it->value().toLong();
367 alt = -1.0 * ratio.first / ratio.second;
369 alt = 1.0 * ratio.first / ratio.second;
375QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
377 Exiv2::ExifData::const_iterator it = data.findKey(key);
378 if (it != data.end() && it->count() > 0) {
379 std::string str = it->value().toString();
387#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