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