rsvg/
shapes.rs

1//! Basic SVG shapes: the `path`, `polygon`, `polyline`, `line`,
2//! `rect`, `circle`, `ellipse` elements.
3
4use cssparser::{Parser, Token};
5use markup5ever::{expanded_name, local_name, ns};
6use std::ops::Deref;
7use std::rc::Rc;
8
9use crate::bbox::BoundingBox;
10use crate::cairo_path::{validate_path, ValidatedPath};
11use crate::document::AcquiredNodes;
12use crate::drawing_ctx::{DrawingCtx, Viewport};
13use crate::element::{set_attribute, ElementTrait};
14use crate::error::*;
15use crate::iri::Iri;
16use crate::is_element_of_type;
17use crate::layout::{Layer, LayerKind, Marker, Shape, StackingContext, Stroke};
18use crate::length::*;
19use crate::node::{CascadedValues, Node, NodeBorrow};
20use crate::parsers::{optional_comma, Parse, ParseValue};
21use crate::path_builder::{LargeArc, Path as SvgPath, PathBuilder, Sweep};
22use crate::properties::ComputedValues;
23use crate::rsvg_log;
24use crate::session::Session;
25use crate::xml::Attributes;
26
27#[derive(PartialEq)]
28enum Markers {
29    No,
30    Yes,
31}
32
33struct ShapeDef {
34    path: Rc<SvgPath>,
35    markers: Markers,
36}
37
38impl ShapeDef {
39    fn new(path: Rc<SvgPath>, markers: Markers) -> ShapeDef {
40        ShapeDef { path, markers }
41    }
42}
43
44trait BasicShape {
45    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef;
46}
47
48fn draw_basic_shape(
49    basic_shape: &dyn BasicShape,
50    node: &Node,
51    acquired_nodes: &mut AcquiredNodes<'_>,
52    cascaded: &CascadedValues<'_>,
53    viewport: &Viewport,
54    session: &Session,
55) -> Result<Option<Layer>, InternalRenderingError> {
56    let values = cascaded.get();
57    let params = NormalizeParams::new(values, viewport);
58    let shape_def = basic_shape.make_shape(&params, values);
59
60    let stroke = Stroke::new(values, &params);
61    let path = match validate_path(&shape_def.path, &stroke, viewport)? {
62        ValidatedPath::Invalid(ref reason) => {
63            rsvg_log!(session, "will not render {node}: {reason}");
64            return Ok(None);
65        }
66
67        ValidatedPath::Validated(path) => path,
68    };
69
70    let paint_order = values.paint_order();
71
72    let stroke_paint = values.stroke().0.resolve(
73        acquired_nodes,
74        values.stroke_opacity().0,
75        values.color().0,
76        cascaded.context_fill.clone(),
77        cascaded.context_stroke.clone(),
78        session,
79    );
80
81    let fill_paint = values.fill().0.resolve(
82        acquired_nodes,
83        values.fill_opacity().0,
84        values.color().0,
85        cascaded.context_fill.clone(),
86        cascaded.context_stroke.clone(),
87        session,
88    );
89
90    let fill_rule = values.fill_rule();
91    let clip_rule = values.clip_rule();
92    let shape_rendering = values.shape_rendering();
93
94    let marker_start_node;
95    let marker_mid_node;
96    let marker_end_node;
97
98    if shape_def.markers == Markers::Yes {
99        marker_start_node = acquire_marker(session, acquired_nodes, &values.marker_start().0);
100        marker_mid_node = acquire_marker(session, acquired_nodes, &values.marker_mid().0);
101        marker_end_node = acquire_marker(session, acquired_nodes, &values.marker_end().0);
102    } else {
103        marker_start_node = None;
104        marker_mid_node = None;
105        marker_end_node = None;
106    }
107
108    let marker_start = Marker {
109        node_ref: marker_start_node,
110        context_stroke: stroke_paint.clone(),
111        context_fill: fill_paint.clone(),
112    };
113
114    let marker_mid = Marker {
115        node_ref: marker_mid_node,
116        context_stroke: stroke_paint.clone(),
117        context_fill: fill_paint.clone(),
118    };
119
120    let marker_end = Marker {
121        node_ref: marker_end_node,
122        context_stroke: stroke_paint.clone(),
123        context_fill: fill_paint.clone(),
124    };
125
126    let normalize_values = NormalizeValues::new(values);
127
128    let stroke_paint_source =
129        stroke_paint.to_user_space(&path.extents, viewport, &normalize_values);
130    let fill_paint_source = fill_paint.to_user_space(&path.extents, viewport, &normalize_values);
131
132    let shape = Box::new(Shape {
133        path,
134        paint_order,
135        stroke_paint: stroke_paint_source,
136        fill_paint: fill_paint_source,
137        stroke,
138        fill_rule,
139        clip_rule,
140        shape_rendering,
141        marker_start,
142        marker_mid,
143        marker_end,
144    });
145
146    let elt = node.borrow_element();
147    let stacking_ctx = StackingContext::new(
148        session,
149        acquired_nodes,
150        &elt,
151        values.transform(),
152        None,
153        values,
154    );
155
156    Ok(Some(Layer {
157        kind: LayerKind::Shape(shape),
158        stacking_ctx,
159    }))
160}
161
162macro_rules! impl_draw {
163    () => {
164        fn layout(
165            &self,
166            node: &Node,
167            acquired_nodes: &mut AcquiredNodes<'_>,
168            cascaded: &CascadedValues<'_>,
169            viewport: &Viewport,
170            draw_ctx: &mut DrawingCtx,
171            _clipping: bool,
172        ) -> Result<Option<Layer>, InternalRenderingError> {
173            draw_basic_shape(
174                self,
175                node,
176                acquired_nodes,
177                cascaded,
178                viewport,
179                &draw_ctx.session().clone(),
180            )
181        }
182
183        fn draw(
184            &self,
185            node: &Node,
186            acquired_nodes: &mut AcquiredNodes<'_>,
187            cascaded: &CascadedValues<'_>,
188            viewport: &Viewport,
189            draw_ctx: &mut DrawingCtx,
190            clipping: bool,
191        ) -> Result<BoundingBox, InternalRenderingError> {
192            self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)
193                .and_then(|layer| {
194                    if let Some(layer) = layer {
195                        draw_ctx.draw_layer(&layer, acquired_nodes, clipping, viewport)
196                    } else {
197                        Ok(viewport.empty_bbox())
198                    }
199                })
200        }
201    };
202}
203
204fn acquire_marker(
205    session: &Session,
206    acquired_nodes: &mut AcquiredNodes<'_>,
207    iri: &Iri,
208) -> Option<Node> {
209    iri.get().and_then(|id| {
210        acquired_nodes
211            .acquire(id)
212            .map_err(|e| {
213                rsvg_log!(session, "cannot render marker: {}", e);
214            })
215            .ok()
216            .and_then(|acquired| {
217                let node = acquired.get();
218
219                if is_element_of_type!(node, Marker) {
220                    Some(node.clone())
221                } else {
222                    rsvg_log!(session, "{} is not a marker element", id);
223                    None
224                }
225            })
226    })
227}
228
229fn make_ellipse(cx: f64, cy: f64, rx: f64, ry: f64) -> SvgPath {
230    let mut builder = PathBuilder::default();
231
232    // Per the spec, rx and ry must be nonnegative
233    if rx <= 0.0 || ry <= 0.0 {
234        return builder.into_path();
235    }
236
237    // 4/3 * (1-cos 45°)/sin 45° = 4/3 * sqrt(2) - 1
238    let arc_magic: f64 = 0.5522847498;
239
240    // approximate an ellipse using 4 Bézier curves
241
242    builder.move_to(cx + rx, cy);
243
244    builder.curve_to(
245        cx + rx,
246        cy + arc_magic * ry,
247        cx + arc_magic * rx,
248        cy + ry,
249        cx,
250        cy + ry,
251    );
252
253    builder.curve_to(
254        cx - arc_magic * rx,
255        cy + ry,
256        cx - rx,
257        cy + arc_magic * ry,
258        cx - rx,
259        cy,
260    );
261
262    builder.curve_to(
263        cx - rx,
264        cy - arc_magic * ry,
265        cx - arc_magic * rx,
266        cy - ry,
267        cx,
268        cy - ry,
269    );
270
271    builder.curve_to(
272        cx + arc_magic * rx,
273        cy - ry,
274        cx + rx,
275        cy - arc_magic * ry,
276        cx + rx,
277        cy,
278    );
279
280    builder.close_path();
281
282    builder.into_path()
283}
284
285#[derive(Default)]
286pub struct Path {
287    path: Rc<SvgPath>,
288}
289
290impl ElementTrait for Path {
291    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
292        for (attr, value) in attrs.iter() {
293            if attr.expanded() == expanded_name!("", "d") {
294                let mut builder = PathBuilder::default();
295                if let Err(e) = builder.parse(value) {
296                    // Creating a partial path is OK per the spec; we don't throw away the partial
297                    // result in case of an error.
298
299                    rsvg_log!(session, "could not parse path: {}", e);
300                }
301                self.path = Rc::new(builder.into_path());
302            }
303        }
304    }
305
306    impl_draw!();
307}
308
309impl BasicShape for Path {
310    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
311        ShapeDef::new(self.path.clone(), Markers::Yes)
312    }
313}
314
315/// List-of-points for polyline and polygon elements.
316///
317/// SVG1.1: <https://www.w3.org/TR/SVG/shapes.html#PointsBNF>
318///
319/// SVG2: <https://www.w3.org/TR/SVG/shapes.html#DataTypePoints>
320#[derive(Debug, Default, PartialEq)]
321struct Points(Vec<(f64, f64)>);
322
323impl Deref for Points {
324    type Target = [(f64, f64)];
325
326    fn deref(&self) -> &[(f64, f64)] {
327        &self.0
328    }
329}
330
331impl Parse for Points {
332    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Points, ParseError<'i>> {
333        let mut v = Vec::new();
334
335        loop {
336            let x = f64::parse(parser)?;
337            optional_comma(parser);
338            let y = f64::parse(parser)?;
339
340            v.push((x, y));
341
342            if parser.is_exhausted() {
343                break;
344            }
345
346            match parser.next_including_whitespace() {
347                Ok(&Token::WhiteSpace(_)) => (),
348                _ => optional_comma(parser),
349            }
350        }
351
352        Ok(Points(v))
353    }
354}
355
356fn make_poly(points: &Points, closed: bool) -> SvgPath {
357    let mut builder = PathBuilder::default();
358
359    for (i, &(x, y)) in points.iter().enumerate() {
360        if i == 0 {
361            builder.move_to(x, y);
362        } else {
363            builder.line_to(x, y);
364        }
365    }
366
367    if closed && !points.is_empty() {
368        builder.close_path();
369    }
370
371    builder.into_path()
372}
373
374#[derive(Default)]
375pub struct Polygon {
376    points: Points,
377}
378
379impl ElementTrait for Polygon {
380    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
381        for (attr, value) in attrs.iter() {
382            if attr.expanded() == expanded_name!("", "points") {
383                set_attribute(&mut self.points, attr.parse(value), session);
384            }
385        }
386    }
387
388    impl_draw!();
389}
390
391impl BasicShape for Polygon {
392    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
393        ShapeDef::new(Rc::new(make_poly(&self.points, true)), Markers::Yes)
394    }
395}
396
397#[derive(Default)]
398pub struct Polyline {
399    points: Points,
400}
401
402impl ElementTrait for Polyline {
403    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
404        for (attr, value) in attrs.iter() {
405            if attr.expanded() == expanded_name!("", "points") {
406                set_attribute(&mut self.points, attr.parse(value), session);
407            }
408        }
409    }
410
411    impl_draw!();
412}
413
414impl BasicShape for Polyline {
415    fn make_shape(&self, _params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
416        ShapeDef::new(Rc::new(make_poly(&self.points, false)), Markers::Yes)
417    }
418}
419
420#[derive(Default)]
421pub struct Line {
422    x1: Length<Horizontal>,
423    y1: Length<Vertical>,
424    x2: Length<Horizontal>,
425    y2: Length<Vertical>,
426}
427
428impl ElementTrait for Line {
429    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
430        for (attr, value) in attrs.iter() {
431            match attr.expanded() {
432                expanded_name!("", "x1") => set_attribute(&mut self.x1, attr.parse(value), session),
433                expanded_name!("", "y1") => set_attribute(&mut self.y1, attr.parse(value), session),
434                expanded_name!("", "x2") => set_attribute(&mut self.x2, attr.parse(value), session),
435                expanded_name!("", "y2") => set_attribute(&mut self.y2, attr.parse(value), session),
436                _ => (),
437            }
438        }
439    }
440
441    impl_draw!();
442}
443
444impl BasicShape for Line {
445    fn make_shape(&self, params: &NormalizeParams, _values: &ComputedValues) -> ShapeDef {
446        let mut builder = PathBuilder::default();
447
448        let x1 = self.x1.to_user(params);
449        let y1 = self.y1.to_user(params);
450        let x2 = self.x2.to_user(params);
451        let y2 = self.y2.to_user(params);
452
453        builder.move_to(x1, y1);
454        builder.line_to(x2, y2);
455
456        ShapeDef::new(Rc::new(builder.into_path()), Markers::Yes)
457    }
458}
459
460/// The `<rect>` element.
461///
462/// Note that its x/y/width/height/rx/ry are properties in SVG2, so they are
463/// defined as part of [the properties machinery](properties.rs).
464#[derive(Default)]
465pub struct Rect {}
466
467impl ElementTrait for Rect {
468    impl_draw!();
469}
470
471impl BasicShape for Rect {
472    #[allow(clippy::many_single_char_names)]
473    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
474        let x = values.x().0.to_user(params);
475        let y = values.y().0.to_user(params);
476
477        let w = match values.width().0 {
478            LengthOrAuto::Length(l) => l.to_user(params),
479            LengthOrAuto::Auto => 0.0,
480        };
481        let h = match values.height().0 {
482            LengthOrAuto::Length(l) => l.to_user(params),
483            LengthOrAuto::Auto => 0.0,
484        };
485
486        let norm_rx = match values.rx().0 {
487            LengthOrAuto::Length(l) => Some(l.to_user(params)),
488            LengthOrAuto::Auto => None,
489        };
490        let norm_ry = match values.ry().0 {
491            LengthOrAuto::Length(l) => Some(l.to_user(params)),
492            LengthOrAuto::Auto => None,
493        };
494
495        let mut rx;
496        let mut ry;
497
498        match (norm_rx, norm_ry) {
499            (None, None) => {
500                rx = 0.0;
501                ry = 0.0;
502            }
503
504            (Some(_rx), None) => {
505                rx = _rx;
506                ry = _rx;
507            }
508
509            (None, Some(_ry)) => {
510                rx = _ry;
511                ry = _ry;
512            }
513
514            (Some(_rx), Some(_ry)) => {
515                rx = _rx;
516                ry = _ry;
517            }
518        }
519
520        let mut builder = PathBuilder::default();
521
522        // Per the spec, w,h must be >= 0
523        if w <= 0.0 || h <= 0.0 {
524            return ShapeDef::new(Rc::new(builder.into_path()), Markers::No);
525        }
526
527        let half_w = w / 2.0;
528        let half_h = h / 2.0;
529
530        if rx > half_w {
531            rx = half_w;
532        }
533
534        if ry > half_h {
535            ry = half_h;
536        }
537
538        if rx == 0.0 {
539            ry = 0.0;
540        } else if ry == 0.0 {
541            rx = 0.0;
542        }
543
544        if rx == 0.0 {
545            // Easy case, no rounded corners
546            builder.move_to(x, y);
547            builder.line_to(x + w, y);
548            builder.line_to(x + w, y + h);
549            builder.line_to(x, y + h);
550            builder.line_to(x, y);
551        } else {
552            /* Hard case, rounded corners
553             *
554             *      (top_x1, top_y)                   (top_x2, top_y)
555             *     *--------------------------------*
556             *    /                                  \
557             *   * (left_x, left_y1)                  * (right_x, right_y1)
558             *   |                                    |
559             *   |                                    |
560             *   |                                    |
561             *   |                                    |
562             *   |                                    |
563             *   |                                    |
564             *   |                                    |
565             *   |                                    |
566             *   |                                    |
567             *   * (left_x, left_y2)                  * (right_x, right_y2)
568             *    \                                  /
569             *     *--------------------------------*
570             *      (bottom_x1, bottom_y)            (bottom_x2, bottom_y)
571             */
572
573            let top_x1 = x + rx;
574            let top_x2 = x + w - rx;
575            let top_y = y;
576
577            let bottom_x1 = top_x1;
578            let bottom_x2 = top_x2;
579            let bottom_y = y + h;
580
581            let left_x = x;
582            let left_y1 = y + ry;
583            let left_y2 = y + h - ry;
584
585            let right_x = x + w;
586            let right_y1 = left_y1;
587            let right_y2 = left_y2;
588
589            builder.move_to(top_x1, top_y);
590            builder.line_to(top_x2, top_y);
591
592            builder.arc(
593                top_x2,
594                top_y,
595                rx,
596                ry,
597                0.0,
598                LargeArc(false),
599                Sweep::Positive,
600                right_x,
601                right_y1,
602            );
603
604            builder.line_to(right_x, right_y2);
605
606            builder.arc(
607                right_x,
608                right_y2,
609                rx,
610                ry,
611                0.0,
612                LargeArc(false),
613                Sweep::Positive,
614                bottom_x2,
615                bottom_y,
616            );
617
618            builder.line_to(bottom_x1, bottom_y);
619
620            builder.arc(
621                bottom_x1,
622                bottom_y,
623                rx,
624                ry,
625                0.0,
626                LargeArc(false),
627                Sweep::Positive,
628                left_x,
629                left_y2,
630            );
631
632            builder.line_to(left_x, left_y1);
633
634            builder.arc(
635                left_x,
636                left_y1,
637                rx,
638                ry,
639                0.0,
640                LargeArc(false),
641                Sweep::Positive,
642                top_x1,
643                top_y,
644            );
645        }
646
647        builder.close_path();
648
649        ShapeDef::new(Rc::new(builder.into_path()), Markers::No)
650    }
651}
652
653/// The `<circle>` element.
654///
655/// Note that its cx/cy/r are properties in SVG2, so they are
656/// defined as part of [the properties machinery](properties.rs).
657#[derive(Default)]
658pub struct Circle {}
659
660impl ElementTrait for Circle {
661    impl_draw!();
662}
663
664impl BasicShape for Circle {
665    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
666        let cx = values.cx().0.to_user(params);
667        let cy = values.cy().0.to_user(params);
668        let r = values.r().0.to_user(params);
669
670        ShapeDef::new(Rc::new(make_ellipse(cx, cy, r, r)), Markers::No)
671    }
672}
673
674/// The `<ellipse>` element.
675///
676/// Note that its cx/cy/rx/ry are properties in SVG2, so they are
677/// defined as part of [the properties machinery](properties.rs).
678#[derive(Default)]
679pub struct Ellipse {}
680
681impl ElementTrait for Ellipse {
682    impl_draw!();
683}
684
685impl BasicShape for Ellipse {
686    fn make_shape(&self, params: &NormalizeParams, values: &ComputedValues) -> ShapeDef {
687        let cx = values.cx().0.to_user(params);
688        let cy = values.cy().0.to_user(params);
689        let norm_rx = match values.rx().0 {
690            LengthOrAuto::Length(l) => Some(l.to_user(params)),
691            LengthOrAuto::Auto => None,
692        };
693        let norm_ry = match values.ry().0 {
694            LengthOrAuto::Length(l) => Some(l.to_user(params)),
695            LengthOrAuto::Auto => None,
696        };
697
698        let rx;
699        let ry;
700
701        match (norm_rx, norm_ry) {
702            (None, None) => {
703                rx = 0.0;
704                ry = 0.0;
705            }
706
707            (Some(_rx), None) => {
708                rx = _rx;
709                ry = _rx;
710            }
711
712            (None, Some(_ry)) => {
713                rx = _ry;
714                ry = _ry;
715            }
716
717            (Some(_rx), Some(_ry)) => {
718                rx = _rx;
719                ry = _ry;
720            }
721        }
722
723        ShapeDef::new(Rc::new(make_ellipse(cx, cy, rx, ry)), Markers::No)
724    }
725}
726
727#[cfg(test)]
728mod tests {
729    use super::*;
730
731    #[test]
732    fn parses_points() {
733        assert_eq!(
734            Points::parse_str(" 1 2 ").unwrap(),
735            Points(vec![(1.0, 2.0)])
736        );
737        assert_eq!(
738            Points::parse_str("1 2 3 4").unwrap(),
739            Points(vec![(1.0, 2.0), (3.0, 4.0)])
740        );
741        assert_eq!(
742            Points::parse_str("1,2,3,4").unwrap(),
743            Points(vec![(1.0, 2.0), (3.0, 4.0)])
744        );
745        assert_eq!(
746            Points::parse_str("1,2 3,4").unwrap(),
747            Points(vec![(1.0, 2.0), (3.0, 4.0)])
748        );
749        assert_eq!(
750            Points::parse_str("1,2 -3,4").unwrap(),
751            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
752        );
753        assert_eq!(
754            Points::parse_str("1,2,-3,4").unwrap(),
755            Points(vec![(1.0, 2.0), (-3.0, 4.0)])
756        );
757    }
758
759    #[test]
760    fn errors_on_invalid_points() {
761        assert!(Points::parse_str("-1-2-3-4").is_err());
762        assert!(Points::parse_str("1 2-3,-4").is_err());
763    }
764}