File indexing completed on 2024-05-12 15:55:36
0001 // SPDX-FileCopyrightText: 2003-2020 The KPhotoAlbum Development Team 0002 // SPDX-FileCopyrightText: 2021 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // SPDX-FileCopyrightText: 2023 Tobias Leupold <tl at stonemx dot de> 0004 // 0005 // SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 #include "Info.h" 0008 0009 #include <kpabase/Logging.h> 0010 0011 #include <kpabase/FileName.h> 0012 #include <kpabase/SettingsData.h> 0013 #include <kpabase/StringSet.h> 0014 0015 #include <QFile> 0016 #include <QFileInfo> 0017 #include <QTextCodec> 0018 #include <exiv2/exv_conf.h> 0019 #include <exiv2/image.hpp> 0020 0021 using namespace Exif; 0022 0023 namespace 0024 { 0025 QString cStringWithEncoding(const char *c_str, const QString &charset) 0026 { 0027 QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1()); 0028 if (!codec) 0029 codec = QTextCodec::codecForLocale(); 0030 return codec->toUnicode(c_str); 0031 } 0032 0033 } // namespace 0034 0035 Info *Info::s_instance = nullptr; 0036 0037 QMap<QString, QStringList> Info::info(const DB::FileName &fileName, StringSet wantedKeys, bool returnFullExifName, const QString &charset) 0038 { 0039 QMap<QString, QStringList> result; 0040 0041 try { 0042 Metadata data = metadata(exifInfoFile(fileName)); 0043 0044 for (Exiv2::ExifData::const_iterator i = data.exif.begin(); i != data.exif.end(); ++i) { 0045 QString key = QString::fromLocal8Bit(i->key().c_str()); 0046 m_keys.insert(key); 0047 0048 if (wantedKeys.contains(key)) { 0049 QString text = key; 0050 if (!returnFullExifName) 0051 text = key.split(QLatin1String(".")).last(); 0052 0053 std::ostringstream stream; 0054 stream << *i; 0055 QString str(cStringWithEncoding(stream.str().c_str(), charset)); 0056 result[text] += str; 0057 } 0058 } 0059 0060 for (Exiv2::IptcData::const_iterator i = data.iptc.begin(); i != data.iptc.end(); ++i) { 0061 QString key = QString::fromLatin1(i->key().c_str()); 0062 m_keys.insert(key); 0063 0064 if (wantedKeys.contains(key)) { 0065 QString text = key; 0066 if (!returnFullExifName) 0067 text = key.split(QString::fromLatin1(".")).last(); 0068 0069 std::ostringstream stream; 0070 stream << *i; 0071 QString str(cStringWithEncoding(stream.str().c_str(), charset)); 0072 result[text] += str; 0073 } 0074 } 0075 } catch (...) { 0076 } 0077 0078 return result; 0079 } 0080 0081 Info *Info::instance() 0082 { 0083 if (!s_instance) 0084 s_instance = new Info; 0085 return s_instance; 0086 } 0087 0088 StringSet Info::availableKeys() 0089 { 0090 return m_keys; 0091 } 0092 0093 QMap<QString, QStringList> Info::infoForViewer(const DB::FileName &fileName, const QString &charset) 0094 { 0095 return info(fileName, ::Settings::SettingsData::instance()->exifForViewer(), false, charset); 0096 } 0097 0098 QMap<QString, QStringList> Info::infoForDialog(const DB::FileName &fileName, const QString &charset) 0099 { 0100 auto keys = ::Settings::SettingsData::instance()->exifForDialog(); 0101 if (keys.isEmpty()) 0102 keys = standardKeys(); 0103 return info(fileName, keys, true, charset); 0104 } 0105 0106 StringSet Info::standardKeys() 0107 { 0108 static StringSet res; 0109 0110 if (!res.empty()) 0111 return res; 0112 0113 QList<const Exiv2::TagInfo *> tags; 0114 std::ostringstream s; 0115 0116 const Exiv2::GroupInfo *gi = Exiv2::ExifTags::groupList(); 0117 while (gi->tagList_ != nullptr) { 0118 Exiv2::TagListFct tl = gi->tagList_; 0119 const Exiv2::TagInfo *ti = tl(); 0120 0121 while (ti->tag_ != 0xFFFF) { 0122 tags << ti; 0123 ++ti; 0124 } 0125 ++gi; 0126 } 0127 0128 for (QList<const Exiv2::TagInfo *>::iterator it = tags.begin(); it != tags.end(); ++it) { 0129 while ((*it)->tag_ != 0xffff) { 0130 res.insert(QString::fromLatin1(Exiv2::ExifKey(**it).key().c_str())); 0131 ++(*it); 0132 } 0133 } 0134 0135 // IPTC tags use yet another format... 0136 Exiv2::IptcDataSets::dataSetList(s); 0137 0138 QStringList lines = QString(QLatin1String(s.str().c_str())).split(QChar::fromLatin1('\n')); 0139 for (QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it) { 0140 if (it->isEmpty()) 0141 continue; 0142 QStringList fields = it->split(QChar::fromLatin1('\t')); 0143 if (fields.size() == 7) { 0144 QString id = fields[4]; 0145 if (id.endsWith(QChar::fromLatin1(','))) 0146 id.chop(1); 0147 res.insert(id); 0148 } else { 0149 fields = it->split(QLatin1String(", ")); 0150 if (fields.size() >= 11) { 0151 res.insert(fields[8]); 0152 } else { 0153 qCWarning(ExifLog) << "Unparsable output from exiv2 library: " << *it; 0154 continue; 0155 } 0156 } 0157 } 0158 return res; 0159 } 0160 0161 Info::Info() 0162 { 0163 m_keys = standardKeys(); 0164 } 0165 0166 void Exif::writeExifInfoToFile(const DB::FileName &srcName, const QString &destName, const QString &imageDescription) 0167 { 0168 // Load Exif from source image 0169 auto image = Exiv2::ImageFactory::open(QFile::encodeName(srcName.absolute()).data()); 0170 image->readMetadata(); 0171 Exiv2::ExifData data = image->exifData(); 0172 0173 // Modify Exif information from database. 0174 data["Exif.Image.ImageDescription"] = imageDescription.toLocal8Bit().data(); 0175 0176 image = Exiv2::ImageFactory::open(QFile::encodeName(destName).data()); 0177 image->setExifData(data); 0178 image->writeMetadata(); 0179 } 0180 0181 /** 0182 * Some Canon cameras stores Exif info in files ending in .thm, so we need to use those files for fetching Exif info 0183 * if they exists. 0184 */ 0185 DB::FileName Exif::Info::exifInfoFile(const DB::FileName &fileName) 0186 { 0187 QString dirName = QFileInfo(fileName.relative()).path(); 0188 QString baseName = QFileInfo(fileName.relative()).baseName(); 0189 DB::FileName name = DB::FileName::fromRelativePath(dirName + QString::fromLatin1("/") + baseName + QString::fromLatin1(".thm")); 0190 if (name.exists()) 0191 return name; 0192 0193 name = DB::FileName::fromRelativePath(dirName + QString::fromLatin1("/") + baseName + QString::fromLatin1(".THM")); 0194 if (name.exists()) 0195 return name; 0196 0197 return fileName; 0198 } 0199 0200 Exif::Metadata Exif::Info::metadata(const DB::FileName &fileName) 0201 { 0202 try { 0203 Exif::Metadata result; 0204 auto image = Exiv2::ImageFactory::open(QFile::encodeName(fileName.absolute()).data()); 0205 Q_ASSERT(image.get() != nullptr); 0206 image->readMetadata(); 0207 result.exif = image->exifData(); 0208 result.iptc = image->iptcData(); 0209 result.comment = image->comment(); 0210 return result; 0211 } catch (...) { 0212 } 0213 return Exif::Metadata(); 0214 } 0215 0216 // vi:expandtab:tabstop=4 shiftwidth=4: