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 - labels 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 
0023 // Local includes
0024 
0025 #include "metaenginesettings.h"
0026 #include "digikam_version.h"
0027 #include "digikam_globals.h"
0028 #include "digikam_debug.h"
0029 
0030 namespace Digikam
0031 {
0032 
0033 int DMetadata::getItemPickLabel() const
0034 {
0035     if (hasXmp())
0036     {
0037         QString value = getXmpTagString("Xmp.digiKam.PickLabel", false);
0038 
0039         if (!value.isEmpty())
0040         {
0041             bool ok     = false;
0042             long pickId = value.toLong(&ok);
0043 
0044             if (ok && (pickId >= NoPickLabel) && (pickId <= AcceptedLabel))
0045             {
0046                 return pickId;
0047             }
0048         }
0049     }
0050 
0051     return -1;
0052 }
0053 
0054 int DMetadata::getItemColorLabel(const DMetadataSettingsContainer& settings) const
0055 {
0056     bool xmpSupported  = hasXmp();
0057     bool exivSupported = hasExif();
0058 
0059     Q_FOREACH (const NamespaceEntry& entry, settings.getReadMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER()))
0060     {
0061         if (entry.isDisabled)
0062         {
0063             continue;
0064         }
0065 
0066         const std::string myStr = entry.namespaceName.toStdString();
0067         const char* nameSpace   = myStr.data();
0068         QString value;
0069 
0070         switch (entry.subspace)
0071         {
0072             case NamespaceEntry::XMP:
0073             {
0074                 if (xmpSupported)
0075                 {
0076                     value = getXmpTagString(nameSpace, false);
0077                 }
0078 
0079                 break;
0080             }
0081 
0082             case NamespaceEntry::IPTC:
0083             {
0084                 break;
0085             }
0086 
0087             case NamespaceEntry::EXIF:
0088             {
0089                 if (exivSupported)
0090                 {
0091                     value = getExifTagString(nameSpace, false);
0092                 }
0093 
0094                 break;
0095             }
0096 
0097             default:
0098             {
0099                 break;
0100             }
0101         }
0102 
0103         if (value.isEmpty())
0104         {
0105             continue;
0106         }
0107 
0108         bool ok      = false;
0109         long colorId = value.toLong(&ok);
0110 
0111         if (ok && (colorId >= NoColorLabel) && (colorId <= WhiteLabel))
0112         {
0113             return colorId;
0114         }
0115 
0116         // LightRoom use this tag to store color name as string.
0117         // Values are limited : see bug #358193.
0118 
0119         if      (value == QLatin1String("Blue"))
0120         {
0121             return BlueLabel;
0122         }
0123         else if (value == QLatin1String("Green"))
0124         {
0125             return GreenLabel;
0126         }
0127         else if (value == QLatin1String("Red"))
0128         {
0129             return RedLabel;
0130         }
0131         else if (value == QLatin1String("Yellow"))
0132         {
0133             return YellowLabel;
0134         }
0135         else if (value == QLatin1String("Purple"))
0136         {
0137             return MagentaLabel;
0138         }
0139     }
0140 
0141     return -1;
0142 }
0143 
0144 int DMetadata::getItemRating(const DMetadataSettingsContainer& settings) const
0145 {
0146     long rating        = -1;
0147     bool xmpSupported  = hasXmp();
0148     bool iptcSupported = hasIptc();
0149     bool exivSupported = hasExif();
0150 
0151     Q_FOREACH (const NamespaceEntry& entry, settings.getReadMapping(NamespaceEntry::DM_RATING_CONTAINER()))
0152     {
0153         if (entry.isDisabled)
0154         {
0155             continue;
0156         }
0157 
0158         const std::string myStr = entry.namespaceName.toStdString();
0159         const char* nameSpace   = myStr.data();
0160         QString value;
0161 
0162         switch (entry.subspace)
0163         {
0164             case NamespaceEntry::XMP:
0165             {
0166                 if (xmpSupported)
0167                 {
0168                     value = getXmpTagString(nameSpace, false);
0169                 }
0170 
0171                 break;
0172             }
0173 
0174             case NamespaceEntry::IPTC:
0175             {
0176                 if (iptcSupported)
0177                 {
0178                     value = QString::fromUtf8(getIptcTagData(nameSpace));
0179                 }
0180 
0181                 break;
0182             }
0183 
0184             case NamespaceEntry::EXIF:
0185             {
0186                 if (exivSupported)
0187                 {
0188                     if (!getExifTagLong(nameSpace, rating))
0189                     {
0190                         continue;
0191                     }
0192                 }
0193 
0194                 break;
0195             }
0196 
0197             default:
0198             {
0199                 break;
0200             }
0201         }
0202 
0203         if (!value.isEmpty())
0204         {
0205             bool ok = false;
0206             rating  = value.toLong(&ok);
0207 
0208             if (!ok)
0209             {
0210                 return -1;
0211             }
0212         }
0213 
0214         int index = entry.convertRatio.indexOf(rating);
0215 
0216         // Exact value was not found,but rating is in range,
0217         // so we try to approximate it
0218 
0219         if ((index == -1)                         &&
0220             (rating > entry.convertRatio.first()) &&
0221             (rating < entry.convertRatio.last()))
0222         {
0223             for (int i = 0 ; i < entry.convertRatio.size() ; ++i)
0224             {
0225                 if (rating > entry.convertRatio.at(i))
0226                 {
0227                     index = i;
0228                 }
0229             }
0230         }
0231 
0232         if (index != -1)
0233         {
0234             return index;
0235         }
0236     }
0237 
0238     return -1;
0239 }
0240 
0241 bool DMetadata::setItemPickLabel(int pickId) const
0242 {
0243     if ((pickId < NoPickLabel) || (pickId > AcceptedLabel))
0244     {
0245         qCDebug(DIGIKAM_METAENGINE_LOG) << "Pick Label value to write is out of range!";
0246         return false;
0247     }
0248 /*
0249     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Pick Label: " << pickId;
0250 */
0251     if (supportXmp())
0252     {
0253         if (!setXmpTagString("Xmp.digiKam.PickLabel", QString::number(pickId)))
0254         {
0255             return false;
0256         }
0257     }
0258 
0259     return true;
0260 }
0261 
0262 bool DMetadata::setItemColorLabel(int colorId, const DMetadataSettingsContainer& settings) const
0263 {
0264     if ((colorId < NoColorLabel) || (colorId > WhiteLabel))
0265     {
0266         qCDebug(DIGIKAM_METAENGINE_LOG) << "Color Label value to write is out of range!";
0267 
0268         return false;
0269     }
0270 /*
0271     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Color Label: " << colorId;
0272 */
0273     QList<NamespaceEntry> toWrite = settings.getReadMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER());
0274 
0275     if (!settings.unifyReadWrite())
0276     {
0277         toWrite = settings.getWriteMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER());
0278     }
0279 
0280     for (const NamespaceEntry& entry : qAsConst(toWrite))
0281     {
0282         if (entry.isDisabled)
0283         {
0284             continue;
0285         }
0286 
0287         const std::string myStr = entry.namespaceName.toStdString();
0288         const char* nameSpace   = myStr.data();
0289 
0290         switch (entry.subspace)
0291         {
0292             case NamespaceEntry::XMP:
0293             {
0294                 if (!supportXmp())
0295                 {
0296                     continue;
0297                 }
0298 
0299                 if (QLatin1String(nameSpace) == QLatin1String("Xmp.xmp.Label"))
0300                 {
0301                     // LightRoom use this XMP tags to store Color Labels name
0302                     // Values are limited : see bug #358193.
0303 
0304                     QString LRLabel;
0305 
0306                     switch (colorId)
0307                     {
0308                         case BlueLabel:
0309                         {
0310                             LRLabel = QLatin1String("Blue");
0311                             break;
0312                         }
0313 
0314                         case GreenLabel:
0315                         {
0316                             LRLabel = QLatin1String("Green");
0317                             break;
0318                         }
0319 
0320                         case RedLabel:
0321                         {
0322                             LRLabel = QLatin1String("Red");
0323                             break;
0324                         }
0325 
0326                         case YellowLabel:
0327                         {
0328                             LRLabel = QLatin1String("Yellow");
0329                             break;
0330                         }
0331 
0332                         case MagentaLabel:
0333                         {
0334                             LRLabel = QLatin1String("Purple");
0335                             break;
0336                         }
0337                     }
0338 
0339                     if (!LRLabel.isEmpty())
0340                     {
0341                         if (!setXmpTagString(nameSpace, LRLabel))
0342                         {
0343                             return false;
0344                         }
0345                     }
0346                     else
0347                     {
0348                         removeXmpTag(nameSpace);
0349                     }
0350                 }
0351                 else
0352                 {
0353                     if (!setXmpTagString(nameSpace, QString::number(colorId)))
0354                     {
0355                         return false;
0356                     }
0357                 }
0358 
0359                 break;
0360             }
0361 
0362             case NamespaceEntry::IPTC:
0363             {
0364                 break;
0365             }
0366 
0367             case NamespaceEntry::EXIF:
0368             {
0369                 if (!setExifTagString(nameSpace, QString::number(colorId)))
0370                 {
0371                     return false;
0372                 }
0373 
0374                 break;
0375             }
0376 
0377             default:
0378             {
0379                 break;
0380             }
0381         }
0382     }
0383 
0384     return true;
0385 }
0386 
0387 bool DMetadata::setItemRating(int rating, const DMetadataSettingsContainer& settings) const
0388 {
0389     if ((rating < RatingMin) || (rating > RatingMax))
0390     {
0391         qCDebug(DIGIKAM_METAENGINE_LOG) << "Rating value to write is out of range!";
0392         return false;
0393     }
0394 /*
0395     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Rating:" << rating;
0396 */
0397     QList<NamespaceEntry> toWrite = settings.getReadMapping(NamespaceEntry::DM_RATING_CONTAINER());
0398 
0399     if (!settings.unifyReadWrite())
0400     {
0401         toWrite = settings.getWriteMapping(NamespaceEntry::DM_RATING_CONTAINER());
0402     }
0403 
0404     for (const NamespaceEntry& entry : qAsConst(toWrite))
0405     {
0406         if (entry.isDisabled)
0407         {
0408             continue;
0409         }
0410 
0411         const std::string myStr = entry.namespaceName.toStdString();
0412         const char* nameSpace   = myStr.data();
0413 
0414         switch (entry.subspace)
0415         {
0416             case NamespaceEntry::XMP:
0417             {
0418                 if (!supportXmp())
0419                 {
0420                     continue;
0421                 }
0422 
0423                 if (!setXmpTagString(nameSpace, QString::number(entry.convertRatio.at(rating))))
0424                 {
0425                     qCDebug(DIGIKAM_METAENGINE_LOG) << "Setting rating failed" << nameSpace;
0426                     return false;
0427                 }
0428 
0429                 break;
0430             }
0431 
0432             case NamespaceEntry::IPTC:
0433             {
0434                 if (!setIptcTagString(nameSpace, QString::number(entry.convertRatio.at(rating))))
0435                 {
0436                     qCDebug(DIGIKAM_METAENGINE_LOG) << "Setting rating failed" << nameSpace;
0437                     return false;
0438                 }
0439 
0440                 break;
0441             }
0442 
0443             case NamespaceEntry::EXIF:
0444             {
0445                 if (!setExifTagLong(nameSpace, entry.convertRatio.at(rating)))
0446                 {
0447                     qCDebug(DIGIKAM_METAENGINE_LOG) << "Setting rating failed" << nameSpace;
0448                     return false;
0449                 }
0450 
0451                 break;
0452             }
0453 
0454             default:
0455             {
0456                 break;
0457             }
0458         }
0459     }
0460 
0461     return true;
0462 }
0463 
0464 } // namespace Digikam