1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use super::svgtree::{AId, SvgNode};
use super::{converter, Options};
use crate::{Group, Node};
// Full list can be found here: https://www.w3.org/TR/SVG11/feature.html
static FEATURES: &[&str] = &[
"http://www.w3.org/TR/SVG11/feature#SVGDOM-static",
"http://www.w3.org/TR/SVG11/feature#SVG-static",
"http://www.w3.org/TR/SVG11/feature#CoreAttribute", // no xml:base and xml:lang
"http://www.w3.org/TR/SVG11/feature#Structure",
"http://www.w3.org/TR/SVG11/feature#BasicStructure",
"http://www.w3.org/TR/SVG11/feature#ContainerAttribute", // `enable-background`
"http://www.w3.org/TR/SVG11/feature#ConditionalProcessing",
"http://www.w3.org/TR/SVG11/feature#Image",
"http://www.w3.org/TR/SVG11/feature#Style",
// "http://www.w3.org/TR/SVG11/feature#ViewportAttribute", // `clip` and `overflow`, not yet
"http://www.w3.org/TR/SVG11/feature#Shape",
"http://www.w3.org/TR/SVG11/feature#Text",
"http://www.w3.org/TR/SVG11/feature#BasicText",
"http://www.w3.org/TR/SVG11/feature#PaintAttribute", // no color-interpolation and color-rendering
"http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute", // no color-interpolation
"http://www.w3.org/TR/SVG11/feature#OpacityAttribute",
"http://www.w3.org/TR/SVG11/feature#GraphicsAttribute",
"http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute",
"http://www.w3.org/TR/SVG11/feature#Marker",
// "http://www.w3.org/TR/SVG11/feature#ColorProfile", // not yet
"http://www.w3.org/TR/SVG11/feature#Gradient",
"http://www.w3.org/TR/SVG11/feature#Pattern",
"http://www.w3.org/TR/SVG11/feature#Clip",
"http://www.w3.org/TR/SVG11/feature#BasicClip",
"http://www.w3.org/TR/SVG11/feature#Mask",
"http://www.w3.org/TR/SVG11/feature#Filter",
"http://www.w3.org/TR/SVG11/feature#BasicFilter",
// only xlink:href
"http://www.w3.org/TR/SVG11/feature#XlinkAttribute",
// "http://www.w3.org/TR/SVG11/feature#Font",
// "http://www.w3.org/TR/SVG11/feature#BasicFont",
];
pub(crate) fn convert(
node: SvgNode,
state: &converter::State,
cache: &mut converter::Cache,
parent: &mut Group,
) -> Option<()> {
let child = node
.children()
.find(|n| is_condition_passed(*n, state.opt))?;
if let Some(g) = converter::convert_group(node, state, false, cache, parent, &|cache, g| {
converter::convert_element(child, state, cache, g);
}) {
parent.children.push(Node::Group(Box::new(g)));
}
Some(())
}
pub(crate) fn is_condition_passed(node: SvgNode, opt: &Options) -> bool {
if !node.is_element() {
return false;
}
if node.has_attribute(AId::RequiredExtensions) {
return false;
}
// 'The value is a list of feature strings, with the individual values separated by white space.
// Determines whether all of the named features are supported by the user agent.
// Only feature strings defined in the Feature String appendix are allowed.
// If all of the given features are supported, then the attribute evaluates to true;
// otherwise, the current element and its children are skipped and thus will not be rendered.'
if let Some(features) = node.attribute::<&str>(AId::RequiredFeatures) {
for feature in features.split(' ') {
if !FEATURES.contains(&feature) {
return false;
}
}
}
if !is_valid_sys_lang(node, opt) {
return false;
}
true
}
/// SVG spec 5.8.5
fn is_valid_sys_lang(node: SvgNode, opt: &Options) -> bool {
// 'The attribute value is a comma-separated list of language names
// as defined in BCP 47.'
//
// But we support only simple cases like `en` or `en-US`.
// No one really uses this, especially with complex BCP 47 values.
if let Some(langs) = node.attribute::<&str>(AId::SystemLanguage) {
let mut has_match = false;
for lang in langs.split(',') {
let lang = lang.trim();
// 'Evaluates to `true` if one of the languages indicated by user preferences exactly
// equals one of the languages given in the value of this parameter.'
if opt.languages.iter().any(|v| v == lang) {
has_match = true;
break;
}
// 'If one of the languages indicated by user preferences exactly equals a prefix
// of one of the languages given in the value of this parameter such that
// the first tag character following the prefix is `-`.'
if let Some(idx) = lang.bytes().position(|c| c == b'-') {
let lang_prefix = &lang[..idx];
if opt.languages.iter().any(|v| v == lang_prefix) {
has_match = true;
break;
}
}
}
has_match
} else {
true
}
}