File indexing completed on 2025-01-05 03:56:24

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-02-23
0007  * Description : item metadata interface - generic helpers
0008  *
0009  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2011      by Leif Huhn <leif at dkstat dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dmetadata.h"
0018 
0019 // Qt includes
0020 
0021 #include <QLocale>
0022 #include <QScopedPointer>
0023 #include <QRegularExpression>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "metaenginesettings.h"
0032 #include "digikam_version.h"
0033 #include "digikam_globals.h"
0034 #include "digikam_debug.h"
0035 
0036 namespace Digikam
0037 {
0038 
0039 QVariant DMetadata::fromExifOrXmp(const char* const exifTagName, const char* const xmpTagName) const
0040 {
0041     QVariant var;
0042 
0043     if (exifTagName)
0044     {
0045         var = getExifTagVariant(exifTagName, false);
0046 
0047         if (!var.isNull())
0048         {
0049             return var;
0050         }
0051     }
0052 
0053     if (xmpTagName)
0054     {
0055         var = getXmpTagVariant(xmpTagName);
0056 
0057         if (!var.isNull())
0058         {
0059             return var;
0060         }
0061     }
0062 
0063     return var;
0064 }
0065 
0066 QVariant DMetadata::fromIptcOrXmp(const char* const iptcTagName, const char* const xmpTagName) const
0067 {
0068     if (iptcTagName)
0069     {
0070         QString iptcValue = getIptcTagString(iptcTagName);
0071 
0072         if (!iptcValue.isNull())
0073         {
0074             return iptcValue;
0075         }
0076     }
0077 
0078     if (xmpTagName)
0079     {
0080         QVariant var = getXmpTagVariant(xmpTagName);
0081 
0082         if (!var.isNull())
0083         {
0084             return var;
0085         }
0086     }
0087 
0088 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0089 
0090     return QVariant(QMetaType(QMetaType::QString));
0091 
0092 #else
0093 
0094     return QVariant(QVariant::String);
0095 
0096 #endif
0097 
0098 }
0099 
0100 QVariant DMetadata::fromExifOrXmpList(const QStringList& tagList) const
0101 {
0102     QVariant var;
0103 
0104     Q_FOREACH (const QString& tagName, tagList)
0105     {
0106         if      (tagName.startsWith(QLatin1String("Exif")))
0107         {
0108             var = getExifTagVariant(tagName.toLatin1().constData(), false);
0109         }
0110         else if (tagName.startsWith(QLatin1String("Xmp")))
0111         {
0112             var = getXmpTagVariant(tagName.toLatin1().constData());
0113         }
0114 
0115         if (!var.isNull())
0116         {
0117             return var;
0118         }
0119     }
0120 
0121     return var;
0122 }
0123 
0124 QVariant DMetadata::getMetadataField(MetadataInfo::Field field) const
0125 {
0126     switch (field)
0127     {
0128         case MetadataInfo::Comment:
0129         {
0130             return getItemComments().value(QLatin1String("x-default")).caption;
0131         }
0132 
0133         case MetadataInfo::CommentJfif:
0134         {
0135             return getCommentsDecoded();
0136         }
0137 
0138         case MetadataInfo::CommentExif:
0139         {
0140             return getExifComment();
0141         }
0142 
0143         case MetadataInfo::CommentIptc:
0144         {
0145             return fromIptcOrXmp("Iptc.Application2.Caption", nullptr);
0146         }
0147 
0148         case MetadataInfo::Description:
0149         {
0150             QVariant var = fromXmpLangAlt("Xmp.dc.description");
0151 
0152             if (!var.isNull())
0153             {
0154                 return var;
0155             }
0156 
0157             var = fromXmpLangAlt("Xmp.tiff.ImageDescription");
0158 
0159             if (!var.isNull())
0160             {
0161                 return var;
0162             }
0163 
0164             return fromIptcEmulateLangAlt("Iptc.Application2.Caption");
0165         }
0166 
0167         case MetadataInfo::Headline:
0168         {
0169             return fromIptcOrXmp("Iptc.Application2.Headline", "Xmp.photoshop.Headline");
0170         }
0171 
0172         case MetadataInfo::Title:
0173         {
0174             QString str = getItemTitles()[QLatin1String("x-default")].caption;
0175 
0176             if (str.isEmpty())
0177             {
0178 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0179 
0180                 return QVariant(QMetaType(QMetaType::QVariantMap));
0181 
0182 #else
0183 
0184                 return QVariant(QVariant::Map);
0185 
0186 #endif
0187 
0188             }
0189 
0190             QMap<QString, QVariant> map;
0191             map[QLatin1String("x-default")] = str;
0192 
0193             return map;
0194         }
0195 
0196         case MetadataInfo::DescriptionWriter:
0197         {
0198             return fromIptcOrXmp("Iptc.Application2.Writer", "Xmp.photoshop.CaptionWriter");
0199         }
0200 
0201         case MetadataInfo::Keywords:
0202         {
0203             QStringList list;
0204             getItemTagsPath(list);
0205 
0206             return toStringListVariant(list);
0207         }
0208 
0209         case MetadataInfo::Faces:
0210         {
0211             QVariant var;
0212 
0213 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0214 
0215             // TODO: With Qt6.2.3, QMultiMap do not inherit of QMap as with Qt5 and QVariant do not handle QMultiMap.
0216             // We needs to find a way to support QVariant conversion from QMultiMap without to lost face entries,
0217             // or wait than Qt6 support QVariant(QMultiMap).
0218             qCWarning(DIGIKAM_METAENGINE_LOG) << "Faces multimap to variant conversion is not yet supported with Qt6!";
0219 
0220 #else
0221 
0222             QMultiMap<QString,QVariant> faceMap;
0223             getItemFacesMap(faceMap);
0224             var = QVariant(faceMap);
0225 
0226 #endif
0227 
0228             return var;
0229         }
0230 
0231         case MetadataInfo::Rating:
0232         {
0233             return getItemRating();
0234         }
0235 
0236         case MetadataInfo::CreationDate:
0237         {
0238             return getItemDateTime();
0239         }
0240 
0241         case MetadataInfo::DigitizationDate:
0242         {
0243             return getDigitizationDateTime(true);
0244         }
0245 
0246         case MetadataInfo::Orientation:
0247         {
0248             return (int)getItemOrientation();
0249         }
0250 
0251         case MetadataInfo::Make:
0252         {
0253             QStringList tagList;
0254             tagList << QLatin1String("Exif.Image.Make");
0255             tagList << QLatin1String("Exif.PanasonicRaw.Make");
0256             tagList << QLatin1String("Xmp.tiff.Make");
0257 
0258             QVariant var = fromExifOrXmpList(tagList);
0259 
0260             return QVariant(var.toString().trimmed());
0261         }
0262 
0263         case MetadataInfo::Model:
0264         {
0265             QStringList tagList;
0266             tagList << QLatin1String("Exif.Image.Model");
0267             tagList << QLatin1String("Exif.PanasonicRaw.Model");
0268             tagList << QLatin1String("Xmp.tiff.Model");
0269 
0270             QVariant var = fromExifOrXmpList(tagList);
0271 
0272             return QVariant(var.toString().trimmed());
0273         }
0274 
0275         case MetadataInfo::Lens:
0276         {
0277             return getLensDescription();
0278         }
0279 
0280         case MetadataInfo::Aperture:
0281         {
0282             QStringList tagList;
0283             tagList << QLatin1String("Exif.Photo.FNumber");
0284             tagList << QLatin1String("Exif.Image.FNumber");
0285             tagList << QLatin1String("Xmp.exif.FNumber");
0286 
0287             QVariant var = fromExifOrXmpList(tagList);
0288 
0289             if (var.isNull())
0290             {
0291                 var = fromExifOrXmp("Exif.Photo.ApertureValue", "Xmp.exif.ApertureValue");
0292 
0293                 if (!var.isNull())
0294                 {
0295                     var = apexApertureToFNumber(var.toDouble());
0296                 }
0297             }
0298 
0299             return var;
0300         }
0301 
0302         case MetadataInfo::FocalLength:
0303         {
0304             QStringList tagList;
0305             tagList << QLatin1String("Exif.Photo.FocalLength");
0306             tagList << QLatin1String("Exif.Image.FocalLength");
0307             tagList << QLatin1String("Xmp.exif.FocalLength");
0308 
0309             return fromExifOrXmpList(tagList);
0310         }
0311 
0312         case MetadataInfo::FocalLengthIn35mm:
0313         {
0314             return fromExifOrXmp("Exif.Photo.FocalLengthIn35mmFilm", "Xmp.exif.FocalLengthIn35mmFilm");
0315         }
0316 
0317         case MetadataInfo::ExposureTime:
0318         {
0319             QStringList tagList;
0320             tagList << QLatin1String("Exif.Photo.ExposureTime");
0321             tagList << QLatin1String("Exif.Image.ExposureTime");
0322             tagList << QLatin1String("Xmp.exif.ExposureTime");
0323 
0324             QVariant var = fromExifOrXmpList(tagList);
0325 
0326             if (var.isNull())
0327             {
0328                 var = fromExifOrXmp("Exif.Photo.ShutterSpeedValue", "Xmp.exif.ShutterSpeedValue");
0329 
0330                 if (!var.isNull())
0331                 {
0332                     var = apexShutterSpeedToExposureTime(var.toDouble());
0333                 }
0334             }
0335 
0336             return var;
0337         }
0338 
0339         case MetadataInfo::ExposureProgram:
0340         {
0341             return fromExifOrXmp("Exif.Photo.ExposureProgram", "Xmp.exif.ExposureProgram");
0342         }
0343 
0344         case MetadataInfo::ExposureMode:
0345         {
0346             return fromExifOrXmp("Exif.Photo.ExposureMode", "Xmp.exif.ExposureMode");
0347         }
0348 
0349         case MetadataInfo::Sensitivity:
0350         {
0351             QStringList tagList;
0352             tagList << QLatin1String("Exif.Photo.ISOSpeedRatings");
0353             tagList << QLatin1String("Exif.Image.ISOSpeedRatings");
0354             tagList << QLatin1String("Exif.PanasonicRaw.ISOSpeed");
0355             tagList << QLatin1String("Xmp.exif.ISOSpeedRatings");
0356 
0357             QVariant var = fromExifOrXmpList(tagList);
0358 /*
0359             if (var.isNull())
0360             {
0361                 TODO: has this ISO format??? We must convert to the format of ISOSpeedRatings!
0362                 var = fromExifOrXmp("Exif.Photo.ExposureIndex", "Xmp.exif.ExposureIndex");
0363             }
0364 */
0365             return var;
0366         }
0367 
0368         case MetadataInfo::FlashMode:
0369         {
0370             return fromExifOrXmp("Exif.Photo.Flash", "Xmp.exif.Flash/exif:Mode");
0371         }
0372 
0373         case MetadataInfo::WhiteBalance:
0374         {
0375             return fromExifOrXmp("Exif.Photo.WhiteBalance", "Xmp.exif.WhiteBalance");
0376         }
0377 
0378         case MetadataInfo::MeteringMode:
0379         {
0380             return fromExifOrXmp("Exif.Photo.MeteringMode", "Xmp.exif.MeteringMode");
0381         }
0382 
0383         case MetadataInfo::SubjectDistance:
0384         {
0385             return fromExifOrXmp("Exif.Photo.SubjectDistance", "Xmp.exif.SubjectDistance");
0386         }
0387 
0388         case MetadataInfo::SubjectDistanceCategory:
0389         {
0390             return fromExifOrXmp("Exif.Photo.SubjectDistanceRange", "Xmp.exif.SubjectDistanceRange");
0391         }
0392 
0393         case MetadataInfo::WhiteBalanceColorTemperature:
0394         {
0395             //TODO: ??
0396 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0397 
0398             return QVariant(QMetaType(QMetaType::Int));
0399 
0400 #else
0401 
0402             return QVariant(QVariant::Int);
0403 
0404 #endif
0405 
0406         }
0407 
0408         case MetadataInfo::Longitude:
0409         {
0410             return getGPSLongitudeString();
0411         }
0412 
0413         case MetadataInfo::LongitudeNumber:
0414         {
0415             double longitude;
0416 
0417             if (getGPSLongitudeNumber(&longitude))
0418             {
0419                 return longitude;
0420             }
0421             else
0422             {
0423 
0424 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0425 
0426                 return QVariant(QMetaType(QMetaType::Double));
0427 
0428 #else
0429 
0430                 return QVariant(QVariant::Double);
0431 
0432 #endif
0433 
0434             }
0435         }
0436 
0437         case MetadataInfo::Latitude:
0438         {
0439             return getGPSLatitudeString();
0440         }
0441 
0442         case MetadataInfo::LatitudeNumber:
0443         {
0444             double latitude;
0445 
0446             if (getGPSLatitudeNumber(&latitude))
0447             {
0448                 return latitude;
0449             }
0450             else
0451             {
0452 
0453 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0454 
0455                 return QVariant(QMetaType(QMetaType::Double));
0456 
0457 #else
0458 
0459                 return QVariant(QVariant::Double);
0460 
0461 #endif
0462 
0463             }
0464         }
0465 
0466         case MetadataInfo::Altitude:
0467         {
0468             double altitude;
0469 
0470             if (getGPSAltitude(&altitude))
0471             {
0472                 return altitude;
0473             }
0474             else
0475             {
0476 
0477 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0478 
0479                 return QVariant(QMetaType(QMetaType::Double));
0480 
0481 #else
0482 
0483                 return QVariant(QVariant::Double);
0484 
0485 #endif
0486 
0487             }
0488         }
0489 
0490         case MetadataInfo::PositionOrientation:
0491         case MetadataInfo::PositionTilt:
0492         case MetadataInfo::PositionRoll:
0493         case MetadataInfo::PositionAccuracy:
0494         {
0495             // TODO or unsupported?
0496 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0497 
0498                 return QVariant(QMetaType(QMetaType::Double));
0499 
0500 #else
0501 
0502                 return QVariant(QVariant::Double);
0503 
0504 #endif
0505 
0506         }
0507 
0508         case MetadataInfo::PositionDescription:
0509         {
0510             // TODO or unsupported?
0511 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0512 
0513                 return QVariant(QMetaType(QMetaType::QString));
0514 
0515 #else
0516 
0517                 return QVariant(QVariant::String);
0518 
0519 #endif
0520 
0521         }
0522 
0523         case MetadataInfo::IptcCoreCopyrightNotice:
0524         {
0525             QVariant var = fromXmpLangAlt("Xmp.dc.rights");
0526 
0527             if (!var.isNull())
0528             {
0529                 return var;
0530             }
0531 
0532             var = fromXmpLangAlt("Xmp.tiff.Copyright");
0533 
0534             if (!var.isNull())
0535             {
0536                 return var;
0537             }
0538 
0539             return fromIptcEmulateLangAlt("Iptc.Application2.Copyright");
0540         }
0541 
0542         case MetadataInfo::IptcCoreCreator:
0543         {
0544             QVariant var = fromXmpList("Xmp.dc.creator");
0545 
0546             if (!var.isNull())
0547             {
0548                 return var;
0549             }
0550 
0551             QString artist = getXmpTagString("Xmp.tiff.Artist");
0552 
0553             if (!artist.isNull())
0554             {
0555                 QStringList list;
0556                 list << artist;
0557 
0558                 return list;
0559             }
0560 
0561             return fromIptcEmulateList("Iptc.Application2.Byline");
0562         }
0563 
0564         case MetadataInfo::IptcCoreProvider:
0565         {
0566             return fromIptcOrXmp("Iptc.Application2.Credit", "Xmp.photoshop.Credit");
0567         }
0568 
0569         case MetadataInfo::IptcCoreRightsUsageTerms:
0570         {
0571             return fromXmpLangAlt("Xmp.xmpRights.UsageTerms");
0572         }
0573 
0574         case MetadataInfo::IptcCoreSource:
0575         {
0576             return fromIptcOrXmp("Iptc.Application2.Source", "Xmp.photoshop.Source");
0577         }
0578 
0579         case MetadataInfo::IptcCoreCreatorJobTitle:
0580         {
0581             return fromIptcOrXmp("Iptc.Application2.BylineTitle", "Xmp.photoshop.AuthorsPosition");
0582         }
0583 
0584         case MetadataInfo::IptcCoreInstructions:
0585         {
0586             return fromIptcOrXmp("Iptc.Application2.SpecialInstructions", "Xmp.photoshop.Instructions");
0587         }
0588 
0589         case MetadataInfo::IptcCoreLocationInfo:
0590         {
0591             IptcCoreLocationInfo location = getIptcCoreLocation();
0592 
0593             if (location.isNull())
0594             {
0595                 return QVariant();
0596             }
0597 
0598             return QVariant::fromValue(location);
0599         }
0600 
0601         case MetadataInfo::IptcCoreCountryCode:
0602         {
0603             return fromIptcOrXmp("Iptc.Application2.CountryCode", "Xmp.iptc.CountryCode");
0604         }
0605 
0606         case MetadataInfo::IptcCoreCountry:
0607         {
0608             return fromIptcOrXmp("Iptc.Application2.CountryName", "Xmp.photoshop.Country");
0609         }
0610 
0611         case MetadataInfo::IptcCoreCity:
0612         {
0613             return fromIptcOrXmp("Iptc.Application2.City", "Xmp.photoshop.City");
0614         }
0615 
0616         case MetadataInfo::IptcCoreLocation:
0617         {
0618             return fromIptcOrXmp("Iptc.Application2.SubLocation", "Xmp.iptc.Location");
0619         }
0620 
0621         case MetadataInfo::IptcCoreProvinceState:
0622         {
0623             return fromIptcOrXmp("Iptc.Application2.ProvinceState", "Xmp.photoshop.State");
0624         }
0625 
0626         case MetadataInfo::IptcCoreIntellectualGenre:
0627         {
0628             return fromIptcOrXmp("Iptc.Application2.ObjectAttribute", "Xmp.iptc.IntellectualGenre");
0629         }
0630 
0631         case MetadataInfo::IptcCoreJobID:
0632         {
0633             return fromIptcOrXmp("Iptc.Application2.TransmissionReference", "Xmp.photoshop.TransmissionReference");
0634         }
0635 
0636         case MetadataInfo::IptcCoreScene:
0637         {
0638             return fromXmpList("Xmp.iptc.Scene");
0639         }
0640 
0641         case MetadataInfo::IptcCoreSubjectCode:
0642         {
0643             return toStringListVariant(getIptcCoreSubjects());
0644         }
0645 
0646         case MetadataInfo::IptcCoreContactInfo:
0647         {
0648             IptcCoreContactInfo info = getCreatorContactInfo();
0649 
0650             if (info.isNull())
0651             {
0652                 return QVariant();
0653             }
0654 
0655             return QVariant::fromValue(info);
0656         }
0657 
0658         case MetadataInfo::IptcCoreContactInfoCity:
0659         {
0660             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity");
0661         }
0662 
0663         case MetadataInfo::IptcCoreContactInfoCountry:
0664         {
0665             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry");
0666         }
0667 
0668         case MetadataInfo::IptcCoreContactInfoAddress:
0669         {
0670             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrExtadr");
0671         }
0672 
0673         case MetadataInfo::IptcCoreContactInfoPostalCode:
0674         {
0675             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrPcode");
0676         }
0677 
0678         case MetadataInfo::IptcCoreContactInfoProvinceState:
0679         {
0680             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrRegion");
0681         }
0682 
0683         case MetadataInfo::IptcCoreContactInfoEmail:
0684         {
0685             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiEmailWork");
0686         }
0687 
0688         case MetadataInfo::IptcCoreContactInfoPhone:
0689         {
0690             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiTelWork");
0691         }
0692 
0693         case MetadataInfo::IptcCoreContactInfoWebUrl:
0694         {
0695             return getXmpTagVariant("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork");
0696         }
0697 
0698         case MetadataInfo::AspectRatio:
0699         {
0700             long num             = 0;
0701             long den             = 1;
0702 
0703             // NOTE: there is a bug in Exiv2 xmp::video tag definition as "Rational" value is defined as "Ratio"...
0704 /*
0705             QList<QVariant> list = getXmpTagVariant("Xmp.video.AspectRatio").toList();
0706 */
0707             QString ar       = getXmpTagString("Xmp.video.AspectRatio");
0708             QStringList list = ar.split(QLatin1Char('/'));
0709 
0710             if (list.size() >= 1)
0711             {
0712                 num = list[0].toInt();
0713             }
0714 
0715             if (list.size() >= 2)
0716             {
0717                 den = list[1].toInt();
0718             }
0719 
0720             return QString::number((double)num / (double)den);
0721         }
0722 
0723         case MetadataInfo::AudioBitRate:
0724         {
0725             return fromXmpLangAlt("Xmp.audio.SampleRate");
0726         }
0727 
0728         case MetadataInfo::AudioChannelType:
0729         {
0730             return fromXmpLangAlt("Xmp.audio.ChannelType");
0731         }
0732 
0733         case MetadataInfo::AudioCodec:
0734         {
0735             return fromXmpLangAlt("Xmp.audio.Codec");
0736         }
0737 
0738         case MetadataInfo::Duration:
0739         {
0740             return fromXmpLangAlt("Xmp.video.duration"); // duration is in ms
0741         }
0742 
0743         case MetadataInfo::FrameRate:
0744         {
0745             return fromXmpLangAlt("Xmp.video.FrameRate");
0746         }
0747 
0748         case MetadataInfo::VideoCodec:
0749         {
0750             return fromXmpLangAlt("Xmp.video.Codec");
0751         }
0752 
0753         case MetadataInfo::VideoBitDepth:
0754         {
0755             return fromXmpLangAlt("Xmp.video.BitDepth");
0756         }
0757 
0758         case MetadataInfo::VideoHeight:
0759         {
0760             return fromXmpLangAlt("Xmp.video.Height");
0761         }
0762 
0763         case MetadataInfo::VideoWidth:
0764         {
0765             return fromXmpLangAlt("Xmp.video.Width");
0766         }
0767 
0768         case MetadataInfo::VideoColorSpace:
0769         {
0770             QString cs = getXmpTagString("Xmp.video.ColorSpace");
0771 
0772             if      (cs == QLatin1String("sRGB"))
0773             {
0774                 return QString::number(VIDEOCOLORMODEL_SRGB);
0775             }
0776             else if (cs == QLatin1String("CCIR-601"))
0777             {
0778                 return QString::number(VIDEOCOLORMODEL_BT601);
0779             }
0780             else if (cs == QLatin1String("CCIR-709"))
0781             {
0782                 return QString::number(VIDEOCOLORMODEL_BT709);
0783             }
0784             else if (cs == QLatin1String("Other"))
0785             {
0786                 return QString::number(VIDEOCOLORMODEL_OTHER);
0787             }
0788             else
0789             {
0790 
0791 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0792 
0793                 return QVariant(QMetaType(QMetaType::Int));
0794 
0795 #else
0796 
0797                 return QVariant(QVariant::Int);
0798 
0799 #endif
0800 
0801             }
0802         }
0803 
0804         default:
0805         {
0806             return QVariant();
0807         }
0808     }
0809 }
0810 
0811 QVariantList DMetadata::getMetadataFields(const MetadataFields& fields) const
0812 {
0813     QVariantList list;
0814 
0815     Q_FOREACH (MetadataInfo::Field field, fields) // krazy:exclude=foreach
0816     {
0817         list << getMetadataField(field);
0818     }
0819 
0820     return list;
0821 }
0822 
0823 QString DMetadata::valueToString(const QVariant& value, MetadataInfo::Field field)
0824 {
0825     if (value.isNull())
0826     {
0827         return QString();
0828     }
0829 
0830     QScopedPointer<MetaEngine> exiv2Iface(new MetaEngine);
0831 
0832     switch (field)
0833     {
0834         case MetadataInfo::Rating:
0835         {
0836             return value.toString();
0837         }
0838 
0839         case MetadataInfo::CreationDate:
0840         case MetadataInfo::DigitizationDate:
0841         {
0842             return QLocale().toString(value.toDateTime(), QLocale::ShortFormat);
0843         }
0844 
0845         case MetadataInfo::Orientation:
0846         {
0847             switch (value.toInt())
0848             {
0849                 // Example why the English text differs from the enum names: ORIENTATION_ROT_90.
0850                 // Rotation by 90 degrees is right (clockwise) rotation.
0851                 // But: The enum names describe what needs to be done to get the image right again.
0852                 // And an image that needs to be rotated 90 degrees is currently rotated 270 degrees = left.
0853 
0854                 case ORIENTATION_UNSPECIFIED:
0855                 {
0856                     return i18nc("@info: rotation", "Unspecified");
0857                 }
0858 
0859                 case ORIENTATION_NORMAL:
0860                 {
0861                     return i18nc("@info: rotation of an unrotated image", "Normal");
0862                 }
0863 
0864                 case ORIENTATION_HFLIP:
0865                 {
0866                     return i18nc("@info: rotation", "Flipped Horizontally");
0867                 }
0868 
0869                 case ORIENTATION_ROT_180:
0870                 {
0871                     return i18nc("@info: rotation", "Rotated by 180 Degrees");
0872                 }
0873 
0874                 case ORIENTATION_VFLIP:
0875                 {
0876                     return i18nc("@info: rotation", "Flipped Vertically");
0877                 }
0878 
0879                 case ORIENTATION_ROT_90_HFLIP:
0880                 {
0881                     return i18nc("@info: rotation", "Flipped Horizontally and Rotated Left");
0882                 }
0883 
0884                 case ORIENTATION_ROT_90:
0885                 {
0886                     return i18nc("@info: rotation", "Rotated Left");
0887                 }
0888 
0889                 case ORIENTATION_ROT_90_VFLIP:
0890                 {
0891                     return i18nc("@info: rotation", "Flipped Vertically and Rotated Left");
0892                 }
0893 
0894                 case ORIENTATION_ROT_270:
0895                 {
0896                     return i18nc("@info: rotation", "Rotated Right");
0897                 }
0898 
0899                 default:
0900                 {
0901                     return i18nc("@info: rotation", "Unknown");
0902                 }
0903             }
0904 
0905             break;
0906         }
0907 
0908         case MetadataInfo::Make:
0909         {
0910             return exiv2Iface->createExifUserStringFromValue("Exif.Image.Make", value);
0911         }
0912 
0913         case MetadataInfo::Model:
0914         {
0915             return exiv2Iface->createExifUserStringFromValue("Exif.Image.Model", value);
0916         }
0917 
0918         case MetadataInfo::Lens:
0919         {
0920             // heterogeneous source, non-standardized string
0921             return value.toString();
0922         }
0923 
0924         case MetadataInfo::Aperture:
0925         {
0926             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.FNumber", value);
0927         }
0928 
0929         case MetadataInfo::FocalLength:
0930         {
0931             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.FocalLength", value);
0932         }
0933 
0934         case MetadataInfo::FocalLengthIn35mm:
0935         {
0936             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.FocalLengthIn35mmFilm", value);
0937         }
0938 
0939         case MetadataInfo::ExposureTime:
0940         {
0941             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.ExposureTime", value);
0942         }
0943 
0944         case MetadataInfo::ExposureProgram:
0945         {
0946             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.ExposureProgram", value);
0947         }
0948 
0949         case MetadataInfo::ExposureMode:
0950         {
0951             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.ExposureMode", value);
0952         }
0953 
0954         case MetadataInfo::Sensitivity:
0955         {
0956             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.ISOSpeedRatings", value);
0957         }
0958 
0959         case MetadataInfo::FlashMode:
0960         {
0961             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.Flash", value);
0962         }
0963 
0964         case MetadataInfo::WhiteBalance:
0965         {
0966             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.WhiteBalance", value);
0967         }
0968 
0969         case MetadataInfo::MeteringMode:
0970         {
0971             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.MeteringMode", value);
0972         }
0973 
0974         case MetadataInfo::SubjectDistance:
0975         {
0976             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.SubjectDistance", value);
0977         }
0978 
0979         case MetadataInfo::SubjectDistanceCategory:
0980         {
0981             return exiv2Iface->createExifUserStringFromValue("Exif.Photo.SubjectDistanceRange", value);
0982         }
0983 
0984         case MetadataInfo::WhiteBalanceColorTemperature:
0985         {
0986             return i18nc("@info: Temperature in Kelvin", "%1 K", value.toInt());
0987         }
0988 
0989         case MetadataInfo::AspectRatio:
0990         case MetadataInfo::AudioBitRate:
0991         case MetadataInfo::AudioChannelType:
0992         case MetadataInfo::AudioCodec:
0993         case MetadataInfo::Duration:
0994         case MetadataInfo::FrameRate:
0995         case MetadataInfo::VideoCodec:
0996         {
0997             return value.toString();
0998         }
0999 
1000         case MetadataInfo::Longitude:
1001         {
1002             int    degrees, minutes;
1003             double seconds;
1004             char   directionRef;
1005 
1006             if (!convertToUserPresentableNumbers(value.toString(), &degrees, &minutes, &seconds, &directionRef))
1007             {
1008                 return QString();
1009             }
1010 
1011             QString direction = (QLatin1Char(directionRef) == QLatin1Char('W'))         ?
1012                                 i18nc("@info: For use in longitude coordinate", "West") :
1013                                 i18nc("@info: For use in longitude coordinate", "East");
1014 
1015             return QString::fromLatin1("%1%2%3%4%L5%6 %7").arg(degrees).arg(QChar(0xB0))
1016                         .arg(minutes).arg(QChar(0x2032))
1017                         .arg(seconds, 'f').arg(QChar(0x2033)).arg(direction);
1018         }
1019 
1020         case MetadataInfo::LongitudeNumber:
1021         {
1022             int    degrees, minutes;
1023             double seconds;
1024             char   directionRef;
1025 
1026             convertToUserPresentableNumbers(false, value.toDouble(), &degrees, &minutes, &seconds, &directionRef);
1027             QString direction = (QLatin1Char(directionRef) == QLatin1Char('W'))         ?
1028                                 i18nc("@info: For use in longitude coordinate", "West") :
1029                                 i18nc("@info: For use in longitude coordinate", "East");
1030 
1031             return QString::fromLatin1("%1%2%3%4%L5%6 %7").arg(degrees).arg(QChar(0xB0))
1032                         .arg(minutes).arg(QChar(0x2032))
1033                         .arg(seconds, 'f').arg(QChar(0x2033)).arg(direction);
1034         }
1035 
1036         case MetadataInfo::Latitude:
1037         {
1038             int    degrees, minutes;
1039             double seconds;
1040             char   directionRef;
1041 
1042             if (!convertToUserPresentableNumbers(value.toString(), &degrees, &minutes, &seconds, &directionRef))
1043             {
1044                 return QString();
1045             }
1046 
1047             QString direction = (QLatin1Char(directionRef) == QLatin1Char('N'))         ?
1048                                 i18nc("@info: For use in latitude coordinate", "North") :
1049                                 i18nc("@info: For use in latitude coordinate", "South");
1050 
1051             return QString::fromLatin1("%1%2%3%4%L5%6 %7").arg(degrees).arg(QChar(0xB0))
1052                         .arg(minutes).arg(QChar(0x2032))
1053                         .arg(seconds, 'f').arg(QChar(0x2033)).arg(direction);
1054         }
1055 
1056         case MetadataInfo::LatitudeNumber:
1057         {
1058             int    degrees, minutes;
1059             double seconds;
1060             char   directionRef;
1061 
1062             convertToUserPresentableNumbers(false, value.toDouble(), &degrees, &minutes, &seconds, &directionRef);
1063             QString direction = (QLatin1Char(directionRef) == QLatin1Char('N'))         ?
1064                                 i18nc("@info: For use in latitude coordinate", "North") :
1065                                 i18nc("@info: For use in latitude coordinate", "South");
1066 
1067             return QString::fromLatin1("%1%2%3%4%L5%6 %7").arg(degrees).arg(QChar(0xB0))
1068                        .arg(minutes).arg(QChar(0x2032))
1069                        .arg(seconds, 'f').arg(QChar(0x2033)).arg(direction);
1070         }
1071 
1072         case MetadataInfo::Altitude:
1073         {
1074             QString meters = QString::fromLatin1("%L1").arg(value.toDouble(), 0, 'f', 2);
1075 
1076             // xgettext: no-c-format
1077 
1078             return i18nc("@info: Height in meters", "%1m", meters);
1079         }
1080 
1081         case MetadataInfo::PositionOrientation:
1082         case MetadataInfo::PositionTilt:
1083         case MetadataInfo::PositionRoll:
1084         case MetadataInfo::PositionAccuracy:
1085         {
1086             //TODO
1087             return value.toString();
1088         }
1089 
1090         case MetadataInfo::PositionDescription:
1091         {
1092             return value.toString();
1093         }
1094 
1095         // Lang Alt
1096         case MetadataInfo::IptcCoreCopyrightNotice:
1097         case MetadataInfo::IptcCoreRightsUsageTerms:
1098         case MetadataInfo::Description:
1099         case MetadataInfo::Title:
1100         {
1101             QMap<QString, QVariant> map = value.toMap();
1102 
1103             // the most common cases
1104 
1105             if      (map.isEmpty())
1106             {
1107                 return QString();
1108             }
1109             else if (map.size() == 1)
1110             {
1111                 return map.begin().value().toString();
1112             }
1113 
1114             // Try "en-us"
1115 
1116             QString spec = QLocale().name().toLower().replace(QLatin1Char('_'), QLatin1Char('-'));
1117 
1118             if (map.contains(spec))
1119             {
1120                 return map[spec].toString();
1121             }
1122 
1123             // Try "en-"
1124 
1125             QStringList keys    = map.keys();
1126             QString spec2       = QLocale().name().toLower();
1127             QRegularExpression exp(spec2.left(spec2.indexOf(QLatin1Char('_'))) + QLatin1Char('-'));
1128             QStringList matches = keys.filter(exp);
1129 
1130             if (!matches.isEmpty())
1131             {
1132                 return map[matches.first()].toString();
1133             }
1134 
1135             // return default
1136 
1137             if (map.contains(QLatin1String("x-default")))
1138             {
1139                 return map[QLatin1String("x-default")].toString();
1140             }
1141 
1142             // return first entry
1143 
1144             return map.begin().value().toString();
1145         }
1146 
1147         // List
1148         case MetadataInfo::IptcCoreCreator:
1149         case MetadataInfo::IptcCoreScene:
1150         case MetadataInfo::IptcCoreSubjectCode:
1151         {
1152             return value.toStringList().join(QLatin1Char(' '));
1153         }
1154 
1155         // Text
1156         case MetadataInfo::Comment:
1157         case MetadataInfo::CommentJfif:
1158         case MetadataInfo::CommentExif:
1159         case MetadataInfo::CommentIptc:
1160         case MetadataInfo::Headline:
1161         case MetadataInfo::DescriptionWriter:
1162         case MetadataInfo::IptcCoreProvider:
1163         case MetadataInfo::IptcCoreSource:
1164         case MetadataInfo::IptcCoreCreatorJobTitle:
1165         case MetadataInfo::IptcCoreInstructions:
1166         case MetadataInfo::IptcCoreCountryCode:
1167         case MetadataInfo::IptcCoreCountry:
1168         case MetadataInfo::IptcCoreCity:
1169         case MetadataInfo::IptcCoreLocation:
1170         case MetadataInfo::IptcCoreProvinceState:
1171         case MetadataInfo::IptcCoreIntellectualGenre:
1172         case MetadataInfo::IptcCoreJobID:
1173         {
1174             return value.toString();
1175         }
1176 
1177         default:
1178         {
1179             break;
1180         }
1181     }
1182 
1183     return QString();
1184 }
1185 
1186 QStringList DMetadata::valuesToString(const QVariantList& values, const MetadataFields& fields)
1187 {
1188     int size = values.size();
1189 
1190     Q_ASSERT(size == values.size());
1191 
1192     QStringList list;
1193 
1194     for (int i = 0 ; i < size ; ++i)
1195     {
1196         list << valueToString(values.at(i), fields.at(i));
1197     }
1198 
1199     return list;
1200 }
1201 
1202 QMap<int, QString> DMetadata::possibleValuesForEnumField(MetadataInfo::Field field)
1203 {
1204     QMap<int, QString> map;
1205     int min, max;
1206 
1207     switch (field)
1208     {
1209         case MetadataInfo::Orientation:                      /// Int, enum from libMetaEngine
1210         {
1211             min = ORIENTATION_UNSPECIFIED;
1212             max = ORIENTATION_ROT_270;
1213             break;
1214         }
1215 
1216         case MetadataInfo::ExposureProgram:                  /// Int, enum from Exif
1217         {
1218             min = 0;
1219             max = 8;
1220             break;
1221         }
1222 
1223         case MetadataInfo::ExposureMode:                     /// Int, enum from Exif
1224         {
1225             min = 0;
1226             max = 2;
1227             break;
1228         }
1229 
1230         case MetadataInfo::WhiteBalance:                     /// Int, enum from Exif
1231         {
1232             min = 0;
1233             max = 1;
1234             break;
1235         }
1236 
1237         case MetadataInfo::MeteringMode:                     /// Int, enum from Exif
1238         {
1239             min = 0;
1240             max = 6;
1241             map[255] = valueToString(255, field);
1242             break;
1243         }
1244 
1245         case MetadataInfo::SubjectDistanceCategory:          /// int, enum from Exif
1246         {
1247             min = 0;
1248             max = 3;
1249             break;
1250         }
1251 
1252         case MetadataInfo::FlashMode:                        /// Int, bit mask from Exif
1253         {
1254             // This one is a bit special.
1255             // We return a bit mask for binary AND searching.
1256             map[0x1]  = i18nc("@info", "Flash has been fired");
1257             map[0x40] = i18nc("@info", "Flash with red-eye reduction mode");
1258             //more: TODO?
1259             return map;
1260         }
1261 
1262         default:
1263         {
1264             qCWarning(DIGIKAM_METAENGINE_LOG) << "Unsupported field " << field << " in DMetadata::possibleValuesForEnumField";
1265             return map;
1266         }
1267     }
1268 
1269     for (int i = min ; i <= max ; ++i)
1270     {
1271         map[i] = valueToString(i, field);
1272     }
1273 
1274     return map;
1275 }
1276 
1277 bool DMetadata::hasValidField(const QVariantList& list) const
1278 {
1279     for (QVariantList::const_iterator it = list.constBegin() ;
1280          it != list.constEnd() ; ++it)
1281     {
1282         if (!(*it).isNull())
1283         {
1284             return true;
1285         }
1286     }
1287 
1288     return false;
1289 }
1290 
1291 QVariant DMetadata::toStringListVariant(const QStringList& list) const
1292 {
1293     if (list.isEmpty())
1294     {
1295 
1296 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
1297 
1298             return QVariant(QMetaType(QMetaType::QStringList));
1299 
1300 #else
1301 
1302             return QVariant(QVariant::StringList);
1303 
1304 #endif
1305 
1306     }
1307 
1308     return list;
1309 }
1310 
1311 } // namespace Digikam