summaryrefslogtreecommitdiff
path: root/src/photos/PhotoMetadata.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/photos/PhotoMetadata.vala')
-rw-r--r--src/photos/PhotoMetadata.vala258
1 files changed, 183 insertions, 75 deletions
diff --git a/src/photos/PhotoMetadata.vala b/src/photos/PhotoMetadata.vala
index a9b7457..3bf77d6 100644
--- a/src/photos/PhotoMetadata.vala
+++ b/src/photos/PhotoMetadata.vala
@@ -241,9 +241,13 @@ public class PhotoMetadata : MediaMetadata {
public override Bytes flatten() throws Error {
unowned GExiv2.PreviewProperties?[] props = owner.exiv2.get_preview_properties();
assert(props != null && props.length > number);
-
- return new
- Bytes(owner.exiv2.get_preview_image(props[number]).get_data());
+
+ try {
+ return new
+ Bytes(owner.exiv2.try_get_preview_image(props[number]).get_data());
+ } catch (Error err) {
+ return new Bytes(null);
+ }
}
}
@@ -278,12 +282,8 @@ public class PhotoMetadata : MediaMetadata {
exiv2 = new GExiv2.Metadata();
exif = null;
-#if NEW_GEXIV2_API
exiv2.open_buf(buffer[0:length]);
-#else
- exiv2.open_buf(buffer, length);
-#endif
- exif = Exif.Data.new_from_data(buffer);
+ exif = Exif.Data.new_from_data(buffer[0:length]);
source_name = "<memory buffer %d bytes>".printf(length);
}
@@ -291,11 +291,8 @@ public class PhotoMetadata : MediaMetadata {
exiv2 = new GExiv2.Metadata();
exif = null;
-#if NEW_GEXIV2_API
exiv2.from_app1_segment(buffer.get_data());
-#else
exif = Exif.Data.new_from_data(buffer.get_data());
-#endif
source_name = "<app1 segment %zu bytes>".printf(buffer.get_size());
}
@@ -371,7 +368,11 @@ public class PhotoMetadata : MediaMetadata {
}
public bool has_tag(string tag) {
- return exiv2.has_tag(tag);
+ try {
+ return exiv2.try_has_tag(tag);
+ } catch (Error error) {
+ return false;
+ }
}
private Gee.Set<string> create_string_set(owned CompareDataFunc<string>? compare_func) {
@@ -397,6 +398,9 @@ public class PhotoMetadata : MediaMetadata {
case MetadataDomain.IPTC:
tags = exiv2.get_iptc_tags();
break;
+ default:
+ // Just ignore any other unknown tags
+ break;
}
if (tags == null || tags.length == 0)
@@ -429,19 +433,35 @@ public class PhotoMetadata : MediaMetadata {
}
public string? get_tag_label(string tag) {
- return GExiv2.Metadata.get_tag_label(tag);
+ try {
+ return GExiv2.Metadata.try_get_tag_label(tag);
+ } catch (Error error) {
+ return null;
+ }
}
public string? get_tag_description(string tag) {
- return GExiv2.Metadata.get_tag_description(tag);
+ try {
+ return GExiv2.Metadata.try_get_tag_description(tag);
+ } catch (Error error) {
+ return null;
+ }
}
public string? get_string(string tag, PrepareInputTextOptions options = PREPARE_STRING_OPTIONS) {
- return prepare_input_text(exiv2.get_tag_string(tag), options, DEFAULT_USER_TEXT_INPUT_LENGTH);
+ try {
+ return prepare_input_text(exiv2.try_get_tag_string(tag), options, DEFAULT_USER_TEXT_INPUT_LENGTH);
+ } catch (Error error) {
+ return null;
+ }
}
public string? get_string_interpreted(string tag, PrepareInputTextOptions options = PREPARE_STRING_OPTIONS) {
- return prepare_input_text(exiv2.get_tag_interpreted_string(tag), options, DEFAULT_USER_TEXT_INPUT_LENGTH);
+ try {
+ return prepare_input_text(exiv2.try_get_tag_interpreted_string(tag), options, DEFAULT_USER_TEXT_INPUT_LENGTH);
+ } catch (Error error) {
+ return null;
+ }
}
public string? get_first_string(string[] tags) {
@@ -469,26 +489,30 @@ public class PhotoMetadata : MediaMetadata {
// NOTE: get_tag_multiple() in gexiv2 currently does not work with EXIF tags (as EXIF can
// never return a list of strings). It will quietly return NULL if attempted. Until fixed
// (there or here), don't use this function to access EXIF. See:
- // http://trac.yorba.org/ticket/2966
+ // https://gitlab.gnome.org/GNOME/gexiv2/issues/10
public Gee.List<string>? get_string_multiple(string tag) {
- string[] values = exiv2.get_tag_multiple(tag);
- if (values == null || values.length == 0)
- return null;
-
- Gee.List<string> list = new Gee.ArrayList<string>();
-
- Gee.HashSet<string> collection = new Gee.HashSet<string>();
- foreach (string value in values) {
- string? prepped = prepare_input_text(value, PREPARE_STRING_OPTIONS,
- DEFAULT_USER_TEXT_INPUT_LENGTH);
-
- if (prepped != null && !collection.contains(prepped)) {
- list.add(prepped);
- collection.add(prepped);
+ try {
+ string[] values = exiv2.try_get_tag_multiple(tag);
+ if (values == null || values.length == 0)
+ return null;
+
+ Gee.List<string> list = new Gee.ArrayList<string>();
+
+ Gee.HashSet<string> collection = new Gee.HashSet<string>();
+ foreach (string value in values) {
+ string? prepped = prepare_input_text(value, PREPARE_STRING_OPTIONS,
+ DEFAULT_USER_TEXT_INPUT_LENGTH);
+
+ if (prepped != null && !collection.contains(prepped)) {
+ list.add(prepped);
+ collection.add(prepped);
+ }
}
+
+ return list.size > 0 ? list : null;
+ } catch (Error error) {
+ return null;
}
-
- return list.size > 0 ? list : null;
}
// Returns a List that has been filtered through a Set, so no duplicates will be found.
@@ -496,7 +520,7 @@ public class PhotoMetadata : MediaMetadata {
// NOTE: get_tag_multiple() in gexiv2 currently does not work with EXIF tags (as EXIF can
// never return a list of strings). It will quietly return NULL if attempted. Until fixed
// (there or here), don't use this function to access EXIF. See:
- // http://trac.yorba.org/ticket/2966
+ // https://gitlab.gnome.org/GNOME/gexiv2/issues/10
public Gee.List<string>? get_first_string_multiple(string[] tags) {
foreach (string tag in tags) {
Gee.List<string>? values = get_string_multiple(tag);
@@ -507,16 +531,20 @@ public class PhotoMetadata : MediaMetadata {
return null;
}
- public void set_string(string tag, string value, PrepareInputTextOptions options = PREPARE_STRING_OPTIONS) {
- string? prepped = prepare_input_text(value, options, DEFAULT_USER_TEXT_INPUT_LENGTH);
+ public void set_string(string tag, string value, PrepareInputTextOptions options = PREPARE_STRING_OPTIONS,
+ int length = DEFAULT_USER_TEXT_INPUT_LENGTH) {
+ string? prepped = prepare_input_text(value, options, length);
if (prepped == null) {
warning("Not setting tag %s to string %s: invalid UTF-8", tag, value);
return;
}
- if (!exiv2.set_tag_string(tag, prepped))
- warning("Unable to set tag %s to string %s from source %s", tag, value, source_name);
+ try {
+ exiv2.try_set_tag_string(tag, prepped);
+ } catch (Error error) {
+ warning("Unable to set tag %s to string %s from source %s: %s", tag, value, source_name, error.message);
+ }
}
private delegate void SetGenericValue(string tag);
@@ -562,13 +590,16 @@ public class PhotoMetadata : MediaMetadata {
return;
// append a null pointer to the end of the string array -- this is a necessary
- // workaround for http://trac.yorba.org/ticket/3264. See also
- // http://trac.yorba.org/ticket/3257, which describes the user-visible behavior
- // seen in the Flickr Connector as a result of the former bug.
+ // workaround for https://bugzilla.gnome.org/show_bug.cgi?id=712479. See also
+ // https://bugzilla.gnome.org/show_bug.cgi?id=717438, which describes the
+ // user-visible behavior seen in the Flickr Connector as a result of the former bug.
values += null;
- if (!exiv2.set_tag_multiple(tag, values))
- warning("Unable to set %d strings to tag %s from source %s", values.length, tag, source_name);
+ try {
+ exiv2.try_set_tag_multiple(tag, values);
+ } catch (Error err) {
+ warning("Unable to set %d strings to tag %s from source %s: %s", values.length, tag, source_name, err.message);
+ }
}
public void set_all_string_multiple(string[] tags, Gee.Collection<string> values, SetOption option) {
@@ -576,13 +607,16 @@ public class PhotoMetadata : MediaMetadata {
}
public bool get_long(string tag, out long value) {
+ value = 0;
if (!has_tag(tag)) {
- value = 0;
-
return false;
}
- value = exiv2.get_tag_long(tag);
+ try {
+ value = exiv2.try_get_tag_long(tag);
+ } catch (Error error) {
+ return false;
+ }
return true;
}
@@ -599,8 +633,11 @@ public class PhotoMetadata : MediaMetadata {
}
public void set_long(string tag, long value) {
- if (!exiv2.set_tag_long(tag, value))
- warning("Unable to set tag %s to long %ld from source %s", tag, value, source_name);
+ try {
+ exiv2.try_set_tag_long(tag, value);
+ } catch (Error err) {
+ warning("Unable to set tag %s to long %ld from source %s: %s", tag, value, source_name, err.message);
+ }
}
public void set_all_long(string[] tags, long value, SetOption option) {
@@ -609,11 +646,19 @@ public class PhotoMetadata : MediaMetadata {
public bool get_rational(string tag, out MetadataRational rational) {
int numerator, denominator;
- bool result = exiv2.get_exif_tag_rational(tag, out numerator, out denominator);
-
- rational = MetadataRational(numerator, denominator);
-
- return result;
+ try {
+ if (exiv2.try_get_exif_tag_rational(tag, out numerator, out denominator)) {
+ rational = MetadataRational(numerator, denominator);
+ } else {
+ rational = MetadataRational.invalid();
+ return false;
+ }
+ } catch (Error error) {
+ rational = MetadataRational.invalid();
+ return false;
+ }
+
+ return true;
}
public bool get_first_rational(string[] tags, out MetadataRational rational) {
@@ -628,9 +673,11 @@ public class PhotoMetadata : MediaMetadata {
}
public void set_rational(string tag, MetadataRational rational) {
- if (!exiv2.set_exif_tag_rational(tag, rational.numerator, rational.denominator)) {
- warning("Unable to set tag %s to rational %s from source %s", tag, rational.to_string(),
- source_name);
+ try {
+ exiv2.try_set_exif_tag_rational(tag, rational.numerator, rational.denominator);
+ } catch (Error err) {
+ warning("Unable to set tag %s to rational %s from source %s: %s", tag, rational.to_string(),
+ source_name, err.message);
}
}
@@ -769,7 +816,10 @@ public class PhotoMetadata : MediaMetadata {
}
public void remove_exif_thumbnail() {
- exiv2.erase_exif_thumbnail();
+ try {
+ exiv2.try_erase_exif_thumbnail();
+ } catch (Error err) { }
+
if (exif != null) {
Exif.Mem.new_default().free(exif.data);
exif.data = null;
@@ -778,7 +828,9 @@ public class PhotoMetadata : MediaMetadata {
}
public void remove_tag(string tag) {
- exiv2.clear_tag(tag);
+ try {
+ exiv2.try_clear_tag(tag);
+ } catch (Error err){}
}
public void remove_tags(string[] tags) {
@@ -799,6 +851,9 @@ public class PhotoMetadata : MediaMetadata {
case MetadataDomain.IPTC:
exiv2.clear_iptc();
break;
+ default:
+ // Just ignore any unknown tags
+ break;
}
}
@@ -881,7 +936,7 @@ public class PhotoMetadata : MediaMetadata {
public static string[] HEIGHT_TAGS = {
"Exif.Photo.PixelYDimension",
"Xmp.exif.PixelYDimension",
- "Xmp.tiff.ImageHeight",
+ "Xmp.tiff.ImageLength",
"Xmp.exif.PixelYDimension"
};
@@ -923,7 +978,7 @@ public class PhotoMetadata : MediaMetadata {
// (sometimes) appropriate tag for the description. And there's general confusion about
// whether Exif.Image.ImageDescription is a description (which is what the tag name
// suggests) or a title (which is what the specification states).
- // See: http://trac.yorba.org/wiki/PhotoTags
+ // See: https://wiki.gnome.org/Apps/Shotwell/PhotoTags
//
// Hence, the following logic tries to do the right thing in most of these cases. If
// the iPhoto title tag is detected, it and the iPhoto description tag are used. Otherwise,
@@ -997,8 +1052,9 @@ public class PhotoMetadata : MediaMetadata {
* newlines from comments */
if (!is_string_empty(comment))
set_all_generic(COMMENT_TAGS, option, (tag) => {
+ // 4095 is coming from acdsee.notes which is limited to that
set_string(tag, comment, PREPARE_STRING_OPTIONS &
- ~PrepareInputTextOptions.STRIP_CRLF);
+ ~PrepareInputTextOptions.STRIP_CRLF, 4095);
});
else
remove_tags(COMMENT_TAGS);
@@ -1139,24 +1195,37 @@ public class PhotoMetadata : MediaMetadata {
}
public bool has_orientation() {
- return exiv2.get_orientation() == GExiv2.Orientation.UNSPECIFIED;
+ try {
+ return exiv2.try_get_orientation() == GExiv2.Orientation.UNSPECIFIED;
+ } catch (Error err) {
+ debug("Failed to get orientation: %s", err.message);
+ return false;
+ }
}
// If not present, returns Orientation.TOP_LEFT.
public Orientation get_orientation() {
// GExiv2.Orientation is the same value-wise as Orientation, with one exception:
// GExiv2.Orientation.UNSPECIFIED must be handled
- GExiv2.Orientation orientation = exiv2.get_orientation();
- if (orientation == GExiv2.Orientation.UNSPECIFIED || orientation < Orientation.MIN ||
- orientation > Orientation.MAX)
+ try {
+ GExiv2.Orientation orientation = exiv2.try_get_orientation();
+ if (orientation == GExiv2.Orientation.UNSPECIFIED || orientation < Orientation.MIN ||
+ orientation > Orientation.MAX)
+ return Orientation.TOP_LEFT;
+ else
+ return (Orientation) orientation;
+ } catch (Error error) {
return Orientation.TOP_LEFT;
- else
- return (Orientation) orientation;
+ }
}
public void set_orientation(Orientation orientation) {
// GExiv2.Orientation is the same value-wise as Orientation
- exiv2.set_orientation((GExiv2.Orientation) orientation);
+ try {
+ exiv2.try_set_orientation((GExiv2.Orientation) orientation);
+ } catch (Error err) {
+ debug("Failed to set the orientation: %s", err.message);
+ }
}
public bool get_gps(out double longitude, out string long_ref, out double latitude, out string lat_ref,
@@ -1164,14 +1233,22 @@ public class PhotoMetadata : MediaMetadata {
longitude = 0.0;
latitude = 0.0;
altitude = 0.0;
- if (!exiv2.get_gps_longitude(out longitude) || !exiv2.get_gps_latitude(out latitude)) {
- long_ref = null;
- lat_ref = null;
-
- return false;
+ try {
+ if (!exiv2.try_get_gps_longitude(out longitude) || !exiv2.try_get_gps_latitude(out latitude)) {
+ long_ref = null;
+ lat_ref = null;
+
+ return false;
+ }
+ } catch (Error err) {
+ debug("Failed to get GPS lon/lat: %s", err.message);
}
- exiv2.get_gps_altitude(out altitude);
+ try {
+ exiv2.try_get_gps_altitude(out altitude);
+ } catch (Error err) {
+ debug("Failed to get GPS altitude: %s", err.message);
+ }
long_ref = get_string("Exif.GPSInfo.GPSLongitudeRef");
lat_ref = get_string("Exif.GPSInfo.GPSLatitudeRef");
@@ -1179,6 +1256,37 @@ public class PhotoMetadata : MediaMetadata {
return true;
}
+ public GpsCoords get_gps_coords() {
+ GpsCoords gps_coords = GpsCoords();
+ try {
+ double altitude;
+ gps_coords.has_gps = exiv2.try_get_gps_info(out gps_coords.longitude, out gps_coords.latitude, out altitude) ? 1 : 0;
+ if (gps_coords.has_gps > 0) {
+ if (get_string("Exif.GPSInfo.GPSLongitudeRef") == "W" && gps_coords.longitude > 0)
+ gps_coords.longitude = -gps_coords.longitude;
+ if (get_string("Exif.GPSInfo.GPSLatitudeRef") == "S" && gps_coords.latitude > 0)
+ gps_coords.latitude = -gps_coords.latitude;
+ }
+ } catch (Error err) {
+ gps_coords.has_gps = 0;
+ }
+
+ return gps_coords;
+ }
+
+ public void set_gps_coords(GpsCoords gps_coords) {
+ try {
+ if (gps_coords.has_gps > 0) {
+ var altitude = 0.0;
+ exiv2.try_get_gps_altitude(out altitude);
+ exiv2.try_set_gps_info(gps_coords.longitude, gps_coords.latitude, altitude);
+ } else
+ exiv2.try_delete_gps_info();
+ } catch (Error err) {
+ debug("Failed to set or remove GPS info: %s", err.message);
+ }
+ }
+
public bool get_exposure(out MetadataRational exposure) {
return get_rational("Exif.Photo.ExposureTime", out exposure);
}
@@ -1326,7 +1434,7 @@ public class PhotoMetadata : MediaMetadata {
// Other photo managers, notably F-Spot, take hints from Urgency fields about what the rating
// of an imported photo should be, and we have decided to do as well. Xmp.xmp.Rating is the only
// field we've seen photo manages export ratings to, while Urgency fields seem to have a fundamentally
- // different meaning. See http://trac.yorba.org/wiki/PhotoTags#Rating for more information.
+ // different meaning. See https://wiki.gnome.org/Apps/Shotwell/PhotoTags#Rating for more information.
public void set_rating(Rating rating) {
int int_rating = rating.serialize();
set_string("Xmp.xmp.Rating", int_rating.to_string());