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