1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2006-02-23
7  * Description : item metadata interface - labels helpers.
8  *
9  * Copyright (C) 2006-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11  * Copyright (C) 2011      by Leif Huhn <leif at dkstat dot com>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #include "dmetadata.h"
27 
28 // Qt includes
29 
30 #include <QLocale>
31 
32 // Local includes
33 
34 #include "metaenginesettings.h"
35 #include "digikam_version.h"
36 #include "digikam_globals.h"
37 #include "digikam_debug.h"
38 
39 namespace Digikam
40 {
41 
getItemPickLabel() const42 int DMetadata::getItemPickLabel() const
43 {
44     if (getFilePath().isEmpty())
45     {
46         return -1;
47     }
48 
49     if (hasXmp())
50     {
51         QString value = getXmpTagString("Xmp.digiKam.PickLabel", false);
52 
53         if (!value.isEmpty())
54         {
55             bool ok     = false;
56             long pickId = value.toLong(&ok);
57 
58             if (ok && (pickId >= NoPickLabel) && (pickId <= AcceptedLabel))
59             {
60                 return pickId;
61             }
62         }
63     }
64 
65     return -1;
66 }
67 
getItemColorLabel(const DMetadataSettingsContainer & settings) const68 int DMetadata::getItemColorLabel(const DMetadataSettingsContainer& settings) const
69 {
70     if (getFilePath().isEmpty())
71     {
72         return -1;
73     }
74 
75     bool xmpSupported  = hasXmp();
76     bool exivSupported = hasExif();
77 
78     foreach (const NamespaceEntry& entry, settings.getReadMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER()))
79     {
80         if (entry.isDisabled)
81         {
82             continue;
83         }
84 
85         const std::string myStr = entry.namespaceName.toStdString();
86         const char* nameSpace   = myStr.data();
87         QString value;
88 
89         switch (entry.subspace)
90         {
91             case NamespaceEntry::XMP:
92             {
93                 if (xmpSupported)
94                 {
95                     value = getXmpTagString(nameSpace, false);
96                 }
97 
98                 break;
99             }
100 
101             case NamespaceEntry::IPTC:
102             {
103                 break;
104             }
105 
106             case NamespaceEntry::EXIF:
107             {
108                 if (exivSupported)
109                 {
110                     value = getExifTagString(nameSpace, false);
111                 }
112 
113                 break;
114             }
115 
116             default:
117             {
118                 break;
119             }
120         }
121 
122         if (value.isEmpty())
123         {
124             continue;
125         }
126 
127         bool ok      = false;
128         long colorId = value.toLong(&ok);
129 
130         if (ok && (colorId >= NoColorLabel) && (colorId <= WhiteLabel))
131         {
132             return colorId;
133         }
134 
135         // LightRoom use this tag to store color name as string.
136         // Values are limited : see bug #358193.
137 
138         if      (value == QLatin1String("Blue"))
139         {
140             return BlueLabel;
141         }
142         else if (value == QLatin1String("Green"))
143         {
144             return GreenLabel;
145         }
146         else if (value == QLatin1String("Red"))
147         {
148             return RedLabel;
149         }
150         else if (value == QLatin1String("Yellow"))
151         {
152             return YellowLabel;
153         }
154         else if (value == QLatin1String("Purple"))
155         {
156             return MagentaLabel;
157         }
158     }
159 
160     return -1;
161 }
162 
getItemRating(const DMetadataSettingsContainer & settings) const163 int DMetadata::getItemRating(const DMetadataSettingsContainer& settings) const
164 {
165     if (getFilePath().isEmpty())
166     {
167         return -1;
168     }
169 
170     long rating        = -1;
171     bool xmpSupported  = hasXmp();
172     bool iptcSupported = hasIptc();
173     bool exivSupported = hasExif();
174 
175     foreach (const NamespaceEntry& entry, settings.getReadMapping(NamespaceEntry::DM_RATING_CONTAINER()))
176     {
177         if (entry.isDisabled)
178         {
179             continue;
180         }
181 
182         const std::string myStr = entry.namespaceName.toStdString();
183         const char* nameSpace   = myStr.data();
184         QString value;
185 
186         switch (entry.subspace)
187         {
188             case NamespaceEntry::XMP:
189             {
190                 if (xmpSupported)
191                 {
192                     value = getXmpTagString(nameSpace, false);
193                 }
194 
195                 break;
196             }
197 
198             case NamespaceEntry::IPTC:
199             {
200                 if (iptcSupported)
201                 {
202                     value = QString::fromUtf8(getIptcTagData(nameSpace));
203                 }
204 
205                 break;
206             }
207 
208             case NamespaceEntry::EXIF:
209             {
210                 if (exivSupported)
211                 {
212                     if (!getExifTagLong(nameSpace, rating))
213                     {
214                         continue;
215                     }
216                 }
217 
218                 break;
219             }
220 
221             default:
222             {
223                 break;
224             }
225         }
226 
227         if (!value.isEmpty())
228         {
229             bool ok = false;
230             rating  = value.toLong(&ok);
231 
232             if (!ok)
233             {
234                 return -1;
235             }
236         }
237 
238         int index = entry.convertRatio.indexOf(rating);
239 
240         // Exact value was not found,but rating is in range,
241         // so we try to approximate it
242 
243         if ((index == -1)                         &&
244             (rating > entry.convertRatio.first()) &&
245             (rating < entry.convertRatio.last()))
246         {
247             for (int i = 0 ; i < entry.convertRatio.size() ; ++i)
248             {
249                 if (rating > entry.convertRatio.at(i))
250                 {
251                     index = i;
252                 }
253             }
254         }
255 
256         if (index != -1)
257         {
258             return index;
259         }
260     }
261 
262     return -1;
263 }
264 
setItemPickLabel(int pickId) const265 bool DMetadata::setItemPickLabel(int pickId) const
266 {
267     if ((pickId < NoPickLabel) || (pickId > AcceptedLabel))
268     {
269         qCDebug(DIGIKAM_METAENGINE_LOG) << "Pick Label value to write is out of range!";
270         return false;
271     }
272 /*
273     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Pick Label: " << pickId;
274 */
275     if (supportXmp())
276     {
277         if (!setXmpTagString("Xmp.digiKam.PickLabel", QString::number(pickId)))
278         {
279             return false;
280         }
281     }
282 
283     return true;
284 }
285 
setItemColorLabel(int colorId,const DMetadataSettingsContainer & settings) const286 bool DMetadata::setItemColorLabel(int colorId, const DMetadataSettingsContainer& settings) const
287 {
288     if ((colorId < NoColorLabel) || (colorId > WhiteLabel))
289     {
290         qCDebug(DIGIKAM_METAENGINE_LOG) << "Color Label value to write is out of range!";
291 
292         return false;
293     }
294 /*
295     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Color Label: " << colorId;
296 */
297     QList<NamespaceEntry> toWrite = settings.getReadMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER());
298 
299     if (!settings.unifyReadWrite())
300     {
301         toWrite = settings.getWriteMapping(NamespaceEntry::DM_COLORLABEL_CONTAINER());
302     }
303 
304     for (const NamespaceEntry& entry : qAsConst(toWrite))
305     {
306         if (entry.isDisabled)
307         {
308             continue;
309         }
310 
311         const std::string myStr = entry.namespaceName.toStdString();
312         const char* nameSpace   = myStr.data();
313 
314         switch (entry.subspace)
315         {
316             case NamespaceEntry::XMP:
317             {
318                 if (!supportXmp())
319                 {
320                     continue;
321                 }
322 
323                 if (QLatin1String(nameSpace) == QLatin1String("Xmp.xmp.Label"))
324                 {
325                     // LightRoom use this XMP tags to store Color Labels name
326                     // Values are limited : see bug #358193.
327 
328                     QString LRLabel;
329 
330                     switch (colorId)
331                     {
332                         case BlueLabel:
333                         {
334                             LRLabel = QLatin1String("Blue");
335                             break;
336                         }
337 
338                         case GreenLabel:
339                         {
340                             LRLabel = QLatin1String("Green");
341                             break;
342                         }
343 
344                         case RedLabel:
345                         {
346                             LRLabel = QLatin1String("Red");
347                             break;
348                         }
349 
350                         case YellowLabel:
351                         {
352                             LRLabel = QLatin1String("Yellow");
353                             break;
354                         }
355 
356                         case MagentaLabel:
357                         {
358                             LRLabel = QLatin1String("Purple");
359                             break;
360                         }
361                     }
362 
363                     if (!LRLabel.isEmpty())
364                     {
365                         if (!setXmpTagString(nameSpace, LRLabel))
366                         {
367                             return false;
368                         }
369                     }
370                     else
371                     {
372                         removeXmpTag(nameSpace);
373                     }
374                 }
375                 else
376                 {
377                     if (!setXmpTagString(nameSpace, QString::number(colorId)))
378                     {
379                         return false;
380                     }
381                 }
382 
383                 break;
384             }
385 
386             case NamespaceEntry::EXIF:
387             {
388                 if (!setExifTagString(nameSpace, QString::number(colorId)))
389                 {
390                     return false;
391                 }
392 
393                 break;
394             }
395 
396             case NamespaceEntry::IPTC:
397             {
398                 break;
399             }
400 
401             default:
402             {
403                 break;
404             }
405         }
406     }
407 
408     return true;
409 }
410 
setItemRating(int rating,const DMetadataSettingsContainer & settings) const411 bool DMetadata::setItemRating(int rating, const DMetadataSettingsContainer& settings) const
412 {
413     // NOTE : with digiKam 0.9.x, we have used IPTC Urgency to store Rating.
414     // Now this way is obsolete, and we use standard XMP rating tag instead.
415 
416     if ((rating < RatingMin) || (rating > RatingMax))
417     {
418         qCDebug(DIGIKAM_METAENGINE_LOG) << "Rating value to write is out of range!";
419         return false;
420     }
421 /*
422     qCDebug(DIGIKAM_METAENGINE_LOG) << getFilePath() << " ==> Rating:" << rating;
423 */
424     QList<NamespaceEntry> toWrite = settings.getReadMapping(NamespaceEntry::DM_RATING_CONTAINER());
425 
426     if (!settings.unifyReadWrite())
427     {
428         toWrite = settings.getWriteMapping(NamespaceEntry::DM_RATING_CONTAINER());
429     }
430 
431     for (const NamespaceEntry& entry : qAsConst(toWrite))
432     {
433         if (entry.isDisabled)
434         {
435             continue;
436         }
437 
438         const std::string myStr = entry.namespaceName.toStdString();
439         const char* nameSpace   = myStr.data();
440 
441         switch (entry.subspace)
442         {
443             case NamespaceEntry::XMP:
444             {
445                 if (!supportXmp())
446                 {
447                     continue;
448                 }
449 
450                 if (!setXmpTagString(nameSpace, QString::number(entry.convertRatio.at(rating))))
451                 {
452                     qCDebug(DIGIKAM_METAENGINE_LOG) << "Setting rating failed" << nameSpace;
453                     return false;
454                 }
455 
456                 break;
457             }
458 
459             case NamespaceEntry::EXIF:
460             {
461                 if (!setExifTagLong(nameSpace, entry.convertRatio.at(rating)))
462                 {
463                     qCDebug(DIGIKAM_METAENGINE_LOG) << "Setting rating failed" << nameSpace;
464                     return false;
465                 }
466 
467                 break;
468             }
469 
470             case NamespaceEntry::IPTC: // IPTC rating deprecated
471             default:
472             {
473                 break;
474             }
475         }
476     }
477 
478     return true;
479 }
480 
481 } // namespace Digikam
482