Commit 2cb42fd2 authored by Gilles Caulier's avatar Gilles Caulier 🗼
Browse files

ExifTool support: now export the list of tags changes from Exif, Iptc, and Xmp...

ExifTool support: now export the list of tags changes from Exif, Iptc, and Xmp in a temporary EXV file.
We will pass later this file to ExifTool to patch target file.
CCBUGS: 134486, 170693, 219856, 237504, 264210, 309341, 325458, 326408, 377622, 384092, 406540, 416516, 421464, 436286
parent 590b673a
Pipeline #61224 passed with stage
in 34 minutes and 45 seconds
......@@ -255,192 +255,6 @@ bool MetaEngine::loadFromData(const QByteArray& imgData)
return false;
}
bool MetaEngine::changedMetadata() const
{
QMutexLocker lock(&s_metaEngineMutex);
try
{
Exiv2::Image::AutoPtr image;
qCDebug(DIGIKAM_METAENGINE_LOG) << "List of changes to perform on:" << getFilePath();
#if defined Q_OS_WIN && defined EXV_UNICODE_PATH
image = Exiv2::ImageFactory::open((const wchar_t*)getFilePath().utf16());
#elif defined Q_OS_WIN
image = Exiv2::ImageFactory::open(QFile::encodeName(getFilePath()).constData());
#else
image = Exiv2::ImageFactory::open(getFilePath().toUtf8().constData());
#endif
image->readMetadata();
// --- Parse differences in comment
std::string orgCom = image->comment();
std::string newCom = d->itemComments();
if (!orgCom.empty() && newCom.empty())
{
qCDebug(DIGIKAM_METAENGINE_LOG) << "Comment Removed";
}
else if (orgCom.empty() && !newCom.empty())
{
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Comment"
<< newCom.c_str();
}
else if (orgCom != newCom)
{
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Comment"
<< newCom.c_str()
<< "old comment"
<< orgCom.c_str();
}
// --- Parse differences in Exif
Exiv2::ExifData orgExif = image->exifData();
Exiv2::ExifData newExif = d->exifMetadata();
for (Exiv2::ExifData::const_iterator it = newExif.begin() ; it != newExif.end() ; ++it)
{
Exiv2::ExifData::const_iterator it2 = orgExif.findKey(Exiv2::ExifKey(it->key()));
if (it2 == orgExif.end())
{
// Orignal Exif do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Exif tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getExifTagData(it2->key().c_str()) != getExifTagData(it->key().c_str()))
{
// Original Exif has already the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Exif tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Exif tags.
for (Exiv2::ExifData::const_iterator it = orgExif.begin() ; it != orgExif.end() ; ++it)
{
Exiv2::ExifData::const_iterator it2 = newExif.findKey(Exiv2::ExifKey(it->key()));
if (it2 == newExif.end())
{
// New Exif do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Exif tag" << it->key().c_str();
}
}
// --- Parse differences in Iptc
Exiv2::IptcData orgIptc = image->iptcData();
Exiv2::IptcData newIptc = d->iptcMetadata();
for (Exiv2::IptcData::const_iterator it = newIptc.begin() ; it != newIptc.end() ; ++it)
{
Exiv2::IptcData::const_iterator it2 = orgIptc.findKey(Exiv2::IptcKey(it->key()));
if (it2 == orgIptc.end())
{
// Orignal Iptc do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Iptc tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getIptcTagData(it2->key().c_str()) != getIptcTagData(it->key().c_str()))
{
// Original Iptc has already the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Iptc tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Iptc tags.
for (Exiv2::IptcData::const_iterator it = orgIptc.begin() ; it != orgIptc.end() ; ++it)
{
Exiv2::IptcData::const_iterator it2 = newIptc.findKey(Exiv2::IptcKey(it->key()));
if (it2 == newIptc.end())
{
// New Iptc do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Iptc tag" << it->key().c_str();
}
}
#ifdef _XMP_SUPPORT_
// --- Parse differences in Xmp
Exiv2::XmpData orgXmp = image->xmpData();
Exiv2::XmpData newXmp = d->xmpMetadata();
for (Exiv2::XmpData::const_iterator it = newXmp.begin() ; it != newXmp.end() ; ++it)
{
Exiv2::XmpData::const_iterator it2 = orgXmp.findKey(Exiv2::XmpKey(it->key()));
if (it2 == orgXmp.end())
{
// Orignal Xmp do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Xmp tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getXmpTagVariant(it2->key().c_str()) != getXmpTagVariant(it->key().c_str()))
{
// Original Xmp has already the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Xmp tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Xmp tags.
for (Exiv2::XmpData::const_iterator it = orgXmp.begin() ; it != orgXmp.end() ; ++it)
{
Exiv2::XmpData::const_iterator it2 = newXmp.findKey(Exiv2::XmpKey(it->key()));
if (it2 == newXmp.end())
{
// New Xmp do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Xmp tag" << it->key().c_str();
}
}
#endif // _XMP_SUPPORT_
return true;
}
catch (Exiv2::AnyError& e)
{
d->printExiv2ExceptionError(QLatin1String("Cannot load metadata using Exiv2 "), e);
}
catch (...)
{
qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
}
return false;
}
bool MetaEngine::isEmpty() const
{
if (!hasComments() && !hasExif() && !hasIptc() && !hasXmp())
......
......@@ -324,11 +324,6 @@ public:
*/
bool updateFileTimeStamp() const;
/**
* List of changed tags compared to original file contents.
*/
bool changedMetadata() const;
//@}
//-------------------------------------------------------------------
......@@ -403,6 +398,14 @@ public:
*/
bool applyChanges(bool setVersion = false) const;
/**
* List changed tags compared to original file contents and export result to
* a temporary EXV file container.
* 'exvTmpFile' is the path to the temporary EXV container to create.
* 'removedTags' is populated with the list of tags removed.
*/
bool exportChanges(const QString& exvTmpFile, QStringList& removedTags) const;
//@}
//-------------------------------------------------------------------
......
......@@ -399,6 +399,202 @@ bool MetaEngine::applyChanges(bool setVersion) const
return save(d->filePath, setVersion);
}
bool MetaEngine::exportChanges(const QString& exvTmpFile, QStringList& removedTags) const
{
if (d->filePath.isEmpty())
{
qCDebug(DIGIKAM_METAENGINE_LOG) << "Failed to export changes: file path is empty!";
return false;
}
QMutexLocker lock(&s_metaEngineMutex);
try
{
Exiv2::Image::AutoPtr image;
qCDebug(DIGIKAM_METAENGINE_LOG) << "List of changes to perform on:" << getFilePath();
#if defined Q_OS_WIN && defined EXV_UNICODE_PATH
image = Exiv2::ImageFactory::open((const wchar_t*)getFilePath().utf16());
#elif defined Q_OS_WIN
image = Exiv2::ImageFactory::open(QFile::encodeName(getFilePath()).constData());
#else
image = Exiv2::ImageFactory::open(getFilePath().toUtf8().constData());
#endif
image->readMetadata();
// --- Parse differences in Exif
Exiv2::ExifData orgExif = image->exifData();
Exiv2::ExifData newExif = d->exifMetadata();
Exiv2::ExifData chgExif;
for (Exiv2::ExifData::const_iterator it = newExif.begin() ; it != newExif.end() ; ++it)
{
Exiv2::ExifData::const_iterator it2 = orgExif.findKey(Exiv2::ExifKey(it->key()));
if (it2 == orgExif.end())
{
// Orignal Exif do not have the tag.
chgExif[it->key().c_str()] = newExif[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Exif tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getExifTagData(it2->key().c_str()) != getExifTagData(it->key().c_str()))
{
// Original Exif has already the tag.
chgExif[it->key().c_str()] = newExif[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Exif tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Exif tags.
for (Exiv2::ExifData::const_iterator it = orgExif.begin() ; it != orgExif.end() ; ++it)
{
Exiv2::ExifData::const_iterator it2 = newExif.findKey(Exiv2::ExifKey(it->key()));
if (it2 == newExif.end())
{
// New Exif do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Exif tag" << it->key().c_str();
removedTags << QString::fromStdString(it->key());
}
}
// --- Parse differences in Iptc
Exiv2::IptcData orgIptc = image->iptcData();
Exiv2::IptcData newIptc = d->iptcMetadata();
Exiv2::IptcData chgIptc;
for (Exiv2::IptcData::const_iterator it = newIptc.begin() ; it != newIptc.end() ; ++it)
{
Exiv2::IptcData::const_iterator it2 = orgIptc.findKey(Exiv2::IptcKey(it->key()));
if (it2 == orgIptc.end())
{
// Orignal Iptc do not have the tag.
chgIptc[it->key().c_str()] = newIptc[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Iptc tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getIptcTagData(it2->key().c_str()) != getIptcTagData(it->key().c_str()))
{
// Original Iptc has already the tag.
chgIptc[it->key().c_str()] = newIptc[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Iptc tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Iptc tags.
for (Exiv2::IptcData::const_iterator it = orgIptc.begin() ; it != orgIptc.end() ; ++it)
{
Exiv2::IptcData::const_iterator it2 = newIptc.findKey(Exiv2::IptcKey(it->key()));
if (it2 == newIptc.end())
{
// New Iptc do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Iptc tag" << it->key().c_str();
removedTags << QString::fromStdString(it->key());
}
}
#ifdef _XMP_SUPPORT_
// --- Parse differences in Xmp
Exiv2::XmpData orgXmp = image->xmpData();
Exiv2::XmpData newXmp = d->xmpMetadata();
Exiv2::XmpData chgXmp;
for (Exiv2::XmpData::const_iterator it = newXmp.begin() ; it != newXmp.end() ; ++it)
{
Exiv2::XmpData::const_iterator it2 = orgXmp.findKey(Exiv2::XmpKey(it->key()));
if (it2 == orgXmp.end())
{
// Orignal Xmp do not have the tag.
chgXmp[it->key().c_str()] = newXmp[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "New Xmp tag" << it->key().c_str()
<< "with value" << it->toString().c_str();
}
else if (getXmpTagVariant(it2->key().c_str()) != getXmpTagVariant(it->key().c_str()))
{
// Original Xmp has already the tag.
chgXmp[it->key().c_str()] = newXmp[it->key().c_str()];
qCDebug(DIGIKAM_METAENGINE_LOG) << "Changed Xmp tag" << it->key().c_str()
<< "old value" << it2->toString().c_str()
<< "new value" << it->toString().c_str();
}
}
// Check for removed Xmp tags.
for (Exiv2::XmpData::const_iterator it = orgXmp.begin() ; it != orgXmp.end() ; ++it)
{
Exiv2::XmpData::const_iterator it2 = newXmp.findKey(Exiv2::XmpKey(it->key()));
if (it2 == newXmp.end())
{
// New Xmp do not have the tag.
qCDebug(DIGIKAM_METAENGINE_LOG) << "Removed Xmp tag" << it->key().c_str();
removedTags << QString::fromStdString(it->key());
}
}
#endif // _XMP_SUPPORT_
// Create target EXV container with list of changed tags.
Exiv2::Image::AutoPtr targetExv = Exiv2::ImageFactory::create(Exiv2::ImageType::exv, exvTmpFile.toStdString());
targetExv->setExifData(chgExif);
targetExv->setIptcData(chgIptc);
targetExv->setXmpData(chgXmp);
targetExv->writeMetadata();
return true;
}
catch (Exiv2::AnyError& e)
{
d->printExiv2ExceptionError(QLatin1String("Cannot export changes using Exiv2 "), e);
}
catch (...)
{
qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2";
}
return false;
}
} // namespace Digikam
#if defined(Q_CC_CLANG)
......
......@@ -27,11 +27,11 @@ target_link_libraries(dmetadataloader_cli
${COMMON_TEST_LINK}
)
set(dmetadatadiff_cli_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/dmetadatadiff_cli.cpp)
add_executable(dmetadatadiff_cli ${dmetadatadiff_cli_SRCS})
ecm_mark_nongui_executable(dmetadatadiff_cli)
set(exportchanges_cli_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/exportchanges_cli.cpp)
add_executable(exportchanges_cli ${exportchanges_cli_SRCS})
ecm_mark_nongui_executable(exportchanges_cli)
target_link_libraries(dmetadatadiff_cli
target_link_libraries(exportchanges_cli
digikamcore
......
......@@ -4,7 +4,7 @@
* https://www.digikam.org
*
* Date : 2012-10-23
* Description : a command line tool to test list metadata differences
* Description : a command line tool to export metatada changes to EXV
*
* Copyright (C) 2012-2021 by Gilles Caulier, <caulier dot gilles at gmail dot com>
*
......@@ -25,7 +25,6 @@
#include <QFileInfo>
#include <QString>
#include <QTextStream>
#include <QApplication>
#include <QDebug>
......@@ -41,7 +40,7 @@ int main(int argc, char** argv)
if (argc != 2)
{
qDebug() << "dmetadatadiff_cli - CLI tool to check metadat difference with DMetadata";
qDebug() << "dmetadatadiff_cli - CLI tool to export metadata changes to EXV";
qDebug() << "Usage: <image>";
return -1;
}
......@@ -61,7 +60,9 @@ int main(int argc, char** argv)
meta.setImageDateTime(QDateTime::currentDateTime(), true);
meta.changedMetadata();
QString exvPath = input.baseName() + QLatin1String("_changes.exv");
QStringList removedTags;
meta.exportChanges(exvPath, removedTags);
return 0;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment