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