rsvg/
marker.rs

1//! The `marker` element, and geometry computations for markers.
2
3use std::f64::consts::*;
4use std::ops::Deref;
5
6use cssparser::Parser;
7use markup5ever::{expanded_name, local_name, ns};
8
9use crate::angle::Angle;
10use crate::aspect_ratio::*;
11use crate::borrow_element_as;
12use crate::document::AcquiredNodes;
13use crate::drawing_ctx::{DrawingCtx, Viewport};
14use crate::element::{DrawResult, ElementTrait, set_attribute};
15use crate::error::*;
16use crate::float_eq_cairo::ApproxEqCairo;
17use crate::layout::{self, Shape, StackingContext};
18use crate::length::*;
19use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
20use crate::parse_identifiers;
21use crate::parsers::{Parse, ParseValue};
22use crate::path_builder::{ArcParameterization, CubicBezierCurve, Path, PathCommand, arc_segment};
23use crate::rect::Rect;
24use crate::rsvg_log;
25use crate::session::Session;
26use crate::transform::Transform;
27use crate::viewbox::*;
28use crate::xml::Attributes;
29
30// markerUnits attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
31#[derive(Debug, Default, Copy, Clone, PartialEq)]
32enum MarkerUnits {
33    UserSpaceOnUse,
34    #[default]
35    StrokeWidth,
36}
37
38impl Parse for MarkerUnits {
39    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerUnits, ParseError<'i>> {
40        Ok(parse_identifiers!(
41            parser,
42            "userSpaceOnUse" => MarkerUnits::UserSpaceOnUse,
43            "strokeWidth" => MarkerUnits::StrokeWidth,
44        )?)
45    }
46}
47
48// orient attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
49#[derive(Debug, Copy, Clone, PartialEq)]
50enum MarkerOrient {
51    Auto,
52    AutoStartReverse,
53    Angle(Angle),
54}
55
56impl Default for MarkerOrient {
57    #[inline]
58    fn default() -> MarkerOrient {
59        MarkerOrient::Angle(Angle::new(0.0))
60    }
61}
62
63impl Parse for MarkerOrient {
64    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerOrient, ParseError<'i>> {
65        if parser
66            .try_parse(|p| p.expect_ident_matching("auto"))
67            .is_ok()
68        {
69            return Ok(MarkerOrient::Auto);
70        }
71
72        if parser
73            .try_parse(|p| p.expect_ident_matching("auto-start-reverse"))
74            .is_ok()
75        {
76            Ok(MarkerOrient::AutoStartReverse)
77        } else {
78            Angle::parse(parser).map(MarkerOrient::Angle)
79        }
80    }
81}
82
83pub struct Marker {
84    units: MarkerUnits,
85    ref_x: Length<Horizontal>,
86    ref_y: Length<Vertical>,
87    width: ULength<Horizontal>,
88    height: ULength<Vertical>,
89    orient: MarkerOrient,
90    aspect: AspectRatio,
91    vbox: Option<ViewBox>,
92}
93
94// From SVG's marker-start, marker-mid, marker-end properties
95#[derive(Debug, Copy, Clone, PartialEq)]
96enum MarkerType {
97    Start,
98    Middle,
99    End,
100}
101
102/// Computed location and orientation for a marker.
103#[derive(Debug, PartialEq)]
104struct MarkerSpec {
105    marker_type: MarkerType,
106    x: f64,
107    y: f64,
108    angle: Angle,
109}
110
111impl Default for Marker {
112    fn default() -> Marker {
113        Marker {
114            units: MarkerUnits::default(),
115            ref_x: Default::default(),
116            ref_y: Default::default(),
117            // the following two are per the spec
118            width: ULength::<Horizontal>::parse_str("3").unwrap(),
119            height: ULength::<Vertical>::parse_str("3").unwrap(),
120            orient: MarkerOrient::default(),
121            aspect: AspectRatio::default(),
122            vbox: None,
123        }
124    }
125}
126
127impl Marker {
128    fn render(
129        &self,
130        node: &Node,
131        acquired_nodes: &mut AcquiredNodes<'_>,
132        viewport: &Viewport,
133        draw_ctx: &mut DrawingCtx,
134        spec: &MarkerSpec,
135        line_width: f64,
136        marker: &layout::Marker,
137    ) -> DrawResult {
138        let mut cascaded = CascadedValues::new_from_node(node);
139        cascaded.context_fill = Some(marker.context_fill.clone());
140        cascaded.context_stroke = Some(marker.context_stroke.clone());
141
142        let values = cascaded.get();
143
144        let params = NormalizeParams::new(values, viewport);
145
146        let marker_width = self.width.to_user(&params);
147        let marker_height = self.height.to_user(&params);
148
149        if marker_width.approx_eq_cairo(0.0) || marker_height.approx_eq_cairo(0.0) {
150            // markerWidth or markerHeight set to 0 disables rendering of the element
151            // https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute
152            return Ok(viewport.empty_bbox());
153        }
154
155        let rotation = match self.orient {
156            MarkerOrient::Auto => spec.angle,
157            MarkerOrient::AutoStartReverse => {
158                if spec.marker_type == MarkerType::Start {
159                    spec.angle.flip()
160                } else {
161                    spec.angle
162                }
163            }
164            MarkerOrient::Angle(a) => a,
165        };
166
167        let mut transform = Transform::new_translate(spec.x, spec.y).pre_rotate(rotation);
168
169        if self.units == MarkerUnits::StrokeWidth {
170            transform = transform.pre_scale(line_width, line_width);
171        }
172
173        let content_viewport = if let Some(vbox) = self.vbox {
174            if vbox.is_empty() {
175                return Ok(viewport.empty_bbox());
176            }
177
178            let r = self
179                .aspect
180                .compute(&vbox, &Rect::from_size(marker_width, marker_height));
181
182            let (vb_width, vb_height) = vbox.size();
183            transform = transform.pre_scale(r.width() / vb_width, r.height() / vb_height);
184
185            viewport.with_view_box(vb_width, vb_height)
186        } else {
187            viewport.with_view_box(marker_width, marker_height)
188        };
189
190        let content_params = NormalizeParams::new(values, &content_viewport);
191
192        transform = transform.pre_translate(
193            -self.ref_x.to_user(&content_params),
194            -self.ref_y.to_user(&content_params),
195        );
196
197        // FIXME: This is the only place in the code where we pass a Some(rect) to
198        // StackingContext::new() for its clip_rect argument.  The effect is to clip to
199        // the viewport that the current marker should establish, but this code for
200        // drawing markers does not yet use the layout_viewport argument in the call to
201        // with_discrete_layer() below and instead does things by hand.  We should do all
202        // this with the layout_viewport instead of clip_rect, and then we can remove the
203        // clip_rect field in StackingContext.
204        let clip_rect = if values.is_overflow() {
205            None
206        } else if let Some(vbox) = self.vbox {
207            Some(*vbox)
208        } else {
209            Some(Rect::from_size(marker_width, marker_height))
210        };
211
212        let elt = node.borrow_element();
213        let stacking_ctx = Box::new(StackingContext::new(
214            draw_ctx,
215            acquired_nodes,
216            &elt,
217            transform,
218            clip_rect,
219            values,
220            &content_viewport,
221        ));
222
223        draw_ctx.with_discrete_layer(
224            &stacking_ctx,
225            acquired_nodes,
226            &content_viewport,
227            None,
228            false,
229            &mut |an, dc, new_viewport| node.draw_children(an, &cascaded, new_viewport, dc), // content_viewport
230        )
231    }
232}
233
234impl ElementTrait for Marker {
235    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
236        for (attr, value) in attrs.iter() {
237            match attr.expanded() {
238                expanded_name!("", "markerUnits") => {
239                    set_attribute(&mut self.units, attr.parse(value), session)
240                }
241                expanded_name!("", "refX") => {
242                    set_attribute(&mut self.ref_x, attr.parse(value), session)
243                }
244                expanded_name!("", "refY") => {
245                    set_attribute(&mut self.ref_y, attr.parse(value), session)
246                }
247                expanded_name!("", "markerWidth") => {
248                    set_attribute(&mut self.width, attr.parse(value), session)
249                }
250                expanded_name!("", "markerHeight") => {
251                    set_attribute(&mut self.height, attr.parse(value), session)
252                }
253                expanded_name!("", "orient") => {
254                    set_attribute(&mut self.orient, attr.parse(value), session)
255                }
256                expanded_name!("", "preserveAspectRatio") => {
257                    set_attribute(&mut self.aspect, attr.parse(value), session)
258                }
259                expanded_name!("", "viewBox") => {
260                    set_attribute(&mut self.vbox, attr.parse(value), session)
261                }
262                _ => (),
263            }
264        }
265    }
266}
267
268// Machinery to figure out marker orientations
269#[derive(Debug, PartialEq)]
270enum Segment {
271    Degenerate {
272        // A single lone point
273        x: f64,
274        y: f64,
275    },
276
277    LineOrCurve {
278        x1: f64,
279        y1: f64,
280        x2: f64,
281        y2: f64,
282        x3: f64,
283        y3: f64,
284        x4: f64,
285        y4: f64,
286        is_subpath_start: bool,
287        is_subpath_end: bool,
288    },
289}
290
291impl Segment {
292    fn degenerate(x: f64, y: f64) -> Segment {
293        Segment::Degenerate { x, y }
294    }
295
296    fn curve(
297        x1: f64,
298        y1: f64,
299        x2: f64,
300        y2: f64,
301        x3: f64,
302        y3: f64,
303        x4: f64,
304        y4: f64,
305        is_subpath_start: bool,
306        is_subpath_end: bool,
307    ) -> Segment {
308        Segment::LineOrCurve {
309            x1,
310            y1,
311            x2,
312            y2,
313            x3,
314            y3,
315            x4,
316            y4,
317            is_subpath_start,
318            is_subpath_end,
319        }
320    }
321
322    fn line(
323        x1: f64,
324        y1: f64,
325        x2: f64,
326        y2: f64,
327        is_subpath_start: bool,
328        is_subpath_end: bool,
329    ) -> Segment {
330        Segment::curve(
331            x1,
332            y1,
333            x2,
334            y2,
335            x1,
336            y1,
337            x2,
338            y2,
339            is_subpath_start,
340            is_subpath_end,
341        )
342    }
343
344    // If the segment has directionality, returns two vectors (v1x, v1y, v2x, v2y); otherwise,
345    // returns None.  The vectors are the tangents at the beginning and at the end of the segment,
346    // respectively.  A segment does not have directionality if it is degenerate (i.e. a single
347    // point) or a zero-length segment, i.e. where all four control points are coincident (the first
348    // and last control points may coincide, but the others may define a loop - thus nonzero length)
349    fn get_directionalities(&self) -> Option<(f64, f64, f64, f64)> {
350        match *self {
351            Segment::Degenerate { .. } => None,
352
353            Segment::LineOrCurve {
354                x1,
355                y1,
356                x2,
357                y2,
358                x3,
359                y3,
360                x4,
361                y4,
362                ..
363            } => {
364                let coincide_1_and_2 = points_equal(x1, y1, x2, y2);
365                let coincide_1_and_3 = points_equal(x1, y1, x3, y3);
366                let coincide_1_and_4 = points_equal(x1, y1, x4, y4);
367                let coincide_2_and_3 = points_equal(x2, y2, x3, y3);
368                let coincide_2_and_4 = points_equal(x2, y2, x4, y4);
369                let coincide_3_and_4 = points_equal(x3, y3, x4, y4);
370
371                if coincide_1_and_2 && coincide_1_and_3 && coincide_1_and_4 {
372                    None
373                } else if coincide_1_and_2 && coincide_1_and_3 {
374                    Some((x4 - x1, y4 - y1, x4 - x3, y4 - y3))
375                } else if coincide_1_and_2 && coincide_3_and_4 {
376                    Some((x4 - x1, y4 - y1, x4 - x1, y4 - y1))
377                } else if coincide_2_and_3 && coincide_2_and_4 {
378                    Some((x2 - x1, y2 - y1, x4 - x1, y4 - y1))
379                } else if coincide_1_and_2 {
380                    Some((x3 - x1, y3 - y1, x4 - x3, y4 - y3))
381                } else if coincide_3_and_4 {
382                    Some((x2 - x1, y2 - y1, x4 - x2, y4 - y2))
383                } else {
384                    Some((x2 - x1, y2 - y1, x4 - x3, y4 - y3))
385                }
386            }
387        }
388    }
389}
390
391fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
392    x1.approx_eq_cairo(x2) && y1.approx_eq_cairo(y2)
393}
394
395enum SegmentState {
396    Initial,
397    NewSubpath,
398    InSubpath,
399    ClosedSubpath,
400}
401
402#[derive(Debug, PartialEq)]
403struct Segments(Vec<Segment>);
404
405impl Deref for Segments {
406    type Target = [Segment];
407
408    fn deref(&self) -> &[Segment] {
409        &self.0
410    }
411}
412
413// This converts a path builder into a vector of curveto-like segments.
414// Each segment can be:
415//
416// 1. Segment::Degenerate => the segment is actually a single point (x, y)
417//
418// 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective
419// lineto that results from a closepath).
420// We have the following points:
421//       P1 = (x1, y1)
422//       P2 = (x2, y2)
423//       P3 = (x3, y3)
424//       P4 = (x4, y4)
425//
426// The start and end points are P1 and P4, respectively.
427// The tangent at the start point is given by the vector (P2 - P1).
428// The tangent at the end point is given by the vector (P4 - P3).
429// The tangents also work if the segment refers to a lineto (they will
430// both just point in the same direction).
431impl From<&Path> for Segments {
432    fn from(path: &Path) -> Segments {
433        let mut last_x: f64;
434        let mut last_y: f64;
435
436        let mut cur_x: f64 = 0.0;
437        let mut cur_y: f64 = 0.0;
438        let mut subpath_start_x: f64 = 0.0;
439        let mut subpath_start_y: f64 = 0.0;
440
441        let mut segments = Vec::new();
442        let mut state = SegmentState::Initial;
443
444        let path_commands: Vec<PathCommand> = path.iter().collect();
445
446        for i in 0..path_commands.len() {
447            last_x = cur_x;
448            last_y = cur_y;
449
450            let path_command = &path_commands[i];
451
452            let is_subpath_start = matches!(state, SegmentState::NewSubpath);
453
454            let is_subpath_end = if i == path_commands.len() - 1 {
455                true
456            } else {
457                // If the next command is a MoveTo, it means that the next Segment will be disconnected
458                // from the current one.  So, make a note to emit a marker for the current segment's
459                // endpoint.
460                //
461                // Normally we only emit markers for the start point of a Segment, since normally
462                // Segments are connected to each other.  They are disconnected only if there are
463                // subpaths.
464
465                matches!(path_commands[i + 1], PathCommand::MoveTo(..))
466            };
467
468            match path_command {
469                &PathCommand::MoveTo(x, y) => {
470                    cur_x = x;
471                    cur_y = y;
472
473                    subpath_start_x = cur_x;
474                    subpath_start_y = cur_y;
475
476                    match state {
477                        SegmentState::Initial
478                        | SegmentState::InSubpath
479                        | SegmentState::ClosedSubpath => {
480                            // Ignore the very first moveto in a sequence (Initial state),
481                            // or if we were already drawing within a subpath, start
482                            // a new subpath.
483                            state = SegmentState::NewSubpath;
484                        }
485
486                        SegmentState::NewSubpath => {
487                            // We had just begun a new subpath (i.e. from a moveto) and we got
488                            // another moveto?  Output a stray point for the
489                            // previous moveto.
490                            segments.push(Segment::degenerate(last_x, last_y));
491                            state = SegmentState::NewSubpath;
492                        }
493                    }
494                }
495
496                &PathCommand::LineTo(x, y) => {
497                    cur_x = x;
498                    cur_y = y;
499
500                    segments.push(Segment::line(
501                        last_x,
502                        last_y,
503                        cur_x,
504                        cur_y,
505                        is_subpath_start,
506                        is_subpath_end,
507                    ));
508
509                    state = SegmentState::InSubpath;
510                }
511
512                PathCommand::CurveTo(curve) => {
513                    let CubicBezierCurve {
514                        pt1: (x2, y2),
515                        pt2: (x3, y3),
516                        to,
517                    } = *curve;
518                    cur_x = to.0;
519                    cur_y = to.1;
520
521                    segments.push(Segment::curve(
522                        last_x,
523                        last_y,
524                        x2,
525                        y2,
526                        x3,
527                        y3,
528                        cur_x,
529                        cur_y,
530                        is_subpath_start,
531                        is_subpath_end,
532                    ));
533
534                    state = SegmentState::InSubpath;
535                }
536
537                PathCommand::Arc(arc) => {
538                    cur_x = arc.to.0;
539                    cur_y = arc.to.1;
540
541                    match arc.center_parameterization() {
542                        ArcParameterization::CenterParameters {
543                            center,
544                            radii,
545                            theta1,
546                            delta_theta,
547                        } => {
548                            let rot = arc.x_axis_rotation;
549                            let theta2 = theta1 + delta_theta;
550                            let n_segs = (delta_theta / (PI * 0.5 + 0.001)).abs().ceil() as u32;
551                            let d_theta = delta_theta / f64::from(n_segs);
552
553                            let segment1 =
554                                arc_segment(center, radii, rot, theta1, theta1 + d_theta);
555                            let segment2 =
556                                arc_segment(center, radii, rot, theta2 - d_theta, theta2);
557
558                            let (x2, y2) = segment1.pt1;
559                            let (x3, y3) = segment2.pt2;
560
561                            segments.push(Segment::curve(
562                                last_x,
563                                last_y,
564                                x2,
565                                y2,
566                                x3,
567                                y3,
568                                cur_x,
569                                cur_y,
570                                is_subpath_start,
571                                is_subpath_end,
572                            ));
573
574                            state = SegmentState::InSubpath;
575                        }
576                        ArcParameterization::LineTo => {
577                            segments.push(Segment::line(
578                                last_x,
579                                last_y,
580                                cur_x,
581                                cur_y,
582                                is_subpath_start,
583                                is_subpath_end,
584                            ));
585
586                            state = SegmentState::InSubpath;
587                        }
588                        ArcParameterization::Omit => {}
589                    }
590                }
591
592                PathCommand::ClosePath => {
593                    cur_x = subpath_start_x;
594                    cur_y = subpath_start_y;
595
596                    segments.push(Segment::line(
597                        last_x,
598                        last_y,
599                        cur_x,
600                        cur_y,
601                        is_subpath_start,
602                        is_subpath_end,
603                    ));
604
605                    state = SegmentState::ClosedSubpath;
606                }
607            }
608        }
609
610        if let SegmentState::NewSubpath = state {
611            // Output a lone point if we started a subpath with a moveto
612            // command, but there are no subsequent commands.
613            segments.push(Segment::degenerate(cur_x, cur_y));
614        };
615
616        Segments(segments)
617    }
618}
619
620// The SVG spec 1.1 says http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes
621// Certain line-capping and line-joining situations and markers
622// require that a path segment have directionality at its start and
623// end points. Zero-length path segments have no directionality. In
624// these cases, the following algorithm is used to establish
625// directionality:  to determine the directionality of the start
626// point of a zero-length path segment, go backwards in the path
627// data specification within the current subpath until you find a
628// segment which has directionality at its end point (e.g., a path
629// segment with non-zero length) and use its ending direction;
630// otherwise, temporarily consider the start point to lack
631// directionality. Similarly, to determine the directionality of the
632// end point of a zero-length path segment, go forwards in the path
633// data specification within the current subpath until you find a
634// segment which has directionality at its start point (e.g., a path
635// segment with non-zero length) and use its starting direction;
636// otherwise, temporarily consider the end point to lack
637// directionality. If the start point has directionality but the end
638// point doesn't, then the end point uses the start point's
639// directionality. If the end point has directionality but the start
640// point doesn't, then the start point uses the end point's
641// directionality. Otherwise, set the directionality for the path
642// segment's start and end points to align with the positive x-axis
643// in user space.
644impl Segments {
645    fn find_incoming_angle_backwards(&self, start_index: usize) -> Option<Angle> {
646        // "go backwards ... within the current subpath until ... segment which has directionality
647        // at its end point"
648        for segment in self[..=start_index].iter().rev() {
649            match *segment {
650                Segment::Degenerate { .. } => {
651                    return None; // reached the beginning of the subpath as we ran into a standalone point
652                }
653
654                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
655                    Some((_, _, v2x, v2y)) => {
656                        return Some(Angle::from_vector(v2x, v2y));
657                    }
658                    None => {
659                        continue;
660                    }
661                },
662            }
663        }
664
665        None
666    }
667
668    fn find_outgoing_angle_forwards(&self, start_index: usize) -> Option<Angle> {
669        // "go forwards ... within the current subpath until ... segment which has directionality at
670        // its start point"
671        for segment in &self[start_index..] {
672            match *segment {
673                Segment::Degenerate { .. } => {
674                    return None; // reached the end of a subpath as we ran into a standalone point
675                }
676
677                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
678                    Some((v1x, v1y, _, _)) => {
679                        return Some(Angle::from_vector(v1x, v1y));
680                    }
681                    None => {
682                        continue;
683                    }
684                },
685            }
686        }
687
688        None
689    }
690}
691
692fn emit_marker_by_node(
693    viewport: &Viewport,
694    draw_ctx: &mut DrawingCtx,
695    acquired_nodes: &mut AcquiredNodes<'_>,
696    marker: &layout::Marker,
697    spec: &MarkerSpec,
698    line_width: f64,
699) -> DrawResult {
700    match acquired_nodes.acquire_ref(marker.node_ref.as_ref().unwrap()) {
701        Ok(acquired) => {
702            let node = acquired.get();
703
704            let marker_elt = borrow_element_as!(node, Marker);
705
706            marker_elt.render(
707                node,
708                acquired_nodes,
709                viewport,
710                draw_ctx,
711                spec,
712                line_width,
713                marker,
714            )
715        }
716
717        Err(e) => {
718            rsvg_log!(draw_ctx.session(), "could not acquire marker: {}", e);
719            Ok(viewport.empty_bbox())
720        }
721    }
722}
723
724#[derive(Debug, Copy, Clone, PartialEq)]
725enum MarkerEndpoint {
726    Start,
727    End,
728}
729
730fn emit_marker(
731    segment: &Segment,
732    endpoint: MarkerEndpoint,
733    marker_type: MarkerType,
734    orient: Angle,
735) -> MarkerSpec {
736    let (x, y) = match *segment {
737        Segment::Degenerate { x, y } => (x, y),
738
739        Segment::LineOrCurve { x1, y1, x4, y4, .. } => match endpoint {
740            MarkerEndpoint::Start => (x1, y1),
741            MarkerEndpoint::End => (x4, y4),
742        },
743    };
744
745    MarkerSpec {
746        marker_type,
747        x,
748        y,
749        angle: orient,
750    }
751}
752
753pub fn render_markers_for_shape(
754    shape: &Shape,
755    viewport: &Viewport,
756    draw_ctx: &mut DrawingCtx,
757    acquired_nodes: &mut AcquiredNodes<'_>,
758) -> DrawResult {
759    if shape.stroke.width.approx_eq_cairo(0.0) {
760        return Ok(viewport.empty_bbox());
761    }
762
763    if shape.marker_start.node_ref.is_none()
764        && shape.marker_mid.node_ref.is_none()
765        && shape.marker_end.node_ref.is_none()
766    {
767        return Ok(viewport.empty_bbox());
768    }
769
770    let specs = emit_markers_for_path(&shape.path.path);
771
772    for spec in specs {
773        let marker = match spec.marker_type {
774            MarkerType::Start => &shape.marker_start,
775            MarkerType::Middle => &shape.marker_mid,
776            MarkerType::End => &shape.marker_end,
777        };
778
779        if marker.node_ref.is_some() {
780            emit_marker_by_node(
781                viewport,
782                draw_ctx,
783                acquired_nodes,
784                marker,
785                &spec,
786                shape.stroke.width,
787            )?;
788        }
789    }
790
791    Ok(viewport.empty_bbox())
792}
793
794fn emit_markers_for_path(path: &Path) -> Vec<MarkerSpec> {
795    enum SubpathState {
796        NoSubpath,
797        InSubpath,
798    }
799
800    let mut specs = Vec::new();
801
802    // Convert the path to a list of segments and bare points
803    let segments = Segments::from(path);
804
805    let mut subpath_state = SubpathState::NoSubpath;
806
807    for (i, segment) in segments.iter().enumerate() {
808        match *segment {
809            Segment::Degenerate { .. } => {
810                if let SubpathState::InSubpath = subpath_state {
811                    assert!(i > 0);
812
813                    // Got a lone point after a subpath; render the subpath's end marker first
814                    let angle = segments
815                        .find_incoming_angle_backwards(i - 1)
816                        .unwrap_or_else(|| Angle::new(0.0));
817                    specs.push(emit_marker(
818                        &segments[i - 1],
819                        MarkerEndpoint::End,
820                        MarkerType::End,
821                        angle,
822                    ));
823                }
824
825                // Render marker for the lone point; no directionality
826                specs.push(emit_marker(
827                    segment,
828                    MarkerEndpoint::Start,
829                    MarkerType::Middle,
830                    Angle::new(0.0),
831                ));
832
833                subpath_state = SubpathState::NoSubpath;
834            }
835
836            Segment::LineOrCurve { is_subpath_end, .. } => {
837                // Not a degenerate segment
838                match subpath_state {
839                    SubpathState::NoSubpath => {
840                        let angle = segments
841                            .find_outgoing_angle_forwards(i)
842                            .unwrap_or_else(|| Angle::new(0.0));
843                        specs.push(emit_marker(
844                            segment,
845                            MarkerEndpoint::Start,
846                            MarkerType::Start,
847                            angle,
848                        ));
849
850                        subpath_state = SubpathState::InSubpath;
851                    }
852
853                    SubpathState::InSubpath => {
854                        assert!(i > 0);
855
856                        let incoming = segments.find_incoming_angle_backwards(i - 1);
857                        let outgoing = segments.find_outgoing_angle_forwards(i);
858
859                        let angle = match (incoming, outgoing) {
860                            (Some(incoming), Some(outgoing)) => incoming.bisect(outgoing),
861                            (Some(incoming), _) => incoming,
862                            (_, Some(outgoing)) => outgoing,
863                            _ => Angle::new(0.0),
864                        };
865
866                        specs.push(emit_marker(
867                            segment,
868                            MarkerEndpoint::Start,
869                            MarkerType::Middle,
870                            angle,
871                        ));
872
873                        if is_subpath_end && i + 1 < segments.len() {
874                            specs.push(emit_marker(
875                                segment,
876                                MarkerEndpoint::End,
877                                MarkerType::Middle,
878                                outgoing.unwrap_or_else(|| Angle::new(0.0)),
879                            ));
880                        }
881                    }
882                }
883            }
884        }
885    }
886
887    // Finally, render the last point
888    if !segments.is_empty() {
889        let segment = &segments[segments.len() - 1];
890        if let Segment::LineOrCurve { .. } = *segment {
891            let incoming = segments
892                .find_incoming_angle_backwards(segments.len() - 1)
893                .unwrap_or_else(|| Angle::new(0.0));
894
895            let angle = {
896                if let PathCommand::ClosePath = path.iter().nth(segments.len()).unwrap() {
897                    let outgoing = segments
898                        .find_outgoing_angle_forwards(0)
899                        .unwrap_or_else(|| Angle::new(0.0));
900                    incoming.bisect(outgoing)
901                } else {
902                    incoming
903                }
904            };
905
906            specs.push(emit_marker(
907                segment,
908                MarkerEndpoint::End,
909                MarkerType::End,
910                angle,
911            ));
912        }
913    }
914
915    specs
916}
917
918#[cfg(test)]
919mod parser_tests {
920    use super::*;
921
922    #[test]
923    fn parsing_invalid_marker_units_yields_error() {
924        assert!(MarkerUnits::parse_str("").is_err());
925        assert!(MarkerUnits::parse_str("foo").is_err());
926    }
927
928    #[test]
929    fn parses_marker_units() {
930        assert_eq!(
931            MarkerUnits::parse_str("userSpaceOnUse").unwrap(),
932            MarkerUnits::UserSpaceOnUse
933        );
934        assert_eq!(
935            MarkerUnits::parse_str("strokeWidth").unwrap(),
936            MarkerUnits::StrokeWidth
937        );
938    }
939
940    #[test]
941    fn parsing_invalid_marker_orient_yields_error() {
942        assert!(MarkerOrient::parse_str("").is_err());
943        assert!(MarkerOrient::parse_str("blah").is_err());
944        assert!(MarkerOrient::parse_str("45blah").is_err());
945    }
946
947    #[test]
948    fn parses_marker_orient() {
949        assert_eq!(MarkerOrient::parse_str("auto").unwrap(), MarkerOrient::Auto);
950        assert_eq!(
951            MarkerOrient::parse_str("auto-start-reverse").unwrap(),
952            MarkerOrient::AutoStartReverse
953        );
954
955        assert_eq!(
956            MarkerOrient::parse_str("0").unwrap(),
957            MarkerOrient::Angle(Angle::new(0.0))
958        );
959        assert_eq!(
960            MarkerOrient::parse_str("180").unwrap(),
961            MarkerOrient::Angle(Angle::from_degrees(180.0))
962        );
963        assert_eq!(
964            MarkerOrient::parse_str("180deg").unwrap(),
965            MarkerOrient::Angle(Angle::from_degrees(180.0))
966        );
967        assert_eq!(
968            MarkerOrient::parse_str("-400grad").unwrap(),
969            MarkerOrient::Angle(Angle::from_degrees(-360.0))
970        );
971        assert_eq!(
972            MarkerOrient::parse_str("1rad").unwrap(),
973            MarkerOrient::Angle(Angle::new(1.0))
974        );
975    }
976}
977
978#[cfg(test)]
979mod directionality_tests {
980    use pretty_assertions::assert_eq;
981
982    use super::*;
983    use crate::path_builder::PathBuilder;
984
985    // Single open path; the easy case
986    fn setup_open_path() -> Segments {
987        let mut builder = PathBuilder::default();
988
989        builder.move_to(10.0, 10.0);
990        builder.line_to(20.0, 10.0);
991        builder.line_to(20.0, 20.0);
992
993        Segments::from(&builder.into_path())
994    }
995
996    #[test]
997    fn path_to_segments_handles_open_path() {
998        let expected_segments: Segments = Segments(vec![
999            Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1000            Segment::line(20.0, 10.0, 20.0, 20.0, false, true),
1001        ]);
1002
1003        assert_eq!(setup_open_path(), expected_segments);
1004    }
1005
1006    fn setup_multiple_open_subpaths() -> Segments {
1007        let mut builder = PathBuilder::default();
1008
1009        builder.move_to(10.0, 10.0);
1010        builder.line_to(20.0, 10.0);
1011        builder.line_to(20.0, 20.0);
1012
1013        builder.move_to(30.0, 30.0);
1014        builder.line_to(40.0, 30.0);
1015        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1016        builder.line_to(80.0, 90.0);
1017
1018        Segments::from(&builder.into_path())
1019    }
1020
1021    #[test]
1022    fn path_to_segments_handles_multiple_open_subpaths() {
1023        let expected_segments: Segments = Segments(vec![
1024            Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1025            Segment::line(20.0, 10.0, 20.0, 20.0, false, true),
1026            Segment::line(30.0, 30.0, 40.0, 30.0, true, false),
1027            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0, false, false),
1028            Segment::line(70.0, 70.0, 80.0, 90.0, false, true),
1029        ]);
1030
1031        assert_eq!(setup_multiple_open_subpaths(), expected_segments);
1032    }
1033
1034    // Closed subpath; must have a line segment back to the first point
1035    fn setup_closed_subpath() -> Segments {
1036        let mut builder = PathBuilder::default();
1037
1038        builder.move_to(10.0, 10.0);
1039        builder.line_to(20.0, 10.0);
1040        builder.line_to(20.0, 20.0);
1041        builder.close_path();
1042
1043        Segments::from(&builder.into_path())
1044    }
1045
1046    #[test]
1047    fn path_to_segments_handles_closed_subpath() {
1048        let expected_segments: Segments = Segments(vec![
1049            Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1050            Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1051            Segment::line(20.0, 20.0, 10.0, 10.0, false, true),
1052        ]);
1053
1054        assert_eq!(setup_closed_subpath(), expected_segments);
1055    }
1056
1057    // Multiple closed subpaths; each must have a line segment back to their
1058    // initial points, with no degenerate segments between subpaths.
1059    fn setup_multiple_closed_subpaths() -> Segments {
1060        let mut builder = PathBuilder::default();
1061
1062        builder.move_to(10.0, 10.0);
1063        builder.line_to(20.0, 10.0);
1064        builder.line_to(20.0, 20.0);
1065        builder.close_path();
1066
1067        builder.move_to(30.0, 30.0);
1068        builder.line_to(40.0, 30.0);
1069        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1070        builder.line_to(80.0, 90.0);
1071        builder.close_path();
1072
1073        Segments::from(&builder.into_path())
1074    }
1075
1076    #[test]
1077    fn path_to_segments_handles_multiple_closed_subpaths() {
1078        let expected_segments: Segments = Segments(vec![
1079            Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1080            Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1081            Segment::line(20.0, 20.0, 10.0, 10.0, false, true),
1082            Segment::line(30.0, 30.0, 40.0, 30.0, true, false),
1083            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0, false, false),
1084            Segment::line(70.0, 70.0, 80.0, 90.0, false, false),
1085            Segment::line(80.0, 90.0, 30.0, 30.0, false, true),
1086        ]);
1087
1088        assert_eq!(setup_multiple_closed_subpaths(), expected_segments);
1089    }
1090
1091    // A lineto follows the first closed subpath, with no moveto to start the second subpath.
1092    // The lineto must start at the first point of the first subpath.
1093    fn setup_no_moveto_after_closepath() -> Segments {
1094        let mut builder = PathBuilder::default();
1095
1096        builder.move_to(10.0, 10.0);
1097        builder.line_to(20.0, 10.0);
1098        builder.line_to(20.0, 20.0);
1099        builder.close_path();
1100
1101        builder.line_to(40.0, 30.0);
1102
1103        Segments::from(&builder.into_path())
1104    }
1105
1106    #[test]
1107    fn path_to_segments_handles_no_moveto_after_closepath() {
1108        let expected_segments: Segments = Segments(vec![
1109            Segment::line(10.0, 10.0, 20.0, 10.0, true, false),
1110            Segment::line(20.0, 10.0, 20.0, 20.0, false, false),
1111            Segment::line(20.0, 20.0, 10.0, 10.0, false, false),
1112            Segment::line(10.0, 10.0, 40.0, 30.0, false, true),
1113        ]);
1114
1115        assert_eq!(setup_no_moveto_after_closepath(), expected_segments);
1116    }
1117
1118    // Sequence of moveto; should generate degenerate points.
1119    // This test is not enabled right now!  We create the
1120    // path fixtures with Cairo, and Cairo compresses
1121    // sequences of moveto into a single one.  So, we can't
1122    // really test this, as we don't get the fixture we want.
1123    //
1124    // Eventually we'll probably have to switch librsvg to
1125    // its own internal path representation which should
1126    // allow for unelided path commands, and which should
1127    // only build a cairo_path_t for the final rendering step.
1128    //
1129    // fn setup_sequence_of_moveto () -> Segments {
1130    // let mut builder = PathBuilder::default ();
1131    //
1132    // builder.move_to (10.0, 10.0);
1133    // builder.move_to (20.0, 20.0);
1134    // builder.move_to (30.0, 30.0);
1135    // builder.move_to (40.0, 40.0);
1136    //
1137    // Segments::from(&builder.into_path())
1138    // }
1139    //
1140    // #[test]
1141    // fn path_to_segments_handles_sequence_of_moveto () {
1142    // let expected_segments: Segments = Segments(vec! [
1143    // Segment::degenerate(10.0, 10.0),
1144    // Segment::degenerate(20.0, 20.0),
1145    // Segment::degenerate(30.0, 30.0),
1146    // Segment::degenerate(40.0, 40.0),
1147    // ]);
1148    //
1149    // assert_eq!(setup_sequence_of_moveto(), expected_segments);
1150    // }
1151
1152    #[test]
1153    fn degenerate_segment_has_no_directionality() {
1154        let s = Segment::degenerate(1.0, 2.0);
1155        assert!(s.get_directionalities().is_none());
1156    }
1157
1158    #[test]
1159    fn line_segment_has_directionality() {
1160        let s = Segment::line(1.0, 2.0, 3.0, 4.0, false, false);
1161        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1162        assert_eq!((2.0, 2.0), (v1x, v1y));
1163        assert_eq!((2.0, 2.0), (v2x, v2y));
1164    }
1165
1166    #[test]
1167    fn line_segment_with_coincident_ends_has_no_directionality() {
1168        let s = Segment::line(1.0, 2.0, 1.0, 2.0, false, false);
1169        assert!(s.get_directionalities().is_none());
1170    }
1171
1172    #[test]
1173    fn curve_has_directionality() {
1174        let s = Segment::curve(1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 20.0, 33.0, false, false);
1175        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1176        assert_eq!((2.0, 3.0), (v1x, v1y));
1177        assert_eq!((12.0, 20.0), (v2x, v2y));
1178    }
1179
1180    #[test]
1181    fn curves_with_loops_and_coincident_ends_have_directionality() {
1182        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.0, 2.0, false, false);
1183        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1184        assert_eq!((2.0, 2.0), (v1x, v1y));
1185        assert_eq!((-4.0, -4.0), (v2x, v2y));
1186
1187        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, false, false);
1188        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1189        assert_eq!((2.0, 2.0), (v1x, v1y));
1190        assert_eq!((-2.0, -2.0), (v2x, v2y));
1191
1192        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 1.0, 2.0, false, false);
1193        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1194        assert_eq!((2.0, 2.0), (v1x, v1y));
1195        assert_eq!((-2.0, -2.0), (v2x, v2y));
1196    }
1197
1198    #[test]
1199    fn curve_with_coincident_control_points_has_no_directionality() {
1200        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0, false, false);
1201        assert!(s.get_directionalities().is_none());
1202    }
1203
1204    #[test]
1205    fn curve_with_123_coincident_has_directionality() {
1206        let s = Segment::curve(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 40.0, false, false);
1207        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1208        assert_eq!((20.0, 40.0), (v1x, v1y));
1209        assert_eq!((20.0, 40.0), (v2x, v2y));
1210    }
1211
1212    #[test]
1213    fn curve_with_234_coincident_has_directionality() {
1214        let s = Segment::curve(20.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false);
1215        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1216        assert_eq!((-20.0, -40.0), (v1x, v1y));
1217        assert_eq!((-20.0, -40.0), (v2x, v2y));
1218    }
1219
1220    #[test]
1221    fn curve_with_12_34_coincident_has_directionality() {
1222        let s = Segment::curve(20.0, 40.0, 20.0, 40.0, 60.0, 70.0, 60.0, 70.0, false, false);
1223        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1224        assert_eq!((40.0, 30.0), (v1x, v1y));
1225        assert_eq!((40.0, 30.0), (v2x, v2y));
1226    }
1227}
1228
1229#[cfg(test)]
1230mod marker_tests {
1231    use pretty_assertions::assert_eq;
1232
1233    use super::*;
1234    use crate::path_builder::PathBuilder;
1235
1236    #[test]
1237    fn emits_for_open_subpath() {
1238        let mut builder = PathBuilder::default();
1239        builder.move_to(0.0, 0.0);
1240        builder.line_to(1.0, 0.0);
1241        builder.line_to(1.0, 1.0);
1242        builder.line_to(0.0, 1.0);
1243
1244        let v = emit_markers_for_path(&builder.into_path());
1245
1246        assert_eq!(
1247            v,
1248            vec![
1249                MarkerSpec {
1250                    marker_type: MarkerType::Start,
1251                    x: 0.0,
1252                    y: 0.0,
1253                    angle: Angle::new(0.0)
1254                },
1255                MarkerSpec {
1256                    marker_type: MarkerType::Middle,
1257                    x: 1.0,
1258                    y: 0.0,
1259                    angle: Angle::from_vector(1.0, 1.0)
1260                },
1261                MarkerSpec {
1262                    marker_type: MarkerType::Middle,
1263                    x: 1.0,
1264                    y: 1.0,
1265                    angle: Angle::from_vector(-1.0, 1.0)
1266                },
1267                MarkerSpec {
1268                    marker_type: MarkerType::End,
1269                    x: 0.0,
1270                    y: 1.0,
1271                    angle: Angle::from_vector(-1.0, 0.0)
1272                },
1273            ]
1274        );
1275    }
1276
1277    #[test]
1278    fn emits_for_closed_subpath() {
1279        let mut builder = PathBuilder::default();
1280        builder.move_to(0.0, 0.0);
1281        builder.line_to(1.0, 0.0);
1282        builder.line_to(1.0, 1.0);
1283        builder.line_to(0.0, 1.0);
1284        builder.close_path();
1285
1286        let v = emit_markers_for_path(&builder.into_path());
1287
1288        assert_eq!(
1289            v,
1290            vec![
1291                MarkerSpec {
1292                    marker_type: MarkerType::Start,
1293                    x: 0.0,
1294                    y: 0.0,
1295                    angle: Angle::new(0.0)
1296                },
1297                MarkerSpec {
1298                    marker_type: MarkerType::Middle,
1299                    x: 1.0,
1300                    y: 0.0,
1301                    angle: Angle::from_vector(1.0, 1.0)
1302                },
1303                MarkerSpec {
1304                    marker_type: MarkerType::Middle,
1305                    x: 1.0,
1306                    y: 1.0,
1307                    angle: Angle::from_vector(-1.0, 1.0)
1308                },
1309                MarkerSpec {
1310                    marker_type: MarkerType::Middle,
1311                    x: 0.0,
1312                    y: 1.0,
1313                    angle: Angle::from_vector(-1.0, -1.0)
1314                },
1315                MarkerSpec {
1316                    marker_type: MarkerType::End,
1317                    x: 0.0,
1318                    y: 0.0,
1319                    angle: Angle::from_vector(1.0, -1.0)
1320                },
1321            ]
1322        );
1323    }
1324
1325    #[test]
1326    fn bug_1218_missing_end_markers_in_subpaths() {
1327        let mut builder = PathBuilder::default();
1328        builder
1329            .parse("M 0,0 h 10 h 10 M 0,6 h 10 h 10 M 0,12 h 10 h 10")
1330            .unwrap();
1331        let path = builder.into_path();
1332
1333        let specs = emit_markers_for_path(&path);
1334
1335        assert_eq!(
1336            specs,
1337            vec![
1338                MarkerSpec {
1339                    marker_type: MarkerType::Start,
1340                    x: 0.0,
1341                    y: 0.0,
1342                    angle: Angle::new(0.0)
1343                },
1344                MarkerSpec {
1345                    marker_type: MarkerType::Middle,
1346                    x: 10.0,
1347                    y: 0.0,
1348                    angle: Angle::new(0.0)
1349                },
1350                MarkerSpec {
1351                    marker_type: MarkerType::Middle,
1352                    x: 20.0,
1353                    y: 0.0,
1354                    angle: Angle::new(0.0)
1355                },
1356                MarkerSpec {
1357                    marker_type: MarkerType::Middle,
1358                    x: 0.0,
1359                    y: 6.0,
1360                    angle: Angle::new(0.0)
1361                },
1362                MarkerSpec {
1363                    marker_type: MarkerType::Middle,
1364                    x: 10.0,
1365                    y: 6.0,
1366                    angle: Angle::new(0.0)
1367                },
1368                MarkerSpec {
1369                    marker_type: MarkerType::Middle,
1370                    x: 20.0,
1371                    y: 6.0,
1372                    angle: Angle::new(0.0)
1373                },
1374                MarkerSpec {
1375                    marker_type: MarkerType::Middle,
1376                    x: 0.0,
1377                    y: 12.0,
1378                    angle: Angle::new(0.0)
1379                },
1380                MarkerSpec {
1381                    marker_type: MarkerType::Middle,
1382                    x: 10.0,
1383                    y: 12.0,
1384                    angle: Angle::new(0.0)
1385                },
1386                MarkerSpec {
1387                    marker_type: MarkerType::End,
1388                    x: 20.0,
1389                    y: 12.0,
1390                    angle: Angle::new(0.0)
1391                }
1392            ]
1393        );
1394    }
1395
1396    #[test]
1397    fn does_not_duplicate_end_marker() {
1398        let mut builder = PathBuilder::default();
1399        builder.parse("M 0,0 h 10 v 10 h -10 Z").unwrap();
1400        let path = builder.into_path();
1401
1402        let specs = emit_markers_for_path(&path);
1403
1404        assert_eq!(
1405            specs,
1406            vec![
1407                MarkerSpec {
1408                    marker_type: MarkerType::Start,
1409                    x: 0.0,
1410                    y: 0.0,
1411                    angle: Angle::new(0.0)
1412                },
1413                MarkerSpec {
1414                    marker_type: MarkerType::Middle,
1415                    x: 10.0,
1416                    y: 0.0,
1417                    angle: Angle::from_degrees(45.0)
1418                },
1419                MarkerSpec {
1420                    marker_type: MarkerType::Middle,
1421                    x: 10.0,
1422                    y: 10.0,
1423                    angle: Angle::from_degrees(135.0)
1424                },
1425                MarkerSpec {
1426                    marker_type: MarkerType::Middle,
1427                    x: 0.0,
1428                    y: 10.0,
1429                    angle: Angle::from_degrees(225.0)
1430                },
1431                MarkerSpec {
1432                    marker_type: MarkerType::End,
1433                    x: 0.0,
1434                    y: 0.0,
1435                    angle: Angle::from_degrees(315.0)
1436                },
1437            ]
1438        );
1439    }
1440}