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