19#include <exiv2/exiv2.hpp> 
   22#include <QRegularExpression> 
   28  const Exiv2::Rational rational = value.toRational( n );
 
   29  return static_cast< double >( rational.first ) / rational.second;
 
   36  for ( 
int i = 0; i < 3; i++ )
 
   44QVariant 
decodeXmpData( 
const QString &key, Exiv2::XmpData::const_iterator &it )
 
   47  if ( key == QLatin1String( 
"Xmp.xmp.MetadataDate" ) ||
 
   48       key == QLatin1String( 
"Xmp.xmp.CreateDate" ) ||
 
   49       key == QLatin1String( 
"Xmp.xmp.ModifyDate" ) )
 
   51    val = QVariant::fromValue( QDateTime::fromString( QString::fromStdString( it->toString() ), Qt::ISODate ) );
 
   55    switch ( it->typeId() )
 
   57      case Exiv2::asciiString:
 
   60      case Exiv2::directory:
 
   62        val = QString::fromStdString( it->toString() );
 
   65      case Exiv2::unsignedLong:
 
   66      case Exiv2::signedLong:
 
   67      case Exiv2::unsignedLongLong:
 
   68      case Exiv2::signedLongLong:
 
   69        val = QVariant::fromValue( it->toLong() );
 
   72      case Exiv2::tiffDouble:
 
   73      case Exiv2::tiffFloat:
 
   74        val = QVariant::fromValue( it->toFloat() );
 
   77      case Exiv2::unsignedShort:
 
   78      case Exiv2::signedShort:
 
   79      case Exiv2::unsignedByte:
 
   80      case Exiv2::signedByte:
 
   83        val = QVariant::fromValue( 
static_cast< int >( it->toLong() ) );
 
   88        const Exiv2::DateValue::Date date = 
static_cast< const Exiv2::DateValue *
>( &it->value() )->getDate();
 
   89        val = QVariant::fromValue( QDate::fromString( QStringLiteral( 
"%1-%2-%3" ).arg( date.year )
 
   90                                   .arg( QString::number( date.month ).rightJustified( 2, 
'0' ) )
 
   91                                   .arg( QString::number( date.day ).rightJustified( 2, 
'0' ) ), QLatin1String( 
"yyyy-MM-dd" ) ) );
 
   97        const Exiv2::TimeValue::Time time = 
static_cast< const Exiv2::TimeValue *
>( &it->value() )->getTime();
 
   98        val = QVariant::fromValue( QTime::fromString( QStringLiteral( 
"%1:%2:%3" ).arg( QString::number( time.hour ).rightJustified( 2, 
'0' ) )
 
   99                                   .arg( QString::number( time.minute ).rightJustified( 2, 
'0' ) )
 
  100                                   .arg( QString::number( time.second ).rightJustified( 2, 
'0' ) ), QLatin1String( 
"hh:mm:ss" ) ) );
 
  104      case Exiv2::unsignedRational:
 
  105      case Exiv2::signedRational:
 
  107        if ( it->count() == 1 )
 
  113          val = QString::fromStdString( it->toString() );
 
  118      case Exiv2::undefined:
 
  123      case Exiv2::invalidTypeId:
 
  124      case Exiv2::lastTypeId:
 
  125        val = QString::fromStdString( it->toString() );
 
  133QVariant 
decodeExifData( 
const QString &key, Exiv2::ExifData::const_iterator &it )
 
  137  if ( key == QLatin1String( 
"Exif.GPSInfo.GPSLatitude" ) ||
 
  138       key == QLatin1String( 
"Exif.GPSInfo.GPSLongitude" ) ||
 
  139       key == QLatin1String( 
"Exif.GPSInfo.GPSDestLatitude" ) ||
 
  140       key == QLatin1String( 
"Exif.GPSInfo.GPSDestLongitude" ) )
 
  144  else if ( key == QLatin1String( 
"Exif.GPSInfo.GPSTimeStamp" ) )
 
  146    const QStringList parts = QString::fromStdString( it->toString() ).split( QRegularExpression( QStringLiteral( 
"\\s+" ) ) );
 
  147    if ( parts.size() == 3 )
 
  152      val = QVariant::fromValue( QTime::fromString( QStringLiteral( 
"%1:%2:%3" )
 
  153                                 .arg( QString::number( hour ).rightJustified( 2, 
'0' ) )
 
  154                                 .arg( QString::number( minute ).rightJustified( 2, 
'0' ) )
 
  155                                 .arg( QString::number( second ).rightJustified( 2, 
'0' ) ), QLatin1String( 
"hh:mm:ss" ) ) );
 
  158  else if ( key == QLatin1String( 
"Exif.GPSInfo.GPSDateStamp" ) )
 
  160    val = QVariant::fromValue( QDate::fromString( QString::fromStdString( it->toString() ), QLatin1String( 
"yyyy:MM:dd" ) ) );
 
  162  else if ( key == QLatin1String( 
"Exif.Image.DateTime" ) ||
 
  163            key == QLatin1String( 
"Exif.Image.DateTime" ) ||
 
  164            key == QLatin1String( 
"Exif.Photo.DateTimeDigitized" ) ||
 
  165            key == QLatin1String( 
"Exif.Photo.DateTimeOriginal" ) )
 
  167    val = QVariant::fromValue( QDateTime::fromString( QString::fromStdString( it->toString() ), QLatin1String( 
"yyyy:MM:dd hh:mm:ss" ) ) );
 
  171    switch ( it->typeId() )
 
  173      case Exiv2::asciiString:
 
  176      case Exiv2::directory:
 
  178        val = QString::fromStdString( it->toString() );
 
  181      case Exiv2::unsignedLong:
 
  182      case Exiv2::signedLong:
 
  183      case Exiv2::unsignedLongLong:
 
  184      case Exiv2::signedLongLong:
 
  185        val = QVariant::fromValue( it->toLong() );
 
  188      case Exiv2::tiffDouble:
 
  189      case Exiv2::tiffFloat:
 
  190        val = QVariant::fromValue( it->toFloat() );
 
  193      case Exiv2::unsignedShort:
 
  194      case Exiv2::signedShort:
 
  195      case Exiv2::unsignedByte:
 
  196      case Exiv2::signedByte:
 
  198      case Exiv2::tiffIfd8:
 
  199        val = QVariant::fromValue( 
static_cast< int >( it->toLong() ) );
 
  204        const Exiv2::DateValue::Date date = 
static_cast< const Exiv2::DateValue *
>( &it->value() )->getDate();
 
  205        val = QVariant::fromValue( QDate::fromString( QStringLiteral( 
"%1-%2-%3" ).arg( date.year )
 
  206                                   .arg( QString::number( date.month ).rightJustified( 2, 
'0' ) )
 
  207                                   .arg( QString::number( date.day ).rightJustified( 2, 
'0' ) ), QLatin1String( 
"yyyy-MM-dd" ) ) );
 
  213        const Exiv2::TimeValue::Time time = 
static_cast< const Exiv2::TimeValue *
>( &it->value() )->getTime();
 
  214        val = QVariant::fromValue( QTime::fromString( QStringLiteral( 
"%1:%2:%3" ).arg( QString::number( time.hour ).rightJustified( 2, 
'0' ) )
 
  215                                   .arg( QString::number( time.minute ).rightJustified( 2, 
'0' ) )
 
  216                                   .arg( QString::number( time.second ).rightJustified( 2, 
'0' ) ), QLatin1String( 
"hh:mm:ss" ) ) );
 
  220      case Exiv2::unsignedRational:
 
  221      case Exiv2::signedRational:
 
  223        if ( it->count() == 1 )
 
  229          val = QString::fromStdString( it->toString() );
 
  234      case Exiv2::undefined:
 
  239      case Exiv2::invalidTypeId:
 
  240      case Exiv2::lastTypeId:
 
  241        val = QString::fromStdString( it->toString() );
 
  250  const double d = std::abs( val );
 
  251  const int degrees = 
static_cast< int >( std::floor( d ) );
 
  252  const double m = 60 * ( d - degrees );
 
  253  const int minutes = 
static_cast< int >( std::floor( m ) );
 
  254  const double s = 60 * ( m - minutes );
 
  255  const int seconds = 
static_cast< int >( std::floor( s * 1000 ) );
 
  256  return QStringLiteral( 
"%1/1 %2/1 %3/1000" ).arg( degrees ).arg( minutes ).arg( seconds );
 
  261  if ( !QFileInfo::exists( imagePath ) )
 
  266    std::unique_ptr< Exiv2::Image > image( Exiv2::ImageFactory::open( imagePath.toStdString() ) );
 
  267    if ( !image || key.isEmpty() )
 
  270    image->readMetadata();
 
  272    if ( key.startsWith( QLatin1String( 
"Xmp." ) ) )
 
  274      Exiv2::XmpData &xmpData = image->xmpData();
 
  275      if ( xmpData.empty() )
 
  279      Exiv2::XmpData::const_iterator i = xmpData.findKey( Exiv2::XmpKey( key.toUtf8().constData() ) );
 
  280      return i != xmpData.end() ? 
decodeXmpData( key, i ) : QVariant();
 
  284      Exiv2::ExifData &exifData = image->exifData();
 
  285      if ( exifData.empty() )
 
  289      Exiv2::ExifData::const_iterator i = exifData.findKey( Exiv2::ExifKey( key.toUtf8().constData() ) );
 
  290      return i != exifData.end() ? 
decodeExifData( key, i ) : QVariant();
 
  301  if ( !QFileInfo::exists( imagePath ) )
 
  302    return QVariantMap();
 
  307    std::unique_ptr< Exiv2::Image > image( Exiv2::ImageFactory::open( imagePath.toStdString() ) );
 
  309      return QVariantMap();
 
  310    image->readMetadata();
 
  312    Exiv2::ExifData &exifData = image->exifData();
 
  313    if ( !exifData.empty() )
 
  315      const Exiv2::ExifData::const_iterator end = exifData.end();
 
  316      for ( Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i )
 
  318        const QString key = QString::fromStdString( i->key() );
 
  323    Exiv2::XmpData &xmpData = image->xmpData();
 
  324    if ( !xmpData.empty() )
 
  326      const Exiv2::XmpData::const_iterator end = xmpData.end();
 
  327      for ( Exiv2::XmpData::const_iterator i = xmpData.begin(); i != end; ++i )
 
  329        const QString key = QString::fromStdString( i->key() );
 
  338    return QVariantMap();
 
  352  if ( !QFileInfo::exists( imagePath ) )
 
  356    std::unique_ptr< Exiv2::Image > image( Exiv2::ImageFactory::open( imagePath.toStdString() ) );
 
  360    image->readMetadata();
 
  361    Exiv2::ExifData &exifData = image->exifData();
 
  363    if ( exifData.empty() )
 
  366    const Exiv2::ExifData::iterator itLatRef = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSLatitudeRef" ) );
 
  367    const Exiv2::ExifData::iterator itLatVal = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSLatitude" ) );
 
  368    const Exiv2::ExifData::iterator itLonRef = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSLongitudeRef" ) );
 
  369    const Exiv2::ExifData::iterator itLonVal = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSLongitude" ) );
 
  371    if ( itLatRef == exifData.end() || itLatVal == exifData.end() ||
 
  372         itLonRef == exifData.end() || itLonVal == exifData.end() )
 
  378    const QString latRef = QString::fromStdString( itLatRef->value().toString() );
 
  379    const QString lonRef = QString::fromStdString( itLonRef->value().toString() );
 
  380    if ( latRef.compare( QLatin1String( 
"S" ), Qt::CaseInsensitive ) == 0 )
 
  384    if ( lonRef.compare( QLatin1String( 
"W" ), Qt::CaseInsensitive ) == 0 )
 
  391    const Exiv2::ExifData::iterator itElevVal = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSAltitude" ) );
 
  392    const Exiv2::ExifData::iterator itElevRefVal = exifData.findKey( Exiv2::ExifKey( 
"Exif.GPSInfo.GPSAltitudeRef" ) );
 
  393    if ( itElevVal != exifData.end() )
 
  396      if ( itElevRefVal != exifData.end() )
 
  398        const QString elevRef = QString::fromStdString( itElevRefVal->value().toString() );
 
  399        if ( elevRef.compare( QLatin1String( 
"1" ), Qt::CaseInsensitive ) == 0 )
 
  421    std::unique_ptr< Exiv2::Image > image( Exiv2::ImageFactory::open( imagePath.toStdString() ) );
 
  425    image->readMetadata();
 
  426    Exiv2::ExifData &exifData = image->exifData();
 
  428    exifData[
"Exif.GPSInfo.GPSVersionID"] = 
"2 0 0 0";
 
  429    exifData[
"Exif.GPSInfo.GPSMapDatum"] = 
"WGS-84";
 
  434      const QString elevationString = QStringLiteral( 
"%1/1000" ).arg( 
static_cast< int>( std::floor( std::abs( details.
elevation ) * 1000 ) ) );
 
  435      exifData[
"Exif.GPSInfo.GPSAltitude"] = elevationString.toStdString();
 
  436      exifData[
"Exif.GPSInfo.GPSAltitudeRef"] = details.
elevation < 0.0 ? 
"1" : 
"0";
 
  438    exifData[
"Exif.GPSInfo.GPSLatitudeRef"] = location.
y() > 0 ? 
"N" : 
"S";
 
  439    exifData[
"Exif.GPSInfo.GPSLongitudeRef"] = location.
x() > 0 ? 
"E" : 
"W";
 
  440    exifData[
"Exif.Image.GPSTag"] = 4908;
 
  441    image->writeMetadata();
 
  454    std::unique_ptr< Exiv2::Image > image( Exiv2::ImageFactory::open( imagePath.toStdString() ) );
 
  458    QVariant actualValue;
 
  459    if ( tag == QLatin1String( 
"Exif.GPSInfo.GPSLatitude" ) ||
 
  460         tag == QLatin1String( 
"Exif.GPSInfo.GPSLongitude" ) ||
 
  461         tag == QLatin1String( 
"Exif.GPSInfo.GPSDestLatitude" ) ||
 
  462         tag == QLatin1String( 
"Exif.GPSInfo.GPSDestLongitude" ) )
 
  466    else if ( tag == QLatin1String( 
"Exif.GPSInfo.GPSAltitude" ) )
 
  468      actualValue = QStringLiteral( 
"%1/1000" ).arg( 
static_cast< int>( std::floor( std::abs( value.toDouble() ) * 1000 ) ) );
 
  470    else if ( value.type() == QVariant::DateTime )
 
  472      const QDateTime dateTime = value.toDateTime();
 
  473      if ( tag == QLatin1String( 
"Exif.Image.DateTime" ) ||
 
  474           tag == QLatin1String( 
"Exif.Image.DateTime" ) ||
 
  475           tag == QLatin1String( 
"Exif.Photo.DateTimeDigitized" ) ||
 
  476           tag == QLatin1String( 
"Exif.Photo.DateTimeOriginal" ) )
 
  478        actualValue = dateTime.toString( QStringLiteral( 
"yyyy:MM:dd hh:mm:ss" ) );
 
  482        actualValue = dateTime.toString( Qt::ISODate );
 
  485    else if ( value.type() == QVariant::Date )
 
  487      const QDate date = value.toDate();
 
  488      if ( tag == QLatin1String( 
"Exif.GPSInfo.GPSDateStamp" ) )
 
  490        actualValue = date.toString( QStringLiteral( 
"yyyy:MM:dd" ) );
 
  494        actualValue = date.toString( QStringLiteral( 
"yyyy-MM-dd" ) );
 
  497    else if ( value.type() == QVariant::Time )
 
  499      const QTime time = value.toTime();
 
  500      if ( tag == QLatin1String( 
"Exif.GPSInfo.GPSTimeStamp" ) )
 
  502        actualValue = QStringLiteral( 
"%1/1 %2/1 %3/1" ).arg( time.hour() ).arg( time.minute() ).arg( time.second() );
 
  506        actualValue = time.toString( QStringLiteral( 
"HH:mm:ss" ) );
 
  514    const bool isXmp = tag.startsWith( QLatin1String( 
"Xmp." ) );
 
  515    image->readMetadata();
 
  516    if ( actualValue.type() == QVariant::Int ||
 
  517         actualValue.type() == QVariant::LongLong )
 
  521        Exiv2::XmpData &xmpData = image->xmpData();
 
  522        xmpData[tag.toStdString()] = 
static_cast<uint32_t
>( actualValue.toLongLong() );
 
  526        Exiv2::ExifData &exifData = image->exifData();
 
  527        exifData[tag.toStdString()] = 
static_cast<uint32_t
>( actualValue.toLongLong() );
 
  530    if ( actualValue.type() == QVariant::UInt ||
 
  531         actualValue.type() ==  QVariant::ULongLong )
 
  535        Exiv2::XmpData &xmpData = image->xmpData();
 
  536        xmpData[tag.toStdString()] = 
static_cast<int32_t
>( actualValue.toULongLong() );
 
  540        Exiv2::ExifData &exifData = image->exifData();
 
  541        exifData[tag.toStdString()] = 
static_cast<int32_t
>( actualValue.toULongLong() );
 
  544    else if ( actualValue.type() == QVariant::Double )
 
  548        Exiv2::XmpData &xmpData = image->xmpData();
 
  549        xmpData[tag.toStdString()] = Exiv2::floatToRationalCast( actualValue.toFloat() );
 
  553        Exiv2::ExifData &exifData = image->exifData();
 
  554        exifData[tag.toStdString()] = Exiv2::floatToRationalCast( actualValue.toFloat() );
 
  561        Exiv2::XmpData &xmpData = image->xmpData();
 
  562        xmpData[tag.toStdString()] = actualValue.toString().toStdString();
 
  566        Exiv2::ExifData &exifData = image->exifData();
 
  567        exifData[tag.toStdString()] = actualValue.toString().toStdString();
 
  570    image->writeMetadata();
 
A class to represent a 2D point.
 
Point geometry type, with support for z-dimension and m-values.