kexiv2exif.cpp 33.2 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  Exif 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.
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.
25
 *
26
27
 * ============================================================ */

Michael Georg Hansen's avatar
Michael Georg Hansen committed
28
29
30
#include "kexiv2.h"
#include "kexiv2_p.h"

31
32
33
34
// C++ includes

#include <cctype>

Gilles Caulier's avatar
polish    
Gilles Caulier committed
35
36
// Qt includes

Laurent Montel's avatar
Laurent Montel committed
37
38
#include <QTextCodec>
#include <QBuffer>
Gilles Caulier's avatar
polish    
Gilles Caulier committed
39

Gilles Caulier's avatar
Gilles Caulier committed
40
41
42
43
// Local includes

#include "rotationmatrix.h"
#include "libkexiv2_debug.h"
44

45
46
47
namespace KExiv2Iface
{

48
49
50
51
52
bool KExiv2::canWriteExif(const QString& filePath)
{
    try
    {
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)
53
                                      (QFile::encodeName(filePath).constData()));
54

Gilles Caulier's avatar
polish    
Gilles Caulier committed
55
        Exiv2::AccessMode mode = image->checkMode(Exiv2::mdExif);
Gilles Caulier's avatar
Gilles Caulier committed
56

57
58
        return (mode == Exiv2::amWrite || mode == Exiv2::amReadWrite);
    }
59
    catch( Exiv2::Error& e )
60
61
    {
        std::string s(e.what());
Gilles Caulier's avatar
Gilles Caulier committed
62
        qCCritical(LIBKEXIV2_LOG) << "Cannot check Exif access mode using Exiv2 (Error #"
Gilles Caulier's avatar
polish    
Gilles Caulier committed
63
                    << e.code() << ": " << s.c_str() << ")";
64
    }
65
66
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
67
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
68
    }
69
70
71
72

    return false;
}

Gilles Caulier's avatar
Gilles Caulier committed
73
bool KExiv2::hasExif() const
74
{
75
    return !d->exifMetadata().empty();
76
77
}

Gilles Caulier's avatar
Gilles Caulier committed
78
bool KExiv2::clearExif() const
79
80
81
{
    try
    {
82
        d->exifMetadata().clear();
83
84
        return true;
    }
85
    catch( Exiv2::Error& e )
86
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
87
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot clear Exif data using Exiv2 "), e);
88
    }
89
90
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
91
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
92
    }
93
94
95
96

    return false;
}

97
QByteArray KExiv2::getExifEncoded(bool addExifHeader) const
98
99
100
{
    try
    {
101
        if (!d->exifMetadata().empty())
102
        {
103
            QByteArray data;
104
            Exiv2::ExifData& exif = d->exifMetadata();
105
106
107
            Exiv2::Blob blob;
            Exiv2::ExifParser::encode(blob, Exiv2::bigEndian, exif);
            QByteArray ba((const char*)&blob[0], blob.size());
Gilles Caulier's avatar
polish    
Gilles Caulier committed
108

109
110
111
112
113
            if (addExifHeader)
            {
                const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
                data.resize(ba.size() + sizeof(ExifHeader));
                memcpy(data.data(), ExifHeader, sizeof(ExifHeader));
Gilles Caulier's avatar
polish    
Gilles Caulier committed
114
                memcpy(data.data() + sizeof(ExifHeader), ba.data(), ba.size());
115
116
117
118
119
            }
            else
            {
                data = ba;
            }
120
121
122
            return data;
        }
    }
123
    catch( Exiv2::Error& e )
124
125
    {
        if (!d->filePath.isEmpty())
Gilles Caulier's avatar
Gilles Caulier committed
126
            qCDebug(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
127

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
128
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get Exif data using Exiv2 "), e);
129
    }
130
131
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
132
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
133
    }
134
135
136
137

    return QByteArray();
}

Gilles Caulier's avatar
Gilles Caulier committed
138
bool KExiv2::setExif(const QByteArray& data) const
139
140
141
142
143
{
    try
    {
        if (!data.isEmpty())
        {
144
145
            Exiv2::ExifParser::decode(d->exifMetadata(), (const Exiv2::byte*)data.data(), data.size());
            return (!d->exifMetadata().empty());
146
147
        }
    }
148
    catch( Exiv2::Error& e )
149
150
    {
        if (!d->filePath.isEmpty())
Gilles Caulier's avatar
Gilles Caulier committed
151
            qCCritical(LIBKEXIV2_LOG) << "From file " << d->filePath.toLatin1().constData();
152

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
153
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif data using Exiv2 "), e);
154
    }
155
156
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
157
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
158
    }
159
160
161
162

    return false;
}

Gilles Caulier's avatar
Gilles Caulier committed
163
KExiv2::MetaDataMap KExiv2::getExifTagsDataList(const QStringList& exifKeysFilter, bool invertSelection) const
164
{
165
    if (d->exifMetadata().empty())
166
167
168
169
       return MetaDataMap();

    try
    {
170
        Exiv2::ExifData exifData = d->exifMetadata();
171
        exifData.sortByKey();
172

173
174
175
176
177
        QString     ifDItemName;
        MetaDataMap metaDataMap;

        for (Exiv2::ExifData::iterator md = exifData.begin(); md != exifData.end(); ++md)
        {
Gilles Caulier's avatar
Qt5 ++    
Gilles Caulier committed
178
            QString key = QString::fromLatin1(md->key().c_str());
179
180
181

            // Decode the tag value with a user friendly output.
            QString tagValue;
Gilles Caulier's avatar
Gilles Caulier committed
182

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
183
            if (key == QString::fromLatin1("Exif.Photo.UserComment"))
184
            {
185
                tagValue = d->convertCommentValue(*md);
186
            }
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
187
            else if (key == QString::fromLatin1("Exif.Image.0x935c"))
188
            {
189
                tagValue = QString::number(md->value().size());
190
191
192
193
194
195
            }
            else
            {
                std::ostringstream os;
                os << *md;

196
                // Exif tag contents can be an translated strings, no only simple ascii.
197
198
                tagValue = QString::fromLocal8Bit(os.str().c_str());
            }
Gilles Caulier's avatar
Gilles Caulier committed
199

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
200
            tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
201
202
203

            // We apply a filter to get only the Exif tags that we need.

204
            if (!exifKeysFilter.isEmpty())
205
            {
206
207
                if (!invertSelection)
                {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
208
                    if (exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
209
210
211
212
                        metaDataMap.insert(key, tagValue);
                }
                else
                {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
213
                    if (!exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
214
215
                        metaDataMap.insert(key, tagValue);
                }
216
            }
217
            else // else no filter at all.
218
            {
219
                metaDataMap.insert(key, tagValue);
220
            }
221
222
223
224
225
226
        }

        return metaDataMap;
    }
    catch (Exiv2::Error& e)
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
227
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot parse EXIF metadata using Exiv2 "), e);
228
    }
229
230
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
231
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
232
    }
233
234
235
236
237
238
239
240

    return MetaDataMap();
}

QString KExiv2::getExifComment() const
{
    try
    {
241
        if (!d->exifMetadata().empty())
242
        {
243
            Exiv2::ExifData exifData(d->exifMetadata());
244
            Exiv2::ExifKey key("Exif.Photo.UserComment");
245
246
247
248
            Exiv2::ExifData::iterator it = exifData.findKey(key);

            if (it != exifData.end())
            {
249
                QString exifComment = d->convertCommentValue(*it);
250
251
252
253
254

                // some cameras fill the UserComment with whitespace
                if (!exifComment.isEmpty() && !exifComment.trimmed().isEmpty())
                    return exifComment;
            }
255
256
257
258
259
260
261
262

            Exiv2::ExifKey key2("Exif.Image.ImageDescription");
            Exiv2::ExifData::iterator it2 = exifData.findKey(key2);

            if (it2 != exifData.end())
            {
                QString exifComment = d->convertCommentValue(*it2);

263
264
                // Some cameras fill in nonsense default values
                QStringList blackList;
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
265
266
267
                blackList << QString::fromLatin1("SONY DSC"); // + whitespace
                blackList << QString::fromLatin1("OLYMPUS DIGITAL CAMERA");
                blackList << QString::fromLatin1("MINOLTA DIGITAL CAMERA");
268
269
270

                QString trimmedComment = exifComment.trimmed();

271
                // some cameras fill the UserComment with whitespace
272
                if (!exifComment.isEmpty() && !trimmedComment.isEmpty() && !blackList.contains(trimmedComment))
273
274
                    return exifComment;
            }
275
276
        }
    }
277
    catch( Exiv2::Error& e )
278
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
279
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif User Comment using Exiv2 "), e);
280
    }
281
282
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
283
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
284
    }
285
286
287
288

    return QString();
}

289
290
291
static bool is7BitAscii(const QByteArray& s)
{
    const int size = s.size();
Gilles Caulier's avatar
Gilles Caulier committed
292

293
294
295
296
297
298
299
    for (int i=0; i<size; i++)
    {
        if (!isascii(s[i]))
        {
            return false;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
300

301
302
303
    return true;
}

Gilles Caulier's avatar
Gilles Caulier committed
304
bool KExiv2::setExifComment(const QString& comment, bool setProgramName) const
305
306
307
308
309
310
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
311
        removeExifTag("Exif.Image.ImageDescription");
312
        removeExifTag("Exif.Photo.UserComment");
313

314
315
        if (!comment.isNull())
        {
316
317
            setExifTagString("Exif.Image.ImageDescription", comment, setProgramName);

318
            // Write as Unicode only when necessary.
319
            QTextCodec* latin1Codec = QTextCodec::codecForName("iso8859-1");
320
321
            if (latin1Codec->canEncode(comment))
            {
322
323
324
325
326
327
328
329
330
331
                // We know it's in the ISO-8859-1 8bit range.
                // Check if it's in the ASCII 7bit range
                if (is7BitAscii(comment.toLatin1()))
                {
                    // write as ASCII
                    std::string exifComment("charset=\"Ascii\" ");
                    exifComment += comment.toLatin1().constData();
                    d->exifMetadata()["Exif.Photo.UserComment"] = exifComment;
                    return true;
                }
332
            }
333
334
335
336
            // write as Unicode (UCS-2)
            std::string exifComment("charset=\"Unicode\" ");
            exifComment += comment.toUtf8().constData();
            d->exifMetadata()["Exif.Photo.UserComment"] = exifComment;
337
        }
338
339

        return true;
340
    }
341
    catch( Exiv2::Error& e )
342
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
343
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif Comment using Exiv2 "), e);
344
    }
345
346
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
347
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
348
    }
349
350
351
352

    return false;
}

353
QString KExiv2::getExifTagTitle(const char* exifTagName)
354
{
355
    try
356
357
    {
        std::string exifkey(exifTagName);
358
        Exiv2::ExifKey ek(exifkey);
Gilles Caulier's avatar
Gilles Caulier committed
359

360
        return QString::fromLocal8Bit( ek.tagLabel().c_str() );
361
    }
362
    catch (Exiv2::Error& e)
363
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
364
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag title using Exiv2 "), e);
365
    }
366
367
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
368
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
369
    }
370
371
372
373

    return QString();
}

374
QString KExiv2::getExifTagDescription(const char* exifTagName)
375
{
376
    try
377
378
    {
        std::string exifkey(exifTagName);
379
        Exiv2::ExifKey ek(exifkey);
Gilles Caulier's avatar
Gilles Caulier committed
380

381
        return QString::fromLocal8Bit( ek.tagDesc().c_str() );
382
    }
383
    catch (Exiv2::Error& e)
384
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
385
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot get metadata tag description using Exiv2 "), 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

    return QString();
}

395
bool KExiv2::removeExifTag(const char* exifTagName, bool setProgramName) const
396
397
398
399
400
{
    if (!setProgramId(setProgramName))
        return false;

    try
401
    {
402
        Exiv2::ExifKey exifKey(exifTagName);
403
        Exiv2::ExifData::iterator it = d->exifMetadata().findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
404

405
        if (it != d->exifMetadata().end())
406
        {
407
            d->exifMetadata().erase(it);
408
409
410
            return true;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
411
    catch( Exiv2::Error& e )
412
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
413
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif tag using Exiv2 "), e);
414
    }
415
416
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
417
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
418
    }
419

420
421
422
    return false;
}

423
bool KExiv2::getExifTagRational(const char* exifTagName, long int& num, long int& den, int component) const
424
425
426
427
{
    try
    {
        Exiv2::ExifKey exifKey(exifTagName);
428
        Exiv2::ExifData exifData(d->exifMetadata());
429
        Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
430

431
432
433
434
        if (it != exifData.end())
        {
            num = (*it).toRational(component).first;
            den = (*it).toRational(component).second;
Gilles Caulier's avatar
Gilles Caulier committed
435

436
437
438
            return true;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
439
    catch( Exiv2::Error& e )
440
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
441
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif Rational value from key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
442
    }
443
444
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
445
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
446
    }
447
448
449
450

    return false;
}

451
bool KExiv2::setExifTagLong(const char* exifTagName, long val, bool setProgramName) const
452
453
454
455
456
457
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
458
        d->exifMetadata()[exifTagName] = static_cast<int32_t>(val);
459
460
        return true;
    }
Gilles Caulier's avatar
Gilles Caulier committed
461
    catch( Exiv2::Error& e )
462
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
463
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag long value into image using Exiv2 "), e);
464
    }
465
466
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
467
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
468
    }
469
470
471
472

    return false;
}

473
bool KExiv2::setExifTagRational(const char* exifTagName, long int num, long int den, bool setProgramName) const
474
475
476
477
478
479
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
480
        d->exifMetadata()[exifTagName] = Exiv2::Rational(num, den);
481
482
        return true;
    }
Gilles Caulier's avatar
Gilles Caulier committed
483
    catch( Exiv2::Error& e )
484
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
485
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag rational value into image using Exiv2 "), e);
486
    }
487
488
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
489
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
490
    }
491
492
493
494

    return false;
}

495
bool KExiv2::setExifTagData(const char* exifTagName, const QByteArray& data, bool setProgramName) const
496
497
498
499
500
501
502
503
504
{
    if (data.isEmpty())
        return false;

    if (!setProgramId(setProgramName))
        return false;

    try
    {
Gilles Caulier's avatar
Gilles Caulier committed
505
        Exiv2::DataValue val((Exiv2::byte*)data.data(), data.size());
506
        d->exifMetadata()[exifTagName] = val;
507
508
        return true;
    }
Gilles Caulier's avatar
Gilles Caulier committed
509
    catch( Exiv2::Error& e )
510
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
511
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag data into image using Exiv2 "), e);
512
    }
513
514
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
515
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
516
    }
517
518
519
520

    return false;
}

521
bool KExiv2::setExifTagVariant(const char* exifTagName, const QVariant& val,
Gilles Caulier's avatar
Gilles Caulier committed
522
                               bool rationalWantSmallDenominator, bool setProgramName) const
523
524
525
526
527
528
529
530
531
532
533
534
535
{
    switch (val.type())
    {
        case QVariant::Int:
        case QVariant::UInt:
        case QVariant::Bool:
        case QVariant::LongLong:
        case QVariant::ULongLong:
            return setExifTagLong(exifTagName, val.toInt(), setProgramName);

        case QVariant::Double:
        {
            long num, den;
Gilles Caulier's avatar
Gilles Caulier committed
536

537
538
539
540
            if (rationalWantSmallDenominator)
                convertToRationalSmallDenominator(val.toDouble(), &num, &den);
            else
                convertToRational(val.toDouble(), &num, &den, 4);
Gilles Caulier's avatar
Gilles Caulier committed
541

542
543
544
545
546
547
            return setExifTagRational(exifTagName, num, den, setProgramName);
        }
        case QVariant::List:
        {
            long num = 0, den = 1;
            QList<QVariant> list = val.toList();
Gilles Caulier's avatar
Gilles Caulier committed
548

549
550
            if (list.size() >= 1)
                num = list[0].toInt();
Gilles Caulier's avatar
Gilles Caulier committed
551

552
553
            if (list.size() >= 2)
                den = list[1].toInt();
Gilles Caulier's avatar
Gilles Caulier committed
554

555
556
557
558
559
560
561
            return setExifTagRational(exifTagName, num, den, setProgramName);
        }

        case QVariant::Date:
        case QVariant::DateTime:
        {
            QDateTime dateTime = val.toDateTime();
Gilles Caulier's avatar
Gilles Caulier committed
562

563
564
565
566
567
568
569
570
            if(!dateTime.isValid())
                return false;

            if (!setProgramId(setProgramName))
                return false;

            try
            {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
571
                const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData());
572
                d->exifMetadata()[exifTagName] = exifdatetime;
573
574
575
            }
            catch( Exiv2::Error &e )
            {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
576
                d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Date & Time in image using Exiv2 "), e);
577
            }
578
579
            catch(...)
            {
Gilles Caulier's avatar
Gilles Caulier committed
580
                qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
581
            }
Gilles Caulier's avatar
Gilles Caulier committed
582

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
            return false;
        }

        case QVariant::String:
        case QVariant::Char:
            return setExifTagString(exifTagName, val.toString(), setProgramName);

        case QVariant::ByteArray:
            return setExifTagData(exifTagName, val.toByteArray(), setProgramName);
        default:
            break;
    }
    return false;
}

598
QString KExiv2::createExifUserStringFromValue(const char* exifTagName, const QVariant& val, bool escapeCR)
599
600
601
602
603
{
    try
    {
        Exiv2::ExifKey key(exifTagName);
        Exiv2::Exifdatum datum(key);
Gilles Caulier's avatar
Gilles Caulier committed
604

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
        switch (val.type())
        {
            case QVariant::Int:
            case QVariant::Bool:
            case QVariant::LongLong:
            case QVariant::ULongLong:
                datum = (int32_t)val.toInt();
                break;
            case QVariant::UInt:
                datum = (uint32_t)val.toUInt();
                break;

            case QVariant::Double:
            {
                long num, den;
                convertToRationalSmallDenominator(val.toDouble(), &num, &den);
                Exiv2::Rational rational;
                rational.first  = num;
                rational.second = den;
                datum = rational;
                break;
            }
            case QVariant::List:
            {
                long num = 0, den = 1;
                QList<QVariant> list = val.toList();
                if (list.size() >= 1)
                    num = list[0].toInt();
                if (list.size() >= 2)
                    den = list[1].toInt();
                Exiv2::Rational rational;
                rational.first  = num;
                rational.second = den;
                datum = rational;
                break;
            }

            case QVariant::Date:
            case QVariant::DateTime:
            {
                QDateTime dateTime = val.toDateTime();
                if(!dateTime.isValid())
                    break;

Gilles Caulier's avatar
port ++    
Gilles Caulier committed
649
                const std::string &exifdatetime(dateTime.toString(QString::fromLatin1("yyyy:MM:dd hh:mm:ss")).toLatin1().constData());
650
651
652
653
654
655
                datum = exifdatetime;
                break;
            }

            case QVariant::String:
            case QVariant::Char:
Gilles Caulier's avatar
Qt5 ++    
Gilles Caulier committed
656
                datum = (std::string)val.toString().toLatin1().constData();
657
658
659
660
661
662
663
664
665
666
                break;
            default:
                break;
        }

        std::ostringstream os;
        os << datum;
        QString tagValue = QString::fromLocal8Bit(os.str().c_str());

        if (escapeCR)
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
667
            tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
668
669
670

        return tagValue;
    }
Gilles Caulier's avatar
Gilles Caulier committed
671
    catch( Exiv2::Error& e )
672
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
673
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Iptc tag string into image using Exiv2 "), e);
674
    }
675
676
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
677
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
678
    }
679
680
681
682

    return QString();
}

683
bool KExiv2::getExifTagLong(const char* exifTagName, long& val) const
684
685
686
687
688
{
    return getExifTagLong(exifTagName, val, 0);
}

bool KExiv2::getExifTagLong(const char* exifTagName, long& val, int component) const
689
690
{
    try
691
    {
692
        Exiv2::ExifKey exifKey(exifTagName);
693
        Exiv2::ExifData exifData(d->exifMetadata());
694
        Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
695

696
        if (it != exifData.end() && it->count() > 0)
697
        {
698
            val = it->toLong(component);
699
700
701
            return true;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
702
    catch( Exiv2::Error& e )
703
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
704
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
705
    }
706
707
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
708
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
709
    }
710
711

    return false;
712
713
714
715
716
717
718
}

QByteArray KExiv2::getExifTagData(const char* exifTagName) const
{
    try
    {
        Exiv2::ExifKey exifKey(exifTagName);
719
        Exiv2::ExifData exifData(d->exifMetadata());
720
        Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
721

722
723
        if (it != exifData.end())
        {
Gilles Caulier's avatar
Gilles Caulier committed
724
            char* const s = new char[(*it).size()];
725
            (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
Gilles Caulier's avatar
Gilles Caulier committed
726
            QByteArray data(s, (*it).size());
727
            delete[] s;
Gilles Caulier's avatar
Gilles Caulier committed
728

729
730
731
            return data;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
732
    catch( Exiv2::Error& e )
733
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
734
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
735
    }
736
737
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
738
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
739
    }
740
741
742
743

    return QByteArray();
}

744
QVariant KExiv2::getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts, bool stringEscapeCR, int component) const
745
746
747
748
{
    try
    {
        Exiv2::ExifKey exifKey(exifTagName);
749
        Exiv2::ExifData exifData(d->exifMetadata());
750
        Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
751

752
753
754
755
756
757
758
759
760
        if (it != exifData.end())
        {
            switch (it->typeId())
            {
                case Exiv2::unsignedByte:
                case Exiv2::unsignedShort:
                case Exiv2::unsignedLong:
                case Exiv2::signedShort:
                case Exiv2::signedLong:
761
762
763
764
                    if (it->count() > component)
                        return QVariant((int)it->toLong(component));
                    else
                        return QVariant(QVariant::Int);
765
766
                case Exiv2::unsignedRational:
                case Exiv2::signedRational:
Gilles Caulier's avatar
Gilles Caulier committed
767

768
769
                    if (rationalAsListOfInts)
                    {
770
771
                        if (it->count() <= component)
                            return QVariant(QVariant::List);
Gilles Caulier's avatar
Gilles Caulier committed
772

773
                        QList<QVariant> list;
774
775
                        list << (*it).toRational(component).first;
                        list << (*it).toRational(component).second;
Gilles Caulier's avatar
Gilles Caulier committed
776

777
778
779
780
                        return QVariant(list);
                    }
                    else
                    {
781
782
                        if (it->count() <= component)
                            return QVariant(QVariant::Double);
Gilles Caulier's avatar
Gilles Caulier committed
783

784
                        // prefer double precision
785
786
                        double num = (*it).toRational(component).first;
                        double den = (*it).toRational(component).second;
Gilles Caulier's avatar
Gilles Caulier committed
787

788
                        if (den == 0.0)
789
                            return QVariant(QVariant::Double);
Gilles Caulier's avatar
Gilles Caulier committed
790

791
792
793
794
795
                        return QVariant(num / den);
                    }
                case Exiv2::date:
                case Exiv2::time:
                {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
796
                    QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
797
798
799
800
801
802
803
804
805
806
807
                    return QVariant(dateTime);
                }
                case Exiv2::asciiString:
                case Exiv2::comment:
                case Exiv2::string:
                {
                    std::ostringstream os;
                    os << *it;
                    QString tagValue = QString::fromLocal8Bit(os.str().c_str());

                    if (stringEscapeCR)
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
808
                        tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
809
810
811
812

                    return QVariant(tagValue);
                }
                default:
813
                    break;
814
815
816
            }
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
817
    catch( Exiv2::Error& e )
818
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
819
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
820
    }
821
822
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
823
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
824
    }
825

826
    return QVariant();
827
828
829
830
831
832
833
}

QString KExiv2::getExifTagString(const char* exifTagName, bool escapeCR) const
{
    try
    {
        Exiv2::ExifKey exifKey(exifTagName);
834
        Exiv2::ExifData exifData(d->exifMetadata());
835
        Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
Gilles Caulier's avatar
Gilles Caulier committed
836

837
838
        if (it != exifData.end())
        {
839
840
841
            // See B.K.O #184156 comment #13
            std::string val  = it->print(&exifData);
            QString tagValue = QString::fromLocal8Bit(val.c_str());
Gilles Caulier's avatar
Gilles Caulier committed
842

843
            if (escapeCR)
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
844
                tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
845
846
847
848

            return tagValue;
        }
    }
Gilles Caulier's avatar
Gilles Caulier committed
849
    catch( Exiv2::Error& e )
850
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
851
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)), e);
852
    }
853
854
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
855
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
856
    }
857
858
859
860

    return QString();
}

861
bool KExiv2::setExifTagString(const char* exifTagName, const QString& value, bool setProgramName) const
862
863
864
865
866
867
{
    if (!setProgramId(setProgramName))
        return false;

    try
    {
Gilles Caulier's avatar
Qt5 ++    
Gilles Caulier committed
868
        d->exifMetadata()[exifTagName] = std::string(value.toLatin1().constData());
869
870
        return true;
    }
Gilles Caulier's avatar
Gilles Caulier committed
871
    catch( Exiv2::Error& e )
872
    {
Gilles Caulier's avatar
port ++    
Gilles Caulier committed
873
        d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif tag string into image using Exiv2 "), e);
874
    }
875
876
    catch(...)
    {
Gilles Caulier's avatar
Gilles Caulier committed
877
        qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
878
    }
879
880
881
882
883
884
885
886

    return false;
}

QImage KExiv2::getExifThumbnail(bool fixOrientation) const
{
    QImage thumbnail;

887
    if (d->exifMetadata().empty())
888
889
890
891
       return thumbnail;

    try
    {
892
        Exiv2::ExifThumbC thumb(d->exifMetadata());
893
        Exiv2::DataBuf const c1 = thumb.copy();
894
895
896
897
898
899
        thumbnail.loadFromData(c1.pData_, c1.size_);

        if (!thumbnail.isNull())
        {
            if (fixOrientation)
            {
900
901
                Exiv2::ExifKey key1("Exif.Thumbnail.Orientation");
                Exiv2::ExifKey key2("Exif.Image.Orientation");
902
                Exiv2::ExifData exifData(d->exifMetadata());
903
                Exiv2::ExifData::iterator it = exifData.findKey(key1);
Gilles Caulier's avatar
Gilles Caulier committed
904

905
906
                if (it == exifData.end())
                    it = exifData.findKey(key2);
Gilles Caulier's avatar
Gilles Caulier committed
907

908
                if (it != exifData.end() && it->count())