File indexing completed on 2024-04-21 15:22:24

0001 /*
0002     SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
0003     SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "kexiv2_p.h"
0009 
0010 // C ANSI includes
0011 
0012 extern "C"
0013 {
0014 #include <sys/stat.h>
0015 
0016 #ifndef _MSC_VER
0017 #include <utime.h>
0018 #else
0019 #include <sys/utime.h>
0020 #endif
0021 }
0022 
0023 // Qt includes
0024 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0025 #include <QStringDecoder>
0026 #else
0027 #include <QTextCodec>
0028 #endif
0029 
0030 // Local includes
0031 
0032 #include "libkexiv2_debug.h"
0033 
0034 // Pragma directives to reduce warnings from Exiv2.
0035 #if !defined(__APPLE__) && defined(__GNUC__)
0036 #pragma GCC diagnostic push
0037 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
0038 #endif
0039 
0040 #if defined(__APPLE__) && defined(__clang__)
0041 #pragma clang diagnostic push
0042 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
0043 #endif
0044 
0045 namespace KExiv2Iface
0046 {
0047 
0048 KExiv2Private::KExiv2Private()
0049     : data(new KExiv2DataPrivate)
0050 {
0051     writeRawFiles         = false;
0052     updateFileTimeStamp   = false;
0053     useXMPSidecar4Reading = false;
0054     metadataWritingMode   = KExiv2::WRITETOIMAGEONLY;
0055     loadedFromSidecar     = false;
0056     Exiv2::LogMsg::setHandler(KExiv2Private::printExiv2MessageHandler);
0057 }
0058 
0059 KExiv2Private::~KExiv2Private() = default;
0060 
0061 void KExiv2Private::copyPrivateData(const KExiv2Private* const other)
0062 {
0063     data                  = other->data;
0064     filePath              = other->filePath;
0065     writeRawFiles         = other->writeRawFiles;
0066     updateFileTimeStamp   = other->updateFileTimeStamp;
0067     useXMPSidecar4Reading = other->useXMPSidecar4Reading;
0068     metadataWritingMode   = other->metadataWritingMode;
0069 }
0070 
0071 bool KExiv2Private::saveToXMPSidecar(const QFileInfo& finfo) const
0072 {
0073     QString filePath = KExiv2::sidecarFilePathForFile(finfo.filePath());
0074 
0075     if (filePath.isEmpty())
0076     {
0077         return false;
0078     }
0079 
0080     try
0081     {
0082 #if EXIV2_TEST_VERSION(0,28,0)
0083         Exiv2::Image::UniquePtr image;
0084 #else
0085         Exiv2::Image::AutoPtr image;
0086 #endif
0087         image = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, (const char*)(QFile::encodeName(filePath).constData()));
0088 #if EXIV2_TEST_VERSION(0,28,0)
0089         return saveOperations(finfo, std::move(image));
0090 #else
0091         return saveOperations(finfo, image);
0092 #endif
0093     }
0094     catch( Exiv2::Error& e )
0095     {
0096         printExiv2ExceptionError(QString::fromLatin1("Cannot save metadata to XMP sidecar using Exiv2 "), e);
0097         return false;
0098     }
0099     catch(...)
0100     {
0101         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0102         return false;
0103     }
0104 }
0105 
0106 bool KExiv2Private::saveToFile(const QFileInfo& finfo) const
0107 {
0108     if (!finfo.isWritable())
0109     {
0110         qCDebug(LIBKEXIV2_LOG) << "File '" << finfo.fileName().toLatin1().constData() << "' is read only. Metadata not written.";
0111         return false;
0112     }
0113 
0114     QStringList rawTiffBasedSupported, rawTiffBasedNotSupported;
0115 
0116     // Raw files supported by Exiv2 0.23
0117     rawTiffBasedSupported << QString::fromLatin1("dng")
0118                           << QString::fromLatin1("nef")
0119                           << QString::fromLatin1("pef")
0120                           << QString::fromLatin1("orf")
0121                           << QString::fromLatin1("srw")
0122                           << QString::fromLatin1("cr2");
0123 
0124     // Raw files not supported by Exiv2 0.23
0125     rawTiffBasedNotSupported << QString::fromLatin1("3fr")
0126                              << QString::fromLatin1("arw")
0127                              << QString::fromLatin1("dcr")
0128                              << QString::fromLatin1("erf")
0129                              << QString::fromLatin1("k25")
0130                              << QString::fromLatin1("kdc") 
0131                              << QString::fromLatin1("mos")
0132                              << QString::fromLatin1("raw")
0133                              << QString::fromLatin1("sr2")
0134                              << QString::fromLatin1("srf")
0135                              << QString::fromLatin1("rw2");
0136 
0137     QString ext = finfo.suffix().toLower();
0138 
0139     if (!writeRawFiles && (rawTiffBasedSupported.contains(ext) || rawTiffBasedNotSupported.contains(ext)) )
0140     {
0141         qCDebug(LIBKEXIV2_LOG) << finfo.fileName()
0142                                << "is a TIFF based RAW file, writing to such a file is disabled by current settings.";
0143         return false;
0144     }
0145 
0146 /*
0147     if (rawTiffBasedNotSupported.contains(ext))
0148     {
0149         qCDebug(LIBKEXIV2_LOG) << finfo.fileName()
0150                                << "is TIFF based RAW file not yet supported. Metadata not saved.";
0151         return false;
0152     }
0153 
0154     if (rawTiffBasedSupported.contains(ext) && !writeRawFiles)
0155     {
0156         qCDebug(LIBKEXIV2_LOG) << finfo.fileName()
0157                                << "is TIFF based RAW file supported but writing mode is disabled. "
0158                                << "Metadata not saved.";
0159         return false;
0160     }
0161 
0162     qCDebug(LIBKEXIV2_LOG) << "File Extension: " << ext << " is supported for writing mode";
0163 
0164     bool ret = false;
0165 */
0166 
0167     try
0168     {
0169 #if EXIV2_TEST_VERSION(0,28,0)
0170         Exiv2::Image::UniquePtr image;
0171 #else
0172         Exiv2::Image::AutoPtr image;
0173 #endif
0174         image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(finfo.filePath()).constData()));
0175 #if EXIV2_TEST_VERSION(0,28,0)
0176         return saveOperations(finfo, std::move(image));
0177 #else
0178         return saveOperations(finfo, image);
0179 #endif
0180     }
0181     catch( Exiv2::Error& e )
0182     {
0183         printExiv2ExceptionError(QString::fromLatin1("Cannot save metadata to image using Exiv2 "), e);
0184         return false;
0185     }
0186     catch(...)
0187     {
0188         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0189         return false;
0190     }
0191 }
0192 
0193 #if EXIV2_TEST_VERSION(0,28,0)
0194 bool KExiv2Private::saveOperations(const QFileInfo& finfo, Exiv2::Image::UniquePtr image) const
0195 #else
0196 bool KExiv2Private::saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const
0197 #endif
0198 {
0199     try
0200     {
0201         Exiv2::AccessMode mode;
0202         bool wroteComment = false, wroteEXIF = false, wroteIPTC = false, wroteXMP = false;
0203 
0204         // We need to load target file metadata to merge with new one. It's mandatory with TIFF format:
0205         // like all tiff file structure is based on Exif.
0206         image->readMetadata();
0207 
0208         // Image Comments ---------------------------------
0209 
0210         mode = image->checkMode(Exiv2::mdComment);
0211 
0212         if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
0213         {
0214             image->setComment(imageComments());
0215             wroteComment = true;
0216         }
0217 
0218         qCDebug(LIBKEXIV2_LOG) << "wroteComment: " << wroteComment;
0219 
0220         // Exif metadata ----------------------------------
0221 
0222         mode = image->checkMode(Exiv2::mdExif);
0223 
0224         if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
0225         {
0226             if (image->mimeType() == "image/tiff")
0227             {
0228                 Exiv2::ExifData orgExif = image->exifData();
0229                 Exiv2::ExifData newExif;
0230                 QStringList     untouchedTags;
0231 
0232                 // With tiff image we cannot overwrite whole Exif data as well, because
0233                 // image data are stored in Exif container. We need to take a care about
0234                 // to not lost image data.
0235                 untouchedTags << QString::fromLatin1("Exif.Image.ImageWidth");
0236                 untouchedTags << QString::fromLatin1("Exif.Image.ImageLength");
0237                 untouchedTags << QString::fromLatin1("Exif.Image.BitsPerSample");
0238                 untouchedTags << QString::fromLatin1("Exif.Image.Compression");
0239                 untouchedTags << QString::fromLatin1("Exif.Image.PhotometricInterpretation");
0240                 untouchedTags << QString::fromLatin1("Exif.Image.FillOrder");
0241                 untouchedTags << QString::fromLatin1("Exif.Image.SamplesPerPixel");
0242                 untouchedTags << QString::fromLatin1("Exif.Image.StripOffsets");
0243                 untouchedTags << QString::fromLatin1("Exif.Image.RowsPerStrip");
0244                 untouchedTags << QString::fromLatin1("Exif.Image.StripByteCounts");
0245                 untouchedTags << QString::fromLatin1("Exif.Image.XResolution");
0246                 untouchedTags << QString::fromLatin1("Exif.Image.YResolution");
0247                 untouchedTags << QString::fromLatin1("Exif.Image.PlanarConfiguration");
0248                 untouchedTags << QString::fromLatin1("Exif.Image.ResolutionUnit");
0249 
0250                 for (Exiv2::ExifData::iterator it = orgExif.begin(); it != orgExif.end(); ++it)
0251                 {
0252                     if (untouchedTags.contains(QString::fromLatin1(it->key().c_str())))
0253                     {
0254                         newExif[it->key().c_str()] = orgExif[it->key().c_str()];
0255                     }
0256                 }
0257 
0258                 Exiv2::ExifData readedExif = exifMetadata();
0259 
0260                 for (Exiv2::ExifData::iterator it = readedExif.begin(); it != readedExif.end(); ++it)
0261                 {
0262                     if (!untouchedTags.contains(QString::fromLatin1(it->key().c_str())))
0263                     {
0264                         newExif[it->key().c_str()] = readedExif[it->key().c_str()];
0265                     }
0266                 }
0267 
0268                 image->setExifData(newExif);
0269             }
0270             else
0271             {
0272                 image->setExifData(exifMetadata());
0273             }
0274 
0275             wroteEXIF = true;
0276         }
0277 
0278         qCDebug(LIBKEXIV2_LOG) << "wroteEXIF: " << wroteEXIF;
0279 
0280         // Iptc metadata ----------------------------------
0281 
0282         mode = image->checkMode(Exiv2::mdIptc);
0283 
0284         if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
0285         {
0286             image->setIptcData(iptcMetadata());
0287             wroteIPTC = true;
0288         }
0289 
0290         qCDebug(LIBKEXIV2_LOG) << "wroteIPTC: " << wroteIPTC;
0291 
0292         // Xmp metadata -----------------------------------
0293 
0294         mode = image->checkMode(Exiv2::mdXmp);
0295 
0296         if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
0297         {
0298 #ifdef _XMP_SUPPORT_
0299             image->setXmpData(xmpMetadata());
0300             wroteXMP = true;
0301 #endif
0302         }
0303 
0304         qCDebug(LIBKEXIV2_LOG) << "wroteXMP: " << wroteXMP;
0305 
0306         if (!wroteComment && !wroteEXIF && !wroteIPTC && !wroteXMP)
0307         {
0308             qCDebug(LIBKEXIV2_LOG) << "Writing metadata is not supported for file" << finfo.fileName();
0309             return false;
0310         }
0311         else if (!wroteEXIF || !wroteIPTC || !wroteXMP)
0312         {
0313             qCDebug(LIBKEXIV2_LOG) << "Support for writing metadata is limited for file" << finfo.fileName();
0314         }
0315 
0316         if (!updateFileTimeStamp)
0317         {
0318             // Don't touch access and modification timestamp of file.
0319             struct stat    st;
0320             struct utimbuf ut;
0321             int ret = ::stat(QFile::encodeName(filePath).constData(), &st);
0322 
0323             if (ret == 0)
0324             {
0325                 ut.modtime = st.st_mtime;
0326                 ut.actime  = st.st_atime;
0327             }
0328 
0329             image->writeMetadata();
0330 
0331             if (ret == 0)
0332             {
0333                 ::utime(QFile::encodeName(filePath).constData(), &ut);
0334             }
0335 
0336             qCDebug(LIBKEXIV2_LOG) << "File time stamp restored";
0337         }
0338         else
0339         {
0340             image->writeMetadata();
0341         }
0342 
0343         return true;
0344     }
0345     catch( Exiv2::Error& e )
0346     {
0347         printExiv2ExceptionError(QString::fromLatin1("Cannot save metadata using Exiv2 "), e);
0348     }
0349     catch(...)
0350     {
0351         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0352     }
0353 
0354     return false;
0355 }
0356 
0357 void KExiv2DataPrivate::clear()
0358 {
0359     imageComments.clear();
0360     exifMetadata.clear();
0361     iptcMetadata.clear();
0362 #ifdef _XMP_SUPPORT_
0363     xmpMetadata.clear();
0364 #endif
0365 }
0366 
0367 void KExiv2Private::printExiv2ExceptionError(const QString& msg, Exiv2::Error& e)
0368 {
0369     std::string s(e.what());
0370     qCCritical(LIBKEXIV2_LOG) << msg.toLatin1().constData() << " (Error #"
0371 #if EXIV2_TEST_VERSION(0,28,0)
0372                               << Exiv2::Error(e.code()).what()
0373 #else
0374                               << e.code() << ": " << s.c_str()
0375 #endif
0376                               << ")";
0377 }
0378 
0379 void KExiv2Private::printExiv2MessageHandler(int lvl, const char* msg)
0380 {
0381     qCDebug(LIBKEXIV2_LOG) << "Exiv2 (" << lvl << ") : " << msg;
0382 }
0383 
0384 QString KExiv2Private::convertCommentValue(const Exiv2::Exifdatum& exifDatum) const
0385 {
0386     try
0387     {
0388         std::string comment;
0389         std::string charset;
0390 
0391         comment = exifDatum.toString();
0392 
0393         // libexiv2 will prepend "charset=\"SomeCharset\" " if charset is specified
0394         // Before conversion to QString, we must know the charset, so we stay with std::string for a while
0395         if (comment.length() > 8 && comment.substr(0, 8) == "charset=")
0396         {
0397             // the prepended charset specification is followed by a blank
0398             std::string::size_type pos = comment.find_first_of(' ');
0399 
0400             if (pos != std::string::npos)
0401             {
0402                 // extract string between the = and the blank
0403                 charset = comment.substr(8, pos-8);
0404                 // get the rest of the string after the charset specification
0405                 comment = comment.substr(pos+1);
0406             }
0407         }
0408 
0409         if (charset == "\"Unicode\"")
0410         {
0411             return QString::fromUtf8(comment.data());
0412         }
0413         else if (charset == "\"Jis\"")
0414         {
0415 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0416             QStringDecoder codec("JIS7");
0417             return codec.decode(comment.c_str());
0418 #else
0419             QTextCodec* const codec = QTextCodec::codecForName("JIS7");
0420             return codec->toUnicode(comment.c_str());
0421 #endif
0422         }
0423         else if (charset == "\"Ascii\"")
0424         {
0425             return QString::fromLatin1(comment.c_str());
0426         }
0427         else
0428         {
0429             return detectEncodingAndDecode(comment);
0430         }
0431     }
0432     catch( Exiv2::Error& e )
0433     {
0434         printExiv2ExceptionError(QString::fromLatin1("Cannot convert Comment using Exiv2 "), e);
0435     }
0436     catch(...)
0437     {
0438         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0439     }
0440 
0441     return QString();
0442 }
0443 
0444 QString KExiv2Private::detectEncodingAndDecode(const std::string& value) const
0445 {
0446     // For charset autodetection, we could use sophisticated code
0447     // (Mozilla chardet, KHTML's autodetection, QTextCodec::codecForContent),
0448     // but that is probably too much.
0449     // We check for UTF8, Local encoding and ASCII.
0450     // Look like KEncodingDetector class can provide a full implementation for encoding detection.
0451 
0452     if (value.empty())
0453     {
0454         return QString();
0455     }
0456 
0457     if (isUtf8(value.c_str()))
0458     {
0459         return QString::fromUtf8(value.c_str());
0460     }
0461 
0462     // Utf8 has a pretty unique byte pattern.
0463     // Thats not true for ASCII, it is not possible
0464     // to reliably autodetect different ISO-8859 charsets.
0465     // So we can use either local encoding, or latin1.
0466 
0467     return QString::fromLocal8Bit(value.c_str());
0468 }
0469 
0470 bool KExiv2Private::isUtf8(const char* const buffer) const
0471 {
0472     int i, n;
0473     unsigned char c;
0474     bool gotone = false;
0475 
0476     if (!buffer)
0477         return true;
0478 
0479 // character never appears in text
0480 #define F 0
0481 // character appears in plain ASCII text
0482 #define T 1
0483 // character appears in ISO-8859 text
0484 #define I 2
0485 // character appears in non-ISO extended ASCII (Mac, IBM PC)
0486 #define X 3
0487 
0488     static const unsigned char text_chars[256] =
0489     {
0490             //                  BEL BS HT LF    FF CR    
0491             F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F,  // 0x0X
0492             //                              ESC          
0493             F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F,  // 0x1X
0494             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x2X
0495             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x3X
0496             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x4X
0497             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x5X
0498             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x6X
0499             T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F,  // 0x7X
0500             //            NEL                            
0501             X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X,  // 0x8X
0502             X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,  // 0x9X
0503             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xaX
0504             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xbX
0505             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xcX
0506             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xdX
0507             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xeX
0508             I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I   // 0xfX
0509     };
0510 
0511     for (i = 0; (c = buffer[i]); ++i)
0512     {
0513         if ((c & 0x80) == 0)
0514         {
0515             // 0xxxxxxx is plain ASCII
0516 
0517             // Even if the whole file is valid UTF-8 sequences,
0518             // still reject it if it uses weird control characters.
0519 
0520             if (text_chars[c] != T)
0521                 return false;
0522 
0523         }
0524         else if ((c & 0x40) == 0)
0525         {
0526             // 10xxxxxx never 1st byte
0527             return false;
0528         }
0529         else
0530         {
0531             // 11xxxxxx begins UTF-8
0532             int following = 0;
0533 
0534             if ((c & 0x20) == 0)
0535             {
0536                 // 110xxxxx
0537                 following = 1;
0538             }
0539             else if ((c & 0x10) == 0)
0540             {
0541                 // 1110xxxx
0542                 following = 2;
0543             }
0544             else if ((c & 0x08) == 0)
0545             {
0546                 // 11110xxx
0547                 following = 3;
0548             }
0549             else if ((c & 0x04) == 0)
0550             {
0551                 // 111110xx
0552                 following = 4;
0553             }
0554             else if ((c & 0x02) == 0)
0555             {
0556                 // 1111110x
0557                 following = 5;
0558             }
0559             else
0560             {
0561                 return false;
0562             }
0563 
0564             for (n = 0; n < following; ++n)
0565             {
0566                 i++;
0567 
0568                 if (!(c = buffer[i]))
0569                     goto done;
0570 
0571                 if ((c & 0x80) == 0 || (c & 0x40))
0572                     return false;
0573             }
0574             
0575             gotone = true;
0576         }
0577     }
0578 
0579 done:
0580 
0581     return gotone;   // don't claim it's UTF-8 if it's all 7-bit.
0582 }
0583 
0584 #undef F
0585 #undef T
0586 #undef I
0587 #undef X
0588 
0589 int KExiv2Private::getXMPTagsListFromPrefix(const QString& pf, KExiv2::TagsMap& tagsMap) const
0590 {
0591     int i = 0;
0592 
0593 #ifdef _XMP_SUPPORT_
0594 
0595     try
0596     {
0597         QList<const Exiv2::XmpPropertyInfo*> tags;
0598         tags << Exiv2::XmpProperties::propertyList(pf.toLatin1().data());
0599 
0600         for (QList<const Exiv2::XmpPropertyInfo*>::iterator it = tags.begin(); it != tags.end(); ++it)
0601         {
0602             while ( (*it) && !QString::fromLatin1((*it)->name_).isNull() )
0603             {
0604                 QString     key = QLatin1String( Exiv2::XmpKey( pf.toLatin1().data(), (*it)->name_ ).key().c_str() );
0605                 QStringList values;
0606                 values << QString::fromLatin1((*it)->name_)
0607                        << QString::fromLatin1((*it)->title_)
0608                        << QString::fromLatin1((*it)->desc_);
0609                 tagsMap.insert(key, values);
0610                 ++(*it);
0611                 i++;
0612             }
0613         }
0614     }
0615     catch( Exiv2::Error& e )
0616     {
0617         printExiv2ExceptionError(QString::fromLatin1("Cannot get Xmp tags list using Exiv2 "), e);
0618     }
0619     catch(...)
0620     {
0621         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0622     }
0623 
0624 #else
0625 
0626     Q_UNUSED(pf);
0627     Q_UNUSED(tagsMap);
0628 
0629 #endif // _XMP_SUPPORT_
0630 
0631     return i;
0632 }
0633 
0634 #ifdef _XMP_SUPPORT_
0635 #if EXIV2_TEST_VERSION(0,28,0)
0636 void KExiv2Private::loadSidecarData(Exiv2::Image::UniquePtr xmpsidecar)
0637 #else
0638 void KExiv2Private::loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar)
0639 #endif
0640 {
0641     // Having a sidecar is a special situation.
0642     // The sidecar data often "dominates", see in particular bug 309058 for important aspects:
0643     // If a field is removed from the sidecar, we must ignore (older) data for this field in the file.
0644 
0645     // First: Ignore file XMP, only use sidecar XMP
0646     xmpMetadata()     = xmpsidecar->xmpData();
0647     loadedFromSidecar = true;
0648 
0649     // EXIF
0650     // Four groups of properties are mapped between EXIF and XMP:
0651     // Date/Time, Description, Copyright, Creator
0652     // A few more tags are defined "writeback" tags in the XMP specification, the sidecar value therefore overrides the Exif value.
0653     // The rest is kept side-by-side.
0654     // (to understand, remember that the xmpsidecar's Exif data is actually XMP data mapped back to Exif)
0655 
0656     // Description, Copyright and Creator is dominated by the sidecar: Remove file Exif fields, if field not in XMP.
0657     ExifMergeHelper exifDominatedHelper;
0658     exifDominatedHelper << QLatin1String("Exif.Image.ImageDescription")
0659                         << QLatin1String("Exif.Photo.UserComment")
0660                         << QLatin1String("Exif.Image.Copyright")
0661                         << QLatin1String("Exif.Image.Artist");
0662     exifDominatedHelper.exclusiveMerge(xmpsidecar->exifData(), exifMetadata());
0663     // Date/Time and "the few more" from the XMP spec are handled as writeback
0664     // Note that Date/Time mapping is slightly contradictory in latest specs.
0665     ExifMergeHelper exifWritebackHelper;
0666     exifWritebackHelper << QLatin1String("Exif.Image.DateTime")
0667                         << QLatin1String("Exif.Image.DateTime")
0668                         << QLatin1String("Exif.Photo.DateTimeOriginal")
0669                         << QLatin1String("Exif.Photo.DateTimeDigitized")
0670                         << QLatin1String("Exif.Image.Orientation")
0671                         << QLatin1String("Exif.Image.XResolution")
0672                         << QLatin1String("Exif.Image.YResolution")
0673                         << QLatin1String("Exif.Image.ResolutionUnit")
0674                         << QLatin1String("Exif.Image.Software")
0675                         << QLatin1String("Exif.Photo.RelatedSoundFile");
0676     exifWritebackHelper.mergeFields(xmpsidecar->exifData(), exifMetadata());
0677 
0678     // IPTC
0679     // These fields cover almost all relevant IPTC data and are defined in the XMP specification for reconciliation.
0680     IptcMergeHelper iptcDominatedHelper;
0681     iptcDominatedHelper << QLatin1String("Iptc.Application2.ObjectName")
0682                         << QLatin1String("Iptc.Application2.Urgency")
0683                         << QLatin1String("Iptc.Application2.Category")
0684                         << QLatin1String("Iptc.Application2.SuppCategory")
0685                         << QLatin1String("Iptc.Application2.Keywords")
0686                         << QLatin1String("Iptc.Application2.SubLocation")
0687                         << QLatin1String("Iptc.Application2.SpecialInstructions")
0688                         << QLatin1String("Iptc.Application2.Byline")
0689                         << QLatin1String("Iptc.Application2.BylineTitle")
0690                         << QLatin1String("Iptc.Application2.City")
0691                         << QLatin1String("Iptc.Application2.ProvinceState")
0692                         << QLatin1String("Iptc.Application2.CountryCode")
0693                         << QLatin1String("Iptc.Application2.CountryName")
0694                         << QLatin1String("Iptc.Application2.TransmissionReference")
0695                         << QLatin1String("Iptc.Application2.Headline")
0696                         << QLatin1String("Iptc.Application2.Credit")
0697                         << QLatin1String("Iptc.Application2.Source")
0698                         << QLatin1String("Iptc.Application2.Copyright")
0699                         << QLatin1String("Iptc.Application2.Caption")
0700                         << QLatin1String("Iptc.Application2.Writer");
0701     iptcDominatedHelper.exclusiveMerge(xmpsidecar->iptcData(), iptcMetadata());
0702 
0703     IptcMergeHelper iptcWritebackHelper;
0704     iptcWritebackHelper << QLatin1String("Iptc.Application2.DateCreated")
0705                         << QLatin1String("Iptc.Application2.TimeCreated")
0706                         << QLatin1String("Iptc.Application2.DigitizationDate")
0707                         << QLatin1String("Iptc.Application2.DigitizationTime");
0708     iptcWritebackHelper.mergeFields(xmpsidecar->iptcData(), iptcMetadata());
0709 
0710     /*
0711      * TODO: Exiv2 (referring to 0.23) does not correctly synchronize all times values as given below.
0712      * Time values and their synchronization:
0713      * Original Date/Time – Creation date of the intellectual content (e.g. the photograph),
0714        rather than the creatio*n date of the content being shown
0715         Exif DateTimeOriginal (36867, 0x9003) and SubSecTimeOriginal (37521, 0x9291)
0716         IPTC DateCreated (IIM 2:55, 0x0237) and TimeCreated (IIM 2:60, 0x023C)
0717         XMP (photoshop:DateCreated)
0718      * Digitized Date/Time – Creation date of the digital representation
0719         Exif DateTimeDigitized (36868, 0x9004) and SubSecTimeDigitized (37522, 0x9292)
0720         IPTC DigitalCreationDate (IIM 2:62, 0x023E) and DigitalCreationTime (IIM 2:63, 0x023F)
0721         XMP (xmp:CreateDate)
0722      * Modification Date/Time – Modification date of the digital image file
0723         Exif DateTime (306, 0x132) and SubSecTime (37520, 0x9290)
0724         XMP (xmp:ModifyDate)
0725      */
0726 }
0727 #endif // _XMP_SUPPORT_
0728 
0729 }  // NameSpace KExiv2Iface
0730 
0731 // Restore warnings
0732 #if !defined(__APPLE__) && defined(__GNUC__)
0733 #pragma GCC diagnostic pop
0734 #endif
0735 
0736 #if defined(__APPLE__) && defined(__clang__)
0737 #pragma clang diagnostic pop
0738 #endif