kexiv2iptc.cpp 25.1 KB
Newer Older
Gilles Caulier's avatar
Gilles Caulier committed
1
/** ===========================================================
2
 * @file
3
 *
Gilles Caulier's avatar
cleanup    
Gilles Caulier committed
4
5
 * This file is a part of KDE project
 *
6
 *
Gilles Caulier's avatar
Gilles Caulier committed
7
8
 * @date   2006-09-15
 * @brief  Iptc manipulation methods
9
 *
Gilles Caulier's avatar
Gilles Caulier committed
10
 * @author Copyright (C) 2006-2015 by Gilles Caulier
Gilles Caulier's avatar
Gilles Caulier committed
11
 *         <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
12
 * @author Copyright (C) 2006-2012 by Marcel Wiesweg
Gilles Caulier's avatar
Gilles Caulier committed
13
 *         <a href="mailto:marcel dot wiesweg at gmx dot de">marcel dot wiesweg at gmx dot de</a>
14
15
16
17
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
Gilles Caulier's avatar
Gilles Caulier committed
18
19
 * either version 2, or (at your option)
 * any later version.
Gilles Caulier's avatar
Gilles Caulier committed
20
 *
21
22
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Gilles Caulier's avatar
Gilles Caulier committed
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU General Public License for more details.
Gilles Caulier's avatar
Gilles Caulier committed
25
 *
26
27
 * ============================================================ */

Gilles Caulier's avatar
polish    
Gilles Caulier committed
28
29
// Local includes

30
#include "kexiv2.h"
Michael Georg Hansen's avatar
Michael Georg Hansen committed
31
#include "kexiv2_p.h"
Gilles Caulier's avatar
Gilles Caulier committed
32
#include "libkexiv2_debug.h"
33
34
35
36

namespace KExiv2Iface
{

37
38
39
40
41
bool KExiv2::canWriteIptc(const QString& filePath)
{
    try
    {
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)
42
                                      (QFile::encodeName(filePath).constData()));
43

Gilles Caulier's avatar
polish    
Gilles Caulier committed
44
        Exiv2::AccessMode mode = image->checkMode(Exiv2::mdIptc);
45
46
        return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
    }
47
    catch(Exiv2::Error& e)
48
49
    {
        std::string s(e.what());
Gilles Caulier's avatar
Gilles Caulier committed
50
51
        qCCritical(LIBKEXIV2_LOG) << "Cannot check Iptc access mode using Exiv2 (Error #"
                                  << e.code() << ": " << s.c_str() << ")";
52
    }
53
54
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
55
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
56
    }
57
58
59
60

    return false;
}

Gilles Caulier's avatar
Gilles Caulier committed
61
bool KExiv2::hasIptc() const
62
{
63
    return !d->iptcMetadata().empty();
64
65
}

Gilles Caulier's avatar
Gilles Caulier committed
66
bool KExiv2::clearIptc() const
67
68
69
{
    try
    {
70
        d->iptcMetadata().clear();
71
72
        return true;
    }
73
    catch(Exiv2::Error& e)
74
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
75
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Iptc data using Exiv2 "), e);
76
    }
77
78
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
79
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
80
    }
81
82
83
84
85
86
87
88

    return false;
}

QByteArray KExiv2::getIptc(bool addIrbHeader) const
{
    try
    {
89
        if (!d->iptcMetadata().empty())
90
        {
91
            Exiv2::IptcData& iptc = d->iptcMetadata();
92
93
            Exiv2::DataBuf c2;

Andi Clemens's avatar
Andi Clemens committed
94
            if (addIrbHeader)
95
            {
Albert Astals Cid's avatar
Albert Astals Cid committed
96
                c2 = Exiv2::Photoshop::setIptcIrb(nullptr, 0, iptc);
97
            }
Andi Clemens's avatar
Andi Clemens committed
98
            else
99
            {
100
                c2 = Exiv2::IptcParser::encode(d->iptcMetadata());
101
            }
102
103
104
105
106
107

            QByteArray data((const char*)c2.pData_, c2.size_);
            return data;

        }
    }
108
    catch(Exiv2::Error& e)
109
110
    {
        if (!d->filePath.isEmpty())
111
        {
Gilles Caulier's avatar
Gilles Caulier committed
112
            qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
113
        }
114

Gilles Caulier's avatar
port++    
Gilles Caulier committed
115
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Iptc data using Exiv2 "), e);
116
    }
117
118
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
119
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
120
    }
121
122
123
124

    return QByteArray();
}

Gilles Caulier's avatar
Gilles Caulier committed
125
bool KExiv2::setIptc(const QByteArray& data) const
126
127
128
129
130
{
    try
    {
        if (!data.isEmpty())
        {
131
132
            Exiv2::IptcParser::decode(d->iptcMetadata(), (const Exiv2::byte*)data.data(), data.size());
            return (!d->iptcMetadata().empty());
133
134
        }
    }
135
    catch(Exiv2::Error& e)
136
137
    {
        if (!d->filePath.isEmpty())
138
        {
Gilles Caulier's avatar
Gilles Caulier committed
139
            qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
140
        }
141

Gilles Caulier's avatar
port++    
Gilles Caulier committed
142
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc data using Exiv2 "), e);
143
    }
144
145
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
146
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
147
    }
148
149
150
151

    return false;
}

152
KExiv2::MetaDataMap KExiv2::getIptcTagsDataList(const QStringList& iptcKeysFilter, bool invertSelection) const
153
{
154
    if (d->iptcMetadata().empty())
155
156
157
158
       return MetaDataMap();

    try
    {
159
        Exiv2::IptcData iptcData = d->iptcMetadata();
160
        iptcData.sortByKey();
Gilles Caulier's avatar
Gilles Caulier committed
161

162
163
164
165
166
        QString     ifDItemName;
        MetaDataMap metaDataMap;

        for (Exiv2::IptcData::iterator md = iptcData.begin(); md != iptcData.end(); ++md)
        {
167
            QString key = QString::fromLocal8Bit(md->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
168

169
170
171
            // Decode the tag value with a user friendly output.
            std::ostringstream os;
            os << *md;
172
173
174

            QString value;

Gilles Caulier's avatar
port++    
Gilles Caulier committed
175
            if (key == QString::fromLatin1("Iptc.Envelope.CharacterSet"))
176
            {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
177
                value = QString::fromLatin1(iptcData.detectCharset());
178
179
180
181
182
183
            }
            else 
            {
                value = QString::fromUtf8(os.str().c_str());
            }

184
            // To make a string just on one line.
Gilles Caulier's avatar
port++    
Gilles Caulier committed
185
            value.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
186

187
            // Some Iptc key are redondancy. check if already one exist...
188
189
            MetaDataMap::iterator it = metaDataMap.find(key);

190
            // We apply a filter to get only the Iptc tags that we need.
191

192
            if (!iptcKeysFilter.isEmpty())
193
            {
194
                if (!invertSelection)
195
                {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
196
                    if (iptcKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
197
                    {
198
199
200
201
202
203
204
                        if (it == metaDataMap.end())
                        {
                            metaDataMap.insert(key, value);
                        }
                        else
                        {
                            QString v = *it;
Gilles Caulier's avatar
port++    
Gilles Caulier committed
205
                            v.append(QString::fromLatin1(", "));
206
207
208
                            v.append(value);
                            metaDataMap.insert(key, v);
                        }
Gilles Caulier's avatar
Gilles Caulier committed
209
                    }
210
                }
211
                else
212
                {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
213
                    if (!iptcKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
214
                    {
215
216
217
218
219
220
221
                        if (it == metaDataMap.end())
                        {
                            metaDataMap.insert(key, value);
                        }
                        else
                        {
                            QString v = *it;
Gilles Caulier's avatar
port++    
Gilles Caulier committed
222
                            v.append(QString::fromLatin1(", "));
223
224
225
                            v.append(value);
                            metaDataMap.insert(key, v);
                        }
Gilles Caulier's avatar
Gilles Caulier committed
226
                    }
227
228
                }
            }
229
230
231
232
233
234
235
236
237
            else // else no filter at all.
            {
                if (it == metaDataMap.end())
                {
                    metaDataMap.insert(key, value);
                }
                else
                {
                    QString v = *it;
Gilles Caulier's avatar
port++    
Gilles Caulier committed
238
                    v.append(QString::fromLatin1(", "));
239
240
241
242
243
                    v.append(value);
                    metaDataMap.insert(key, v);
                    }
            }

244
245
246
247
248
249
        }

        return metaDataMap;
    }
    catch (Exiv2::Error& e)
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
250
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse Iptc metadata using Exiv2 "), e);
251
    }
252
253
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
254
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
255
    }
256
257
258
259

    return MetaDataMap();
}

260
QString KExiv2::getIptcTagTitle(const char* iptcTagName)
261
{
Andi Clemens's avatar
Andi Clemens committed
262
    try
263
264
    {
        std::string iptckey(iptcTagName);
Andi Clemens's avatar
Andi Clemens committed
265
        Exiv2::IptcKey ik(iptckey);
266
267
        return QString::fromLocal8Bit( Exiv2::IptcDataSets::dataSetTitle(ik.tag(), ik.record()) );
    }
Andi Clemens's avatar
Andi Clemens committed
268
    catch (Exiv2::Error& e)
269
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
270
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag title using Exiv2 "), e);
271
    }
272
273
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
274
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
275
    }
276
277
278
279

    return QString();
}

280
QString KExiv2::getIptcTagDescription(const char* iptcTagName)
281
{
Gilles Caulier's avatar
Gilles Caulier committed
282
    try
283
284
    {
        std::string iptckey(iptcTagName);
Andi Clemens's avatar
Andi Clemens committed
285
        Exiv2::IptcKey ik(iptckey);
286
287
        return QString::fromLocal8Bit( Exiv2::IptcDataSets::dataSetDesc(ik.tag(), ik.record()) );
    }
Andi Clemens's avatar
Andi Clemens committed
288
    catch (Exiv2::Error& e)
289
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
290
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag description using Exiv2 "), e);
291
    }
292
293
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
294
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
295
    }
296
297
298
299

    return QString();
}

300
bool KExiv2::removeIptcTag(const char* iptcTagName, bool setProgramName) const
301
302
303
304
305
{
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
306
    {
307
        Exiv2::IptcData::iterator it = d->iptcMetadata().begin();
308
309
        int i                        = 0;

310
        while(it != d->iptcMetadata().end())
311
        {
312
            QString key = QString::fromLocal8Bit(it->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
313

Gilles Caulier's avatar
port++    
Gilles Caulier committed
314
            if (key == QString::fromLatin1(iptcTagName))
315
            {
316
                it = d->iptcMetadata().erase(it);
317
318
319
320
                ++i;
            }
            else
            {
321
                ++it;
322
            }
Gilles Caulier's avatar
Gilles Caulier committed
323
        };
324

325
326
        if (i > 0)
            return true;
327
    }
328
    catch(Exiv2::Error& e)
329
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
330
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Iptc tag using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
331
    }
332
333
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
334
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
335
    }
Gilles Caulier's avatar
Gilles Caulier committed
336

337
338
339
    return false;
}

340
bool KExiv2::setIptcTagData(const char* iptcTagName, const QByteArray& data, bool setProgramName) const
341
342
343
{
    if (data.isEmpty())
        return false;
Gilles Caulier's avatar
Gilles Caulier committed
344

345
346
347
348
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
349
    {
350
        Exiv2::DataValue val((Exiv2::byte *)data.data(), data.size());
351
        d->iptcMetadata()[iptcTagName] = val;
352
353
        return true;
    }
354
    catch(Exiv2::Error& e)
355
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
356
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc tag data into image using Exiv2 "), e);
357
    }
358
359
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
360
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
361
    }
362
363
364
365

    return false;
}

366
QByteArray KExiv2::getIptcTagData(const char* iptcTagName) const
367
368
369
{
    try
    {
Gilles Caulier's avatar
polish    
Gilles Caulier committed
370
        Exiv2::IptcKey  iptcKey(iptcTagName);
371
        Exiv2::IptcData iptcData(d->iptcMetadata());
372
        Exiv2::IptcData::iterator it = iptcData.findKey(iptcKey);
373

374
375
        if (it != iptcData.end())
        {
376
            char* const s = new char[(*it).size()];
377
            (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
Gilles Caulier's avatar
Gilles Caulier committed
378
            QByteArray data(s, (*it).size());
Andi Clemens's avatar
Andi Clemens committed
379
            delete [] s;
380
381
382
            return data;
        }
    }
383
    catch(Exiv2::Error& e)
384
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
385
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image using Exiv2 ").arg(QString::fromLatin1(iptcTagName)), e);
386
    }
387
388
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
389
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
390
    }
391
392
393
394
395
396
397
398

    return QByteArray();
}

QString KExiv2::getIptcTagString(const char* iptcTagName, bool escapeCR) const
{
    try
    {
Gilles Caulier's avatar
polish    
Gilles Caulier committed
399
        Exiv2::IptcKey  iptcKey(iptcTagName);
400
        Exiv2::IptcData iptcData(d->iptcMetadata());
401
        Exiv2::IptcData::iterator it = iptcData.findKey(iptcKey);
402

403
404
405
406
        if (it != iptcData.end())
        {
            std::ostringstream os;
            os << *it;
Gilles Caulier's avatar
port++    
Gilles Caulier committed
407
            QString tagValue(QString::fromLatin1(os.str().c_str()));
408
409

            if (escapeCR)
Gilles Caulier's avatar
port++    
Gilles Caulier committed
410
                tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
411
412
413
414

            return tagValue;
        }
    }
415
    catch(Exiv2::Error& e)
416
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
417
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image using Exiv2 ").arg(QString::fromLatin1(iptcTagName)), e);
418
    }
419
420
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
421
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
422
    }
423
424
425
426

    return QString();
}

427
bool KExiv2::setIptcTagString(const char* iptcTagName, const QString& value, bool setProgramName) const
428
429
430
431
432
433
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
434
435
436
437
        d->iptcMetadata()[iptcTagName] = std::string(value.toUtf8().constData());

        // Make sure we have set the charset to UTF-8
        d->iptcMetadata()["Iptc.Envelope.CharacterSet"] = "\33%G";
438
439
        return true;
    }
440
    catch(Exiv2::Error& e)
441
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
442
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc tag string into image using Exiv2 "), e);
443
    }
444
445
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
446
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
447
    }
448
449
450
451
452

    return false;
}

QStringList KExiv2::getIptcTagsStringList(const char* iptcTagName, bool escapeCR) const
453
454
{
    try
Gilles Caulier's avatar
Gilles Caulier committed
455
    {
456
        if (!d->iptcMetadata().empty())
457
        {
458
            QStringList values;
459
            Exiv2::IptcData iptcData(d->iptcMetadata());
460
461
462
463

            for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
            {
                QString key = QString::fromLocal8Bit(it->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
464

Gilles Caulier's avatar
port++    
Gilles Caulier committed
465
                if (key == QString::fromLatin1(iptcTagName))
466
                {
467
                    QString tagValue = QString::fromUtf8(it->toString().c_str());
468
469

                    if (escapeCR)
Gilles Caulier's avatar
port++    
Gilles Caulier committed
470
                        tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
471
472
473
474

                    values.append(tagValue);
                }
            }
Gilles Caulier's avatar
Gilles Caulier committed
475

476
477
478
            return values;
        }
    }
479
    catch(Exiv2::Error& e)
480
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
481
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Iptc key '%1' into image using Exiv2 ").arg(QString::fromLatin1(iptcTagName)), e);
Gilles Caulier's avatar
Gilles Caulier committed
482
    }
483
484
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
485
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
486
    }
Gilles Caulier's avatar
Gilles Caulier committed
487

488
489
490
    return QStringList();
}

491
bool KExiv2::setIptcTagsStringList(const char* iptcTagName, int maxSize,
Andi Clemens's avatar
Andi Clemens committed
492
                                   const QStringList& oldValues, const QStringList& newValues,
493
                                   bool setProgramName) const
494
495
496
497
498
{
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
499
    {
500
501
        QStringList oldvals = oldValues;
        QStringList newvals = newValues;
Gilles Caulier's avatar
Gilles Caulier committed
502

Gilles Caulier's avatar
Gilles Caulier committed
503
        qCDebug(LIBKEXIV2_LOG) << d->filePath.toLatin1().constData() << " : " << iptcTagName
Gilles Caulier's avatar
port++    
Gilles Caulier committed
504
                 << " => " << newvals.join(QString::fromLatin1(",")).toLatin1().constData();
Gilles Caulier's avatar
Gilles Caulier committed
505

506
        // Remove all old values.
507
        Exiv2::IptcData iptcData(d->iptcMetadata());
508
509
510
511
512
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
513
            QString val = QString::fromUtf8(it->toString().c_str());
514
515

            // Also remove new values to avoid duplicates. They will be added again below.
Gilles Caulier's avatar
port++    
Gilles Caulier committed
516
            if ( key == QString::fromLatin1(iptcTagName) &&
517
518
519
                 (oldvals.contains(val) || newvals.contains(val))
               )
                it = iptcData.erase(it);
Andi Clemens's avatar
Andi Clemens committed
520
            else
521
522
523
524
525
526
527
528
529
530
531
                ++it;
        };

        // Add new values.

        Exiv2::IptcKey iptcTag(iptcTagName);

        for (QStringList::iterator it = newvals.begin(); it != newvals.end(); ++it)
        {
            QString key = *it;
            key.truncate(maxSize);
Gilles Caulier's avatar
Gilles Caulier committed
532

533
            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
534
            val->read(key.toUtf8().constData());
Gilles Caulier's avatar
Gilles Caulier committed
535
            iptcData.add(iptcTag, val.get());
536
537
        }

538
        d->iptcMetadata() = iptcData;
539

540
        // Make sure character set is UTF-8
Gilles Caulier's avatar
port++    
Gilles Caulier committed
541
        setIptcTagString("Iptc.Envelope.CharacterSet", QString::fromLatin1("\33%G"), false);
542

543
544
        return true;
    }
545
    catch(Exiv2::Error& e)
546
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
547
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc key '%1' into image using Exiv2 ").arg(QString::fromLatin1(iptcTagName)), e);
Gilles Caulier's avatar
Gilles Caulier committed
548
    }
549
550
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
551
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
552
    }
Gilles Caulier's avatar
Gilles Caulier committed
553

554
555
556
    return false;
}

557
558
559
QStringList KExiv2::getIptcKeywords() const
{
    try
Gilles Caulier's avatar
Gilles Caulier committed
560
    {
561
        if (!d->iptcMetadata().empty())
562
        {
Gilles Caulier's avatar
Gilles Caulier committed
563
            QStringList keywords;
564
            Exiv2::IptcData iptcData(d->iptcMetadata());
565
566
567
568

            for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
            {
                QString key = QString::fromLocal8Bit(it->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
569

Gilles Caulier's avatar
port++    
Gilles Caulier committed
570
                if (key == QString::fromLatin1("Iptc.Application2.Keywords"))
571
                {
572
                    QString val = QString::fromUtf8(it->toString().c_str());
573
574
575
                    keywords.append(val);
                }
            }
Gilles Caulier's avatar
Gilles Caulier committed
576

Gilles Caulier's avatar
Gilles Caulier committed
577
            qCDebug(LIBKEXIV2_LOG) << d->filePath << " ==> Read Iptc Keywords: " << keywords;
578

579
580
581
            return keywords;
        }
    }
582
    catch(Exiv2::Error& e)
583
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
584
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Iptc Keywords from image using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
585
    }
586
587
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
588
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
589
    }
Gilles Caulier's avatar
Gilles Caulier committed
590

591
592
593
    return QStringList();
}

Andi Clemens's avatar
Andi Clemens committed
594
bool KExiv2::setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords,
595
596
597
598
599
600
                             bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
601
    {
602
603
        QStringList oldkeys = oldKeywords;
        QStringList newkeys = newKeywords;
Gilles Caulier's avatar
Gilles Caulier committed
604

Gilles Caulier's avatar
Gilles Caulier committed
605
        qCDebug(LIBKEXIV2_LOG) << d->filePath << " ==> New Iptc Keywords: " << newkeys;
Gilles Caulier's avatar
Gilles Caulier committed
606

607
        // Remove all old keywords.
608
        Exiv2::IptcData iptcData(d->iptcMetadata());
609
610
611
612
613
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
614
            QString val = QString::fromUtf8(it->toString().c_str());
615
616

            // Also remove new keywords to avoid duplicates. They will be added again below.
Gilles Caulier's avatar
port++    
Gilles Caulier committed
617
            if ( key == QString::fromLatin1("Iptc.Application2.Keywords") &&
618
619
                 (oldKeywords.contains(val) || newKeywords.contains(val))
               )
620
                it = iptcData.erase(it);
Andi Clemens's avatar
Andi Clemens committed
621
            else
622
623
624
                ++it;
        };

625
        // Add new keywords. Note that Keywords Iptc tag is limited to 64 char but can be redondant.
626
627
628
629
630
631
632

        Exiv2::IptcKey iptcTag("Iptc.Application2.Keywords");

        for (QStringList::iterator it = newkeys.begin(); it != newkeys.end(); ++it)
        {
            QString key = *it;
            key.truncate(64);
Gilles Caulier's avatar
Gilles Caulier committed
633

634
            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
635
            val->read(key.toUtf8().constData());
Gilles Caulier's avatar
Gilles Caulier committed
636
            iptcData.add(iptcTag, val.get());
637
638
        }

639
        d->iptcMetadata() = iptcData;
640

641
        // Make sure character set is UTF-8
Gilles Caulier's avatar
port++    
Gilles Caulier committed
642
        setIptcTagString("Iptc.Envelope.CharacterSet", QString::fromLatin1("\33%G"), false);
643

644
645
        return true;
    }
646
    catch(Exiv2::Error& e)
647
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
648
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc Keywords into image using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
649
    }
650
651
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
652
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
653
    }
Gilles Caulier's avatar
Gilles Caulier committed
654

655
656
657
658
659
660
    return false;
}

QStringList KExiv2::getIptcSubjects() const
{
    try
Gilles Caulier's avatar
Gilles Caulier committed
661
    {
662
        if (!d->iptcMetadata().empty())
663
        {
Gilles Caulier's avatar
Gilles Caulier committed
664
            QStringList subjects;
665
            Exiv2::IptcData iptcData(d->iptcMetadata());
666
667
668
669

            for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
            {
                QString key = QString::fromLocal8Bit(it->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
670

Gilles Caulier's avatar
port++    
Gilles Caulier committed
671
                if (key == QString::fromLatin1("Iptc.Application2.Subject"))
672
                {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
673
                    QString val(QString::fromLatin1(it->toString().c_str()));
674
675
676
                    subjects.append(val);
                }
            }
Gilles Caulier's avatar
Gilles Caulier committed
677

678
679
680
            return subjects;
        }
    }
681
    catch(Exiv2::Error& e)
682
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
683
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Iptc Subjects from image using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
684
    }
685
686
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
687
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
688
    }
Gilles Caulier's avatar
Gilles Caulier committed
689

690
691
692
    return QStringList();
}

Andi Clemens's avatar
Andi Clemens committed
693
bool KExiv2::setIptcSubjects(const QStringList& oldSubjects, const QStringList& newSubjects,
694
695
696
697
698
699
                             bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
700
    {
701
702
        QStringList oldDef = oldSubjects;
        QStringList newDef = newSubjects;
Gilles Caulier's avatar
Gilles Caulier committed
703

704
        // Remove all old subjects.
705
        Exiv2::IptcData iptcData(d->iptcMetadata());
706
707
708
709
710
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
711
            QString val = QString::fromUtf8(it->toString().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
712

Gilles Caulier's avatar
port++    
Gilles Caulier committed
713
            if (key == QString::fromLatin1("Iptc.Application2.Subject") && oldDef.contains(val))
714
                it = iptcData.erase(it);
Andi Clemens's avatar
Andi Clemens committed
715
            else
716
717
718
                ++it;
        };

719
        // Add new subjects. Note that Keywords Iptc tag is limited to 236 char but can be redondant.
720
721
722
723
724
725
726

        Exiv2::IptcKey iptcTag("Iptc.Application2.Subject");

        for (QStringList::iterator it = newDef.begin(); it != newDef.end(); ++it)
        {
            QString key = *it;
            key.truncate(236);
Gilles Caulier's avatar
Gilles Caulier committed
727

728
            Exiv2::Value::AutoPtr val = Exiv2::Value::create(Exiv2::string);
729
            val->read(key.toUtf8().constData());
Gilles Caulier's avatar
Gilles Caulier committed
730
            iptcData.add(iptcTag, val.get());
731
732
        }

733
        d->iptcMetadata() = iptcData;
734

735
        // Make sure character set is UTF-8
Gilles Caulier's avatar
port++    
Gilles Caulier committed
736
        setIptcTagString("Iptc.Envelope.CharacterSet", QString::fromLatin1("\33%G"), false);
737

738
739
        return true;
    }
740
    catch(Exiv2::Error& e)
741
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
742
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc Subjects into image using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
743
    }
744
745
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
746
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
747
    }
Gilles Caulier's avatar
Gilles Caulier committed
748

749
750
751
752
753
754
    return false;
}

QStringList KExiv2::getIptcSubCategories() const
{
    try
Gilles Caulier's avatar
Gilles Caulier committed
755
    {
756
        if (!d->iptcMetadata().empty())
757
        {
Gilles Caulier's avatar
Gilles Caulier committed
758
            QStringList subCategories;
759
            Exiv2::IptcData iptcData(d->iptcMetadata());
760
761
762
763

            for (Exiv2::IptcData::iterator it = iptcData.begin(); it != iptcData.end(); ++it)
            {
                QString key = QString::fromLocal8Bit(it->key().c_str());
Gilles Caulier's avatar
Gilles Caulier committed
764

Gilles Caulier's avatar
port++    
Gilles Caulier committed
765
                if (key == QString::fromLatin1("Iptc.Application2.SuppCategory"))
766
                {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
767
                    QString val(QString::fromLatin1(it->toString().c_str()));
768
769
770
                    subCategories.append(val);
                }
            }
Gilles Caulier's avatar
Gilles Caulier committed
771

772
773
774
            return subCategories;
        }
    }
775
    catch(Exiv2::Error& e)
776
    {
Gilles Caulier's avatar
port++    
Gilles Caulier committed
777
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Iptc Sub Categories from image using Exiv2 "), e);
Gilles Caulier's avatar
Gilles Caulier committed
778
    }
779
780
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
781
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
782
    }
Gilles Caulier's avatar
Gilles Caulier committed
783

784
785
786
    return QStringList();
}

Andi Clemens's avatar
Andi Clemens committed
787
bool KExiv2::setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories,
788
789
790
791
792
793
                                  bool setProgramName) const
{
    if (!setProgramId(setProgramName))
        return false;

    try
Gilles Caulier's avatar
Gilles Caulier committed
794
    {
795
796
        QStringList oldkeys = oldSubCategories;
        QStringList newkeys = newSubCategories;
Gilles Caulier's avatar
Gilles Caulier committed
797

798
        // Remove all old Sub Categories.
799
        Exiv2::IptcData iptcData(d->iptcMetadata());
800
801
802
803
804
        Exiv2::IptcData::iterator it = iptcData.begin();

        while(it != iptcData.end())
        {
            QString key = QString::fromLocal8Bit(it->key().c_str());
805