Merge pull request #1599 from switch87/fix/1598-filter-warning

SVG import: an unsupported filter's warning names the filter and the object
This commit is contained in:
BFlat 2026-10-13 14:03:18 +07:00 committed by GitHub
commit ce558ffb73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 1 deletions

View File

@ -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<usvg::filter::Filter>]) -> String {
use usvg::filter::Kind as K;
let names: Vec<String> = 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 (`<clipPath clip-path>`) 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![]
}),
};

View File

@ -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#"<filter id="f"><feColorMatrix type="saturate" values="0"/></filter><filter id="t"><feTurbulence baseFrequency="0.05"/></filter>
<rect x="20" y="20" width="60" height="60" fill="red" filter="url(#f)"/>
<rect id="r" x="100" y="20" width="60" height="60" fill="blue" filter="url(#t)"/>"#;
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");
}