Skip to content

Commit 559de5b

Browse files
authored
Fix selector bug when handling Inkscape attributes (#122)
* CSS selector character escaping with backslash * Fix namespacing lookup bug
1 parent ac884e4 commit 559de5b

1 file changed

Lines changed: 26 additions & 6 deletions

File tree

star/src/lower/selector.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,17 @@ peg::parser!(grammar selector_parser() for str {
106106
rule _() = whitespace()*
107107
rule s() = whitespace()+
108108

109+
rule escape() -> char = "\\" c:[_] { c }
110+
111+
rule ident_char() -> char
112+
= escape()
113+
/ c:['a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_'] { c }
114+
109115
// Tag names, class names, and IDs
110-
rule ident() -> String = s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_']+) { s.to_string() }
116+
rule ident() -> String = chars:ident_char()+ { chars.into_iter().collect() }
111117

112118
// Attribute names
113-
rule attr_name() -> String = s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | ':']+) { s.to_string() }
119+
rule attr_name() -> String = chars:ident_char()+ { chars.into_iter().collect() }
114120

115121
// Attribute value
116122
rule unquoted() -> char = [c if c != ']' && !c.is_ascii_whitespace()]
@@ -298,7 +304,17 @@ impl AttributeSelector {
298304
// like `inkscape:label` which roxmltree may expose under a namespace.
299305
let value = node
300306
.attributes()
301-
.find(|a| a.name() == self.name)
307+
.find(|a| {
308+
if let Some((prefix, local)) = self.name.split_once(':') {
309+
if let Some(ns_uri) = node.lookup_namespace_uri(Some(prefix)) {
310+
a.namespace() == Some(ns_uri) && a.name() == local
311+
} else {
312+
false
313+
}
314+
} else {
315+
a.name() == self.name
316+
}
317+
})
302318
.map(|a| a.value());
303319

304320
match (&self.op, value) {
@@ -343,7 +359,9 @@ mod tests {
343359
("[id=foo]", true),
344360
("[id=\"foo bar\"]", true),
345361
("[style*=\"display:none\"]", true),
346-
("[inkscape:label=\"Layer 1\"]", true),
362+
("[inkscape\\:label=\"Layer 1\"]", true),
363+
("[inkscape:label=\"Layer 1\"]", false),
364+
("g\\#layer1", true),
347365
// combinators
348366
("g path", true),
349367
("g > path", true),
@@ -385,8 +403,8 @@ mod tests {
385403

386404
#[test]
387405
fn test_match() {
388-
let svg = r#"<svg>
389-
<g id="layer1">
406+
let svg = r#"<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
407+
<g id="layer1" inkscape:label="Layer 1">
390408
<path id="p-cut" class="cut"/>
391409
<path id="p-draft" class="draft"/>
392410
<g id="nested">
@@ -415,6 +433,8 @@ mod tests {
415433
// attribute selector
416434
("[style*=\"display:none\"]", "p-outer", true),
417435
("[style*=\"display:none\"]", "r-draft", false),
436+
("[inkscape\\:label=\"Layer 1\"]", "layer1", true),
437+
("[inkscape\\:label=\"Layer 1\"]", "p-cut", false),
418438
// descendant combinator — p-cut is inside layer1, p-outer is not
419439
("#layer1 path", "p-cut", true),
420440
("#layer1 path", "p-nested", true),

0 commit comments

Comments
 (0)