1
//! The `filter` element.
2

            
3
use cssparser::{Color, Parser};
4
use markup5ever::{expanded_name, local_name, namespace_url, ns};
5
use std::slice::Iter;
6

            
7
use crate::coord_units::CoordUnits;
8
use crate::document::{AcquiredNodes, NodeId};
9
use crate::drawing_ctx::Viewport;
10
use crate::element::{set_attribute, ElementData, ElementTrait};
11
use crate::error::ValueErrorKind;
12
use crate::filter_func::FilterFunction;
13
use crate::filters::{FilterResolveError, FilterSpec};
14
use crate::length::*;
15
use crate::node::{Node, NodeBorrow};
16
use crate::parsers::{Parse, ParseValue};
17
use crate::rect::Rect;
18
use crate::rsvg_log;
19
use crate::session::Session;
20
use crate::xml::Attributes;
21
use crate::{borrow_element_as, is_element_of_type};
22

            
23
/// The `<filter>` node.
24
pub struct Filter {
25
    x: Length<Horizontal>,
26
    y: Length<Vertical>,
27
    width: ULength<Horizontal>,
28
    height: ULength<Vertical>,
29
    filter_units: CoordUnits,
30
    primitive_units: CoordUnits,
31
}
32

            
33
/// A `<filter>` element definition in user-space coordinates.
34
pub struct UserSpaceFilter {
35
    pub rect: Rect,
36
    pub filter_units: CoordUnits,
37
    pub primitive_units: CoordUnits,
38
}
39

            
40
impl Default for Filter {
41
    /// Constructs a new `Filter` with default properties.
42
292
    fn default() -> Self {
43
292
        Self {
44
292
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
45
292
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
46
292
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
47
292
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
48
292
            filter_units: CoordUnits::ObjectBoundingBox,
49
292
            primitive_units: CoordUnits::UserSpaceOnUse,
50
        }
51
292
    }
52
}
53

            
54
impl Filter {
55
285
    pub fn get_filter_units(&self) -> CoordUnits {
56
285
        self.filter_units
57
285
    }
58

            
59
301
    pub fn to_user_space(&self, params: &NormalizeParams) -> UserSpaceFilter {
60
301
        let x = self.x.to_user(params);
61
301
        let y = self.y.to_user(params);
62
301
        let w = self.width.to_user(params);
63
301
        let h = self.height.to_user(params);
64

            
65
301
        let rect = Rect::new(x, y, x + w, y + h);
66

            
67
301
        UserSpaceFilter {
68
            rect,
69
301
            filter_units: self.filter_units,
70
301
            primitive_units: self.primitive_units,
71
        }
72
301
    }
73
}
74

            
75
impl ElementTrait for Filter {
76
277
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
77
1547
        for (attr, value) in attrs.iter() {
78
1270
            match attr.expanded() {
79
                expanded_name!("", "filterUnits") => {
80
130
                    set_attribute(&mut self.filter_units, attr.parse(value), session)
81
                }
82
206
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
83
206
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
84
                expanded_name!("", "width") => {
85
207
                    set_attribute(&mut self.width, attr.parse(value), session)
86
                }
87
                expanded_name!("", "height") => {
88
207
                    set_attribute(&mut self.height, attr.parse(value), session)
89
                }
90
                expanded_name!("", "primitiveUnits") => {
91
30
                    set_attribute(&mut self.primitive_units, attr.parse(value), session)
92
                }
93
                _ => (),
94
            }
95
1270
        }
96
277
    }
97
}
98

            
99
2031
#[derive(Debug, Clone, PartialEq)]
100
pub enum FilterValue {
101
1933
    Url(NodeId),
102
96
    Function(FilterFunction),
103
}
104

            
105
impl FilterValue {
106
304
    pub fn to_filter_spec(
107
        &self,
108
        acquired_nodes: &mut AcquiredNodes<'_>,
109
        user_space_params: &NormalizeParams,
110
        current_color: Color,
111
        viewport: &Viewport,
112
        session: &Session,
113
        node_being_filtered_name: &str,
114
    ) -> Result<FilterSpec, FilterResolveError> {
115
304
        match *self {
116
288
            FilterValue::Url(ref node_id) => filter_spec_from_filter_node(
117
                acquired_nodes,
118
                viewport,
119
                session,
120
                node_id,
121
                node_being_filtered_name,
122
            ),
123

            
124
16
            FilterValue::Function(ref func) => {
125
16
                Ok(func.to_filter_spec(user_space_params, current_color))
126
16
            }
127
        }
128
304
    }
129
}
130

            
131
/// Holds the viewport parameters for both objectBoundingBox and userSpaceOnUse units.
132
///
133
/// When collecting a set of filter primitives (`feFoo`) into a [`FilterSpec`], which is
134
/// in user space, we need to convert each primitive's units into user space units.  So,
135
/// pre-compute both cases and pass them around.
136
///
137
/// This struct needs a better name; I didn't want to make it seem specific to filters by
138
/// calling `FiltersViewport` or `FilterCollectionProcessViewport`.  Maybe the
139
/// original [`Viewport`] should be this struct, with both cases included...
140
struct ViewportGen {
141
    object_bounding_box: Viewport,
142
    user_space_on_use: Viewport,
143
}
144

            
145
impl ViewportGen {
146
288
    pub fn new(viewport: &Viewport) -> Self {
147
288
        ViewportGen {
148
288
            object_bounding_box: viewport.with_units(CoordUnits::ObjectBoundingBox),
149
288
            user_space_on_use: viewport.with_units(CoordUnits::UserSpaceOnUse),
150
        }
151
288
    }
152

            
153
570
    fn get(&self, units: CoordUnits) -> &Viewport {
154
1140
        match units {
155
299
            CoordUnits::ObjectBoundingBox => &self.object_bounding_box,
156
271
            CoordUnits::UserSpaceOnUse => &self.user_space_on_use,
157
        }
158
570
    }
159
}
160

            
161
1097
fn extract_filter_from_filter_node(
162
    filter_node: &Node,
163
    acquired_nodes: &mut AcquiredNodes<'_>,
164
    session: &Session,
165
    filter_view_params: &ViewportGen,
166
) -> Result<FilterSpec, FilterResolveError> {
167
1097
    assert!(is_element_of_type!(filter_node, Filter));
168

            
169
1097
    let filter_element = filter_node.borrow_element();
170

            
171
    let user_space_filter = {
172
1097
        let filter_values = filter_element.get_computed_values();
173

            
174
285
        let filter = borrow_element_as!(filter_node, Filter);
175

            
176
570
        filter.to_user_space(&NormalizeParams::new(
177
            filter_values,
178
285
            filter_view_params.get(filter.get_filter_units()),
179
        ))
180
285
    };
181

            
182
285
    let primitive_view_params = filter_view_params.get(user_space_filter.primitive_units);
183

            
184
285
    let primitive_nodes = filter_node
185
        .children()
186
1105
        .filter(|c| c.is_element())
187
        // Keep only filter primitives (those that implement the Filter trait)
188
1507
        .filter(|c| c.borrow_element().as_filter_effect().is_some());
189

            
190
1097
    let mut user_space_primitives = Vec::new();
191

            
192
1097
    for primitive_node in primitive_nodes {
193
406
        let elt = primitive_node.borrow_element();
194
406
        let effect = elt.as_filter_effect().unwrap();
195

            
196
406
        let primitive_name = format!("{primitive_node}");
197

            
198
406
        let primitive_values = elt.get_computed_values();
199
406
        let params = NormalizeParams::new(primitive_values, primitive_view_params);
200

            
201
406
        let primitives = match effect.resolve(acquired_nodes, &primitive_node) {
202
406
            Ok(primitives) => primitives,
203
            Err(e) => {
204
                rsvg_log!(
205
                    session,
206
                    "(filter primitive {} returned an error: {})",
207
                    primitive_name,
208
                    e
209
                );
210
                return Err(e);
211
            }
212
        };
213

            
214
816
        for p in primitives {
215
410
            user_space_primitives.push(p.into_user_space(&params));
216
        }
217
406
    }
218

            
219
285
    Ok(FilterSpec {
220
285
        name: filter_element
221
            .get_id()
222
            .unwrap_or("(filter element without id)")
223
            .to_string(),
224
        user_space_filter,
225
285
        primitives: user_space_primitives,
226
    })
227
285
}
228

            
229
288
fn filter_spec_from_filter_node(
230
    acquired_nodes: &mut AcquiredNodes<'_>,
231
    viewport: &Viewport,
232
    session: &Session,
233
    node_id: &NodeId,
234
    node_being_filtered_name: &str,
235
) -> Result<FilterSpec, FilterResolveError> {
236
288
    let filter_view_params = ViewportGen::new(viewport);
237

            
238
864
    acquired_nodes
239
288
        .acquire(node_id)
240
290
        .map_err(|e| {
241
2
            rsvg_log!(
242
2
                *session,
243
                "element {} will not be filtered with \"{}\": {}",
244
                node_being_filtered_name,
245
                node_id,
246
                e
247
            );
248
2
            FilterResolveError::ReferenceToNonFilterElement
249
2
        })
250
574
        .and_then(|acquired| {
251
286
            let node = acquired.get();
252

            
253
286
            match *node.borrow_element_data() {
254
285
                ElementData::Filter(_) => extract_filter_from_filter_node(
255
                    node,
256
285
                    acquired_nodes,
257
285
                    session,
258
285
                    &filter_view_params,
259
                ),
260

            
261
                _ => {
262
1
                    rsvg_log!(
263
1
                        *session,
264
                        "element {} will not be filtered since \"{}\" is not a filter",
265
                        node_being_filtered_name,
266
                        node_id,
267
                    );
268
1
                    Err(FilterResolveError::ReferenceToNonFilterElement)
269
                }
270
            }
271
286
        })
272
288
}
273

            
274
4638
#[derive(Debug, Default, Clone, PartialEq)]
275
2319
pub struct FilterValueList(Vec<FilterValue>);
276

            
277
impl FilterValueList {
278
    pub fn is_empty(&self) -> bool {
279
        self.0.is_empty()
280
    }
281

            
282
301
    pub fn iter(&self) -> Iter<'_, FilterValue> {
283
301
        self.0.iter()
284
301
    }
285
}
286

            
287
impl Parse for FilterValueList {
288
309
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
289
309
        let mut result = FilterValueList::default();
290

            
291
5
        loop {
292
314
            let loc = parser.current_source_location();
293

            
294
628
            let filter_value = if let Ok(func) = parser.try_parse(|p| FilterFunction::parse(p)) {
295
16
                FilterValue::Function(func)
296
            } else {
297
298
                let url = parser.expect_url()?;
298
294
                let node_id = NodeId::parse(&url)
299
294
                    .map_err(|e| loc.new_custom_error(ValueErrorKind::from(e)))?;
300

            
301
294
                FilterValue::Url(node_id)
302
314
            };
303

            
304
310
            result.0.push(filter_value);
305

            
306
310
            if parser.is_exhausted() {
307
                break;
308
            }
309
314
        }
310

            
311
305
        Ok(result)
312
309
    }
313
}
314

            
315
#[cfg(test)]
316
mod tests {
317
    use super::*;
318

            
319
    #[test]
320
2
    fn parses_filter_value_list() {
321
1
        let n1 = NodeId::External("foo.svg".to_string(), "bar".to_string());
322
1
        let n2 = NodeId::External("test.svg".to_string(), "baz".to_string());
323
2
        assert_eq!(
324
1
            FilterValueList::parse_str("url(foo.svg#bar) url(test.svg#baz)").unwrap(),
325
1
            FilterValueList(vec![FilterValue::Url(n1), FilterValue::Url(n2)])
326
        );
327
2
    }
328

            
329
    #[test]
330
2
    fn detects_invalid_filter_value_list() {
331
1
        assert!(FilterValueList::parse_str("none").is_err());
332
1
        assert!(FilterValueList::parse_str("").is_err());
333
1
        assert!(FilterValueList::parse_str("fail").is_err());
334
1
        assert!(FilterValueList::parse_str("url(#test) none").is_err());
335
2
    }
336
}