diff --git a/crates/meta/src/xmp.rs b/crates/meta/src/xmp.rs index a91a508c3..dbfdd5458 100644 --- a/crates/meta/src/xmp.rs +++ b/crates/meta/src/xmp.rs @@ -544,6 +544,8 @@ pub fn parse_xmp(s: &str) -> Result { } if let (Some(lat), Some(lon)) = (first("exif:GPSLatitude").and_then(|s| parse_gps_coord(&s)), first("exif:GPSLongitude").and_then(|s| parse_gps_coord(&s))) + && (-90.0..=90.0).contains(&lat) + && (-180.0..=180.0).contains(&lon) { let altitude = num("exif:GPSAltitude").map(|a| if first("exif:GPSAltitudeRef").as_deref() == Some("1") { -a } else { a }); m.gps = Some(Gps { latitude: lat, longitude: lon, altitude }); @@ -866,6 +868,24 @@ mod tests { assert_eq!(fmt_gps_coord(-45.51, 'N', 'S'), "45,30.600000S"); } + /// Like the EXIF and GPX readers, XMP drops a position outside latitude ±90° / longitude ±180°. + #[test] + fn xmp_gps_out_of_range_rejected() { + let gps = |lat: &str, lon: &str| { + let x = format!( + r#""# + ); + parse_xmp(&x).unwrap().metadata.gps + }; + assert_eq!(gps("95,0N", "200,30E"), None); + assert_eq!(gps("95,0N", "10,0E"), None); + assert_eq!(gps("10,0S", "200,30W"), None); + assert_eq!(gps("90,0,1N", "0,0E"), None); + assert_eq!(gps("90,0N", "180,0E"), Some(Gps { latitude: 90.0, longitude: 180.0, altitude: None })); + assert_eq!(gps("90,0S", "180,0W"), Some(Gps { latitude: -90.0, longitude: -180.0, altitude: None })); + } + /// MWG Region Guidelines v2.0, element form (`rdf:parseType="Resource"`, self-closing `Area`) — how /// Lightroom typically writes it. One named face, one untyped/unnamed region (still usable), one /// region with a description. `AppliedToDimensions` is present but irrelevant: normalized areas