bool KExiv2::Private::saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const { try { Exiv2::AccessMode mode; bool wroteComment = false, wroteEXIF = false, wroteIPTC = false, wroteXMP = false; // We need to load target file metadata to merge with new one. It's mandatory with TIFF format: // like all tiff file structure is based on Exif. image->readMetadata(); // Image Comments --------------------------------- mode = image->checkMode(Exiv2::mdComment); if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite)) { image->setComment(imageComments()); wroteComment = true; } // Exif metadata ---------------------------------- mode = image->checkMode(Exiv2::mdExif); if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite)) { if (image->mimeType() == "image/tiff") { Exiv2::ExifData orgExif = image->exifData(); Exiv2::ExifData newExif; QStringList untouchedTags; // With tiff image we cannot overwrite whole Exif data as well, because // image data are stored in Exif container. We need to take a care about // to not lost image data. untouchedTags << "Exif.Image.ImageWidth"; untouchedTags << "Exif.Image.ImageLength"; untouchedTags << "Exif.Image.BitsPerSample"; untouchedTags << "Exif.Image.Compression"; untouchedTags << "Exif.Image.PhotometricInterpretation"; untouchedTags << "Exif.Image.FillOrder"; untouchedTags << "Exif.Image.SamplesPerPixel"; untouchedTags << "Exif.Image.StripOffsets"; untouchedTags << "Exif.Image.RowsPerStrip"; untouchedTags << "Exif.Image.StripByteCounts"; untouchedTags << "Exif.Image.XResolution"; untouchedTags << "Exif.Image.YResolution"; untouchedTags << "Exif.Image.PlanarConfiguration"; untouchedTags << "Exif.Image.ResolutionUnit"; for (Exiv2::ExifData::iterator it = orgExif.begin(); it != orgExif.end(); ++it) { if (untouchedTags.contains(it->key().c_str())) { newExif[it->key().c_str()] = orgExif[it->key().c_str()]; } } Exiv2::ExifData readedExif = exifMetadata(); for (Exiv2::ExifData::iterator it = readedExif.begin(); it != readedExif.end(); ++it) { if (!untouchedTags.contains(it->key().c_str())) { newExif[it->key().c_str()] = readedExif[it->key().c_str()]; } } image->setExifData(newExif); } else { image->setExifData(exifMetadata()); } wroteEXIF = true; } // Iptc metadata ---------------------------------- mode = image->checkMode(Exiv2::mdIptc); if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite)) { image->setIptcData(iptcMetadata()); wroteIPTC = true; } // Xmp metadata ----------------------------------- mode = image->checkMode(Exiv2::mdXmp); if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite)) { #ifdef _XMP_SUPPORT_ image->setXmpData(xmpMetadata()); wroteXMP = true; #endif } if (!wroteComment && !wroteEXIF && !wroteIPTC && !wroteXMP) { kDebug() << "Writing metadata is not supported for file" << finfo.fileName(); return false; } else if (!wroteEXIF || !wroteIPTC || !wroteXMP) { kDebug() << "Support for writing metadata is limited for file" << finfo.fileName() << "EXIF" << wroteEXIF << "IPTC" << wroteIPTC << "XMP" << wroteXMP; } if (!updateFileTimeStamp) { // Don't touch access and modification timestamp of file. struct stat st; ::stat(QFile::encodeName(filePath), &st); struct utimbuf ut; ut.modtime = st.st_mtime; ut.actime = st.st_atime; image->writeMetadata(); ::utime(QFile::encodeName(filePath), &ut); } else { image->writeMetadata(); } return true; } catch( Exiv2::Error& e ) { printExiv2ExceptionError("Cannot save metadata using Exiv2 ", e); } return false; }
void MetaEngine::Private::loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar) { // Having a sidecar is a special situation. // The sidecar data often "dominates", see in particular bug 309058 for important aspects: // If a field is removed from the sidecar, we must ignore (older) data for this field in the file. // First: Ignore file XMP, only use sidecar XMP xmpMetadata() = xmpsidecar->xmpData(); loadedFromSidecar = true; // EXIF // Four groups of properties are mapped between EXIF and XMP: // Date/Time, Description, Copyright, Creator // A few more tags are defined "writeback" tags in the XMP specification, the sidecar value therefore overrides the Exif value. // The rest is kept side-by-side. // (to understand, remember that the xmpsidecar's Exif data is actually XMP data mapped back to Exif) // Description, Copyright and Creator is dominated by the sidecar: Remove file Exif fields, if field not in XMP. ExifMergeHelper exifDominatedHelper; exifDominatedHelper << QLatin1String("Exif.Image.ImageDescription") << QLatin1String("Exif.Photo.UserComment") << QLatin1String("Exif.Image.Copyright") << QLatin1String("Exif.Image.Artist"); exifDominatedHelper.exclusiveMerge(xmpsidecar->exifData(), exifMetadata()); // Date/Time and "the few more" from the XMP spec are handled as writeback // Note that Date/Time mapping is slightly contradictory in latest specs. ExifMergeHelper exifWritebackHelper; exifWritebackHelper << QLatin1String("Exif.Image.DateTime") << QLatin1String("Exif.Image.DateTime") << QLatin1String("Exif.Photo.DateTimeOriginal") << QLatin1String("Exif.Photo.DateTimeDigitized") << QLatin1String("Exif.Image.Orientation") << QLatin1String("Exif.Image.XResolution") << QLatin1String("Exif.Image.YResolution") << QLatin1String("Exif.Image.ResolutionUnit") << QLatin1String("Exif.Image.Software") << QLatin1String("Exif.Photo.RelatedSoundFile"); exifWritebackHelper.mergeFields(xmpsidecar->exifData(), exifMetadata()); // IPTC // These fields cover almost all relevant IPTC data and are defined in the XMP specification for reconciliation. IptcMergeHelper iptcDominatedHelper; iptcDominatedHelper << QLatin1String("Iptc.Application2.ObjectName") << QLatin1String("Iptc.Application2.Urgency") << QLatin1String("Iptc.Application2.Category") << QLatin1String("Iptc.Application2.SuppCategory") << QLatin1String("Iptc.Application2.Keywords") << QLatin1String("Iptc.Application2.SubLocation") << QLatin1String("Iptc.Application2.SpecialInstructions") << QLatin1String("Iptc.Application2.Byline") << QLatin1String("Iptc.Application2.BylineTitle") << QLatin1String("Iptc.Application2.City") << QLatin1String("Iptc.Application2.ProvinceState") << QLatin1String("Iptc.Application2.CountryCode") << QLatin1String("Iptc.Application2.CountryName") << QLatin1String("Iptc.Application2.TransmissionReference") << QLatin1String("Iptc.Application2.Headline") << QLatin1String("Iptc.Application2.Credit") << QLatin1String("Iptc.Application2.Source") << QLatin1String("Iptc.Application2.Copyright") << QLatin1String("Iptc.Application2.Caption") << QLatin1String("Iptc.Application2.Writer"); iptcDominatedHelper.exclusiveMerge(xmpsidecar->iptcData(), iptcMetadata()); IptcMergeHelper iptcWritebackHelper; iptcWritebackHelper << QLatin1String("Iptc.Application2.DateCreated") << QLatin1String("Iptc.Application2.TimeCreated") << QLatin1String("Iptc.Application2.DigitizationDate") << QLatin1String("Iptc.Application2.DigitizationTime"); iptcWritebackHelper.mergeFields(xmpsidecar->iptcData(), iptcMetadata()); /* * TODO: Exiv2 (referring to 0.23) does not correctly synchronize all times values as given below. * Time values and their synchronization: * Original Date/Time – Creation date of the intellectual content (e.g. the photograph), rather than the creatio*n date of the content being shown Exif DateTimeOriginal (36867, 0x9003) and SubSecTimeOriginal (37521, 0x9291) IPTC DateCreated (IIM 2:55, 0x0237) and TimeCreated (IIM 2:60, 0x023C) XMP (photoshop:DateCreated) * Digitized Date/Time – Creation date of the digital representation Exif DateTimeDigitized (36868, 0x9004) and SubSecTimeDigitized (37522, 0x9292) IPTC DigitalCreationDate (IIM 2:62, 0x023E) and DigitalCreationTime (IIM 2:63, 0x023F) XMP (xmp:CreateDate) * Modification Date/Time – Modification date of the digital image file Exif DateTime (306, 0x132) and SubSecTime (37520, 0x9290) XMP (xmp:ModifyDate) */ }