Commit f6bd799f authored by Tobias Ellinghaus's avatar Tobias Ellinghaus

Fix scanf in Lightroom XMP import

parent 4f4c80d8
......@@ -99,85 +99,6 @@ const char *dt_xmp_keys[]
static const guint dt_xmp_keys_n = G_N_ELEMENTS(dt_xmp_keys); // the number of XmpBag XmpSeq keys that dt uses
/* a few helper functions inspired by
https://projects.kde.org/projects/kde/kdegraphics/libs/libkexiv2/repository/revisions/master/entry/libkexiv2/kexiv2gps.cpp
*/
static double _gps_string_to_number(const gchar *input)
{
double res = 0;
gchar *s = g_strdup(input);
gchar dir = toupper(s[strlen(s) - 1]);
gchar **list = g_strsplit(s, ",", 0);
if(list)
{
if(list[2] == NULL) // format DDD,MM.mm{N|S}
res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtod(list[1], NULL) / 60.0);
else if(list[3] == NULL) // format DDD,MM,SS{N|S}
res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtoll(list[1], NULL, 10) / 60.0)
+ (g_ascii_strtoll(list[2], NULL, 10) / 3600.0);
if(dir == 'S' || dir == 'W') res *= -1.0;
}
g_strfreev(list);
g_free(s);
return res;
}
static gboolean _gps_rationale_to_number(const double r0_1, const double r0_2, const double r1_1,
const double r1_2, const double r2_1, const double r2_2, char sign,
double *result)
{
if(!result) return FALSE;
double res = 0.0;
// Latitude decoding from Exif.
double num, den, min, sec;
num = r0_1;
den = r0_2;
if(den == 0) return FALSE;
res = num / den;
num = r1_1;
den = r1_2;
if(den == 0) return FALSE;
min = num / den;
if(min != -1.0) res += min / 60.0;
num = r2_1;
den = r2_2;
if(den == 0)
{
// be relaxed and accept 0/0 seconds. See #246077.
if(num == 0)
den = 1;
else
return FALSE;
}
sec = num / den;
if(sec != -1.0) res += sec / 3600.0;
if(sign == 'S' || sign == 'W') res *= -1.0;
*result = res;
return TRUE;
}
static gboolean _gps_elevation_to_number(const double r_1, const double r_2, char sign, double *result)
{
if(!result) return FALSE;
double res = 0.0;
// Altitude decoding from Exif.
const double num = r_1;
const double den = r_2;
if(den == 0) return FALSE;
res = num / den;
if(sign != '0') res *= -1.0;
*result = res;
return TRUE;
}
// inspired by ufraw_exiv2.cc:
static void dt_strlcpy_to_utf8(char *dest, size_t dest_max, Exiv2::ExifData::const_iterator &pos,
......@@ -381,12 +302,12 @@ static bool dt_exif_read_xmp_data(dt_image_t *img, Exiv2::XmpData &xmpData, int
/* read gps location */
if(FIND_XMP_TAG("Xmp.exif.GPSLatitude"))
{
img->latitude = _gps_string_to_number(pos->toString().c_str());
img->latitude = dt_util_gps_string_to_number(pos->toString().c_str());
}
if(FIND_XMP_TAG("Xmp.exif.GPSLongitude"))
{
img->longitude = _gps_string_to_number(pos->toString().c_str());
img->longitude = dt_util_gps_string_to_number(pos->toString().c_str());
}
if(FIND_XMP_TAG("Xmp.exif.GPSAltitude"))
......@@ -397,7 +318,7 @@ static bool dt_exif_read_xmp_data(dt_image_t *img, Exiv2::XmpData &xmpData, int
std::string sign_str = ref->toString();
const char *sign = sign_str.c_str();
double elevation = 0.0;
if(_gps_elevation_to_number(pos->toRational(0).first, pos->toRational(0).second, sign[0], &elevation))
if(dt_util_gps_elevation_to_number(pos->toRational(0).first, pos->toRational(0).second, sign[0], &elevation))
img->elevation = elevation;
}
}
......@@ -713,9 +634,9 @@ static bool dt_exif_read_exif_data(dt_image_t *img, Exiv2::ExifData &exifData)
std::string sign_str = ref->toString();
const char *sign = sign_str.c_str();
double latitude = 0.0;
if(_gps_rationale_to_number(pos->toRational(0).first, pos->toRational(0).second,
pos->toRational(1).first, pos->toRational(1).second,
pos->toRational(2).first, pos->toRational(2).second, sign[0], &latitude))
if(dt_util_gps_rationale_to_number(pos->toRational(0).first, pos->toRational(0).second,
pos->toRational(1).first, pos->toRational(1).second,
pos->toRational(2).first, pos->toRational(2).second, sign[0], &latitude))
img->latitude = latitude;
}
}
......@@ -728,9 +649,9 @@ static bool dt_exif_read_exif_data(dt_image_t *img, Exiv2::ExifData &exifData)
std::string sign_str = ref->toString();
const char *sign = sign_str.c_str();
double longitude = 0.0;
if(_gps_rationale_to_number(pos->toRational(0).first, pos->toRational(0).second,
pos->toRational(1).first, pos->toRational(1).second,
pos->toRational(2).first, pos->toRational(2).second, sign[0], &longitude))
if(dt_util_gps_rationale_to_number(pos->toRational(0).first, pos->toRational(0).second,
pos->toRational(1).first, pos->toRational(1).second,
pos->toRational(2).first, pos->toRational(2).second, sign[0], &longitude))
img->longitude = longitude;
}
}
......@@ -743,7 +664,7 @@ static bool dt_exif_read_exif_data(dt_image_t *img, Exiv2::ExifData &exifData)
std::string sign_str = ref->toString();
const char *sign = sign_str.c_str();
double elevation = 0.0;
if(_gps_elevation_to_number(pos->toRational(0).first, pos->toRational(0).second, sign[0], &elevation))
if(dt_util_gps_elevation_to_number(pos->toRational(0).first, pos->toRational(0).second, sign[0], &elevation))
img->elevation = elevation;
}
}
......
......@@ -41,6 +41,7 @@
#include <glib/gi18n.h>
#include <sys/stat.h>
#include <ctype.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
......@@ -507,6 +508,83 @@ gchar *dt_util_elevation_str(float elevation)
return g_strdup_printf("%.2f %s %s", elevation, _("m"), _(c));
}
/* a few helper functions inspired by
* https://projects.kde.org/projects/kde/kdegraphics/libs/libkexiv2/repository/revisions/master/entry/libkexiv2/kexiv2gps.cpp
*/
double dt_util_gps_string_to_number(const gchar *input)
{
double res = NAN;
gchar dir = toupper(input[strlen(input) - 1]);
gchar **list = g_strsplit(input, ",", 0);
if(list)
{
if(list[2] == NULL) // format DDD,MM.mm{N|S}
res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtod(list[1], NULL) / 60.0);
else if(list[3] == NULL) // format DDD,MM,SS{N|S}
res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtoll(list[1], NULL, 10) / 60.0)
+ (g_ascii_strtoll(list[2], NULL, 10) / 3600.0);
if(dir == 'S' || dir == 'W') res *= -1.0;
}
g_strfreev(list);
return res;
}
gboolean dt_util_gps_rationale_to_number(const double r0_1, const double r0_2, const double r1_1,
const double r1_2, const double r2_1, const double r2_2, char sign,
double *result)
{
if(!result) return FALSE;
double res = 0.0;
// Latitude decoding from Exif.
double num, den, min, sec;
num = r0_1;
den = r0_2;
if(den == 0) return FALSE;
res = num / den;
num = r1_1;
den = r1_2;
if(den == 0) return FALSE;
min = num / den;
if(min != -1.0) res += min / 60.0;
num = r2_1;
den = r2_2;
if(den == 0)
{
// be relaxed and accept 0/0 seconds. See #246077.
if(num == 0)
den = 1;
else
return FALSE;
}
sec = num / den;
if(sec != -1.0) res += sec / 3600.0;
if(sign == 'S' || sign == 'W') res *= -1.0;
*result = res;
return TRUE;
}
gboolean dt_util_gps_elevation_to_number(const double r_1, const double r_2, char sign, double *result)
{
if(!result) return FALSE;
double res = 0.0;
// Altitude decoding from Exif.
const double num = r_1;
const double den = r_2;
if(den == 0) return FALSE;
res = num / den;
if(sign != '0') res *= -1.0;
*result = res;
return TRUE;
}
// make paths absolute and try to normalize on Windows. also deal with character encoding on Windows.
gchar *dt_util_normalize_path(const gchar *_input)
{
......
......@@ -58,6 +58,11 @@ cairo_surface_t *dt_util_get_logo(float size);
gchar *dt_util_latitude_str(float latitude);
gchar *dt_util_longitude_str(float longitude);
gchar *dt_util_elevation_str(float elevation);
double dt_util_gps_string_to_number(const gchar *input);
gboolean dt_util_gps_rationale_to_number(const double r0_1, const double r0_2, const double r1_1,
const double r1_2, const double r2_1, const double r2_2, char sign,
double *result);
gboolean dt_util_gps_elevation_to_number(const double r_1, const double r_2, char sign, double *result);
// make paths absolute and try to normalize on Windows. also deal with character encoding on Windows.
gchar *dt_util_normalize_path(const gchar *input);
......
......@@ -431,6 +431,43 @@ typedef struct lr_data_t
int orientation;
} lr_data_t;
// three helper functions for parsing RetouchInfo entries. sscanf doesn't work due to floats.
static gboolean _read_float(const char **startptr, const char *key, float *value)
{
const char *iter = *startptr;
while(*iter == ' ') iter++;
if(!g_str_has_prefix(iter, key))
return FALSE;
iter += strlen(key);
while(*iter == ' ') iter++;
if(*iter++ != '=')
return FALSE;
while(*iter == ' ') iter++;
*value = g_ascii_strtod(iter, (char **)startptr);
return iter != *startptr;
}
static gboolean _skip_key_value_pair(const char **startptr, const char *key)
{
const char *iter = *startptr;
while(*iter == ' ') iter++;
if(!g_str_has_prefix(iter, key))
return FALSE;
iter += strlen(key);
while(*iter == ' ') iter++;
if(*iter++ != '=')
return FALSE;
while(*iter == ' ') iter++;
while((*iter >= 'a' && *iter <= 'z') || (*iter >= 'A' && *iter <= 'Z')) iter++;
*startptr = iter;
return TRUE;
}
static gboolean _skip_comma(const char **startptr)
{
return *(*startptr)++ == ',';
}
/* lrop handle the Lr operation and convert it as a dt iop */
static void _lrop(const dt_develop_t *dev, const xmlDocPtr doc, const int imgid,
const xmlChar *name, const xmlChar *value, const xmlNodePtr node, lr_data_t *data)
......@@ -760,27 +797,19 @@ static void _lrop(const dt_develop_t *dev, const xmlDocPtr doc, const int imgid,
}
else if(!xmlStrcmp(name, (const xmlChar *)"GPSLatitude"))
{
int deg;
double msec;
char d;
if(sscanf((const char *)value, "%d,%lf%c", &deg, &msec, &d))
double latitude = dt_util_gps_string_to_number((const char *)value);
if(!isnan(latitude))
{
data->lat = deg + msec / 60.0;
if(d == 'S') data->lat = -data->lat;
data->lat = latitude;
data->has_gps = TRUE;
}
}
else if(!xmlStrcmp(name, (const xmlChar *)"GPSLongitude"))
{
int deg;
double msec;
char d;
if(sscanf((const char *)value, "%d,%lf%c", &deg, &msec, &d))
double longitude = dt_util_gps_string_to_number((const char *)value);
if(!isnan(longitude))
{
data->lon = deg + msec / 60.0;
if(d == 'W') data->lon = -data->lon;
data->lon = longitude;
data->has_gps = TRUE;
}
}
......@@ -832,10 +861,25 @@ static void _lrop(const dt_develop_t *dev, const xmlDocPtr doc, const int imgid,
{
xmlChar *cvalue = xmlNodeListGetString(doc, riNode->xmlChildrenNode, 1);
spot_t *p = &data->ps.spot[data->ps.num_spots];
if(sscanf((const char *)cvalue, "centerX = %f, centerY = %f, radius = %f, sourceState = %*[a-zA-Z], "
"sourceX = %f, sourceY = %f",
&(p->x), &(p->y), &(p->radius), &(p->xc), &(p->yc)))
float x, y, radius, xc, yc;
const char *startptr = (const char *)cvalue;
if(_read_float(&startptr, "centerX", &x) &&
_skip_comma(&startptr) &&
_read_float(&startptr, "centerY", &y) &&
_skip_comma(&startptr) &&
_read_float(&startptr, "radius", &radius) &&
_skip_comma(&startptr) &&
_skip_key_value_pair(&startptr, "sourceState") &&
_skip_comma(&startptr) &&
_read_float(&startptr, "sourceX", &xc) &&
_skip_comma(&startptr) &&
_read_float(&startptr, "sourceY", &yc))
{
p->x = x;
p->y = y;
p->radius = radius;
p->xc = xc;
p->yc = yc;
data->ps.num_spots++;
data->has_spots = TRUE;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment