KFileMetaData

exiv2extractor.cpp
1 /*
2  <one line to give the library's name and an idea of what it does.>
3  Copyright (C) 2012 Vishesh Handa <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 
21 #include "exiv2extractor.h"
22 #include <cmath>
23 
24 using namespace KFileMetaData;
25 
26 
27 Exiv2Extractor::Exiv2Extractor(QObject* parent)
28  : ExtractorPlugin(parent)
29 {
30 }
31 
32 namespace
33 {
34 static const QStringList supportedMimeTypes = {
35  QStringLiteral("image/bmp"),
36  QStringLiteral("image/gif"),
37  QStringLiteral("image/jp2"),
38  QStringLiteral("image/jpeg"),
39  QStringLiteral("image/pgf"),
40  QStringLiteral("image/png"),
41  QStringLiteral("image/tiff"),
42 #ifdef HAVE_WEBP_SUPPORT
43  QStringLiteral("image/webp"),
44 #endif
45  QStringLiteral("image/x-exv"),
46  QStringLiteral("image/x-canon-cr2"),
47  QStringLiteral("image/x-canon-crw"),
48  QStringLiteral("image/x-fuji-raf"),
49  QStringLiteral("image/x-minolta-mrw"),
50  QStringLiteral("image/x-nikon-nef"),
51  QStringLiteral("image/x-olympus-orf"),
52  QStringLiteral("image/x-panasonic-rw2"),
53  QStringLiteral("image/x-pentax-pef"),
54  QStringLiteral("image/x-photoshop"),
55  QStringLiteral("image/x-samsung-srw"),
56  QStringLiteral("image/x-tga"),
57 };
58 
59 QString toString(const Exiv2::Value& value)
60 {
61  const std::string str = value.toString();
62  return QString::fromUtf8(str.c_str(), str.length());
63 }
64 
65 QVariant toVariantDateTime(const Exiv2::Value& value)
66 {
67  if (value.typeId() == Exiv2::asciiString) {
68  QDateTime val = ExtractorPlugin::dateTimeFromString(QString::fromLatin1(value.toString().c_str()));
69  if (val.isValid()) {
70  // Datetime is stored in exif as local time.
71  val.setOffsetFromUtc(0);
72  return QVariant(val);
73  }
74  }
75 
76  return QVariant();
77 }
78 
79 QVariant toVariantLong(const Exiv2::Value& value)
80 {
81  if (value.typeId() == Exiv2::unsignedLong || value.typeId() == Exiv2::signedLong) {
82  qlonglong val = value.toLong();
83  return QVariant(val);
84  }
85 
86  QString str(toString(value));
87  bool ok = false;
88  int val = str.toInt(&ok);
89  if (ok)
90  return QVariant(val);
91 
92  return QVariant();
93 }
94 
95 QVariant toVariantDouble(const Exiv2::Value& value)
96 {
97  if (value.typeId() == Exiv2::tiffFloat || value.typeId() == Exiv2::tiffDouble
98  || value.typeId() == Exiv2::unsignedRational || value.typeId() == Exiv2::signedRational) {
99  return QVariant(static_cast<double>(value.toFloat()));
100  }
101 
102  QString str(toString(value));
103  bool ok = false;
104  double val = str.toDouble(&ok);
105  if (ok)
106  return QVariant(val);
107 
108  return QVariant();
109 }
110 
111 QVariant toVariantString(const Exiv2::Value& value)
112 {
113  QString str = toString(value);
114  if (!str.isEmpty())
115  return QVariant(str);
116 
117  return QVariant();
118 }
119 
120 QVariant toVariant(const Exiv2::Value& value, QVariant::Type type) {
121  if (value.count() == 0) {
122  return QVariant();
123  }
124  switch (type) {
125  case QVariant::Int:
126  return toVariantLong(value);
127 
128  case QVariant::DateTime:
129  return toVariantDateTime(value);
130 
131  case QVariant::Double:
132  return toVariantDouble(value);
133 
134  case QVariant::String:
135  default:
136  return toVariantString(value);
137  }
138 }
139 }
140 
141 QStringList Exiv2Extractor::mimetypes() const
142 {
143  return supportedMimeTypes;
144 }
145 
146 void Exiv2Extractor::extract(ExtractionResult* result)
147 {
148  QByteArray arr = result->inputUrl().toUtf8();
149  std::string fileString(arr.data(), arr.length());
150 
151 #if EXIV2_TEST_VERSION(0, 28, 0)
152  Exiv2::Image::UniquePtr image;
153 #else
154  Exiv2::Image::AutoPtr image;
155 #endif
156  try {
157  image = Exiv2::ImageFactory::open(fileString);
158  } catch (const std::exception&) {
159  return;
160  }
161  if (!image.get()) {
162  return;
163  }
164 
165  try {
166  image->readMetadata();
167  } catch (const std::exception&) {
168  return;
169  }
170  result->addType(Type::Image);
171 
172  if (!(result->inputFlags() & ExtractionResult::ExtractMetaData)) {
173  return;
174  }
175 
176  if (image->pixelHeight()) {
177  result->add(Property::Height, image->pixelHeight());
178  }
179 
180  if (image->pixelWidth()) {
181  result->add(Property::Width, image->pixelWidth());
182  }
183 
184  std::string comment = image->comment();
185  if (!comment.empty()) {
186  result->add(Property::Comment, QString::fromUtf8(comment.c_str(), comment.length()));
187  }
188 
189  const Exiv2::ExifData& data = image->exifData();
190 
191  add(result, data, Property::Manufacturer, "Exif.Image.Make", QVariant::String);
192  add(result, data, Property::Model, "Exif.Image.Model", QVariant::String);
193  add(result, data, Property::Description, "Exif.Image.ImageDescription", QVariant::String);
194  add(result, data, Property::Artist, "Exif.Image.Artist", QVariant::String);
195  add(result, data, Property::Copyright, "Exif.Image.Copyright", QVariant::String);
196  add(result, data, Property::Generator, "Exif.Image.Software", QVariant::String);
197  add(result, data, Property::ImageDateTime, "Exif.Image.DateTime", QVariant::DateTime);
198  add(result, data, Property::ImageOrientation, "Exif.Image.Orientation", QVariant::Int);
199  add(result, data, Property::PhotoFlash, "Exif.Photo.Flash", QVariant::Int);
200  add(result, data, Property::PhotoPixelXDimension, "Exif.Photo.PixelXDimension", QVariant::Int);
201  add(result, data, Property::PhotoPixelYDimension, "Exif.Photo.PixelYDimension", QVariant::Int);
202  add(result, data, Property::PhotoDateTimeOriginal, "Exif.Photo.DateTimeOriginal", QVariant::DateTime);
203  add(result, data, Property::PhotoFocalLength, "Exif.Photo.FocalLength", QVariant::Double);
204  add(result, data, Property::PhotoFocalLengthIn35mmFilm, "Exif.Photo.FocalLengthIn35mmFilm", QVariant::Double);
205  add(result, data, Property::PhotoExposureTime, "Exif.Photo.ExposureTime", QVariant::Double);
206  add(result, data, Property::PhotoExposureBiasValue, "Exif.Photo.ExposureBiasValue", QVariant::Double);
207  add(result, data, Property::PhotoFNumber, "Exif.Photo.FNumber", QVariant::Double);
208  add(result, data, Property::PhotoApertureValue, "Exif.Photo.ApertureValue", QVariant::Double);
209  add(result, data, Property::PhotoWhiteBalance, "Exif.Photo.WhiteBalance", QVariant::Int);
210  add(result, data, Property::PhotoMeteringMode, "Exif.Photo.MeteringMode", QVariant::Int);
211  add(result, data, Property::PhotoISOSpeedRatings, "Exif.Photo.ISOSpeedRatings", QVariant::Int);
212  add(result, data, Property::PhotoSaturation, "Exif.Photo.Saturation", QVariant::Int);
213  add(result, data, Property::PhotoSharpness, "Exif.Photo.Sharpness", QVariant::Int);
214 
215  double latitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLatitude");
216  double longitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLongitude");
217  double altitude = fetchGpsAltitude(data);
218 
219  QByteArray latRef = fetchByteArray(data, "Exif.GPSInfo.GPSLatitudeRef");
220  if (!latRef.isEmpty() && latRef[0] == 'S')
221  latitude *= -1;
222 
223  QByteArray longRef = fetchByteArray(data, "Exif.GPSInfo.GPSLongitudeRef");
224  if (!longRef.isEmpty() && longRef[0] == 'W')
225  longitude *= -1;
226 
227  if (!std::isnan(latitude)) {
228  result->add(Property::PhotoGpsLatitude, latitude);
229  }
230 
231  if (!std::isnan(longitude)) {
232  result->add(Property::PhotoGpsLongitude, longitude);
233  }
234 
235  if (!std::isnan(altitude)) {
236  result->add(Property::PhotoGpsAltitude, altitude);
237  }
238 }
239 
240 void Exiv2Extractor::add(ExtractionResult* result, const Exiv2::ExifData& data,
241  Property::Property prop, const char* name,
242  QVariant::Type type)
243 {
244  Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
245  if (it != data.end()) {
246  QVariant value = toVariant(it->value(), type);
247  if (!value.isNull())
248  result->add(prop, value);
249  }
250 }
251 
252 double Exiv2Extractor::fetchGpsDouble(const Exiv2::ExifData& data, const char* name)
253 {
254  Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
255  if (it != data.end() && it->count() == 3) {
256  double n = 0.0;
257  double d = 0.0;
258 
259  n = (*it).toRational(0).first;
260  d = (*it).toRational(0).second;
261 
262  if (d == 0.0) {
263  return std::numeric_limits<double>::quiet_NaN();
264  }
265 
266  double deg = n / d;
267 
268  n = (*it).toRational(1).first;
269  d = (*it).toRational(1).second;
270 
271  if (d == 0.0) {
272  return deg;
273  }
274 
275  double min = n / d;
276  if (min != -1.0) {
277  deg += min / 60.0;
278  }
279 
280  n = (*it).toRational(2).first;
281  d = (*it).toRational(2).second;
282 
283  if (d == 0.0) {
284  return deg;
285  }
286 
287  double sec = n / d;
288  if (sec != -1.0) {
289  deg += sec / 3600.0;
290  }
291 
292  return deg;
293  }
294 
295  return std::numeric_limits<double>::quiet_NaN();
296 }
297 
298 double Exiv2Extractor::fetchGpsAltitude(const Exiv2::ExifData& data)
299 {
300  double alt = std::numeric_limits<double>::quiet_NaN();
301  Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude"));
302  if (it != data.end() && it->count() > 0 &&
303  (it->value().typeId() == Exiv2::unsignedRational || it->value().typeId() == Exiv2::signedRational)) {
304  auto ratio = it->value().toRational();
305  if (ratio.second == 0) {
306  return alt;
307  }
308  it = data.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"));
309  if (it != data.end() && it->count() > 0 &&
310  (it->value().typeId() == Exiv2::unsignedByte || it->value().typeId() == Exiv2::signedByte)) {
311  auto altRef = it->value().toLong();
312  if (altRef) {
313  alt = -1.0 * ratio.first / ratio.second;
314  } else {
315  alt = 1.0 * ratio.first / ratio.second;
316  }
317  }
318  }
319  return alt;
320 }
321 
322 QByteArray Exiv2Extractor::fetchByteArray(const Exiv2::ExifData& data, const char* name)
323 {
324  Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
325  if (it != data.end() && it->count() > 0) {
326  std::string str = it->value().toString();
327  return QByteArray(str.c_str(), str.size());
328  }
329 
330  return QByteArray();
331 }
void setOffsetFromUtc(int offsetSeconds)
static QDateTime dateTimeFromString(const QString &dateString)
Tries to extract a valid date time from the string provided.
virtual void add(Property::Property property, const QVariant &value)=0
This function is called by the plugins when they wish to add a key value pair which should be indexed...
The ExtractorPlugin is the base class for all file metadata extractors.
bool isEmpty() const const
KIOFILEWIDGETS_EXPORT void add(const QString &fileClass, const QString &directory)
int length() const const
virtual void addType(Type::Type type)=0
This function is called by the plugins.
QString fromUtf8(const char *str, int size)
bool isNull() const const
bool isEmpty() const const
Property
The Property enum contains all files property types that KFileMetaData manipulates.
Definition: properties.h:38
bool isValid() const const
char * toString(const T &value)
char * data()
QString fromLatin1(const char *str, int size)
The ExtractionResult class is where all the data extracted by the indexer is saved.
Flags inputFlags() const
The flags which the extraction plugin should considering following when extracting metadata from the ...
QString inputUrl() const
The input url which the plugins will use to locate the file.
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 23:11:16 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.