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(), °rees, &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(), °rees, &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(), °rees, &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(), °rees, &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