Esempio n. 1
0
    bool getExifDateTime(Exiv2::ExifData &exifData, QDateTime &dateTime) {
        bool anyFound = false;

        try {
            Exiv2::ExifKey key(EXIF_PHOTO_DATETIMEORIGINAL);
            Exiv2::ExifData::iterator it = exifData.findKey(key);
            if (it != exifData.end()) {
                dateTime = QDateTime::fromString(QString::fromLatin1(it->toString().c_str()), Qt::ISODate);
                anyFound = dateTime.isValid();
            }

            if (!anyFound) {
                Exiv2::ExifKey imageKey(EXIF_IMAGE_DATETIMEORIGINAL);
                Exiv2::ExifData::iterator imageIt = exifData.findKey(imageKey);
                if (imageIt != exifData.end()) {
                    dateTime = QDateTime::fromString(QString::fromLatin1(imageIt->toString().c_str()), Qt::ISODate);
                    anyFound = dateTime.isValid();
                }
            }
        }
        catch (Exiv2::Error &e) {
            LOG_WARNING << "Exiv2 error:" << e.what();
            anyFound = false;
        }
        catch (...) {
            LOG_WARNING << "Exception";
            anyFound = false;
#ifdef QT_DEBUG
            throw;
#endif
        }

        return anyFound;
    }
void DB::FileInfo::parseEXIV2( const DB::FileName& fileName )
{
    Exiv2::ExifData map = Exif::Info::instance()->metadata( fileName ).exif;

    // Date
    m_date = fetchEXIV2Date( map, "Exif.Photo.DateTimeOriginal" );
    if ( !m_date.isValid() ) {
        m_date = fetchEXIV2Date( map, "Exif.Photo.DateTimeDigitized" );
        if ( !m_date.isValid() )
            m_date = fetchEXIV2Date( map, "Exif.Image.DateTime" );
    }

    // Angle
    if ( map.findKey( Exiv2::ExifKey( "Exif.Image.Orientation" ) ) != map.end() ) {
        const Exiv2::Exifdatum& datum = map["Exif.Image.Orientation"];

        int orientation = 0;
        if (datum.count() > 0)
            orientation =  datum.toLong();
        m_angle = orientationToAngle( orientation );
    }

    // Description
    if( map.findKey( Exiv2::ExifKey( "Exif.Image.ImageDescription" ) ) != map.end() ) {
        const Exiv2::Exifdatum& datum = map["Exif.Image.ImageDescription"];
        m_description = QString::fromLocal8Bit( datum.toString().c_str() );
    }
}
Esempio n. 3
0
void print(const std::string& file)
{
    Exiv2::ExifData ed;
    int rc = ed.read(file);
    if (rc) {
        std::string error = Exiv2::ExifData::strError(rc, file);
        throw Exiv2::Error(error);
    }

    Exiv2::ExifData::const_iterator end = ed.end();
    for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
        std::cout << std::setw(45) << std::setfill(' ') << std::left
                  << i->key() << " "
                  << "0x" << std::setw(4) << std::setfill('0') << std::right
                  << std::hex << i->tag() << " " 
                  << std::setw(12) << std::setfill(' ') << std::left
                  << i->ifdName() << " "
                  << std::setw(9) << std::setfill(' ') << std::left
                  << i->typeName() << " "
                  << std::dec << std::setw(3) 
                  << std::setfill(' ') << std::right
                  << i->count() << " "
                  << std::dec << i->value() 
                  << "\n";

    }
}
QString exifDataToString(Exiv2::ExifData exifData) {
    if (exifData.empty()) {
        return QString(QObject::tr("No EXIF found"));
    }
    try {
        Exiv2::ExifData::const_iterator end = exifData.end();
        QString tmpExif, final;
        for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
            const char* tn = i->typeName();
            /*std::string str = i->key() << " ";
                      << "0x" << std::setw(4) << std::setfill('0') << std::right
                      << std::hex << i->tag() << " "
                      << std::setw(9) << std::setfill(' ') << std::left
                      << (tn ? tn : "Unknown") << " "
                      << std::dec << std::setw(3)
                      << std::setfill(' ') << std::right
                      << i->count() << "  "
                      << std::dec << i->value()
                      << "\n";*/
            tmpExif = "<span style='color:#1cb495;'>" + QString::fromStdString(i->key()).split(".").at(2) + "</span>&emsp;" +
                      //QString::number(i->tag()) + "\t" +
                      //(tn ? tn : "Unknown") + "\t" +
                      //QString::number(i->count()) + "\t" +
                      QString::fromStdString(i->value().toString()) + "<br />";
            final.append(tmpExif);
        }
        return final;
    } catch(Exiv2::Error& e) {
Esempio n. 5
0
 const double getExiv2ValueDouble(Exiv2::ExifData& exifData, Exiv2::ExifData::const_iterator it)
 {
     if(it!=exifData.end() && it->count())
     {
         return it->toFloat();
     }
     return 0;
 };
Esempio n. 6
0
 const std::string getExiv2ValueString(Exiv2::ExifData& exifData,Exiv2::ExifData::const_iterator it)
 {
     if(it!=exifData.end() && it->count())
     {
         return hugin_utils::StrTrim(it->toString());
     };
     return std::string("");
 };
Esempio n. 7
0
 const long getExiv2ValueLong(Exiv2::ExifData& exifData, Exiv2::ExifData::const_iterator it)
 {
     if(it!=exifData.end() && it->count())
     {
         return it->toLong();
     }
     return 0;
 };
Esempio n. 8
0
/** \fn     ImageUtils::HasExifKey(Exiv2::ExifData, const QString &)
 *  \brief  Checks if the exif data of the file contains the given key
 *  \param  exifData The entire exif data
 *  \param  exifTag The key that shall be checked
 *  \return True if the exif key exists, otherwise false
 */
bool ImageUtils::HasExifKey(Exiv2::ExifData exifData,
                            const QString &exifTag)
{
    Exiv2::ExifKey key(exifTag.toLocal8Bit().constData());
    Exiv2::ExifData::iterator it = exifData.findKey(key);

    // If the iterator has is the end of the
    // list then the key has not been found
    return !(it == exifData.end());
}
Esempio n. 9
0
 bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, float & value)
 {
     Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
     if (itr != exifData.end() && itr->count())
     {
         value = itr->toFloat();
         return true;
     }
     else
     {
         return false;
     };
 };
Esempio n. 10
0
 bool _getExiv2Value(Exiv2::ExifData& exifData, uint16_t tagID, std::string groupName, std::string & value)
 {
     Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(tagID, groupName));
     if (itr != exifData.end() && itr->count())
     {
         value = itr->toString();
         return true;
     }
     else
     {
         return false;
     };
 };
Esempio n. 11
0
static void EraseGpsTags(Exiv2::ExifData &ExifInfo)
{
	// Search through, find the keys that we want, and wipe them
	// Code below submitted by Marc Horowitz
	Exiv2::ExifData::iterator Iter;
	for (Exiv2::ExifData::iterator Iter = ExifInfo.begin();
		Iter != ExifInfo.end(); )
	{
		if (Iter->key().find("Exif.GPSInfo") == 0)
			Iter = ExifInfo.erase(Iter);
		else
			Iter++;
	}
}
QDateTime FileInfo::fetchEXIV2Date( Exiv2::ExifData& map, const char* key )
{
    try
    {
        if ( map.findKey( Exiv2::ExifKey( key ) ) != map.end() ) {
            const Exiv2::Exifdatum& datum = map[key ];
            return QDateTime::fromString( QString::fromLatin1(datum.toString().c_str()), Qt::ISODate );
        }
    }
    catch (...)
    {
    }

    return QDateTime();
}
Esempio n. 13
0
// uses the exiv2 class to readout the exifdata from images
int ExifScout::showExifData(std::string a){
    try {
        Ui_MainWindow::textEdit->clear();
        std::cerr << "ImageFactory::open ";
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(a);
        std::cerr << "DONE\n";
        assert(image.get() != 0);

        std::cerr << "image->readMetadata() ";
        image->readMetadata();
        std::cerr << "DONE\n";

        std::cerr << "Exiv2::ExifData& exifData = image->exifData() ";
        Exiv2::ExifData exifData = image->exifData();
        std::cerr << "DONE\n";

	cout << "Number of found exiv2 data: " << exifData.count() << endl;
        if (exifData.empty()) {
            Ui_MainWindow::textEdit->setPlainText("No Exif data found in file");
            return 1;
        }

        Exiv2::ExifData::const_iterator end = exifData.end();
        std::cerr << "Generate the exif-output ";
        QString output;
        for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
            std::string tagname = i->tagName();

            if(tagname != std::string("MakerNote") and
               tagname.substr(0,2) != "0x"){
                // Print out the exif-data in the QtextEdit Field
                std::ostringstream oss;
                oss << i->tagName() << ":\n    " << i->value() << "\n";
                output = output+(QString)oss.str().c_str();
            }
        }
        std::cerr << "Done\n";
        Ui_MainWindow::textEdit->setPlainText(output);

        return 0;
    }
    //catch (std::exception& e) {
    //catch (Exiv2::AnyError& e) {
    catch (Exiv2::Error& e) {
        std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
        return -1;
    }
}
Esempio n. 14
0
 bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, std::vector<float> & values)
 {
     values.clear();
     Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
     if (itr != exifData.end() && itr->count())
     {
         for(long i=0; i<itr->count(); i++)
         {
             values.push_back(itr->toFloat(i));
         };
         return true;
     }
     else
     {
         return false;
     }
 }
Esempio n. 15
0
QString ExifReaderWriter::readExifItem( Exiv2::ExifData &exifData, std::string keyStr)
{
    try{
        Exiv2::ExifKey key(keyStr);
        Exiv2::ExifData::iterator pos = exifData.findKey(key);

        if(pos != exifData.end())
        {
            return exifData[keyStr].toString().data();
        }
    }
    catch (Exiv2::Error& e) {
        ;//std::cerr << "Caught Exiv2 exception '" << e.what() << "'\n";
    }
    return "";

}
Esempio n. 16
0
    QString getExifCommentValue(Exiv2::ExifData &exifData, const char *propertyName) {
        QString result;

        Exiv2::ExifKey key(propertyName);
        Exiv2::ExifData::iterator it = exifData.findKey(key);
        if (it != exifData.end()) {
            const Exiv2::Exifdatum& exifDatum = *it;

            std::string comment;
            std::string charset;

            comment = exifDatum.toString();

            // libexiv2 will prepend "charset=\"SomeCharset\" " if charset is specified
            // Before conversion to QString, we must know the charset, so we stay with std::string for a while
            if (comment.length() > 8 && comment.substr(0, 8) == "charset=") {
                // the prepended charset specification is followed by a blank
                std::string::size_type pos = comment.find_first_of(' ');

                if (pos != std::string::npos) {
                    // extract string between the = and the blank
                    charset = comment.substr(8, pos-8);
                    // get the rest of the string after the charset specification
                    comment = comment.substr(pos+1);
                }
            }

            if (charset == "\"Unicode\"") {
                result = QString::fromUtf8(comment.data());
            }
            else if (charset == "\"Jis\"") {
                QTextCodec* const codec = QTextCodec::codecForName("JIS7");
                result = codec->toUnicode(comment.c_str());
            }
            else if (charset == "\"Ascii\"") {
                result = QString::fromLatin1(comment.c_str());
            } else {
                result = Helpers::detectEncodingAndDecode(comment);
            }
        }

        return result;
    }
Esempio n. 17
0
void ExifReaderWriter::writeData(Exiv2::ExifData &exifData, std::string keyStr, QString str)
{
    try{
        Exiv2::ExifKey key(keyStr);
        Exiv2::ExifData::iterator pos = exifData.findKey(key);
        if(pos != exifData.end()){
            exifData[keyStr].setValue(str.toStdString());
        }
        else    //vytvorim novou polozku
        {
            exifData[keyStr].setValue(str.toStdString());
        }
    }
    catch (Exiv2::Error& e) {
        ;//std::cerr << "Caught Exiv2 exception '" << e.what() << "'\n";
    }


}
Esempio n. 18
0
// tries to find a specified exif tag in a specified file
QString ExifScout::getExifData(QString fname, QString etag){
    std::string filename = fname.toAscii().data();
    std::string exiftag = etag.toAscii().data();

    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
    assert(image.get() != 0);
    image->readMetadata();
    Exiv2::ExifData exifData = image->exifData();
    if (!exifData.empty()) {
        Exiv2::ExifData::const_iterator end = exifData.end();
        QString output;
        for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
            if(i->tagName()==exiftag){
                std::ostringstream oss;
                oss << i->value();
                return (QString)oss.str().c_str();
            }
        }
    }
    return NULL;
}
Esempio n. 19
0
        const std::string getLensName(Exiv2::ExifData &exifData)
        {
            std::string lensName;
            // first we are reading LensModel in Exif section, this is only available
            // with EXIF >= 2.3
#if EXIV2_TEST_VERSION(0,22,0)
            //the string "Exif.Photo.LensModel" is only defined in exiv2 0.22.0 and above
            if(_getExiv2Value(exifData, "Exif.Photo.LensModel", lensName))
#else
            if(_getExiv2Value(exifData, 0xa434, "Photo", lensName))
#endif
            {
                if(lensName.length()>0)
                {
                    return lensName;
                };
            }
            else
            {
                //no lens in Exif found, now look in makernotes
                Exiv2::ExifData::const_iterator itr2 = Exiv2::lensName(exifData);
                if (itr2!=exifData.end() && itr2->count())
                {
                    //we are using prettyPrint function to get string of lens name
                    //it2->toString returns for many cameras only an ID number
                    lensName=itr2->print(&exifData);
                    //check returned lens name
                    if(lensName.length()>0)
                    {
                        //for Canon it can contain (65535) or (0) for unknown lenses
                        //for Pentax it can contain Unknown (0xHEX)
                        if(lensName.compare(0, 1, "(")!=0 && lensName.compare(0, 7, "Unknown")!=0)
                        {
                            return lensName;
                        }
                    };
                };
            };
            return std::string("");
        };
Esempio n. 20
0
void clearGPSFields(Exiv2::ExifData &exifData) {
    std::list<std::string> fields = {"Exif.GPSInfo.GPSLatitude",
                                     "Exif.GPSInfo.GPSLongitude",
                                     "Exif.GPSInfo.GPSMapDatum",
                                     "Exif.GPSInfo.GPSLatitudeRef",
                                     "Exif.GPSInfo.GPSLongitudeRef",
                                     "Exif.GPSInfo.GPSLatitude",
                                     "Exif.GPSInfo.GPSLongitude",
                                     
                                     "Exif.GPSInfo.GPSAltitude",
                                     "Exif.GPSInfo.GPSAltitudeRef",

                                     "Exif.GPSInfo.GPSTimeStamp",
                                     "Exif.GPSInfo.GPSDateStamp",};
    for (auto fname : fields) {
        auto key = Exiv2::ExifKey(fname.c_str());
        auto iter = exifData.findKey(key);
        while (iter != exifData.end()) {
            exifData.erase(iter);
            iter = exifData.findKey(key);
        }
    }
}
Esempio n. 21
0
bool ptImageHelper::WriteExif(const QString              &AFileName,
                              const std::vector<uint8_t> &AExifBuffer,
                              Exiv2::IptcData            &AIptcData,
                              Exiv2::XmpData             &AXmpData) {
  try {
#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */

    Exiv2::ExifData hInExifData;

    Exiv2::ExifParser::decode(hInExifData,
                              AExifBuffer.data() + CExifHeader.size(),
                              AExifBuffer.size() - CExifHeader.size());

    // Reset orientation
    Exiv2::ExifData::iterator pos = hInExifData.begin();
    if ((pos = hInExifData.findKey(Exiv2::ExifKey("Exif.Image.Orientation"))) != hInExifData.end()) {
      pos->setValue("1"); // Normal orientation
    }

    // Adapted from UFRaw, necessary for Tiff files
    QStringList ExifKeys;
    ExifKeys  << "Exif.Image.ImageWidth"
              << "Exif.Image.ImageLength"
              << "Exif.Image.BitsPerSample"
              << "Exif.Image.Compression"
              << "Exif.Image.PhotometricInterpretation"
              << "Exif.Image.FillOrder"
              << "Exif.Image.SamplesPerPixel"
              << "Exif.Image.StripOffsets"
              << "Exif.Image.RowsPerStrip"
              << "Exif.Image.StripByteCounts"
              << "Exif.Image.XResolution"
              << "Exif.Image.YResolution"
              << "Exif.Image.PlanarConfiguration"
              << "Exif.Image.ResolutionUnit";

    for (short i = 0; i < ExifKeys.count(); i++) {
      if ((pos = hInExifData.findKey(Exiv2::ExifKey(ExifKeys.at(i).toLocal8Bit().data()))) != hInExifData.end())
        hInExifData.erase(pos);
    }

    if (Settings->GetInt("EraseExifThumbnail")) {
      Exiv2::ExifThumb Thumb(hInExifData);
      Thumb.erase();
    }

    QStringList JpegExtensions;
    JpegExtensions << "jpg" << "JPG" << "Jpg" << "jpeg" << "Jpeg" << "JPEG";
    bool deleteDNGdata = false;
    for (short i = 0; i < JpegExtensions.count(); i++) {
      if (!AFileName.endsWith(JpegExtensions.at(i))) deleteDNGdata = true;
    }

    Exiv2::Image::AutoPtr Exiv2Image = Exiv2::ImageFactory::open(AFileName.toLocal8Bit().data());

    Exiv2Image->readMetadata();
    Exiv2::ExifData outExifData = Exiv2Image->exifData();

    for (auto hPos = hInExifData.begin(); hPos != hInExifData.end(); hPos++) {
      if (!deleteDNGdata || (*hPos).key() != "Exif.Image.DNGPrivateData") {
        outExifData.add(*hPos);
      }
    }

    // IPTC data

    QStringList Tags        = Settings->GetStringList("TagsList");
    QStringList DigikamTags = Settings->GetStringList("DigikamTagsList");

    Exiv2::StringValue StringValue;
    for (int i = 0; i < Tags.size(); i++) {
      StringValue.read(Tags.at(i).toStdString());
      AIptcData.add(Exiv2::IptcKey("Iptc.Application2.Keywords"), &StringValue);
    }


    // XMP data

    for (int i = 0; i < Tags.size(); i++) {
      AXmpData["Xmp.dc.subject"] = Tags.at(i).toStdString();
    }
    for (int i = 0; i < DigikamTags.size(); i++) {
      AXmpData["Xmp.digiKam.TagsList"] = DigikamTags.at(i).toStdString();
    }

    // Image rating
    outExifData["Exif.Image.Rating"] = Settings->GetInt("ImageRating");
    AXmpData["Xmp.xmp.Rating"]       = Settings->GetInt("ImageRating");

    // Program name
    outExifData["Exif.Image.ProcessingSoftware"]  = ProgramName;
    outExifData["Exif.Image.Software"]            = ProgramName;
    AIptcData["Iptc.Application2.Program"]        = ProgramName;
    AIptcData["Iptc.Application2.ProgramVersion"] = "idle";
    AXmpData["Xmp.xmp.CreatorTool"]               = ProgramName;
    AXmpData["Xmp.tiff.Software"]                 = ProgramName;

    // Title
    QString TitleWorking = Settings->GetString("ImageTitle");
    StringClean(TitleWorking);
    if (TitleWorking != "") {
      outExifData["Exif.Photo.UserComment"]  = TitleWorking.toStdString();
      AIptcData["Iptc.Application2.Caption"] = TitleWorking.toStdString();
      AXmpData["Xmp.dc.description"]         = TitleWorking.toStdString();
      AXmpData["Xmp.exif.UserComment"]       = TitleWorking.toStdString();
      AXmpData["Xmp.tiff.ImageDescription"]  = TitleWorking.toStdString();
    }

    // Copyright
    QString CopyrightWorking = Settings->GetString("Copyright");
    StringClean(CopyrightWorking);
    if (CopyrightWorking != "") {
      outExifData["Exif.Image.Copyright"]      = CopyrightWorking.toStdString();
      AIptcData["Iptc.Application2.Copyright"] = CopyrightWorking.toStdString();
      AXmpData["Xmp.tiff.Copyright"]           = CopyrightWorking.toStdString();
    }

    Exiv2Image->setExifData(outExifData);
    Exiv2Image->setIptcData(AIptcData);
    Exiv2Image->setXmpData(AXmpData);
    Exiv2Image->writeMetadata();
    return true;
#endif
  } catch (Exiv2::AnyError& Error) {
    if (Settings->GetInt("JobMode") == 0) {
      ptMessageBox::warning(0 ,"Exiv2 Error","No exif data written!\nCaught Exiv2 exception '" + QString(Error.what()) + "'\n");
    } else {
      std::cout << "Caught Exiv2 exception '" << Error << "'\n";
    }
  }
  return false;
}
Esempio n. 22
0
// [Private]
//
// Exif.GPSInfo.GPSLatitudeRef (Ascii)
//    Indicates whether the latitude is north or south latitude.
//    The ASCII value 'N' indicates north latitude, and 'S' is south latitude.
//
// Exif.GpsInfo.GPSLatitude (Rational)
//     Indicates the latitude. The latitude is expressed as three RATIONAL values
//     giving the degrees, minutes, and seconds, respectively.
//     When degrees, minutes and seconds are expressed, the format is dd/1,mm/1,ss/1.
//     When degrees and minutes are used and, for example, fractions of minutes
//     are given up to two decimal places, the format is dd/1,mmmm/100,0/1.
//
// Exif.GPSInfo.GPSLongitudeRef (Ascii)
//    Indicates whether the longitude is east or west longitude.
//    ASCII 'E' indicates east longitude, and 'W' is west longitude.
//
// Exif.GPSInfo.GPSLongitude (Rational)
//    Indicates the longitude. The longitude is expressed as three RATIONAL values
//    giving the degrees, minutes, and seconds, respectively.
//    When degrees, minutes and seconds are expressed, the format is ddd/1,mm/1,ss/1.
//    When degrees and minutes are used and, for example, fractions of minutes are
//    given up to two decimal places, the format is ddd/1,mmmm/100,0/1.
//
bool QExiv2::exifGpsCoordinate(const QString &coordTag, const QString &refTag, double *value) const
{
    const QByteArray ref = exifTagData(refTag.toLatin1().constData());
    if (ref.isEmpty()) {
        return false;
    }

    bool dir = false;
    switch (ref[0]) {
    case 'N':
    case 'E':
        break;
    case 'S':
    case 'W':
        dir = true;
        break;
    default:
        qWarning() << Q_FUNC_INFO << "Invalid Ref. data:" << ref[0];
        return false;
    }

    double degrees;

    const Exiv2::ExifKey  key(coordTag.toLatin1().constData());
    const Exiv2::ExifData data(d->exifMetadata);
    Exiv2::ExifData::const_iterator it = data.findKey(key);
    if (it != data.end() && (*it).count() == 3) {
        // Latitude decoding from Exif.
        double num;
        double den;

        // .toRational(long n) Return the n-th component of the value converted
        // to Rational. The return value is -1/1 if the value is not set and the
        // behaviour of the method is undefined if there is no n-th component.
        num = (double)((*it).toRational(0).first);
        den = (double)((*it).toRational(0).second);
        if (den == 0) {
            return false;
        }
        degrees = num/den;

        // Calc minutes
        num = (double)((*it).toRational(1).first);
        den = (double)((*it).toRational(1).second);
        if (den == 0) {
            return false;
        }

        double v;
        v = num/den;
        if (v != -1.0) {
            degrees += v / 60.0;
        }

        // Calc seconds
        num = (double)((*it).toRational(2).first);
        den = (double)((*it).toRational(2).second);
        if (den == 0) {
            // Accept 0/0 seconds.
            if (num == 0) {
                den = 1;
            } else {
                return false;
            }
        }

        v = num/den;
        if (v != -1.0) {
            degrees += v / 3600.0;
        }

        if (dir) {
            degrees *= -1.0;
        }
        *value = degrees;
        return true;
    }

    return false;
}
Esempio n. 23
0
// *****************************************************************************
// Main
int main()
try {
    // Container for all metadata
    Exiv2::ExifData exifData;

    // *************************************************************************
    // Add to the Exif data

    // Create a ASCII string value (note the use of create)
    Exiv2::Value* v = Exiv2::Value::create(Exiv2::asciiString);
    // Set the value to a string
    v->read("1999:12:31 23:59:59");
    // Add the value together with its key to the Exif data container
    Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
    exifData.add(key, v);

    std::cout << "Added key \"" << key << "\", value \"" << *v << "\"\n";
    // Delete the memory allocated by Value::create
    delete v;

    // Now create a more interesting value (without using the create method)
    Exiv2::URationalValue* rv = new Exiv2::URationalValue;
    // Set two rational components from a string
    rv->read("1/2 1/3");
    // Add more elements through the extended interface of rational value 
    rv->value_.push_back(std::make_pair(2,3));
    rv->value_.push_back(std::make_pair(3,4));
    // Add the key and value pair to the Exif data
    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
    exifData.add(key, rv);

    std::cout << "Added key \"" << key << "\", value \"" << *rv << "\"\n";
    // Delete memory allocated on the heap
    delete rv;

    // *************************************************************************
    // Modify Exif data

    // Find the timestamp metadatum by its key
    key = Exiv2::ExifKey("Exif.Photo.DateTimeOriginal");
    Exiv2::ExifData::iterator pos = exifData.findKey(key);
    if (pos == exifData.end()) throw Exiv2::Error("Key not found");
    // Modify the value
    std::string date = pos->toString();
    date.replace(0,4,"2000");
    pos->setValue(date); 
    std::cout << "Modified key \"" << key 
              << "\", new value \"" << pos->value() << "\"\n";

    // Find the other key
    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
    pos = exifData.findKey(key);
    if (pos == exifData.end()) throw Exiv2::Error("Key not found");
    // Get a pointer to a copy of the value
    v = pos->getValue();
    // Downcast the Value pointer to its actual type
    rv = dynamic_cast<Exiv2::URationalValue*>(v);
    if (rv == 0) throw Exiv2::Error("Downcast failed");
    // Modify the value directly through the interface of URationalValue
    rv->value_[2] = std::make_pair(88,77);
    // Copy the modified value back to the metadatum
    pos->setValue(rv);
    // Delete the memory allocated by getValue
    delete v;
    std::cout << "Modified key \"" << key 
              << "\", new value \"" << pos->value() << "\"\n";

    // *************************************************************************
    // Delete metadata from the Exif data container

    // Delete the metadatum at iterator position pos
    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
    pos = exifData.findKey(key);
    if (pos == exifData.end()) throw Exiv2::Error("Key not found");
    exifData.erase(pos);
    std::cout << "Deleted key \"" << key << "\"\n";

    // *************************************************************************
    // Finally, write the remaining Exif data to an image file
    int rc = exifData.write("img_2158.jpg");
    if (rc) {
        std::string error = Exiv2::ExifData::strError(rc, "img_2158.jpg");
        throw Exiv2::Error(error);
    }

    return 0;
}
catch (Exiv2::Error& e) {
    std::cout << "Caught Exiv2 exception '" << e << "'\n";
    return -1;
}
Esempio n. 24
0
bool KExiv2::Private::saveOperations(const QFileInfo& finfo, Exiv2::Image::AutoPtr image) const
{
    try
    {
        Exiv2::AccessMode mode;
        bool wroteComment = false, wroteEXIF = false, wroteIPTC = false, wroteXMP = false;

        // We need to load target file metadata to merge with new one. It's mandatory with TIFF format:
        // like all tiff file structure is based on Exif.
        image->readMetadata();

        // Image Comments ---------------------------------

        mode = image->checkMode(Exiv2::mdComment);

        if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
        {
            image->setComment(imageComments());
            wroteComment = true;
        }

        // Exif metadata ----------------------------------

        mode = image->checkMode(Exiv2::mdExif);

        if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
        {
            if (image->mimeType() == "image/tiff")
            {
                Exiv2::ExifData orgExif = image->exifData();
                Exiv2::ExifData newExif;
                QStringList     untouchedTags;

                // With tiff image we cannot overwrite whole Exif data as well, because
                // image data are stored in Exif container. We need to take a care about
                // to not lost image data.
                untouchedTags << "Exif.Image.ImageWidth";
                untouchedTags << "Exif.Image.ImageLength";
                untouchedTags << "Exif.Image.BitsPerSample";
                untouchedTags << "Exif.Image.Compression";
                untouchedTags << "Exif.Image.PhotometricInterpretation";
                untouchedTags << "Exif.Image.FillOrder";
                untouchedTags << "Exif.Image.SamplesPerPixel";
                untouchedTags << "Exif.Image.StripOffsets";
                untouchedTags << "Exif.Image.RowsPerStrip";
                untouchedTags << "Exif.Image.StripByteCounts";
                untouchedTags << "Exif.Image.XResolution";
                untouchedTags << "Exif.Image.YResolution";
                untouchedTags << "Exif.Image.PlanarConfiguration";
                untouchedTags << "Exif.Image.ResolutionUnit";

                for (Exiv2::ExifData::iterator it = orgExif.begin(); it != orgExif.end(); ++it)
                {
                    if (untouchedTags.contains(it->key().c_str()))
                    {
                        newExif[it->key().c_str()] = orgExif[it->key().c_str()];
                    }
                }

                Exiv2::ExifData readedExif = exifMetadata();

                for (Exiv2::ExifData::iterator it = readedExif.begin(); it != readedExif.end(); ++it)
                {
                    if (!untouchedTags.contains(it->key().c_str()))
                    {
                        newExif[it->key().c_str()] = readedExif[it->key().c_str()];
                    }
                }

                image->setExifData(newExif);
            }
            else
            {
                image->setExifData(exifMetadata());
            }

            wroteEXIF = true;
        }

        // Iptc metadata ----------------------------------

        mode = image->checkMode(Exiv2::mdIptc);

        if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
        {
            image->setIptcData(iptcMetadata());
            wroteIPTC = true;
        }

        // Xmp metadata -----------------------------------

        mode = image->checkMode(Exiv2::mdXmp);

        if ((mode == Exiv2::amWrite) || (mode == Exiv2::amReadWrite))
        {
#ifdef _XMP_SUPPORT_
            image->setXmpData(xmpMetadata());
            wroteXMP = true;
#endif
        }

        if (!wroteComment && !wroteEXIF && !wroteIPTC && !wroteXMP)
        {
            kDebug() << "Writing metadata is not supported for file" << finfo.fileName();
            return false;
        }
        else if (!wroteEXIF || !wroteIPTC || !wroteXMP)
        {
            kDebug() << "Support for writing metadata is limited for file" << finfo.fileName()
                     << "EXIF" << wroteEXIF << "IPTC" << wroteIPTC << "XMP" << wroteXMP;
        }

        if (!updateFileTimeStamp)
        {
            // Don't touch access and modification timestamp of file.
            struct stat st;
            ::stat(QFile::encodeName(filePath), &st);

            struct utimbuf ut;
            ut.modtime = st.st_mtime;
            ut.actime  = st.st_atime;

            image->writeMetadata();

            ::utime(QFile::encodeName(filePath), &ut);
        }
        else
        {
            image->writeMetadata();
        }

        return true;
    }
    catch( Exiv2::Error& e )
    {
        printExiv2ExceptionError("Cannot save metadata using Exiv2 ", e);
    }

    return false;
}
Esempio n. 25
0
bool ptImageHelper::ReadExif(const QString        &AFileName,
                             Exiv2::ExifData      &AExifData,
                             std::vector<uint8_t> &AExifBuffer)
{
  if (AFileName.trimmed().isEmpty()) return false;

  try {
    if (Exiv2::ImageFactory::getType(AFileName.toLocal8Bit().data()) == Exiv2::ImageType::none)
      return false;

    Exiv2::Image::AutoPtr hImage = Exiv2::ImageFactory::open(AFileName.toLocal8Bit().data());

    if (!hImage.get()) return false;

    hImage->readMetadata();

    AExifData = hImage->exifData();
    if (AExifData.empty()) {
      ptLogWarning(ptWarning_Argument, "No Exif data found in %s", AFileName.toLocal8Bit().data());
      return false;
    }

    Exiv2::ExifData::iterator Pos;
    size_t                    Size;

#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */
    Exiv2::Blob               Blob;
    Exiv2::ExifParser::encode(Blob, Exiv2::bigEndian, AExifData);
    Size = Blob.size();
#else
    Exiv2::DataBuf            Buf(AExifData.copy());
    Size = Buf.size_;
#endif

    /* If buffer too big for JPEG, try deleting some stuff. */
    if (Size + CExifHeader.size() > CMaxHeaderLength) {
      if ((Pos = AExifData.findKey(Exiv2::ExifKey("Exif.Photo.MakerNote"))) != AExifData.end() ) {
        AExifData.erase(Pos);
        ptLogWarning(ptWarning_Argument, "Exif buffer too big, erasing Exif.Photo.MakerNote");
#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */
        Exiv2::ExifParser::encode(Blob, Exiv2::bigEndian, AExifData);
        Size = Blob.size();
#else
        Buf  = AExifData.copy();
        Size = Buf.size_;
#endif
      }
    }

    // Erase embedded thumbnail if needed
    if (Settings->GetInt("EraseExifThumbnail") ||
        (Size + CExifHeader.size()) > CMaxHeaderLength ) {
#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */
      Exiv2::ExifThumb Thumb(AExifData);
      Thumb.erase();
#else
      AExifData.eraseThumbnail();
#endif

      if (!Settings->GetInt("EraseExifThumbnail"))
        ptLogWarning(ptWarning_Argument, "Exif buffer too big, erasing Thumbnail");

#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */
      Exiv2::ExifParser::encode(Blob, Exiv2::bigEndian, AExifData);
      Size = Blob.size();
#else
      Buf = AExifData.copy();
      Size = Buf.size_;
#endif
    }

    AExifBuffer.clear();
    AExifBuffer.insert(AExifBuffer.end(),
                       CExifHeader.begin(),
                       CExifHeader.end());
#if EXIV2_TEST_VERSION(0,17,91)   /* Exiv2 0.18-pre1 */
    AExifBuffer.insert(AExifBuffer.end(),
                       Blob.begin(),
                       Blob.end());
#else
    // old code will currently not compile
    memcpy(AExifBuffer+sizeof(ExifHeader), Buf.pData_, Buf.size_);
#endif
    return true;

  } catch(Exiv2::Error& Error) {
    // Exiv2 errors are in this context hopefully harmless
    // (unsupported tags etc.)

    ptLogWarning(ptWarning_Exiv2,"Exiv2 : %s\n",Error.what());
  }
  return false;
}
Esempio n. 26
0
// *****************************************************************************
// Main
int main(int argc, char* const argv[])
try {

    if (argc != 2) {
        std::cout << "Usage: " << argv[0] << " file\n";
        return 1;
    }

    Exiv2::ExifData exifData;
    int rc = exifData.read(argv[1]);
    if (rc) {
        std::string error = Exiv2::ExifData::strError(rc, argv[1]);
        throw Exiv2::Error(error);
    }

    /*
      There are two pitfalls that we need to consider when setting the Exif user
      comment (Exif.Photo.UserComment) of an image:

      First, the type of the Exif user comment tag is "undefined" (and not
      ASCII) according to the Exif standard. This means that in Exiv2, we have
      to deal with a DataValue (and not an AsciiValue). DataValue has the usual
      two read methods, however, the one taking a const std::string& buf expects
      the string to contain a series of integers (e.g., "0 1 2") and not a text
      string. Thus, we need to use the read function that reads the value from a
      character buffer of a given length.

      Second, the Exif comment field starts with an eight character area that
      identifies the used character set. For ASCII, these eight characters are
      "ASCII\0\0\0". The actual comment follows after this code.

      Note: There is a more simple Exif tag for the title of an image. It is a
      20 byte string (type ASCII) and does not store two-byte characters.
      (Image.OtherTags.ImageDescription)
     */

    // Initialise a data value with the character set and comment
    std::string charset("ASCII\0\0\0", 8);
    std::string comment("A comment added to the Exif metadata through Exiv2");
    Exiv2::DataValue value;
    value.read(reinterpret_cast<const Exiv2::byte*>((charset + comment).data()), 
                8 + static_cast<long>(comment.size()));

    // Set the Exif comment
    Exiv2::ExifKey key("Exif.Photo.UserComment");
    Exiv2::ExifData::iterator pos = exifData.findKey(key);
    if (pos != exifData.end()) {
        // Use the existing Exif UserComment metadatum if there is one
        pos->setValue(&value);
    }
    else {
        // Otherwise add a new UserComment metadatum
        exifData.add(key, &value);
        pos = exifData.findKey(key);
    }

    // Now we should have a valid iterator in any case. We use the metadatum
    // output operator to print the formatted value
    std::cout << "Writing user comment '" << *pos << "' back to the image\n";

    rc = exifData.write(argv[1]);
    if (rc) {
        std::string error = Exiv2::ExifData::strError(rc, argv[1]);
        throw Exiv2::Error(error);
    }

   return rc;
}
catch (Exiv2::Error& e) {
    std::cout << "Caught Exiv2 exception '" << e << "'\n";
    return -1;
}
Esempio n. 27
0
bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
    ioDevice->open(QIODevice::ReadOnly);
    if (!ioDevice->isOpen()) {
        return false;
    }
    QByteArray arr = ioDevice->readAll();
    Exiv2::ExifData exifData;
    Exiv2::ByteOrder byteOrder;
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
    exifData.load((const Exiv2::byte*)arr.data(), arr.size());
    byteOrder = exifData.byteOrder();
#else
    try {
        byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size());
    }
    catch (const std::exception& ex) {
        warnKrita << "Received exception trying to parse exiv data" << ex.what();
        return false;
    }
    catch (...) {
        dbgKrita << "Received unknown exception trying to parse exiv data";
        return false;
    }

#endif
    dbgFile << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
    dbgFile << "There are" << exifData.count() << " entries in the exif section";
    const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
    Q_ASSERT(tiffSchema);
    const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
    Q_ASSERT(exifSchema);
    const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
    Q_ASSERT(dcSchema);
    const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
    Q_ASSERT(xmpSchema);
    for (Exiv2::ExifMetadata::const_iterator it = exifData.begin();
            it != exifData.end(); ++it) {
        if (it->key() == "Exif.Photo.StripOffsets"
                || it->key() == "RowsPerStrip"
                || it->key() == "StripByteCounts"
                || it->key() == "JPEGInterchangeFormat"
                || it->key() == "JPEGInterchangeFormatLength"
                || it->tagName() == "0x0000" ) {
            dbgFile << it->key().c_str() << " is ignored";
        } else if (it->key() == "Exif.Photo.MakerNote") {
            const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
            store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate
            store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue()))));
        } else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description"
            store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool"
            store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->key() == "Exif.Image.Artist") { // load as dc:creator
            QList<KisMetaData::Value> creators;
            creators.push_back(exivValueToKMDValue(it->getValue(), false));
            store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray)));
        } else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights
            store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false)));
        } else if (it->groupName() == "Image") {
            // Tiff tags
            QString fixedTN = it->tagName().c_str();
            if (it->key() == "Exif.Image.ExifTag") {
                dbgFile << "Ignoring " << it->key().c_str();
            } else if (KisMetaData::Entry::isValidName(fixedTN)) {
                store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ;
            } else {
                dbgFile << "Invalid tag name: " << fixedTN;
            }
        } else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) {
            // Exif tags (and GPS tags)
            KisMetaData::Value v;
            if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") {
                v = exifVersionToKMDValue(it->getValue());
            } else if (it->key() == "Exif.Photo.FileSource") {
                v = KisMetaData::Value(3);
            } else if (it->key() == "Exif.Photo.SceneType") {
                v = KisMetaData::Value(1);
            } else if (it->key() == "Exif.Photo.ComponentsConfiguration") {
                v = exifArrayToKMDIntOrderedArray(it->getValue());
            } else if (it->key() == "Exif.Photo.OECF") {
                v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") {
                v = KisMetaData::Value(exivValueToDateTime(it->getValue()));
            } else if (it->key() == "Exif.Photo.DeviceSettingDescription") {
                v = deviceSettingDescriptionExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.CFAPattern") {
                v = cfaPatternExifToKMD(it->getValue(), byteOrder);
            } else if (it->key() == "Exif.Photo.Flash") {
                v = flashExifToKMD(it->getValue());
            } else if (it->key() == "Exif.Photo.UserComment") {
                KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false);
                Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
                QVariant commentVar = vUC.asVariant();
                QString comment;
                if (commentVar.type() == QVariant::String) {
                    comment = commentVar.toString();
                } else if (commentVar.type() == QVariant::ByteArray) {
                    const QByteArray commentString = commentVar.toByteArray();
                    comment = QString::fromLatin1(commentString.constData(), commentString.size());
                } else {
                    warnKrita << "KisExifIO: Unhandled UserComment value type.";
                }
                KisMetaData::Value vcomment(comment);
                vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
                QList<KisMetaData::Value> alt;
                alt.append(vcomment);
                v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
            } else {
                bool forceSeq = false;
                KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
                if (it->key() == "Exif.Photo.ISOSpeedRatings") {
                    forceSeq = true;
                    arrayType = KisMetaData::Value::OrderedArray;
                }
                v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType);
            }
            if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag
                dbgFile << "Ignoring " << it->key().c_str();
            } else {
                store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v));
            }
        } else if (it->groupName() == "Thumbnail") {
            dbgFile << "Ignoring thumbnail tag :" << it->key().c_str();
        } else {
            dbgFile << "Unknown exif tag, cannot load:" << it->key().c_str();
        }
    }
    ioDevice->close();
    return true;
}
Esempio n. 28
0
long GalleryUtil::GetNaturalRotation(const QString &filePathString)
{
    long rotateAngle = 0;
#ifdef EXIF_SUPPORT
    QByteArray filePathBA = filePathString.toLocal8Bit();
    const char *filePath = filePathBA.constData();

    try
    {
        char *exifvalue = new char[1024];
        ExifData *data = exif_data_new_from_file (filePath);
        if (data)
        {
            for (int i = 0; i < EXIF_IFD_COUNT; i++)
            {
                ExifEntry *entry = exif_content_get_entry (data->ifd[i],
                                                        EXIF_TAG_ORIENTATION);
                ExifByteOrder byteorder = exif_data_get_byte_order (data);

                if (entry)
                {
                    ExifShort v_short = exif_get_short (entry->data, byteorder);
                    VERBOSE(VB_GENERAL|VB_EXTRA, QString("Exif entry=%1").arg(v_short));
                    /* See http://sylvana.net/jpegcrop/exif_orientation.html*/
                    if (v_short == 8)
                    {
                        rotateAngle = -90;
                    }
                    else if (v_short == 6)
                    {
                        rotateAngle = 90;
                    }
                    break;
                }
            }
            exif_data_free(data);
        }
        else
        {
            VERBOSE(VB_FILE, LOC_ERR +
                    QString("Could not load exif data from '%1'")
                    .arg(filePath));
        }
        
        delete [] exifvalue;
        
#if 0
        Exiv2::ExifData exifData;
        int rc = exifData.read(filePath);
        if (!rc)
        {
            Exiv2::ExifKey key = Exiv2::ExifKey("Exif.Image.Orientation");
            Exiv2::ExifData::iterator pos = exifData.findKey(key);
            if (pos != exifData.end())
            {
                long orientation = pos->toLong();
                switch (orientation)
                {
                    case 6:
                        rotateAngle = 90;
                        break;
                    case 8:
                        rotateAngle = -90;
                        break;
                    default:
                        rotateAngle = 0;
                        break;
                }
            }
        }
#endif
    }
    catch (...)
    {
        VERBOSE(VB_IMPORTANT, LOC_ERR +
                QString("Failed to extract EXIF headers from '%1'")
                .arg(filePathString));
    }

#else
    // Shut the compiler up about the unused argument
    (void)filePathString;
#endif // EXIF_SUPPORT
    return rotateAngle;
}
Esempio n. 29
-1
bool tag_location( const std::string &filename, const location &loc)
{
    bool result = false;
    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open( filename);
    if (!image.get()) throw std::runtime_error( "could not open image file " + filename + " for metadata tags\n");
    image->readMetadata();
    Exiv2::ExifData data = image->exifData();

    if (data.findKey( Exiv2::ExifKey("Exif.GPSInfo.GPSLatitude")) == data.end())
    {
        add_exif_coordinate( data, "Exif.GPSInfo.GPSLatitude", std::abs( loc.latitude));
        add_exif_coordinate( data, "Exif.GPSInfo.GPSLongitude", std::abs( loc.longitude));
        data["Exif.GPSInfo.GPSLatitudeRef"] = loc.latitude < 0 ? "S":"N";
        data["Exif.GPSInfo.GPSLongitudeRef"] = loc.longitude < 0 ? "W":"E";
        Exiv2::byte version[] = { 2, 0, 0, 0};
        data["Exif.GPSInfo.GPSVersionID"].getValue()->read( version, 4, Exiv2::invalidByteOrder);
        image->setExifData( data);
        image->writeMetadata();
        result = true;
    }

    return result;
}