Skip to main content

rsvg/
pattern.rs

1//! The `pattern` element.
2
3use markup5ever::{expanded_name, local_name, ns};
4
5use crate::coord_units;
6use crate::coord_units::CoordUnits;
7
8use crate::aspect_ratio::*;
9use crate::document::{AcquiredNode, AcquiredNodes, NodeId, NodeStack};
10use crate::drawing_ctx::Viewport;
11use crate::element::{ElementData, ElementTrait, set_attribute};
12use crate::error::*;
13use crate::href::{is_href, set_href};
14use crate::length::*;
15use crate::node::{Node, NodeBorrow, WeakNode};
16use crate::parsers::ParseValue;
17use crate::rect::Rect;
18use crate::rsvg_log;
19use crate::session::Session;
20use crate::transform::{Transform, TransformAttribute};
21use crate::unit_interval::UnitInterval;
22use crate::viewbox::*;
23use crate::xml::Attributes;
24
25coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
26coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);
27
28#[derive(Clone, Default)]
29struct Common {
30    units: Option<PatternUnits>,
31    content_units: Option<PatternContentUnits>,
32    // This Option<Option<ViewBox>> is a bit strange.  We want a field
33    // with value None to mean, "this field isn't resolved yet".  However,
34    // the vbox can very well be *not* specified in the SVG file.
35    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
36    vbox: Option<Option<ViewBox>>,
37    preserve_aspect_ratio: Option<AspectRatio>,
38    transform: Option<TransformAttribute>,
39    x: Option<Length<Horizontal>>,
40    y: Option<Length<Vertical>>,
41    width: Option<ULength<Horizontal>>,
42    height: Option<ULength<Vertical>>,
43}
44
45/// State used during the pattern resolution process
46///
47/// This is the current node's pattern information, plus the fallback
48/// that should be used in case that information is not complete for a
49/// resolved pattern yet.
50struct Unresolved {
51    pattern: UnresolvedPattern,
52    fallback: Option<NodeId>,
53}
54
55/// Keeps track of which Pattern provided a non-empty set of children during pattern resolution
56#[derive(Clone)]
57enum UnresolvedChildren {
58    /// Points back to the original Pattern if it had no usable children
59    Unresolved,
60
61    /// Points back to the original Pattern, as no pattern in the
62    /// chain of fallbacks had usable children.  This only gets returned
63    /// by resolve_from_defaults().
64    ResolvedEmpty,
65
66    /// Points back to the Pattern that had usable children.
67    WithChildren(WeakNode),
68}
69
70/// Keeps track of which Pattern provided a non-empty set of children during pattern resolution
71#[derive(Clone)]
72enum Children {
73    Empty,
74
75    /// Points back to the Pattern that had usable children
76    WithChildren(WeakNode),
77}
78
79/// Main structure used during pattern resolution.  For unresolved
80/// patterns, we store all fields as `Option<T>` - if `None`, it means
81/// that the field is not specified; if `Some(T)`, it means that the
82/// field was specified.
83struct UnresolvedPattern {
84    common: Common,
85
86    // Point back to our corresponding node, or to the fallback node which has children.
87    // If the value is None, it means we are fully resolved and didn't find any children
88    // among the fallbacks.
89    children: UnresolvedChildren,
90}
91
92#[derive(Clone)]
93pub struct ResolvedPattern {
94    units: PatternUnits,
95    content_units: PatternContentUnits,
96    vbox: Option<ViewBox>,
97    preserve_aspect_ratio: AspectRatio,
98    transform: TransformAttribute,
99    x: Length<Horizontal>,
100    y: Length<Vertical>,
101    width: ULength<Horizontal>,
102    height: ULength<Vertical>,
103    opacity: UnitInterval,
104
105    // Link to the node whose children are the pattern's resolved children.
106    children: Children,
107}
108
109/// Pattern normalized to user-space units.
110pub struct UserSpacePattern {
111    pub width: f64,
112    pub height: f64,
113    pub transform: Transform,
114    pub coord_transform: Transform,
115    pub content_transform: Transform,
116    pub opacity: UnitInterval,
117
118    // This one is private so the caller has to go through fn acquire_pattern_node()
119    node_with_children: Node,
120}
121
122#[derive(Default)]
123pub struct Pattern {
124    common: Common,
125    fallback: Option<NodeId>,
126}
127
128impl ElementTrait for Pattern {
129    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
130        for (attr, value) in attrs.iter() {
131            match attr.expanded() {
132                expanded_name!("", "patternUnits") => {
133                    set_attribute(&mut self.common.units, attr.parse(value), session)
134                }
135                expanded_name!("", "patternContentUnits") => {
136                    set_attribute(&mut self.common.content_units, attr.parse(value), session);
137                }
138                expanded_name!("", "viewBox") => {
139                    set_attribute(&mut self.common.vbox, attr.parse(value), session)
140                }
141                expanded_name!("", "preserveAspectRatio") => {
142                    set_attribute(
143                        &mut self.common.preserve_aspect_ratio,
144                        attr.parse(value),
145                        session,
146                    );
147                }
148                expanded_name!("", "patternTransform") => {
149                    set_attribute(&mut self.common.transform, attr.parse(value), session);
150                }
151                ref a if is_href(a) => {
152                    let mut href = None;
153                    set_attribute(
154                        &mut href,
155                        NodeId::parse(value).map(Some).attribute(attr.clone()),
156                        session,
157                    );
158                    set_href(a, &mut self.fallback, href);
159                }
160                expanded_name!("", "x") => {
161                    set_attribute(&mut self.common.x, attr.parse(value), session)
162                }
163                expanded_name!("", "y") => {
164                    set_attribute(&mut self.common.y, attr.parse(value), session)
165                }
166                expanded_name!("", "width") => {
167                    set_attribute(&mut self.common.width, attr.parse(value), session)
168                }
169                expanded_name!("", "height") => {
170                    set_attribute(&mut self.common.height, attr.parse(value), session)
171                }
172                _ => (),
173            }
174        }
175    }
176}
177
178impl UnresolvedPattern {
179    fn into_resolved(self, opacity: UnitInterval) -> ResolvedPattern {
180        assert!(self.is_resolved());
181
182        ResolvedPattern {
183            units: self.common.units.unwrap(),
184            content_units: self.common.content_units.unwrap(),
185            vbox: self.common.vbox.unwrap(),
186            preserve_aspect_ratio: self.common.preserve_aspect_ratio.unwrap(),
187            transform: self.common.transform.unwrap(),
188            x: self.common.x.unwrap(),
189            y: self.common.y.unwrap(),
190            width: self.common.width.unwrap(),
191            height: self.common.height.unwrap(),
192            opacity,
193
194            children: self.children.to_resolved(),
195        }
196    }
197
198    fn is_resolved(&self) -> bool {
199        self.common.units.is_some()
200            && self.common.content_units.is_some()
201            && self.common.vbox.is_some()
202            && self.common.preserve_aspect_ratio.is_some()
203            && self.common.transform.is_some()
204            && self.common.x.is_some()
205            && self.common.y.is_some()
206            && self.common.width.is_some()
207            && self.common.height.is_some()
208            && self.children.is_resolved()
209    }
210
211    fn resolve_from_fallback(&self, fallback: &UnresolvedPattern) -> UnresolvedPattern {
212        let units = self.common.units.or(fallback.common.units);
213        let content_units = self.common.content_units.or(fallback.common.content_units);
214        let vbox = self.common.vbox.or(fallback.common.vbox);
215        let preserve_aspect_ratio = self
216            .common
217            .preserve_aspect_ratio
218            .or(fallback.common.preserve_aspect_ratio);
219        let transform = self.common.transform.or(fallback.common.transform);
220        let x = self.common.x.or(fallback.common.x);
221        let y = self.common.y.or(fallback.common.y);
222        let width = self.common.width.or(fallback.common.width);
223        let height = self.common.height.or(fallback.common.height);
224        let children = self.children.resolve_from_fallback(&fallback.children);
225
226        UnresolvedPattern {
227            common: Common {
228                units,
229                content_units,
230                vbox,
231                preserve_aspect_ratio,
232                transform,
233                x,
234                y,
235                width,
236                height,
237            },
238            children,
239        }
240    }
241
242    fn resolve_from_defaults(&self) -> UnresolvedPattern {
243        let units = self.common.units.or_else(|| Some(PatternUnits::default()));
244        let content_units = self
245            .common
246            .content_units
247            .or_else(|| Some(PatternContentUnits::default()));
248        let vbox = self.common.vbox.or(Some(None));
249        let preserve_aspect_ratio = self
250            .common
251            .preserve_aspect_ratio
252            .or_else(|| Some(AspectRatio::default()));
253        let transform = self
254            .common
255            .transform
256            .or_else(|| Some(TransformAttribute::default()));
257        let x = self.common.x.or_else(|| Some(Default::default()));
258        let y = self.common.y.or_else(|| Some(Default::default()));
259        let width = self.common.width.or_else(|| Some(Default::default()));
260        let height = self.common.height.or_else(|| Some(Default::default()));
261        let children = self.children.resolve_from_defaults();
262
263        UnresolvedPattern {
264            common: Common {
265                units,
266                content_units,
267                vbox,
268                preserve_aspect_ratio,
269                transform,
270                x,
271                y,
272                width,
273                height,
274            },
275            children,
276        }
277    }
278}
279
280impl UnresolvedChildren {
281    fn from_node(node: &Node) -> UnresolvedChildren {
282        let weak = node.downgrade();
283
284        if node.children().any(|child| child.is_element()) {
285            UnresolvedChildren::WithChildren(weak)
286        } else {
287            UnresolvedChildren::Unresolved
288        }
289    }
290
291    fn is_resolved(&self) -> bool {
292        !matches!(*self, UnresolvedChildren::Unresolved)
293    }
294
295    fn resolve_from_fallback(&self, fallback: &UnresolvedChildren) -> UnresolvedChildren {
296        use UnresolvedChildren::*;
297
298        match (self, fallback) {
299            (&Unresolved, &Unresolved) => Unresolved,
300            (WithChildren(wc), _) => WithChildren(wc.clone()),
301            (_, WithChildren(wc)) => WithChildren(wc.clone()),
302            (_, _) => unreachable!(),
303        }
304    }
305
306    fn resolve_from_defaults(&self) -> UnresolvedChildren {
307        use UnresolvedChildren::*;
308
309        match *self {
310            Unresolved => ResolvedEmpty,
311            _ => (*self).clone(),
312        }
313    }
314
315    fn to_resolved(&self) -> Children {
316        use UnresolvedChildren::*;
317
318        assert!(self.is_resolved());
319
320        match *self {
321            ResolvedEmpty => Children::Empty,
322            WithChildren(ref wc) => Children::WithChildren(wc.clone()),
323            _ => unreachable!(),
324        }
325    }
326}
327
328fn nonempty_rect(bbox: &Option<Rect>) -> Option<Rect> {
329    match *bbox {
330        None => None,
331        Some(r) if r.is_empty() => None,
332        Some(r) => Some(r),
333    }
334}
335
336impl ResolvedPattern {
337    fn node_with_children(&self) -> Option<Node> {
338        match self.children {
339            // This means we didn't find any children among the fallbacks,
340            // so there is nothing to render.
341            Children::Empty => None,
342
343            Children::WithChildren(ref wc) => Some(wc.upgrade().unwrap()),
344        }
345    }
346
347    fn get_rect(&self, params: &NormalizeParams) -> Rect {
348        let x = self.x.to_user(params);
349        let y = self.y.to_user(params);
350        let w = self.width.to_user(params);
351        let h = self.height.to_user(params);
352
353        Rect::new(x, y, x + w, y + h)
354    }
355
356    pub fn to_user_space(
357        &self,
358        object_bbox: &Option<Rect>,
359        viewport: &Viewport,
360        values: &NormalizeValues,
361    ) -> Option<UserSpacePattern> {
362        let node_with_children = self.node_with_children()?;
363
364        let viewport = viewport.with_units(self.units.0);
365        let params = NormalizeParams::from_values(values, &viewport);
366
367        let rect = self.get_rect(&params);
368
369        // Create the pattern coordinate system
370        let (width, height, coord_transform) = match self.units {
371            PatternUnits(CoordUnits::ObjectBoundingBox) => {
372                let bbrect = nonempty_rect(object_bbox)?;
373                (
374                    rect.width() * bbrect.width(),
375                    rect.height() * bbrect.height(),
376                    Transform::new_translate(
377                        bbrect.x0 + rect.x0 * bbrect.width(),
378                        bbrect.y0 + rect.y0 * bbrect.height(),
379                    ),
380                )
381            }
382            PatternUnits(CoordUnits::UserSpaceOnUse) => (
383                rect.width(),
384                rect.height(),
385                Transform::new_translate(rect.x0, rect.y0),
386            ),
387        };
388
389        let pattern_transform = self.transform.to_transform();
390
391        let coord_transform = coord_transform.post_transform(&pattern_transform);
392
393        // Create the pattern contents coordinate system
394        let content_transform = if let Some(vbox) = self.vbox {
395            // If there is a vbox, use that
396            let r = self
397                .preserve_aspect_ratio
398                .compute(&vbox, &Rect::from_size(width, height));
399
400            let sw = r.width() / vbox.width();
401            let sh = r.height() / vbox.height();
402            let x = r.x0 - vbox.x0 * sw;
403            let y = r.y0 - vbox.y0 * sh;
404
405            Transform::new_scale(sw, sh).pre_translate(x, y)
406        } else {
407            match self.content_units {
408                PatternContentUnits(CoordUnits::ObjectBoundingBox) => {
409                    let bbrect = nonempty_rect(object_bbox)?;
410                    Transform::new_scale(bbrect.width(), bbrect.height())
411                }
412                PatternContentUnits(CoordUnits::UserSpaceOnUse) => Transform::identity(),
413            }
414        };
415
416        Some(UserSpacePattern {
417            width,
418            height,
419            transform: pattern_transform,
420            coord_transform,
421            content_transform,
422            opacity: self.opacity,
423            node_with_children,
424        })
425    }
426}
427
428impl UserSpacePattern {
429    /// Gets the `<pattern>` node that contains the children to be drawn for the pattern's contents.
430    ///
431    /// This has to go through [AcquiredNodes] to catch circular references among
432    /// patterns and their children.
433    pub fn acquire_pattern_node(
434        &self,
435        acquired_nodes: &mut AcquiredNodes<'_>,
436    ) -> Result<AcquiredNode, AcquireError> {
437        acquired_nodes.acquire_ref(&self.node_with_children)
438    }
439}
440
441impl Pattern {
442    fn get_unresolved(&self, node: &Node) -> Unresolved {
443        let pattern = UnresolvedPattern {
444            common: self.common.clone(),
445            children: UnresolvedChildren::from_node(node),
446        };
447
448        Unresolved {
449            pattern,
450            fallback: self.fallback.clone(),
451        }
452    }
453
454    pub fn resolve(
455        &self,
456        node: &Node,
457        acquired_nodes: &mut AcquiredNodes<'_>,
458        opacity: UnitInterval,
459        session: &Session,
460    ) -> Result<ResolvedPattern, AcquireError> {
461        let Unresolved {
462            mut pattern,
463            mut fallback,
464        } = self.get_unresolved(node);
465
466        let mut stack = NodeStack::new();
467
468        while !pattern.is_resolved() {
469            if let Some(ref node_id) = fallback {
470                match acquired_nodes.acquire(node_id) {
471                    Ok(acquired) => {
472                        let acquired_node = acquired.get();
473
474                        if stack.contains(acquired_node) {
475                            return Err(AcquireError::CircularReference(acquired_node.clone()));
476                        }
477
478                        match *acquired_node.borrow_element_data() {
479                            ElementData::Pattern(ref p) => {
480                                let unresolved = p.get_unresolved(acquired_node);
481                                pattern = pattern.resolve_from_fallback(&unresolved.pattern);
482                                fallback = unresolved.fallback;
483
484                                stack.push(acquired_node);
485                            }
486                            _ => return Err(AcquireError::InvalidLinkType(node_id.clone())),
487                        }
488                    }
489
490                    Err(AcquireError::MaxReferencesExceeded) => {
491                        return Err(AcquireError::MaxReferencesExceeded);
492                    }
493
494                    Err(e) => {
495                        rsvg_log!(session, "Stopping pattern resolution: {}", e);
496                        pattern = pattern.resolve_from_defaults();
497                        break;
498                    }
499                }
500            } else {
501                pattern = pattern.resolve_from_defaults();
502                break;
503            }
504        }
505
506        Ok(pattern.into_resolved(opacity))
507    }
508}
509
510#[cfg(test)]
511mod tests {
512    use super::*;
513
514    use markup5ever::{QualName, ns};
515
516    use crate::borrow_element_as;
517    use crate::node::NodeData;
518
519    #[test]
520    fn pattern_resolved_from_defaults_is_really_resolved() {
521        let node = Node::new(NodeData::new_element(
522            &Session::default(),
523            &QualName::new(None, ns!(svg), local_name!("pattern")),
524            Attributes::new(),
525        ));
526
527        let unresolved = borrow_element_as!(node, Pattern).get_unresolved(&node);
528        let pattern = unresolved.pattern.resolve_from_defaults();
529        assert!(pattern.is_resolved());
530    }
531}