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.