21#include "exiv2extractor.h"
31#include "geolocation/cities.h"
32#include "geolocation/city.h"
47void Exiv2Extractor::setUrl(
const QUrl &url)
49 qDebug() <<
"Start parsing image file url for metadata";
52 qDebug() <<
"Image file is not valid or does not exists.";
58 }
catch (
const std::exception &) {
59 qDebug() <<
"Failed to open image to extract metadata information.";
64 qDebug() <<
"Image can not be accessed.";
68 if (!m_image->good()) {
69 qDebug() <<
"Image is not good.";
74 m_image->readMetadata();
75 }
catch (
const std::exception &) {
76 qDebug() <<
"Can not read metadta from the image.";
84Exiv2::ExifData & Exiv2Extractor::exifData()
const
86 Exiv2::ExifData &exifData = m_image->exifData();
94Coordinates Exiv2Extractor::extractGPS()
const
96 double latitude = fetchGpsDouble(
"Exif.GPSInfo.GPSLatitude");
97 double longitude = fetchGpsDouble(
"Exif.GPSInfo.GPSLongitude");
99 QByteArray latRef = getExifTagData(
"Exif.GPSInfo.GPSLatitudeRef");
100 if (!latRef.
isEmpty() && latRef[0] ==
'S')
103 QByteArray longRef = getExifTagData(
"Exif.GPSInfo.GPSLongitudeRef");
104 if (!longRef.
isEmpty() && longRef[0] ==
'W')
107 return {latitude, longitude};
110double Exiv2Extractor::fetchGpsDouble(
const char *name)
const
112 Exiv2::ExifData &data = (exifData());
113 Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
114 if (it != data.end() && it->count() == 3) {
118 n = (*it).toRational(0).first;
119 d = (*it).toRational(0).second;
127 n = (*it).toRational(1).first;
128 d = (*it).toRational(1).second;
139 n = (*it).toRational(2).first;
140 d = (*it).toRational(2).second;
157bool Exiv2Extractor::error()
const
162QString Exiv2Extractor::getExifTagString(
const char* exifTagName,
bool escapeCR)
const
166 Exiv2::ExifKey exifKey(exifTagName);
167 Exiv2::ExifData &data = (exifData());
168 Exiv2::ExifData::iterator it = data.findKey(exifKey);
171 if (it != data.end())
174 std::string val = it->print(&data);
183 catch( Exiv2::Error& e )
189 qWarning() <<
"Default exception from Exiv2";
195QByteArray Exiv2Extractor::getExifTagData(
const char* exifTagName)
const
199 Exiv2::ExifKey exifKey(exifTagName);
200 Exiv2::ExifData &data = (exifData());
201 Exiv2::ExifData::iterator it = data.findKey(exifKey);
203 if (it != data.end())
205 char*
const s =
new char[(*it).size()];
206 (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
213 catch( Exiv2::Error& e )
219 qWarning() <<
"Default exception from Exiv2";
225QVariant Exiv2Extractor::getExifTagVariant(
const char* exifTagName,
bool rationalAsListOfInts,
bool stringEscapeCR,
int component)
const
229 Exiv2::ExifKey exifKey(exifTagName);
230 Exiv2::ExifData &data = (exifData());
231 Exiv2::ExifData::iterator it = data.findKey(exifKey);
233 if (it != data.end())
235 switch (it->typeId())
237 case Exiv2::unsignedByte:
238 case Exiv2::unsignedShort:
239 case Exiv2::unsignedLong:
240 case Exiv2::signedShort:
241 case Exiv2::signedLong:
242 if (it->count() > component)
243 return QVariant((
int)it->toFloat(component));
246 case Exiv2::unsignedRational:
247 case Exiv2::signedRational:
249 if (rationalAsListOfInts)
251 if (it->count() <= component)
255 list << (*it).toRational(component).
first;
256 list << (*it).toRational(component).second;
262 if (it->count() <= component)
266 double num = (*it).toRational(component).first;
267 double den = (*it).toRational(component).second;
280 case Exiv2::asciiString:
284 std::ostringstream os;
298 catch( Exiv2::Error& e )
304 qWarning() <<
"Default exception from Exiv2";
310static bool isUtf8(
const char*
const buffer)
328 static const unsigned char text_chars[256] =
331 F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F,
333 F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F,
334 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
335 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
336 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
337 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
338 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
339 T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F,
341 X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X,
342 X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,
343 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
344 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
345 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
346 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
347 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
348 I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I
351 for (i = 0; (c = buffer[i]); ++i)
360 if (text_chars[c] != T)
364 else if ((c & 0x40) == 0)
379 else if ((c & 0x10) == 0)
384 else if ((c & 0x08) == 0)
389 else if ((c & 0x04) == 0)
394 else if ((c & 0x02) == 0)
404 for (n = 0; n < following; ++n)
408 if (!(c = buffer[i]))
411 if ((c & 0x80) == 0 || (c & 0x40))
424static QString detectEncodingAndDecode(
const std::string& value)
437 if (isUtf8(value.c_str()))
450static QString convertCommentValue(
const Exiv2::Exifdatum& exifDatum)
457 comment = exifDatum.toString();
461 if (comment.length() > 8 && comment.substr(0, 8) ==
"charset=")
464 std::string::size_type pos = comment.find_first_of(
' ');
466 if (pos != std::string::npos)
469 charset = comment.substr(8, pos-8);
471 comment = comment.substr(pos+1);
475 if (charset ==
"\"Unicode\"")
479 else if (charset ==
"\"Jis\"")
482 return codec.decode(comment.c_str());
484 else if (charset ==
"\"Ascii\"")
490 return detectEncodingAndDecode(comment);
493 catch( Exiv2::Error& e )
499 qWarning()<<
"Default exception from Exiv2";
505MetaDataMap Exiv2Extractor::getExifTagsDataList(
const QStringList& exifKeysFilter,
bool invertSelection)
const
507 if (exifData().empty())
512 Exiv2::ExifData &data = exifData();
517 for (Exiv2::ExifData::iterator md = data.begin(); md != data.end(); ++md)
526 tagValue = convertCommentValue(*md);
534 std::ostringstream os;
547 if (!invertSelection)
550 metaDataMap.
insert(key, tagValue);
555 metaDataMap.
insert(key, tagValue);
560 metaDataMap.
insert(key, tagValue);
566 catch (Exiv2::Error& e)
572 qWarning() <<
"Default exception from Exiv2";
578QString Exiv2Extractor::getExifComment()
const
582 if (!exifData().empty())
584 Exiv2::ExifData &data(exifData());
585 Exiv2::ExifKey key(
"Exif.Photo.UserComment");
586 Exiv2::ExifData::iterator it = data.findKey(key);
588 if (it != data.end())
590 QString exifComment = convertCommentValue(*it);
597 Exiv2::ExifKey key2(
"Exif.Image.ImageDescription");
598 Exiv2::ExifData::iterator it2 = data.findKey(key2);
600 if (it2 != data.end())
602 QString exifComment = convertCommentValue(*it2);
618 catch( Exiv2::Error& e )
620 qWarning() << (
QString::fromLatin1(
"Cannot find Exif User Comment using Exiv2 "), e.what());
624 qWarning() <<
"Default exception from Exiv2";
630QString Exiv2Extractor::GPSString()
const
639 if(!m_city.isValid())
644 return m_city.name();
647QString Exiv2Extractor::cityId()
const
657City Exiv2Extractor::city()
const
664 auto c = extractGPS();
666 if(c.first == 0.0 || c.second == 0.0)
671 return Cities::getInstance()->findCity(c.first, c.second);
674bool Exiv2Extractor::writeTag(
const char *tagName,
const QVariant &value)
678 qDebug() <<
"trying to write tag4";
680 Exiv2::ExifKey exifKey(tagName);
681 Exiv2::ExifData &data = (exifData());
682 Exiv2::ExifData::iterator it = data.findKey(exifKey);
683 qDebug() <<
"trying to write tag5";
685 if (it != data.end())
687 qDebug() <<
"trying to write tag2";
689 switch (it->typeId())
691 case Exiv2::unsignedByte:
692 case Exiv2::unsignedShort:
693 case Exiv2::unsignedLong:
694 case Exiv2::signedShort:
695 case Exiv2::signedLong:
696 case Exiv2::unsignedLongLong:
697 case Exiv2::signedLongLong:
702 qDebug() <<
"Writting number metadata" << tagName;
704#if EXIV2_TEST_VERSION(0,28,0)
705 Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::signedLongLong);
708 Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::signedLongLong);
711 it->setValue(v.get());
715 case Exiv2::unsignedRational:
716 case Exiv2::signedRational:
720 qDebug() <<
"Writting rational metadata" << tagName;
722#if EXIV2_TEST_VERSION(0,28,0)
723 Exiv2::RationalValue::UniquePtr rv(
new Exiv2::RationalValue);
726 Exiv2::RationalValue::AutoPtr rv(
new Exiv2::RationalValue);
729 it->setValue(rv.get());
741#if EXIV2_TEST_VERSION(0,28,0)
742 Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::asciiString);
745 Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::asciiString);
747 v->read(date.toStdString());
748 it->setValue(v.get());
752 case Exiv2::asciiString:
758 qDebug() <<
"Writting ascii metadata" << tagName;
762#if EXIV2_TEST_VERSION(0,28,0)
763 Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::asciiString);
766 Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::asciiString);
768 v->read(
string.toStdString());
769 it->setValue(v.get());
775 qDebug() <<
"Writting unkown metadata" << tagName;
780 qDebug() <<
"Writting metadata EXIF tag to file" << tagName;
782 m_image->writeMetadata();
786 Exiv2::Exifdatum& tag = data[tagName];
789 m_image->writeMetadata();
793 catch( Exiv2::Error& e )
801 qWarning() <<
"Default exception from Exiv2";
808bool Exiv2Extractor::removeTag(
const char *tagName)
813 Exiv2::ExifKey key = Exiv2::ExifKey(tagName);
814 Exiv2::ExifData &data = (exifData());
816 Exiv2::ExifData::iterator it = data.findKey(key);
818 if (it != data.end())
821 m_image->writeMetadata();
825 catch( Exiv2::Error& e )
833 qWarning() <<
"Default exception from Exiv2";
A class for representing the GPS coordinates and information of a city.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QByteArray tagValue(const Elem &elem, const char *keyName)
bool isEmpty() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool exists() const const
bool isEmpty() const const
iterator insert(const Key &key, const T &value)
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
std::string toStdString() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isValid() const const
QString toLocalFile() const const
bool canConvert() const const
QString toString() const const