Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libkexiv2/src/kexiv2xmp.cpp
Line
Count
Source
1
/*
2
    SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
3
    SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
4
5
    SPDX-License-Identifier: GPL-2.0-or-later
6
*/
7
8
// Local includes
9
10
#include "kexiv2.h"
11
#include "kexiv2_p.h"
12
#include "libkexiv2_debug.h"
13
14
namespace KExiv2Iface
15
{
16
17
bool KExiv2::canWriteXmp(const QString& filePath)
18
0
{
19
0
#ifdef _XMP_SUPPORT_
20
0
    try
21
0
    {
22
0
#if EXIV2_TEST_VERSION(0,28,0)
23
0
        Exiv2::Image::UniquePtr image =
24
#else
25
        Exiv2::Image::AutoPtr image = 
26
#endif
27
0
                                      Exiv2::ImageFactory::open((const char*)
28
0
                                      (QFile::encodeName(filePath).constData()));
29
30
0
        Exiv2::AccessMode mode = image->checkMode(Exiv2::mdXmp);
31
0
        return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
32
0
    }
33
0
    catch( Exiv2::Error& e )
34
0
    {
35
0
        std::string s(e.what());
36
0
        qCCritical(LIBKEXIV2_LOG) << "Cannot check Xmp access mode using Exiv2 (Error #"
37
0
#if EXIV2_TEST_VERSION(0,28,0)
38
0
                    << Exiv2::Error(e.code()).what()
39
#else
40
                    << e.code() << ": " << s.c_str()
41
#endif
42
0
                    << ")";
43
0
    }
44
0
    catch(...)
45
0
    {
46
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
47
0
    }
48
49
#else
50
51
    Q_UNUSED(filePath);
52
53
#endif // _XMP_SUPPORT_
54
55
0
    return false;
56
0
}
57
58
bool KExiv2::hasXmp() const
59
0
{
60
0
#ifdef _XMP_SUPPORT_
61
62
0
    return !d->xmpMetadata().empty();
63
64
#else
65
66
    return false;
67
68
#endif // _XMP_SUPPORT_
69
0
}
70
71
bool KExiv2::clearXmp() const
72
0
{
73
0
#ifdef _XMP_SUPPORT_
74
75
0
    try
76
0
    {
77
0
        d->xmpMetadata().clear();
78
0
        return true;
79
0
    }
80
0
    catch( Exiv2::Error& e )
81
0
    {
82
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Xmp data using Exiv2 "), e);
83
0
    }
84
0
    catch(...)
85
0
    {
86
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
87
0
    }
88
89
0
#endif // _XMP_SUPPORT_
90
91
0
    return false;
92
0
}
93
94
QByteArray KExiv2::getXmp() const
95
0
{
96
0
#ifdef _XMP_SUPPORT_
97
98
0
    try
99
0
    {
100
0
        if (!d->xmpMetadata().empty())
101
0
        {
102
103
0
            std::string xmpPacket;
104
0
            Exiv2::XmpParser::encode(xmpPacket, d->xmpMetadata());
105
0
            QByteArray data(xmpPacket.data(), xmpPacket.size());
106
0
            return data;
107
0
        }
108
0
    }
109
0
    catch( Exiv2::Error& e )
110
0
    {
111
0
        if (!d->filePath.isEmpty())
112
113
114
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp data using Exiv2 "), e);
115
0
    }
116
0
    catch(...)
117
0
    {
118
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
119
0
    }
120
121
0
#endif // _XMP_SUPPORT_
122
123
0
    return QByteArray();
124
0
}
125
126
bool KExiv2::setXmp(const QByteArray& data) const
127
0
{
128
0
#ifdef _XMP_SUPPORT_
129
130
0
    try
131
0
    {
132
0
        if (!data.isEmpty())
133
0
        {
134
0
            std::string xmpPacket;
135
0
            xmpPacket.assign(data.data(), data.size());
136
137
0
            if (Exiv2::XmpParser::decode(d->xmpMetadata(), xmpPacket) != 0)
138
0
                return false;
139
0
            else
140
0
                return true;
141
0
        }
142
0
    }
143
0
    catch( Exiv2::Error& e )
144
0
    {
145
0
        if (!d->filePath.isEmpty())
146
0
            qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
147
148
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp data using Exiv2 "), e);
149
0
    }
150
0
    catch(...)
151
0
    {
152
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
153
0
    }
154
155
#else
156
157
    Q_UNUSED(data);
158
159
#endif // _XMP_SUPPORT_
160
161
0
    return false;
162
0
}
163
164
KExiv2::MetaDataMap KExiv2::getXmpTagsDataList(const QStringList& xmpKeysFilter, bool invertSelection) const
165
0
{
166
0
#ifdef _XMP_SUPPORT_
167
168
0
    if (d->xmpMetadata().empty())
169
0
       return MetaDataMap();
170
171
0
    try
172
0
    {
173
0
        Exiv2::XmpData xmpData = d->xmpMetadata();
174
0
        xmpData.sortByKey();
175
176
0
        QString     ifDItemName;
177
0
        MetaDataMap metaDataMap;
178
179
0
        for (Exiv2::XmpData::iterator md = xmpData.begin(); md != xmpData.end(); ++md)
180
0
        {
181
0
            QString key = QString::fromLatin1(md->key().c_str());
182
183
            // Decode the tag value with a user friendly output.
184
0
            std::ostringstream os;
185
0
            os << *md;
186
0
            QString value = QString::fromUtf8(os.str().c_str());
187
188
            // If the tag is a language alternative type, parse content to detect language.
189
0
            if (md->typeId() == Exiv2::langAlt)
190
0
            {
191
0
                QString lang;
192
0
                value = detectLanguageAlt(value, lang);
193
0
            }
194
0
            else
195
0
            {
196
0
                value = QString::fromUtf8(os.str().c_str());
197
0
            }
198
199
            // To make a string just on one line.
200
0
            value.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
201
202
            // Some XMP key are redondancy. check if already one exist...
203
0
            MetaDataMap::iterator it = metaDataMap.find(key);
204
205
            // We apply a filter to get only the XMP tags that we need.
206
207
0
            if (!xmpKeysFilter.isEmpty())
208
0
            {
209
0
                if (!invertSelection)
210
0
                {
211
0
                    if (xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
212
0
                    {
213
0
                        if (it == metaDataMap.end())
214
0
                        {
215
0
                            metaDataMap.insert(key, value);
216
0
                        }
217
0
                        else
218
0
                        {
219
0
                            QString v = *it;
220
0
                            v.append(QString::fromLatin1(", "));
221
0
                            v.append(value);
222
0
                            metaDataMap.insert(key, v);
223
0
                        }
224
0
                    }
225
0
                }
226
0
                else
227
0
                {
228
0
                    if (!xmpKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
229
0
                    {
230
0
                        if (it == metaDataMap.end())
231
0
                        {
232
0
                            metaDataMap.insert(key, value);
233
0
                        }
234
0
                        else
235
0
                        {
236
0
                            QString v = *it;
237
0
                            v.append(QString::fromLatin1(", "));
238
0
                            v.append(value);
239
0
                            metaDataMap.insert(key, v);
240
0
                        }
241
0
                    }
242
0
                }
243
0
            }
244
0
            else // else no filter at all.
245
0
            {
246
0
                if (it == metaDataMap.end())
247
0
                {
248
0
                    metaDataMap.insert(key, value);
249
0
                }
250
0
                else
251
0
                {
252
0
                    QString v = *it;
253
0
                    v.append(QString::fromLatin1(", "));
254
0
                    v.append(value);
255
0
                    metaDataMap.insert(key, v);
256
0
                }
257
0
            }
258
0
        }
259
260
0
        return metaDataMap;
261
0
    }
262
0
    catch (Exiv2::Error& e)
263
0
    {
264
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Xmp metadata using Exiv2 "), e);
265
0
    }
266
0
    catch(...)
267
0
    {
268
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
269
0
    }
270
271
#else
272
273
    Q_UNUSED(xmpKeysFilter);
274
    Q_UNUSED(invertSelection);
275
276
#endif // _XMP_SUPPORT_
277
278
0
    return MetaDataMap();
279
0
}
280
281
QString KExiv2::getXmpTagTitle(const char* xmpTagName)
282
0
{
283
0
#ifdef _XMP_SUPPORT_
284
285
0
    try
286
0
    {
287
0
        std::string xmpkey(xmpTagName);
288
0
        Exiv2::XmpKey xk(xmpkey);
289
0
        return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyTitle(xk) );
290
0
    }
291
0
    catch (Exiv2::Error& e)
292
0
    {
293
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag title using Exiv2 "), e);
294
0
    }
295
0
    catch(...)
296
0
    {
297
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
298
0
    }
299
300
#else
301
302
    Q_UNUSED(xmpTagName);
303
304
#endif // _XMP_SUPPORT_
305
306
0
    return QString();
307
0
}
308
309
QString KExiv2::getXmpTagDescription(const char* xmpTagName)
310
0
{
311
0
#ifdef _XMP_SUPPORT_
312
0
    try
313
0
    {
314
0
        std::string xmpkey(xmpTagName);
315
0
        Exiv2::XmpKey xk(xmpkey);
316
0
        return QString::fromLocal8Bit( Exiv2::XmpProperties::propertyDesc(xk) );
317
0
    }
318
0
    catch (Exiv2::Error& e)
319
0
    {
320
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp metadata tag description using Exiv2 "), e);
321
0
    }
322
0
    catch(...)
323
0
    {
324
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
325
0
    }
326
327
#else
328
329
    Q_UNUSED(xmpTagName);
330
331
#endif // _XMP_SUPPORT_
332
333
0
    return QString();
334
0
}
335
336
QString KExiv2::getXmpTagString(const char* xmpTagName, bool escapeCR) const
337
0
{
338
0
#ifdef _XMP_SUPPORT_
339
340
0
    try
341
0
    {
342
0
        Exiv2::XmpData xmpData(d->xmpMetadata());
343
0
        Exiv2::XmpKey key(xmpTagName);
344
0
        Exiv2::XmpData::iterator it = xmpData.findKey(key);
345
346
0
        if (it != xmpData.end())
347
0
        {
348
0
            std::ostringstream os;
349
0
            os << *it;
350
0
            QString tagValue = QString::fromUtf8(os.str().c_str());
351
352
0
            if (escapeCR)
353
0
                tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
354
355
0
            return tagValue;
356
0
        }
357
0
    }
358
0
    catch( Exiv2::Error& e )
359
0
    {
360
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
361
0
    }
362
0
    catch(...)
363
0
    {
364
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
365
0
    }
366
367
#else
368
369
    Q_UNUSED(xmpTagName);
370
    Q_UNUSED(escapeCR);
371
372
#endif // _XMP_SUPPORT_
373
374
0
    return QString();
375
0
}
376
377
bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value, bool setProgramName) const
378
0
{
379
0
#ifdef _XMP_SUPPORT_
380
381
0
    if (!setProgramId(setProgramName))
382
0
        return false;
383
384
0
    try
385
0
    {
386
0
        const std::string &txt(value.toUtf8().constData());
387
0
#if EXIV2_TEST_VERSION(0,28,0)
388
0
        Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
389
#else
390
        Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::xmpText);
391
#endif
392
0
        xmpTxtVal->read(txt);
393
0
        d->xmpMetadata()[xmpTagName].setValue(xmpTxtVal.get());
394
0
        return true;
395
0
    }
396
0
    catch( Exiv2::Error& e )
397
0
    {
398
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
399
0
    }
400
0
    catch(...)
401
0
    {
402
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
403
0
    }
404
405
#else
406
407
    Q_UNUSED(xmpTagName);
408
    Q_UNUSED(value);
409
    Q_UNUSED(setProgramName);
410
411
#endif // _XMP_SUPPORT_
412
413
0
    return false;
414
0
}
415
bool KExiv2::setXmpTagString(const char* xmpTagName, const QString& value,
416
                             KExiv2::XmpTagType type, bool setProgramName) const
417
0
{
418
0
#ifdef _XMP_SUPPORT_
419
420
0
    if (!setProgramId(setProgramName))
421
0
        return false;
422
423
0
    try
424
0
    {
425
0
        const std::string &txt(value.toUtf8().constData());
426
0
        Exiv2::XmpTextValue xmpTxtVal("");
427
428
0
        if (type == KExiv2::NormalTag) // normal type
429
0
        {
430
0
            xmpTxtVal.read(txt);
431
0
            d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
432
0
            return true;
433
0
        }
434
435
0
        if (type == KExiv2::ArrayBagTag) // xmp type = bag
436
0
        {
437
0
            xmpTxtVal.setXmpArrayType(Exiv2::XmpValue::xaBag);
438
0
            xmpTxtVal.read("");
439
0
            d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
440
0
        }
441
442
0
        if (type == KExiv2::StructureTag) // xmp type = struct
443
0
        {
444
0
            xmpTxtVal.setXmpStruct();
445
0
            d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName),&xmpTxtVal);
446
0
        }
447
0
    }
448
0
    catch( Exiv2::Error& e )
449
0
    {
450
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string into image using Exiv2 "), e);
451
0
    }
452
0
    catch(...)
453
0
    {
454
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
455
0
    }
456
457
#else
458
459
    Q_UNUSED(xmpTagName);
460
    Q_UNUSED(value);
461
    Q_UNUSED(setProgramName);
462
463
#endif // _XMP_SUPPORT_
464
465
0
    return false;
466
0
}
467
KExiv2::AltLangMap KExiv2::getXmpTagStringListLangAlt(const char* xmpTagName, bool escapeCR) const
468
0
{
469
0
#ifdef _XMP_SUPPORT_
470
471
0
    try
472
0
    {
473
0
        Exiv2::XmpData xmpData = d->xmpMetadata();
474
475
0
        for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
476
0
        {
477
0
            if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
478
0
            {
479
0
                AltLangMap map;
480
0
                const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value());
481
482
0
                for (Exiv2::LangAltValue::ValueType::const_iterator it2 = value.value_.begin();
483
0
                     it2 != value.value_.end(); ++it2)
484
0
                {
485
0
                    QString lang = QString::fromUtf8(it2->first.c_str());
486
0
                    QString text = QString::fromUtf8(it2->second.c_str());
487
488
0
                    if (escapeCR)
489
0
                        text.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
490
491
0
                    map.insert(lang, text);
492
0
                }
493
494
0
                return map;
495
0
            }
496
0
        }
497
0
    }
498
0
    catch( Exiv2::Error& e )
499
0
    {
500
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
501
0
    }
502
0
    catch(...)
503
0
    {
504
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
505
0
    }
506
507
#else
508
509
    Q_UNUSED(xmpTagName);
510
    Q_UNUSED(escapeCR);
511
512
#endif // _XMP_SUPPORT_
513
514
0
    return AltLangMap();
515
0
}
516
517
bool KExiv2::setXmpTagStringListLangAlt(const char* xmpTagName, const KExiv2::AltLangMap& values,
518
                                        bool setProgramName) const
519
0
{
520
0
#ifdef _XMP_SUPPORT_
521
522
0
    if (!setProgramId(setProgramName))
523
0
        return false;
524
525
0
    try
526
0
    {
527
        // Remove old XMP alternative Language tag.
528
0
        removeXmpTag(xmpTagName);
529
530
0
        if (!values.isEmpty())
531
0
        {
532
0
#if EXIV2_TEST_VERSION(0,28,0)
533
0
            Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
534
#else
535
            Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
536
#endif
537
538
0
            for (AltLangMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
539
0
            {
540
0
                QString lang = it.key();
541
0
                QString text = it.value();
542
0
                QString txtLangAlt = QString::fromLatin1("lang=%1 %2").arg(lang).arg(text);
543
0
                const std::string &txt(txtLangAlt.toUtf8().constData());
544
0
                xmpTxtVal->read(txt);
545
0
            }
546
547
            // ...and add the new one instead.
548
0
            d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
549
0
        }
550
0
        return true;
551
0
    }
552
0
    catch( Exiv2::Error& e )
553
0
    {
554
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
555
0
    }
556
0
    catch(...)
557
0
    {
558
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
559
0
    }
560
561
#else
562
563
    Q_UNUSED(xmpTagName);
564
    Q_UNUSED(values);
565
    Q_UNUSED(setProgramName);
566
567
#endif // _XMP_SUPPORT_
568
569
0
    return false;
570
0
}
571
572
QString KExiv2::getXmpTagStringLangAlt(const char* xmpTagName, const QString& langAlt, bool escapeCR) const
573
0
{
574
0
#ifdef _XMP_SUPPORT_
575
576
0
    try
577
0
    {
578
0
        Exiv2::XmpData xmpData(d->xmpMetadata());
579
0
        Exiv2::XmpKey key(xmpTagName);
580
581
0
        for (Exiv2::XmpData::iterator it = xmpData.begin(); it != xmpData.end(); ++it)
582
0
        {
583
0
            if (it->key() == xmpTagName && it->typeId() == Exiv2::langAlt)
584
0
            {
585
0
                for (int i = 0; i < it->count(); i++)
586
0
                {
587
0
                    std::ostringstream os;
588
0
                    os << it->toString(i);
589
0
                    QString lang;
590
0
                    QString tagValue = QString::fromUtf8(os.str().c_str());
591
0
                    tagValue = detectLanguageAlt(tagValue, lang);
592
593
0
                    if (langAlt == lang)
594
0
                    {
595
0
                        if (escapeCR)
596
0
                            tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
597
598
0
                        return tagValue;
599
0
                    }
600
0
                }
601
0
            }
602
0
        }
603
0
    }
604
0
    catch( Exiv2::Error& e )
605
0
    {
606
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
607
0
    }
608
0
    catch(...)
609
0
    {
610
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
611
0
    }
612
613
#else
614
615
    Q_UNUSED(xmpTagName);
616
    Q_UNUSED(langAlt);
617
    Q_UNUSED(escapeCR);
618
619
#endif // _XMP_SUPPORT_
620
621
0
    return QString();
622
0
}
623
624
bool KExiv2::setXmpTagStringLangAlt(const char* xmpTagName, const QString& value,
625
                                    const QString& langAlt, bool setProgramName) const
626
0
{
627
0
#ifdef _XMP_SUPPORT_
628
629
0
    if (!setProgramId(setProgramName))
630
0
        return false;
631
632
0
    try
633
0
    {
634
0
        QString language(QString::fromLatin1("x-default")); // default alternative language.
635
636
0
        if (!langAlt.isEmpty())
637
0
            language = langAlt;
638
639
0
        QString txtLangAlt = QString(QString::fromLatin1("lang=%1 %2")).arg(language).arg(value);
640
641
0
        const std::string &txt(txtLangAlt.toUtf8().constData());
642
0
#if EXIV2_TEST_VERSION(0,28,0)
643
0
        Exiv2::Value::UniquePtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
644
#else
645
        Exiv2::Value::AutoPtr xmpTxtVal = Exiv2::Value::create(Exiv2::langAlt);
646
#endif
647
648
        // Search if an Xmp tag already exist.
649
650
0
        AltLangMap map = getXmpTagStringListLangAlt(xmpTagName, false);
651
652
0
        if (!map.isEmpty())
653
0
        {
654
0
            for (AltLangMap::iterator it = map.begin(); it != map.end(); ++it)
655
0
            {
656
0
                if (it.key() != langAlt)
657
0
                {
658
0
                    const std::string &val((*it).toUtf8().constData());
659
0
                    xmpTxtVal->read(val);
660
0
                    qCDebug(LIBKEXIV2_LOG) << *it;
661
0
                }
662
0
            }
663
0
        }
664
665
0
        xmpTxtVal->read(txt);
666
0
        removeXmpTag(xmpTagName);
667
0
        d->xmpMetadata().add(Exiv2::XmpKey(xmpTagName), xmpTxtVal.get());
668
0
        return true;
669
0
    }
670
0
    catch( Exiv2::Error& e )
671
0
    {
672
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string lang-alt into image using Exiv2 "), e);
673
0
    }
674
0
    catch(...)
675
0
    {
676
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
677
0
    }
678
679
#else
680
681
    Q_UNUSED(xmpTagName);
682
    Q_UNUSED(value);
683
    Q_UNUSED(langAlt);
684
    Q_UNUSED(setProgramName);
685
686
#endif // _XMP_SUPPORT_
687
688
0
    return false;
689
0
}
690
691
QStringList KExiv2::getXmpTagStringSeq(const char* xmpTagName, bool escapeCR) const
692
0
{
693
0
#ifdef _XMP_SUPPORT_
694
695
0
    try
696
0
    {
697
0
        Exiv2::XmpData xmpData(d->xmpMetadata());
698
0
        Exiv2::XmpKey key(xmpTagName);
699
0
        Exiv2::XmpData::iterator it = xmpData.findKey(key);
700
701
0
        if (it != xmpData.end())
702
0
        {
703
0
            if (it->typeId() == Exiv2::xmpSeq)
704
0
            {
705
0
                QStringList seq;
706
707
0
                for (int i = 0; i < it->count(); i++)
708
0
                {
709
0
                    std::ostringstream os;
710
0
                    os << it->toString(i);
711
0
                    QString seqValue = QString::fromUtf8(os.str().c_str());
712
713
0
                    if (escapeCR)
714
0
                        seqValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
715
716
0
                    seq.append(seqValue);
717
0
                }
718
0
                qCDebug(LIBKEXIV2_LOG) << "XMP String Seq (" << xmpTagName << "): " << seq;
719
720
0
                return seq;
721
0
            }
722
0
        }
723
0
    }
724
0
    catch( Exiv2::Error& e )
725
0
    {
726
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
727
0
    }
728
0
    catch(...)
729
0
    {
730
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
731
0
    }
732
733
#else
734
735
    Q_UNUSED(xmpTagName);
736
    Q_UNUSED(escapeCR);
737
738
#endif // _XMP_SUPPORT_
739
740
0
    return QStringList();
741
0
}
742
743
bool KExiv2::setXmpTagStringSeq(const char* xmpTagName, const QStringList& seq,
744
                                bool setProgramName) const
745
0
{
746
0
#ifdef _XMP_SUPPORT_
747
748
0
    if (!setProgramId(setProgramName))
749
0
        return false;
750
751
0
    try
752
0
    {
753
0
        if (seq.isEmpty())
754
0
        {
755
0
            removeXmpTag(xmpTagName);
756
0
        }
757
0
        else
758
0
        {
759
0
            const QStringList list = seq;
760
0
#if EXIV2_TEST_VERSION(0,28,0)
761
0
            Exiv2::Value::UniquePtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
762
#else
763
            Exiv2::Value::AutoPtr xmpTxtSeq = Exiv2::Value::create(Exiv2::xmpSeq);
764
#endif
765
766
0
            for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
767
0
            {
768
0
                const std::string &txt((*it).toUtf8().constData());
769
0
                xmpTxtSeq->read(txt);
770
0
            }
771
772
0
            d->xmpMetadata()[xmpTagName].setValue(xmpTxtSeq.get());
773
0
        }
774
0
        return true;
775
0
    }
776
0
    catch( Exiv2::Error& e )
777
0
    {
778
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Seq into image using Exiv2 "), e);
779
0
    }
780
0
    catch(...)
781
0
    {
782
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
783
0
    }
784
785
#else
786
787
    Q_UNUSED(xmpTagName);
788
    Q_UNUSED(seq);
789
    Q_UNUSED(setProgramName);
790
791
#endif // _XMP_SUPPORT_
792
793
0
    return false;
794
0
}
795
796
QStringList KExiv2::getXmpTagStringBag(const char* xmpTagName, bool escapeCR) const
797
0
{
798
0
#ifdef _XMP_SUPPORT_
799
800
0
    try
801
0
    {
802
0
        Exiv2::XmpData xmpData(d->xmpMetadata());
803
0
        Exiv2::XmpKey key(xmpTagName);
804
0
        Exiv2::XmpData::iterator it = xmpData.findKey(key);
805
806
0
        if (it != xmpData.end())
807
0
        {
808
0
            if (it->typeId() == Exiv2::xmpBag)
809
0
            {
810
0
                QStringList bag;
811
812
0
                for (int i = 0; i < it->count(); i++)
813
0
                {
814
0
                    std::ostringstream os;
815
0
                    os << it->toString(i);
816
0
                    QString bagValue = QString::fromUtf8(os.str().c_str());
817
818
0
                    if (escapeCR)
819
0
                        bagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
820
821
0
                    bag.append(bagValue);
822
0
                }
823
824
0
                return bag;
825
0
            }
826
0
        }
827
0
    }
828
0
    catch( Exiv2::Error& e )
829
0
    {
830
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
831
0
    }
832
0
    catch(...)
833
0
    {
834
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
835
0
    }
836
837
#else
838
839
    Q_UNUSED(xmpTagName);
840
    Q_UNUSED(escapeCR);
841
842
#endif // _XMP_SUPPORT_
843
844
0
    return QStringList();
845
0
}
846
847
bool KExiv2::setXmpTagStringBag(const char* xmpTagName, const QStringList& bag,
848
                                bool setProgramName) const
849
0
{
850
0
#ifdef _XMP_SUPPORT_
851
852
0
    if (!setProgramId(setProgramName))
853
0
        return false;
854
855
0
    try
856
0
    {
857
0
        if (bag.isEmpty())
858
0
        {
859
0
            removeXmpTag(xmpTagName);
860
0
        }
861
0
        else
862
0
        {
863
0
            QStringList list = bag;
864
0
#if EXIV2_TEST_VERSION(0,28,0)
865
0
            Exiv2::Value::UniquePtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
866
#else
867
            Exiv2::Value::AutoPtr xmpTxtBag = Exiv2::Value::create(Exiv2::xmpBag);
868
#endif
869
870
0
            for (QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
871
0
            {
872
0
                const std::string &txt((*it).toUtf8().constData());
873
0
                xmpTxtBag->read(txt);
874
0
            }
875
876
0
            d->xmpMetadata()[xmpTagName].setValue(xmpTxtBag.get());
877
0
        }
878
0
        return true;
879
0
    }
880
0
    catch( Exiv2::Error& e )
881
0
    {
882
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Xmp tag string Bag into image using Exiv2 "), e);
883
0
    }
884
0
    catch(...)
885
0
    {
886
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
887
0
    }
888
889
#else
890
891
    Q_UNUSED(xmpTagName);
892
    Q_UNUSED(bag);
893
    Q_UNUSED(setProgramName);
894
895
#endif // _XMP_SUPPORT_
896
897
0
    return false;
898
0
}
899
900
bool KExiv2::addToXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToAdd,
901
                                     bool setProgramName) const
902
0
{
903
0
    if (!setProgramId(setProgramName))
904
0
        return false;
905
906
0
    QStringList oldEntries = getXmpTagStringBag(xmpTagName, false);
907
0
    QStringList newEntries = entriesToAdd;
908
909
    // Create a list of keywords including old one which already exists.
910
0
    for (QStringList::const_iterator it = oldEntries.constBegin(); it != oldEntries.constEnd(); ++it )
911
0
    {
912
0
        if (!newEntries.contains(*it))
913
0
            newEntries.append(*it);
914
0
    }
915
916
0
    if (setXmpTagStringBag(xmpTagName, newEntries, false))
917
0
        return true;
918
919
0
    return false;
920
0
}
921
922
bool KExiv2::removeFromXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToRemove,
923
                                       bool setProgramName) const
924
0
{
925
0
    if (!setProgramId(setProgramName))
926
0
        return false;
927
928
0
    QStringList currentEntries = getXmpTagStringBag(xmpTagName, false);
929
0
    QStringList newEntries;
930
931
    // Create a list of current keywords except those that shall be removed
932
0
    for (QStringList::const_iterator it = currentEntries.constBegin(); it != currentEntries.constEnd(); ++it )
933
0
    {
934
0
        if (!entriesToRemove.contains(*it))
935
0
            newEntries.append(*it);
936
0
    }
937
938
0
    if (setXmpTagStringBag(xmpTagName, newEntries, false))
939
0
        return true;
940
941
0
    return false;
942
0
}
943
944
QVariant KExiv2::getXmpTagVariant(const char* xmpTagName, bool rationalAsListOfInts, bool stringEscapeCR) const
945
0
{
946
0
#ifdef _XMP_SUPPORT_
947
0
    try
948
0
    {
949
0
        Exiv2::XmpData xmpData(d->xmpMetadata());
950
0
        Exiv2::XmpKey key(xmpTagName);
951
0
        Exiv2::XmpData::iterator it = xmpData.findKey(key);
952
953
0
        if (it != xmpData.end())
954
0
        {
955
0
            switch (it->typeId())
956
0
            {
957
0
                case Exiv2::unsignedByte:
958
0
                case Exiv2::unsignedShort:
959
0
                case Exiv2::unsignedLong:
960
0
                case Exiv2::signedShort:
961
0
                case Exiv2::signedLong:
962
0
#if EXIV2_TEST_VERSION(0,28,0)
963
0
                    return QVariant((int)it->toUint32());
964
#else
965
                    return QVariant((int)it->toLong());
966
#endif
967
0
                case Exiv2::unsignedRational:
968
0
                case Exiv2::signedRational:
969
0
                    if (rationalAsListOfInts)
970
0
                    {
971
0
                        QList<QVariant> list;
972
0
                        list << (*it).toRational().first;
973
0
                        list << (*it).toRational().second;
974
0
                        return QVariant(list);
975
0
                    }
976
0
                    else
977
0
                    {
978
                        // prefer double precision
979
0
                        double num = (*it).toRational().first;
980
0
                        double den = (*it).toRational().second;
981
982
0
                        if (den == 0.0)
983
0
                            return QVariant(QMetaType(QMetaType::Double));
984
985
0
                        return QVariant(num / den);
986
0
                    }
987
0
                case Exiv2::date:
988
0
                case Exiv2::time:
989
0
                {
990
0
                    QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
991
0
                    return QVariant(dateTime);
992
0
                }
993
0
                case Exiv2::asciiString:
994
0
                case Exiv2::comment:
995
0
                case Exiv2::string:
996
0
                {
997
0
                    std::ostringstream os;
998
0
                    os << *it;
999
0
                    QString tagValue = QString::fromLocal8Bit(os.str().c_str());
1000
1001
0
                    if (stringEscapeCR)
1002
0
                        tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
1003
1004
0
                    return QVariant(tagValue);
1005
0
                }
1006
0
                case Exiv2::xmpText:
1007
0
                {
1008
0
                    std::ostringstream os;
1009
0
                    os << *it;
1010
0
                    QString tagValue = QString::fromUtf8(os.str().c_str());
1011
1012
0
                    if (stringEscapeCR)
1013
0
                        tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
1014
1015
0
                    return tagValue;
1016
0
                }
1017
0
                case Exiv2::xmpBag:
1018
0
                case Exiv2::xmpSeq:
1019
0
                case Exiv2::xmpAlt:
1020
0
                {
1021
0
                    QStringList list;
1022
1023
0
                    for (int i=0; i < it->count(); i++)
1024
0
                    {
1025
0
                        list << QString::fromUtf8(it->toString(i).c_str());
1026
0
                    }
1027
1028
0
                    return list;
1029
0
                }
1030
0
                case Exiv2::langAlt:
1031
0
                {
1032
                    // access the value directly
1033
0
                    const Exiv2::LangAltValue &value = static_cast<const Exiv2::LangAltValue &>(it->value());
1034
0
                    QMap<QString, QVariant> map;
1035
                    // access the ValueType std::map< std::string, std::string>
1036
0
                    Exiv2::LangAltValue::ValueType::const_iterator i;
1037
1038
0
                    for (i = value.value_.begin(); i != value.value_.end(); ++i)
1039
0
                    {
1040
0
                        map[QString::fromUtf8(i->first.c_str())] = QString::fromUtf8(i->second.c_str());
1041
0
                    }
1042
1043
0
                    return map;
1044
0
                }
1045
0
                default:
1046
0
                    break;
1047
0
            }
1048
0
        }
1049
0
    }
1050
0
    catch( Exiv2::Error& e )
1051
0
    {
1052
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Xmp key '%1' into image using Exiv2 ").arg(QString::fromLatin1(xmpTagName)), e);
1053
0
    }
1054
0
    catch(...)
1055
0
    {
1056
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1057
0
    }
1058
1059
#else
1060
1061
    Q_UNUSED(xmpTagName);
1062
    Q_UNUSED(rationalAsListOfInts);
1063
    Q_UNUSED(stringEscapeCR);
1064
1065
#endif // _XMP_SUPPORT_
1066
1067
0
    return QVariant();
1068
0
}
1069
1070
bool KExiv2::registerXmpNameSpace(const QString& uri, const QString& prefix)
1071
0
{
1072
0
#ifdef _XMP_SUPPORT_
1073
1074
0
    try
1075
0
    {
1076
0
        QString ns = uri;
1077
1078
0
        if (!uri.endsWith(QString::fromLatin1("/")))
1079
0
            ns.append(QString::fromLatin1("/"));
1080
1081
0
        Exiv2::XmpProperties::registerNs(ns.toLatin1().constData(), prefix.toLatin1().constData());
1082
0
        return true;
1083
0
    }
1084
0
    catch( Exiv2::Error& e )
1085
0
    {
1086
0
        KExiv2Private::printExiv2ExceptionError(QString::fromLatin1("Cannot register a new Xmp namespace using Exiv2 "), e);
1087
0
    }
1088
0
    catch(...)
1089
0
    {
1090
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1091
0
    }
1092
1093
#else
1094
1095
    Q_UNUSED(uri);
1096
    Q_UNUSED(prefix);
1097
1098
#endif // _XMP_SUPPORT_
1099
1100
0
    return false;
1101
0
}
1102
1103
bool KExiv2::unregisterXmpNameSpace(const QString& uri)
1104
0
{
1105
0
#ifdef _XMP_SUPPORT_
1106
1107
0
    try
1108
0
    {
1109
0
        QString ns = uri;
1110
1111
0
        if (!uri.endsWith(QString::fromLatin1("/")))
1112
0
            ns.append(QString::fromLatin1("/"));
1113
1114
0
        Exiv2::XmpProperties::unregisterNs(ns.toLatin1().constData());
1115
0
        return true;
1116
0
    }
1117
0
    catch( Exiv2::Error& e )
1118
0
    {
1119
0
        KExiv2Private::printExiv2ExceptionError(QString::fromLatin1("Cannot unregister a new Xmp namespace using Exiv2 "), e);
1120
0
    }
1121
0
    catch(...)
1122
0
    {
1123
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1124
0
    }
1125
1126
#else
1127
1128
    Q_UNUSED(uri);
1129
1130
#endif // _XMP_SUPPORT_
1131
1132
0
    return false;
1133
0
}
1134
1135
bool KExiv2::removeXmpTag(const char* xmpTagName, bool setProgramName) const
1136
0
{
1137
0
#ifdef _XMP_SUPPORT_
1138
1139
0
    if (!setProgramId(setProgramName))
1140
0
        return false;
1141
1142
0
    try
1143
0
    {
1144
0
        Exiv2::XmpKey xmpKey(xmpTagName);
1145
0
        Exiv2::XmpData::iterator it = d->xmpMetadata().findKey(xmpKey);
1146
1147
0
        if (it != d->xmpMetadata().end())
1148
0
        {
1149
0
            d->xmpMetadata().erase(it);
1150
0
            return true;
1151
0
        }
1152
0
    }
1153
0
    catch( Exiv2::Error& e )
1154
0
    {
1155
0
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Xmp tag using Exiv2 "), e);
1156
0
    }
1157
0
    catch(...)
1158
0
    {
1159
0
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
1160
0
    }
1161
1162
#else
1163
1164
    Q_UNUSED(xmpTagName);
1165
    Q_UNUSED(setProgramName);
1166
1167
#endif // _XMP_SUPPORT_
1168
1169
0
    return false;
1170
0
}
1171
1172
QStringList KExiv2::getXmpKeywords() const
1173
0
{
1174
0
    return (getXmpTagStringBag("Xmp.dc.subject", false));
1175
0
}
1176
1177
bool KExiv2::setXmpKeywords(const QStringList& newKeywords, bool setProgramName) const
1178
0
{
1179
0
    return addToXmpTagStringBag("Xmp.dc.subject", newKeywords, setProgramName);
1180
0
}
1181
1182
bool KExiv2::removeXmpKeywords(const QStringList& keywordsToRemove, bool setProgramName)
1183
0
{
1184
0
    return removeFromXmpTagStringBag("Xmp.dc.subject", keywordsToRemove, setProgramName);
1185
0
}
1186
1187
QStringList KExiv2::getXmpSubCategories() const
1188
0
{
1189
0
    return (getXmpTagStringBag("Xmp.photoshop.SupplementalCategories", false));
1190
0
}
1191
1192
bool KExiv2::setXmpSubCategories(const QStringList& newSubCategories, bool setProgramName) const
1193
0
{
1194
0
    return addToXmpTagStringBag("Xmp.photoshop.SupplementalCategories", newSubCategories, setProgramName);
1195
0
}
1196
1197
bool KExiv2::removeXmpSubCategories(const QStringList& subCategoriesToRemove, bool setProgramName)
1198
0
{
1199
0
    return removeFromXmpTagStringBag("Xmp.photoshop.SupplementalCategories", subCategoriesToRemove, setProgramName);
1200
0
}
1201
1202
QStringList KExiv2::getXmpSubjects() const
1203
0
{
1204
0
    return (getXmpTagStringBag("Xmp.iptc.SubjectCode", false));
1205
0
}
1206
1207
bool KExiv2::setXmpSubjects(const QStringList& newSubjects, bool setProgramName) const
1208
0
{
1209
0
    return addToXmpTagStringBag("Xmp.iptc.SubjectCode", newSubjects, setProgramName);
1210
0
}
1211
1212
bool KExiv2::removeXmpSubjects(const QStringList& subjectsToRemove, bool setProgramName)
1213
0
{
1214
0
    return removeFromXmpTagStringBag("Xmp.iptc.SubjectCode", subjectsToRemove, setProgramName);
1215
0
}
1216
1217
KExiv2::TagsMap KExiv2::getXmpTagsList() const
1218
0
{
1219
0
    TagsMap tagsMap;
1220
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("dc"),             tagsMap);
1221
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("digiKam"),        tagsMap);
1222
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmp"),            tagsMap);
1223
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpRights"),      tagsMap);
1224
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpMM"),          tagsMap);
1225
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpBJ"),          tagsMap);
1226
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpTPg"),         tagsMap);
1227
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("xmpDM"),          tagsMap);
1228
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("MicrosoftPhoto"), tagsMap);
1229
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("pdf"),            tagsMap);
1230
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("photoshop"),      tagsMap);
1231
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("crs"),            tagsMap);
1232
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("tiff"),           tagsMap);
1233
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("exif"),           tagsMap);
1234
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("aux"),            tagsMap);
1235
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("iptc"),           tagsMap);
1236
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("iptcExt"),        tagsMap);
1237
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("plus"),           tagsMap);
1238
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("mwg-rs"),         tagsMap);
1239
0
    d->getXMPTagsListFromPrefix(QString::fromLatin1("dwc"),            tagsMap);
1240
0
    return tagsMap;
1241
0
}
1242
1243
}  // NameSpace KExiv2Iface