diff --git a/crates/svg/src/import.rs b/crates/svg/src/import.rs index 5a591bad..eb907d70 100644 --- a/crates/svg/src/import.rs +++ b/crates/svg/src/import.rs @@ -245,6 +245,44 @@ impl RootUnits { } } +/// `'id' (feA, feB)` for each filter: its id and the primitives it uses, for warnings. +fn filter_names(filters: &[Arc]) -> String { + use usvg::filter::Kind as K; + let names: Vec = filters + .iter() + .map(|f| { + let mut kinds: Vec<&str> = Vec::new(); + for p in f.primitives() { + let k = match p.kind() { + K::Blend(_) => "feBlend", + K::ColorMatrix(_) => "feColorMatrix", + K::ComponentTransfer(_) => "feComponentTransfer", + K::Composite(_) => "feComposite", + K::ConvolveMatrix(_) => "feConvolveMatrix", + K::DiffuseLighting(_) => "feDiffuseLighting", + K::DisplacementMap(_) => "feDisplacementMap", + K::DropShadow(_) => "feDropShadow", + K::Flood(_) => "feFlood", + K::GaussianBlur(_) => "feGaussianBlur", + K::Image(_) => "feImage", + K::Merge(_) => "feMerge", + K::Morphology(_) => "feMorphology", + K::Offset(_) => "feOffset", + K::SpecularLighting(_) => "feSpecularLighting", + K::Tile(_) => "feTile", + K::Turbulence(_) => "feTurbulence", + }; + if !kinds.contains(&k) { + kinds.push(k); + } + } + let id = if f.id().is_empty() || made_up(f.id()) { String::new() } else { format!("'{}' ", f.id()) }; + format!("{id}({})", kinds.join(", ")) + }) + .collect(); + names.join(", ") +} + /// How many clip paths deep (``) an object's clip is read. const MAX_CLIP_NEST: usize = 16; @@ -902,7 +940,19 @@ impl Importer { let effects = match g.filters() { [] => vec![], filters => fx::effects(filters, ts, &self.blends).unwrap_or_else(|| { - self.warn(format!("filter on {label} ignored")); + // usvg wraps a filtered element in a group of its own: name the element then. + let on = match g.children() { + [usvg::Node::Group(_)] | [] => label.clone(), + [one] if made_up(g.id()) || g.id().is_empty() => { + if one.id().is_empty() || made_up(one.id()) { + "an object".to_string() + } else { + format!("'{}'", one.id()) + } + } + _ => label.clone(), + }; + self.warn(format!("filter {} on {on} isn't supported; drawn without it", filter_names(filters))); vec![] }), }; diff --git a/crates/svg/tests/import_details.rs b/crates/svg/tests/import_details.rs index 4634dec7..467e1a93 100644 --- a/crates/svg/tests/import_details.rs +++ b/crates/svg/tests/import_details.rs @@ -216,3 +216,16 @@ fn percentages_in_a_symbol_are_of_its_view_box() { }; assert_similar(&drawing("100%", "100%", "25%", "50%"), &drawing("100", "50", "25", "25"), 24.0, 0.001); } + +/// #1598: an unsupported filter's warning names the filter, its primitives and the object, and +/// says it is drawn without it (not "a group": usvg wraps the element in one). +#[test] +fn unsupported_filters_are_named_in_the_warning() { + let body = r#" + + "#; + let (d, w) = import_with_report(&svg(body)).unwrap(); + assert!(w.iter().any(|w| w == "filter 'f' (feColorMatrix) on an object isn't supported; drawn without it"), "{w:?}"); + assert!(w.iter().any(|w| w == "filter 't' (feTurbulence) on 'r' isn't supported; drawn without it"), "{w:?}"); + assert_eq!(art(&d).iter().filter(|n| n.path_data().is_some()).count(), 2, "both drawn"); +}