File indexing completed on 2024-04-28 04:18:53
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "timeutils.h" 0023 0024 // STL 0025 #include <memory> 0026 0027 // Qt 0028 #include <QDateTime> 0029 0030 // KF 0031 #include <KFileItem> 0032 0033 // Exiv2 0034 #include <exiv2/exiv2.hpp> 0035 0036 // Local 0037 #include "gwenview_lib_debug.h" 0038 #include <lib/exiv2imageloader.h> 0039 #include <lib/urlutils.h> 0040 0041 namespace Gwenview 0042 { 0043 namespace TimeUtils 0044 { 0045 static Exiv2::ExifData::const_iterator findDateTimeKey(const Exiv2::ExifData &exifData) 0046 { 0047 // Ordered list of keys to try 0048 static QList<Exiv2::ExifKey> lst = QList<Exiv2::ExifKey>() << Exiv2::ExifKey("Exif.Photo.DateTimeOriginal") << Exiv2::ExifKey("Exif.Image.DateTimeOriginal") 0049 << Exiv2::ExifKey("Exif.Photo.DateTimeDigitized") << Exiv2::ExifKey("Exif.Image.DateTime"); 0050 0051 Exiv2::ExifData::const_iterator it, end = exifData.end(); 0052 for (const Exiv2::ExifKey &key : qAsConst(lst)) { 0053 it = exifData.findKey(key); 0054 if (it != end) { 0055 return it; 0056 } 0057 } 0058 return end; 0059 } 0060 0061 struct CacheItem { 0062 QDateTime fileMTime; 0063 QDateTime realTime; 0064 0065 void update(const KFileItem &fileItem) 0066 { 0067 QDateTime time = fileItem.time(KFileItem::ModificationTime); 0068 if (fileMTime == time) { 0069 return; 0070 } 0071 0072 fileMTime = time; 0073 0074 if (!updateFromExif(fileItem.url())) { 0075 realTime = time; 0076 } 0077 } 0078 0079 bool updateFromExif(const QUrl &url) 0080 { 0081 if (!UrlUtils::urlIsFastLocalFile(url)) { 0082 return false; 0083 } 0084 const QString path = url.path(); 0085 Exiv2ImageLoader loader; 0086 0087 if (!loader.load(path)) { 0088 return false; 0089 } 0090 std::unique_ptr<Exiv2::Image> img(loader.popImage().release()); 0091 try { 0092 Exiv2::ExifData exifData = img->exifData(); 0093 if (exifData.empty()) { 0094 return false; 0095 } 0096 auto it = findDateTimeKey(exifData); 0097 if (it == exifData.end()) { 0098 qCWarning(GWENVIEW_LIB_LOG) << "No date in exif header of" << path; 0099 return false; 0100 } 0101 0102 std::ostringstream stream; 0103 stream << *it; 0104 const QString value = QString::fromLocal8Bit(stream.str().c_str()); 0105 0106 const QDateTime dt = QDateTime::fromString(value, QStringLiteral("yyyy:MM:dd hh:mm:ss")); 0107 if (!dt.isValid()) { 0108 qCWarning(GWENVIEW_LIB_LOG) << "Invalid date in exif header of" << path; 0109 return false; 0110 } 0111 0112 realTime = dt; 0113 return true; 0114 } catch (const Exiv2::Error &error) { 0115 qCWarning(GWENVIEW_LIB_LOG) << "Failed to read date from exif header of" << path << ". Error:" << error.what(); 0116 return false; 0117 } 0118 } 0119 }; 0120 0121 using Cache = QHash<QUrl, CacheItem>; 0122 0123 QDateTime dateTimeForFileItem(const KFileItem &fileItem, CachePolicy cachePolicy) 0124 { 0125 if (cachePolicy == SkipCache) { 0126 CacheItem item; 0127 item.update(fileItem); 0128 return item.realTime; 0129 } 0130 0131 static Cache cache; 0132 const QUrl url = fileItem.targetUrl(); 0133 0134 Cache::iterator it = cache.find(url); 0135 if (it == cache.end()) { 0136 it = cache.insert(url, CacheItem()); 0137 } 0138 0139 it.value().update(fileItem); 0140 return it.value().realTime; 0141 } 0142 0143 } // namespace 0144 0145 } // namespace