diff options
Diffstat (limited to 'src/photos/PhotoMetadata.vala')
-rw-r--r-- | src/photos/PhotoMetadata.vala | 258 |
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()); |