File indexing completed on 2025-01-26 04:25:03

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 
0003 /*
0004  *    Copyright (C) 2012-15  Vishesh Handa <vhanda@kde.org>
0005  * 
0006  *    This library is free software; you can redistribute it and/or
0007  *    modify it under the terms of the GNU Lesser General Public
0008  *    License as published by the Free Software Foundation; either
0009  *    version 2.1 of the License, or (at your option) any later version.
0010  * 
0011  *    This library 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 GNU
0014  *    Lesser General Public License for more details.
0015  * 
0016  *    You should have received a copy of the GNU Lesser General Public
0017  *    License along with this library; if not, write to the Free Software
0018  *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0019  */
0020 
0021 #include "exiv2extractor.h"
0022 
0023 #include <QGeoAddress>
0024 #include <QFileInfo>
0025 #include <QDateTime>
0026 #include <QDebug>
0027 #include <QFile>
0028 #include <QTextCodec>
0029 
0030 #include "geolocation/cities.h"
0031 #include "geolocation/city.h"
0032 
0033 Exiv2Extractor::Exiv2Extractor(const QUrl &url, QObject *parent) : QObject(parent)
0034 , m_error(true)
0035 
0036 {
0037     this->setUrl(url);
0038 }
0039 
0040 Exiv2Extractor::Exiv2Extractor(QObject *parent) : QObject(parent)
0041 , m_error(true)
0042 
0043 {
0044     
0045 }
0046 
0047 void Exiv2Extractor::setUrl(const QUrl &url)
0048 {
0049     m_url = url;
0050     if (!QFileInfo::exists(m_url.toLocalFile()) || m_url.isEmpty() || !m_url.isValid()) {
0051         m_error = true;
0052     }    
0053     
0054     try {
0055         m_image =  Exiv2::ImageFactory::open(m_url.toLocalFile().toStdString());
0056     } catch (const std::exception &) {
0057         return;
0058     }
0059     if (!m_image.get()) {
0060         return;
0061     }
0062     
0063     if (!m_image->good()) {
0064         return;
0065     }
0066     
0067     try {
0068         m_image->readMetadata();
0069     } catch (const std::exception &) {
0070         return;
0071     }
0072     
0073     m_error = false;
0074 }
0075 
0076 
0077 Exiv2::ExifData & Exiv2Extractor::exifData() const
0078 {   
0079     Exiv2::ExifData &exifData = m_image->exifData();
0080 //     if (exifData.empty()) {
0081 //         qWarning() <<  "No EXIF data in : " << m_url.toString();
0082 //     }    
0083     
0084     return exifData;
0085 }
0086 
0087 Coordinates Exiv2Extractor::extractGPS() const
0088 {
0089     double latitude = fetchGpsDouble("Exif.GPSInfo.GPSLatitude");
0090     double longitude = fetchGpsDouble("Exif.GPSInfo.GPSLongitude");
0091     
0092     QByteArray latRef = getExifTagData("Exif.GPSInfo.GPSLatitudeRef");
0093     if (!latRef.isEmpty() && latRef[0] == 'S')
0094         latitude *= -1;
0095     
0096     QByteArray longRef = getExifTagData("Exif.GPSInfo.GPSLongitudeRef");
0097     if (!longRef.isEmpty() && longRef[0] == 'W')
0098         longitude *= -1;
0099     
0100     return {latitude, longitude};
0101 }
0102 
0103 double Exiv2Extractor::fetchGpsDouble(const char *name) const
0104 {
0105     Exiv2::ExifData &data = (exifData());
0106     Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
0107     if (it != data.end() && it->count() == 3) {
0108         double n = 0.0;
0109         double d = 0.0;
0110         
0111         n = (*it).toRational(0).first;
0112         d = (*it).toRational(0).second;
0113         
0114         if (d == 0) {
0115             return 0.0;
0116         }
0117         
0118         double deg = n / d;
0119         
0120         n = (*it).toRational(1).first;
0121         d = (*it).toRational(1).second;
0122         
0123         if (d == 0) {
0124             return deg;
0125         }
0126         
0127         double min = n / d;
0128         if (min != -1.0) {
0129             deg += min / 60.0;
0130         }
0131         
0132         n = (*it).toRational(2).first;
0133         d = (*it).toRational(2).second;
0134         
0135         if (d == 0) {
0136             return deg;
0137         }
0138         
0139         double sec = n / d;
0140         if (sec != -1.0) {
0141             deg += sec / 3600.0;
0142         }
0143         
0144         return deg;
0145     }
0146     
0147     return 0.0;
0148 }
0149 
0150 bool Exiv2Extractor::error() const
0151 {
0152     return m_error;
0153 }
0154 
0155 QString Exiv2Extractor::getExifTagString(const char* exifTagName, bool escapeCR) const
0156 {    
0157     try
0158     {
0159         Exiv2::ExifKey exifKey(exifTagName);
0160         Exiv2::ExifData &data = (exifData());
0161         Exiv2::ExifData::iterator it = data.findKey(exifKey);
0162         
0163         
0164         if (it != data.end())
0165         {
0166             // See B.K.O #184156 comment #13
0167             std::string val  = it->print(&data);
0168             QString tagValue = QString::fromLocal8Bit(val.c_str());
0169             
0170             if (escapeCR)
0171                 tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0172             
0173             return tagValue;
0174         }
0175     }
0176     catch( Exiv2::Error& e )
0177     {
0178         qWarning() << QString("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)) << e.what();
0179     }
0180     catch(...)
0181     {
0182         qWarning() << "Default exception from Exiv2";
0183     }
0184     
0185     return QString();
0186 }
0187 
0188 QByteArray Exiv2Extractor::getExifTagData(const char* exifTagName) const
0189 {
0190     try
0191     {
0192         Exiv2::ExifKey exifKey(exifTagName);
0193         Exiv2::ExifData &data = (exifData());        
0194         Exiv2::ExifData::iterator it = data.findKey(exifKey);
0195         
0196         if (it != data.end())
0197         {
0198             char* const s = new char[(*it).size()];
0199             (*it).copy((Exiv2::byte*)s, Exiv2::bigEndian);
0200             QByteArray data(s, (*it).size());
0201             delete[] s;
0202             
0203             return data;
0204         }
0205     }
0206     catch( Exiv2::Error& e )
0207     {
0208         qWarning() << QString("Cannot find Exif key '%1' into image using Exiv2 ").arg(QString::fromLatin1(exifTagName)) << e.what();
0209     }
0210     catch(...)
0211     {
0212         qWarning() << "Default exception from Exiv2";
0213     }
0214     
0215     return QByteArray();
0216 }
0217 
0218 QVariant Exiv2Extractor::getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts, bool stringEscapeCR, int component) const
0219 {
0220     try
0221     {
0222         Exiv2::ExifKey exifKey(exifTagName);
0223         Exiv2::ExifData &data = (exifData());
0224         Exiv2::ExifData::iterator it = data.findKey(exifKey);
0225         
0226         if (it != data.end())
0227         {
0228             switch (it->typeId())
0229             {
0230                 case Exiv2::unsignedByte:
0231                 case Exiv2::unsignedShort:
0232                 case Exiv2::unsignedLong:
0233                 case Exiv2::signedShort:
0234                 case Exiv2::signedLong:
0235                     if (it->count() > component)
0236                         return QVariant((int)it->toLong(component));
0237                 else
0238                     return QVariant(QVariant::Int);
0239                 case Exiv2::unsignedRational:
0240                 case Exiv2::signedRational:
0241                     
0242                     if (rationalAsListOfInts)
0243                     {
0244                         if (it->count() <= component)
0245                             return QVariant(QVariant::List);
0246                         
0247                         QList<QVariant> list;
0248                         list << (*it).toRational(component).first;
0249                         list << (*it).toRational(component).second;
0250                         
0251                         return QVariant(list);
0252                     }
0253                     else
0254                     {
0255                         if (it->count() <= component)
0256                             return QVariant(QVariant::Double);
0257                         
0258                         // prefer double precision
0259                         double num = (*it).toRational(component).first;
0260                         double den = (*it).toRational(component).second;
0261                         
0262                         if (den == 0.0)
0263                             return QVariant(QVariant::Double);
0264                         
0265                         return QVariant(num / den);
0266                     }
0267                 case Exiv2::date:
0268                 case Exiv2::time:
0269                 {
0270                     QDateTime dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
0271                     return QVariant(dateTime);
0272                 }
0273                 case Exiv2::asciiString:
0274                 case Exiv2::comment:
0275                 case Exiv2::string:
0276                 {
0277                     std::ostringstream os;
0278                     os << *it;
0279                     QString tagValue = QString::fromLocal8Bit(os.str().c_str());
0280                     
0281                     if (stringEscapeCR)
0282                         tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0283                     
0284                     return QVariant(tagValue);
0285                 }
0286                 default:
0287                     break;
0288             }
0289         }
0290     }
0291     catch( Exiv2::Error& e )
0292     {
0293         qWarning () << QString("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(exifTagName)) << e.what();
0294     }
0295     catch(...)
0296     {
0297         qWarning() << "Default exception from Exiv2";
0298     }
0299     
0300     return QVariant();
0301 }
0302 
0303 static bool isUtf8(const char* const buffer)
0304 {
0305     int i, n;
0306     unsigned char c;
0307     bool gotone = false;
0308     
0309     if (!buffer)
0310         return true;
0311     
0312     // character never appears in text
0313     #define F 0
0314     // character appears in plain ASCII text
0315     #define T 1
0316     // character appears in ISO-8859 text
0317     #define I 2
0318     // character appears in non-ISO extended ASCII (Mac, IBM PC)
0319     #define X 3
0320     
0321     static const unsigned char text_chars[256] =
0322     {
0323         //                  BEL BS HT LF    FF CR    
0324         F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F,  // 0x0X
0325         //                              ESC          
0326         F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F,  // 0x1X
0327         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x2X
0328         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x3X
0329         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x4X
0330         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x5X
0331         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,  // 0x6X
0332         T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F,  // 0x7X
0333         //            NEL                            
0334         X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X,  // 0x8X
0335         X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X,  // 0x9X
0336         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xaX
0337         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xbX
0338         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xcX
0339         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xdX
0340         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,  // 0xeX
0341         I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I   // 0xfX
0342     };
0343     
0344     for (i = 0; (c = buffer[i]); ++i)
0345     {
0346         if ((c & 0x80) == 0)
0347         {
0348             // 0xxxxxxx is plain ASCII
0349             
0350             // Even if the whole file is valid UTF-8 sequences,
0351             // still reject it if it uses weird control characters.
0352             
0353             if (text_chars[c] != T)
0354                 return false;
0355             
0356         }
0357         else if ((c & 0x40) == 0)
0358         {
0359             // 10xxxxxx never 1st byte
0360             return false;
0361         }
0362         else
0363         {
0364             // 11xxxxxx begins UTF-8
0365             int following = 0;
0366             
0367             if ((c & 0x20) == 0)
0368             {
0369                 // 110xxxxx
0370                 following = 1;
0371             }
0372             else if ((c & 0x10) == 0)
0373             {
0374                 // 1110xxxx
0375                 following = 2;
0376             }
0377             else if ((c & 0x08) == 0)
0378             {
0379                 // 11110xxx
0380                 following = 3;
0381             }
0382             else if ((c & 0x04) == 0)
0383             {
0384                 // 111110xx
0385                 following = 4;
0386             }
0387             else if ((c & 0x02) == 0)
0388             {
0389                 // 1111110x
0390                 following = 5;
0391             }
0392             else
0393             {
0394                 return false;
0395             }
0396             
0397             for (n = 0; n < following; ++n)
0398             {
0399                 i++;
0400                 
0401                 if (!(c = buffer[i]))
0402                     goto done;
0403                 
0404                 if ((c & 0x80) == 0 || (c & 0x40))
0405                     return false;
0406             }
0407             
0408             gotone = true;
0409         }
0410     }
0411     
0412     done:
0413     
0414     return gotone;   // don't claim it's UTF-8 if it's all 7-bit.
0415 }
0416 
0417 static QString detectEncodingAndDecode(const std::string& value) 
0418 {
0419     // For charset autodetection, we could use sophisticated code
0420     // (Mozilla chardet, KHTML's autodetection, QTextCodec::codecForContent),
0421     // but that is probably too much.
0422     // We check for UTF8, Local encoding and ASCII.
0423     // Look like KEncodingDetector class can provide a full implementation for encoding detection.
0424     
0425     if (value.empty())
0426     {
0427         return QString();
0428     }
0429     
0430     if (isUtf8(value.c_str()))
0431     {
0432         return QString::fromUtf8(value.c_str());
0433     }
0434     
0435     // Utf8 has a pretty unique byte pattern.
0436     // Thats not true for ASCII, it is not possible
0437     // to reliably autodetect different ISO-8859 charsets.
0438     // So we can use either local encoding, or latin1.
0439     
0440     return QString::fromLocal8Bit(value.c_str());
0441 }
0442 
0443 static QString convertCommentValue(const Exiv2::Exifdatum& exifDatum)
0444 {
0445     try
0446     {
0447         std::string comment;
0448         std::string charset;
0449         
0450         comment = exifDatum.toString();
0451         
0452         // libexiv2 will prepend "charset=\"SomeCharset\" " if charset is specified
0453         // Before conversion to QString, we must know the charset, so we stay with std::string for a while
0454         if (comment.length() > 8 && comment.substr(0, 8) == "charset=")
0455         {
0456             // the prepended charset specification is followed by a blank
0457             std::string::size_type pos = comment.find_first_of(' ');
0458             
0459             if (pos != std::string::npos)
0460             {
0461                 // extract string between the = and the blank
0462                 charset = comment.substr(8, pos-8);
0463                 // get the rest of the string after the charset specification
0464                 comment = comment.substr(pos+1);
0465             }
0466         }
0467         
0468         if (charset == "\"Unicode\"")
0469         {
0470             return QString::fromUtf8(comment.data());
0471         }
0472         else if (charset == "\"Jis\"")
0473         {
0474             QTextCodec* const codec = QTextCodec::codecForName("JIS7");
0475             return codec->toUnicode(comment.c_str());
0476         }
0477         else if (charset == "\"Ascii\"")
0478         {
0479             return QString::fromLatin1(comment.c_str());
0480         }
0481         else
0482         {
0483             return detectEncodingAndDecode(comment);
0484         }
0485     }
0486     catch( Exiv2::Error& e )
0487     {
0488         qWarning() << (QString::fromLatin1("Cannot convert Comment using Exiv2 "), e.what());
0489     }
0490     catch(...)
0491     {
0492         qWarning()<< "Default exception from Exiv2";
0493     }
0494     
0495     return QString();
0496 }
0497 
0498 MetaDataMap Exiv2Extractor::getExifTagsDataList(const QStringList& exifKeysFilter, bool invertSelection) const
0499 {
0500     if (exifData().empty())
0501         return MetaDataMap();
0502     
0503     try
0504     {
0505         Exiv2::ExifData &data = exifData();
0506         data.sortByKey();
0507         
0508         MetaDataMap metaDataMap;
0509         
0510         for (Exiv2::ExifData::iterator md = data.begin(); md != data.end(); ++md)
0511         {
0512             QString key = QString::fromLatin1(md->key().c_str());
0513             
0514             // Decode the tag value with a user friendly output.
0515             QString tagValue;
0516             
0517             if (key == QString::fromLatin1("Exif.Photo.UserComment"))
0518             {
0519                 tagValue = convertCommentValue(*md);
0520             }
0521             else if (key == QString::fromLatin1("Exif.Image.0x935c"))
0522             {
0523                 tagValue = QString::number(md->value().size());
0524             }
0525             else
0526             {
0527                 std::ostringstream os;
0528                 os << *md;
0529                 
0530                 // Exif tag contents can be an translated strings, no only simple ascii.
0531                 tagValue = QString::fromLocal8Bit(os.str().c_str());
0532             }
0533             
0534             tagValue.replace(QString::fromLatin1("\n"), QString::fromLatin1(" "));
0535             
0536             // We apply a filter to get only the Exif tags that we need.
0537             
0538             if (!exifKeysFilter.isEmpty())
0539             {
0540                 if (!invertSelection)
0541                 {
0542                     if (exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
0543                         metaDataMap.insert(key, tagValue);
0544                 }
0545                 else
0546                 {
0547                     if (!exifKeysFilter.contains(key.section(QString::fromLatin1("."), 1, 1)))
0548                         metaDataMap.insert(key, tagValue);
0549                 }
0550             }
0551             else // else no filter at all.
0552             {
0553                 metaDataMap.insert(key, tagValue);
0554             }
0555         }
0556         
0557         return metaDataMap;
0558     }
0559     catch (Exiv2::Error& e)
0560     {
0561         qWarning() << (QString::fromLatin1("Cannot parse EXIF metadata using Exiv2 "), e.what());
0562     }
0563     catch(...)
0564     {
0565         qWarning() << "Default exception from Exiv2";
0566     }
0567     
0568     return MetaDataMap();
0569 }
0570 
0571 QString Exiv2Extractor::getExifComment() const
0572 {
0573     try
0574     {
0575         if (!exifData().empty())
0576         {
0577             Exiv2::ExifData &data(exifData());
0578             Exiv2::ExifKey key("Exif.Photo.UserComment");
0579             Exiv2::ExifData::iterator it = data.findKey(key);
0580             
0581             if (it != data.end())
0582             {
0583                 QString exifComment = convertCommentValue(*it);
0584                 
0585                 // some cameras fill the UserComment with whitespace
0586                 if (!exifComment.isEmpty() && !exifComment.trimmed().isEmpty())
0587                     return exifComment;
0588             }
0589             
0590             Exiv2::ExifKey key2("Exif.Image.ImageDescription");
0591             Exiv2::ExifData::iterator it2 = data.findKey(key2);
0592             
0593             if (it2 != data.end())
0594             {
0595                 QString exifComment = convertCommentValue(*it2);
0596                 
0597                 // Some cameras fill in nonsense default values
0598                 QStringList blackList;
0599                 blackList << QString::fromLatin1("SONY DSC"); // + whitespace
0600                 blackList << QString::fromLatin1("OLYMPUS DIGITAL CAMERA");
0601                 blackList << QString::fromLatin1("MINOLTA DIGITAL CAMERA");
0602                 
0603                 QString trimmedComment = exifComment.trimmed();
0604                 
0605                 // some cameras fill the UserComment with whitespace
0606                 if (!exifComment.isEmpty() && !trimmedComment.isEmpty() && !blackList.contains(trimmedComment))
0607                     return exifComment;
0608             }
0609         }
0610     }
0611     catch( Exiv2::Error& e )
0612     {
0613         qWarning() << (QString::fromLatin1("Cannot find Exif User Comment using Exiv2 "), e.what());
0614     }
0615     catch(...)
0616     {
0617         qWarning() << "Default exception from Exiv2";
0618     }
0619     
0620     return QString();
0621 }
0622 
0623 QString Exiv2Extractor::GPSString() const
0624 {
0625     if(error())
0626     {
0627         return QString();
0628     }
0629     
0630     std::unique_ptr<City>m_city(city());
0631     
0632     if(!m_city)
0633     {
0634         return QString();
0635     }
0636     
0637     if(!m_city->isValid())
0638     {
0639         return QString();
0640     }
0641     
0642     return m_city->name();
0643 }
0644 
0645 QString Exiv2Extractor::cityId() const
0646 {
0647     if(error())
0648     {
0649         return QString();
0650     }
0651     
0652     std::unique_ptr<City>m_city(city());
0653     
0654     if(!m_city)
0655     {
0656         return QString();
0657     }
0658     
0659     if(!m_city->isValid())
0660     {
0661         return QString();
0662     }
0663     
0664     return m_city->id();
0665 }
0666 
0667 City* Exiv2Extractor::city() const
0668 {
0669     if(error())
0670     {
0671         return nullptr;
0672     }
0673     
0674     auto c = extractGPS();
0675     
0676     if(c.first == 0.0 || c.second == 0.0)
0677     {
0678         return nullptr;
0679     }
0680     
0681     return Cities::getInstance()->findCity(c.first, c.second);
0682 }
0683 
0684 bool Exiv2Extractor::writeTag(const char *tagName, const QVariant &value)
0685 {
0686     try
0687     {
0688         qDebug() << "trying to write tag4";
0689         
0690         Exiv2::ExifKey exifKey(tagName);
0691         Exiv2::ExifData &data = (exifData());
0692         Exiv2::ExifData::iterator it = data.findKey(exifKey);
0693         qDebug() << "trying to write tag5";
0694         
0695         if (it != data.end())
0696         {
0697             qDebug() << "trying to write tag2";
0698             
0699             switch (it->typeId())
0700             {
0701                 case Exiv2::unsignedByte:
0702                 case Exiv2::unsignedShort:
0703                 case Exiv2::unsignedLong:
0704                 case Exiv2::signedShort:
0705                 case Exiv2::signedLong:
0706                 case Exiv2::unsignedLongLong:
0707                 case Exiv2::signedLongLong:
0708                 {
0709                     if(!value.canConvert<QString>())
0710                         return false;
0711                     
0712                     qDebug() << "Writting number metadata" << tagName;
0713                     
0714                     Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::signedLongLong);
0715                     v->read(value.toString().toStdString());
0716                     it->setValue(v.get());
0717                     break;
0718                 }
0719                 
0720                 case Exiv2::unsignedRational:
0721                 case Exiv2::signedRational:
0722                 {
0723                     if(!value.canConvert<QString>())
0724                         return false;                
0725                                                             qDebug() << "Writting rational metadata" << tagName;
0726 
0727                     Exiv2::RationalValue::AutoPtr rv(new Exiv2::RationalValue);
0728                     rv->read(value.toString().toStdString());
0729                     it->setValue(rv.get());
0730                     break;               
0731                     
0732                 }
0733                 case Exiv2::date:
0734                 case Exiv2::time:
0735                 {
0736                     if(!value.canConvert<QString>())
0737                         return false;
0738                     
0739                     auto date = value.toString();
0740                     Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::asciiString);
0741                     v->read(date.toStdString());
0742                     it->setValue(v.get());
0743                     break;
0744                     
0745                 }
0746                 case Exiv2::asciiString:
0747                 case Exiv2::comment:
0748                 case Exiv2::string:
0749                 {
0750                     if(!value.canConvert<QString>())
0751                         return false;
0752                     qDebug() << "Writting ascii metadata" << tagName;
0753                     
0754                     auto string = value.toString();
0755                     Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::asciiString);
0756                     v->read(string.toStdString());
0757                     it->setValue(v.get());
0758                     break;
0759                     
0760                     
0761                 }
0762                 default:
0763                                         qDebug() << "Writting unkown metadata" << tagName;
0764 
0765                     return false;
0766             }
0767             
0768             qDebug() << "Writting metadata EXIF tag to file" << tagName;
0769             //             m_image->setExifData(data);
0770             m_image->writeMetadata();
0771             return true;
0772         }else
0773         {
0774             Exiv2::Exifdatum& tag = data[tagName];
0775             std::string str = value.toString().toStdString();
0776             tag.setValue(str);
0777             m_image->writeMetadata();
0778             return true;
0779         }
0780     }
0781     catch( Exiv2::Error& e )
0782     {
0783         qWarning () << QString("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(tagName)) << e.what();
0784         return false;
0785         
0786     }
0787     catch(...)
0788     {
0789         qWarning() << "Default exception from Exiv2";
0790         return false;
0791         
0792     }
0793     return false;
0794 }
0795 
0796 bool Exiv2Extractor::removeTag(const char *tagName)
0797 {
0798     
0799     try
0800     {
0801         Exiv2::ExifKey key = Exiv2::ExifKey(tagName);
0802         Exiv2::ExifData &data = (exifData());
0803         
0804         Exiv2::ExifData::iterator it = data.findKey(key);
0805         
0806         if (it != data.end())
0807         {
0808             data.erase(it);
0809             m_image->writeMetadata();
0810             return true;
0811         }
0812     }
0813     catch( Exiv2::Error& e )
0814     {
0815         qWarning () << QString("Cannot find Exif key '%1' in the image using Exiv2 ").arg(QString::fromLatin1(tagName)) << e.what();
0816         return false;
0817         
0818     }
0819     catch(...)
0820     {
0821         qWarning() << "Default exception from Exiv2";
0822         return false;
0823         
0824     }
0825     
0826     return false;
0827 }
0828