8#include "exiv2extractor.h"
9#include "kfilemetadata_debug.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 Exiv2::enableBMFF(
true);
37 QStringLiteral(
"image/bmp"),
38 QStringLiteral(
"image/gif"),
39 QStringLiteral(
"image/jp2"),
40 QStringLiteral(
"image/jpeg"),
41 QStringLiteral(
"image/pgf"),
42 QStringLiteral(
"image/png"),
43 QStringLiteral(
"image/tiff"),
44 QStringLiteral(
"image/webp"),
46 QStringLiteral(
"image/avif"),
47 QStringLiteral(
"image/heif"),
48 QStringLiteral(
"image/jxl"),
49 QStringLiteral(
"image/x-canon-cr3"),
51 QStringLiteral(
"image/x-exv"),
52 QStringLiteral(
"image/x-canon-cr2"),
53 QStringLiteral(
"image/x-canon-crw"),
54 QStringLiteral(
"image/x-fuji-raf"),
55 QStringLiteral(
"image/x-minolta-mrw"),
56 QStringLiteral(
"image/x-nikon-nef"),
57 QStringLiteral(
"image/x-olympus-orf"),
58 QStringLiteral(
"image/x-panasonic-rw2"),
59 QStringLiteral(
"image/x-pentax-pef"),
60 QStringLiteral(
"image/x-photoshop"),
61 QStringLiteral(
"image/x-samsung-srw"),
62 QStringLiteral(
"image/x-tga"),
67 const std::string str = value.toString();
71QVariant toVariantDateTime(
const Exiv2::Value& value)
73 if (value.typeId() == Exiv2::asciiString) {
85QVariant toVariantLong(
const Exiv2::Value& value)
87 if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) {
88#if EXIV2_TEST_VERSION(0,28,0)
89 qlonglong val = value.toInt64();
91 qlonglong val = value.toLong();
98 int val = str.toInt(&ok);
106QVariant toVariantDouble(
const Exiv2::Value& value)
108 if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble
109 || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) {
110 return QVariant(
static_cast<double>(value.toFloat()));
115 double val = str.toDouble(&ok);
123QVariant toVariantString(
const Exiv2::Value& value)
134 if (value.count() == 0) {
139 return toVariantLong(value);
142 return toVariantDateTime(value);
145 return toVariantDouble(value);
149 return toVariantString(value);
156 return supportedMimeTypes;
161 using namespace std::string_literals;
164 std::string fileString(arr.
data(), arr.
length());
166#if EXIV2_TEST_VERSION(0, 28, 0)
167 Exiv2::Image::UniquePtr image;
169 Exiv2::Image::AutoPtr image;
172 image = Exiv2::ImageFactory::open(fileString);
173 }
catch (
const std::exception&) {
181 image->readMetadata();
182 }
catch (
const std::exception&) {
187 if (!(result->
inputFlags() & ExtractionResult::ExtractMetaData)) {
191 if (image->pixelHeight()) {
192 result->
add(Property::Height, image->pixelHeight());
195 if (image->pixelWidth()) {
196 result->
add(Property::Width, image->pixelWidth());
199 std::string comment = image->comment();
200 if (!comment.empty()) {
204 const Exiv2::ExifData& data = image->exifData();
206 using EK = Exiv2::ExifKey;
214 add(result, data, Property::ImageOrientation, EK{
"Exif.Image.Orientation"s},
QMetaType::Int);
215 add(result, data, Property::PhotoFlash, EK{
"Exif.Photo.Flash"s},
QMetaType::Int);
216 add(result, data, Property::PhotoPixelXDimension, EK{
"Exif.Photo.PixelXDimension"s},
QMetaType::Int);
217 add(result, data, Property::PhotoPixelYDimension, EK{
"Exif.Photo.PixelYDimension"s},
QMetaType::Int);
219 add(result, data, Property::PhotoFocalLength, EK{
"Exif.Photo.FocalLength"s},
QMetaType::Double);
220 add(result, data, Property::PhotoFocalLengthIn35mmFilm, EK{
"Exif.Photo.FocalLengthIn35mmFilm"s},
QMetaType::Double);
221 add(result, data, Property::PhotoExposureTime, EK{
"Exif.Photo.ExposureTime"s},
QMetaType::Double);
222 add(result, data, Property::PhotoExposureBiasValue, EK{
"Exif.Photo.ExposureBiasValue"s},
QMetaType::Double);
224 add(result, data, Property::PhotoApertureValue, EK{
"Exif.Photo.ApertureValue"s},
QMetaType::Double);
225 add(result, data, Property::PhotoWhiteBalance, EK{
"Exif.Photo.WhiteBalance"s},
QMetaType::Int);
226 add(result, data, Property::PhotoMeteringMode, EK{
"Exif.Photo.MeteringMode"s},
QMetaType::Int);
227 add(result, data, Property::PhotoISOSpeedRatings, EK{
"Exif.Photo.ISOSpeedRatings"s},
QMetaType::Int);
228 add(result, data, Property::PhotoSaturation, EK{
"Exif.Photo.Saturation"s},
QMetaType::Int);
229 add(result, data, Property::PhotoSharpness, EK{
"Exif.Photo.Sharpness"s},
QMetaType::Int);
233 double latitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLatitude"s});
234 double longitude = fetchGpsDouble(data, EK{
"Exif.GPSInfo.GPSLongitude"s});
235 double altitude = fetchGpsAltitude(data);
237 QByteArray latRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLatitudeRef"s});
238 if (!latRef.
isEmpty() && latRef[0] ==
'S') {
242 QByteArray longRef = fetchByteArray(data, EK{
"Exif.GPSInfo.GPSLongitudeRef"s});
243 if (!longRef.
isEmpty() && longRef[0] ==
'W') {
247 if (!std::isnan(latitude)) {
248 result->
add(Property::PhotoGpsLatitude, latitude);
251 if (!std::isnan(longitude)) {
252 result->
add(Property::PhotoGpsLongitude, longitude);
255 if (!std::isnan(altitude)) {
256 result->
add(Property::PhotoGpsAltitude, altitude);
259 const Exiv2::XmpData& xmpData = image->xmpData();
260 for (
const auto& entry : xmpData) {
261 if (entry.groupName() !=
"dc"s) {
265 std::map<std::string, Property::Property> propMap = {
266 {
"subject"s, Property::Subject },
267 {
"title"s, Property::Title},
268 {
"description"s, Property::Description},
270 if (
auto type = propMap.find(entry.tagName()); type != propMap.end()) {
271 auto xmpType = Exiv2::XmpValue::xmpArrayType(entry.value().typeId());
272 size_t limit = ((xmpType == Exiv2::XmpValue::xaBag) || (xmpType == Exiv2::XmpValue::xaSeq)) ? entry.count() : 1;
273 for (
size_t i = 0; i < limit; i++) {
275 if (!value.isEmpty()) {
276 result->
add(
type->second, value);
288 Exiv2::ExifData::const_iterator it = data.findKey(key);
289 if (it != data.end()) {
290 QVariant value = toVariant(it->value(), type);
292 result->
add(prop, value);
297double fetchGpsDouble(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
299 Exiv2::ExifData::const_iterator it = data.findKey(key);
300 if (it != data.end() && it->count() == 3) {
304 n = (*it).toRational(0).first;
305 d = (*it).toRational(0).second;
308 return std::numeric_limits<double>::quiet_NaN();
313 n = (*it).toRational(1).first;
314 d = (*it).toRational(1).second;
325 n = (*it).toRational(2).first;
326 d = (*it).toRational(2).second;
340 return std::numeric_limits<double>::quiet_NaN();
343double fetchGpsAltitude(
const Exiv2::ExifData& data)
345 using namespace std::string_literals;
347 double alt = std::numeric_limits<double>::quiet_NaN();
348 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitude"s));
349 if (it != data.end() && it->count() > 0 &&
350 (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) {
351 auto ratio = it->value().toRational();
352 if (ratio.second == 0) {
355 it = data.findKey(Exiv2::ExifKey(
"Exif.GPSInfo.GPSAltitudeRef"s));
356 if (it != data.end() && it->count() > 0 &&
357 (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) {
358#if EXIV2_TEST_VERSION(0,28,0)
359 auto altRef = it->value().toInt64();
361 auto altRef = it->value().toLong();
364 alt = -1.0 * ratio.first / ratio.second;
366 alt = 1.0 * ratio.first / ratio.second;
372QByteArray fetchByteArray(
const Exiv2::ExifData& data,
const Exiv2::ExifKey& key)
374 Exiv2::ExifData::const_iterator it = data.findKey(key);
375 if (it != data.end() && it->count() > 0) {
376 std::string str = it->value().toString();
384#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 fromLatin1(QByteArrayView str)
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