1
//! The `marker` element, and geometry computations for markers.
2

            
3
use std::f64::consts::*;
4
use std::ops::Deref;
5

            
6
use cssparser::Parser;
7
use markup5ever::{expanded_name, local_name, namespace_url, ns};
8

            
9
use crate::angle::Angle;
10
use crate::aspect_ratio::*;
11
use crate::bbox::BoundingBox;
12
use crate::borrow_element_as;
13
use crate::document::AcquiredNodes;
14
use crate::drawing_ctx::{DrawingCtx, Viewport};
15
use crate::element::{set_attribute, ElementTrait};
16
use crate::error::*;
17
use crate::float_eq_cairo::ApproxEqCairo;
18
use crate::layout::{self, Shape, StackingContext};
19
use crate::length::*;
20
use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
21
use crate::parse_identifiers;
22
use crate::parsers::{Parse, ParseValue};
23
use crate::path_builder::{arc_segment, ArcParameterization, CubicBezierCurve, Path, PathCommand};
24
use crate::rect::Rect;
25
use crate::rsvg_log;
26
use crate::session::Session;
27
use crate::transform::Transform;
28
use crate::viewbox::*;
29
use crate::xml::Attributes;
30

            
31
// markerUnits attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
32
388
#[derive(Debug, Default, Copy, Clone, PartialEq)]
33
enum MarkerUnits {
34
    UserSpaceOnUse,
35
    #[default]
36
96
    StrokeWidth,
37
}
38

            
39
impl Parse for MarkerUnits {
40
31
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerUnits, ParseError<'i>> {
41
30
        Ok(parse_identifiers!(
42
            parser,
43
            "userSpaceOnUse" => MarkerUnits::UserSpaceOnUse,
44
            "strokeWidth" => MarkerUnits::StrokeWidth,
45
1
        )?)
46
31
    }
47
}
48

            
49
// orient attribute: https://www.w3.org/TR/SVG/painting.html#MarkerElement
50
12
#[derive(Debug, Copy, Clone, PartialEq)]
51
enum MarkerOrient {
52
    Auto,
53
    AutoStartReverse,
54
22
    Angle(Angle),
55
}
56

            
57
impl Default for MarkerOrient {
58
    #[inline]
59
96
    fn default() -> MarkerOrient {
60
96
        MarkerOrient::Angle(Angle::new(0.0))
61
96
    }
62
}
63

            
64
impl Parse for MarkerOrient {
65
80
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MarkerOrient, ParseError<'i>> {
66
80
        if parser
67
80
            .try_parse(|p| p.expect_ident_matching("auto"))
68
80
            .is_ok()
69
        {
70
58
            return Ok(MarkerOrient::Auto);
71
        }
72

            
73
22
        if parser
74
22
            .try_parse(|p| p.expect_ident_matching("auto-start-reverse"))
75
22
            .is_ok()
76
        {
77
2
            Ok(MarkerOrient::AutoStartReverse)
78
        } else {
79
20
            Angle::parse(parser).map(MarkerOrient::Angle)
80
        }
81
80
    }
82
}
83

            
84
pub struct Marker {
85
    units: MarkerUnits,
86
    ref_x: Length<Horizontal>,
87
    ref_y: Length<Vertical>,
88
    width: ULength<Horizontal>,
89
    height: ULength<Vertical>,
90
    orient: MarkerOrient,
91
    aspect: AspectRatio,
92
    vbox: Option<ViewBox>,
93
}
94

            
95
impl Default for Marker {
96
96
    fn default() -> Marker {
97
96
        Marker {
98
96
            units: MarkerUnits::default(),
99
96
            ref_x: Default::default(),
100
96
            ref_y: Default::default(),
101
            // the following two are per the spec
102
96
            width: ULength::<Horizontal>::parse_str("3").unwrap(),
103
96
            height: ULength::<Vertical>::parse_str("3").unwrap(),
104
96
            orient: MarkerOrient::default(),
105
96
            aspect: AspectRatio::default(),
106
96
            vbox: None,
107
        }
108
96
    }
109
}
110

            
111
impl Marker {
112
194
    fn render(
113
        &self,
114
        node: &Node,
115
        acquired_nodes: &mut AcquiredNodes<'_>,
116
        viewport: &Viewport,
117
        draw_ctx: &mut DrawingCtx,
118
        xpos: f64,
119
        ypos: f64,
120
        computed_angle: Angle,
121
        line_width: f64,
122
        clipping: bool,
123
        marker_type: MarkerType,
124
        marker: &layout::Marker,
125
    ) -> Result<BoundingBox, InternalRenderingError> {
126
194
        let mut cascaded = CascadedValues::new_from_node(node);
127
194
        cascaded.context_fill = Some(marker.context_fill.clone());
128
194
        cascaded.context_stroke = Some(marker.context_stroke.clone());
129

            
130
194
        let values = cascaded.get();
131

            
132
194
        let params = NormalizeParams::new(values, viewport);
133

            
134
194
        let marker_width = self.width.to_user(&params);
135
194
        let marker_height = self.height.to_user(&params);
136

            
137
194
        if marker_width.approx_eq_cairo(0.0) || marker_height.approx_eq_cairo(0.0) {
138
            // markerWidth or markerHeight set to 0 disables rendering of the element
139
            // https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute
140
            return Ok(draw_ctx.empty_bbox());
141
        }
142

            
143
194
        let rotation = match self.orient {
144
33
            MarkerOrient::Auto => computed_angle,
145
            MarkerOrient::AutoStartReverse => {
146
2
                if marker_type == MarkerType::Start {
147
1
                    computed_angle.flip()
148
                } else {
149
1
                    computed_angle
150
                }
151
            }
152
159
            MarkerOrient::Angle(a) => a,
153
        };
154

            
155
194
        let mut transform = Transform::new_translate(xpos, ypos).pre_rotate(rotation);
156

            
157
369
        if self.units == MarkerUnits::StrokeWidth {
158
175
            transform = transform.pre_scale(line_width, line_width);
159
        }
160

            
161
194
        let content_viewport = if let Some(vbox) = self.vbox {
162
155
            if vbox.is_empty() {
163
1
                return Ok(draw_ctx.empty_bbox());
164
            }
165

            
166
154
            let r = self
167
                .aspect
168
154
                .compute(&vbox, &Rect::from_size(marker_width, marker_height));
169

            
170
154
            let (vb_width, vb_height) = vbox.size();
171
154
            transform = transform.pre_scale(r.width() / vb_width, r.height() / vb_height);
172

            
173
154
            viewport.with_view_box(vb_width, vb_height)
174
        } else {
175
39
            viewport.with_view_box(marker_width, marker_height)
176
        };
177

            
178
193
        let content_params = NormalizeParams::new(values, &content_viewport);
179

            
180
193
        transform = transform.pre_translate(
181
193
            -self.ref_x.to_user(&content_params),
182
193
            -self.ref_y.to_user(&content_params),
183
        );
184

            
185
        // FIXME: This is the only place in the code where we pass a Some(rect) to
186
        // StackingContext::new() for its clip_rect argument.  The effect is to clip to
187
        // the viewport that the current marker should establish, but this code for
188
        // drawing markers does not yet use the viewports machinery and instead does
189
        // things by hand.  We should encode the information about the overflow property
190
        // in the viewport, so it knows whether to clip or not.
191
193
        let clip_rect = if values.is_overflow() {
192
9
            None
193
220
        } else if let Some(vbox) = self.vbox {
194
148
            Some(*vbox)
195
        } else {
196
36
            Some(Rect::from_size(marker_width, marker_height))
197
        };
198

            
199
193
        let elt = node.borrow_element();
200
193
        let stacking_ctx = Box::new(StackingContext::new(
201
193
            draw_ctx.session(),
202
            acquired_nodes,
203
193
            &elt,
204
193
            transform,
205
193
            clip_rect,
206
            values,
207
        ));
208

            
209
193
        draw_ctx.with_discrete_layer(
210
193
            &stacking_ctx,
211
            acquired_nodes,
212
            &content_viewport,
213
193
            None,
214
            clipping,
215
386
            &mut |an, dc, new_viewport| {
216
193
                node.draw_children(an, &cascaded, new_viewport, dc, clipping)
217
193
            }, // content_viewport
218
        )
219
194
    }
220
}
221

            
222
impl ElementTrait for Marker {
223
96
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
224
757
        for (attr, value) in attrs.iter() {
225
663
            match attr.expanded() {
226
                expanded_name!("", "markerUnits") => {
227
27
                    set_attribute(&mut self.units, attr.parse(value), session)
228
                }
229
                expanded_name!("", "refX") => {
230
87
                    set_attribute(&mut self.ref_x, attr.parse(value), session)
231
31
                }
232
                expanded_name!("", "refY") => {
233
117
                    set_attribute(&mut self.ref_y, attr.parse(value), session)
234
30
                }
235
                expanded_name!("", "markerWidth") => {
236
45
                    set_attribute(&mut self.width, attr.parse(value), session)
237
                }
238
                expanded_name!("", "markerHeight") => {
239
44
                    set_attribute(&mut self.height, attr.parse(value), session)
240
                }
241
                expanded_name!("", "orient") => {
242
69
                    set_attribute(&mut self.orient, attr.parse(value), session)
243
                }
244
                expanded_name!("", "preserveAspectRatio") => {
245
                    set_attribute(&mut self.aspect, attr.parse(value), session)
246
                }
247
                expanded_name!("", "viewBox") => {
248
34
                    set_attribute(&mut self.vbox, attr.parse(value), session)
249
                }
250
                _ => (),
251
            }
252
661
        }
253
94
    }
254
}
255

            
256
// Machinery to figure out marker orientations
257
42
#[derive(Debug, PartialEq)]
258
enum Segment {
259
    Degenerate {
260
        // A single lone point
261
        x: f64,
262
        y: f64,
263
    },
264

            
265
    LineOrCurve {
266
63
        x1: f64,
267
42
        y1: f64,
268
63
        x2: f64,
269
63
        y2: f64,
270
63
        x3: f64,
271
63
        y3: f64,
272
63
        x4: f64,
273
63
        y4: f64,
274
    },
275
}
276

            
277
impl Segment {
278
1
    fn degenerate(x: f64, y: f64) -> Segment {
279
1
        Segment::Degenerate { x, y }
280
1
    }
281

            
282
225
    fn curve(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
283
225
        Segment::LineOrCurve {
284
            x1,
285
            y1,
286
            x2,
287
            y2,
288
            x3,
289
            y3,
290
            x4,
291
            y4,
292
        }
293
225
    }
294

            
295
192
    fn line(x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
296
192
        Segment::curve(x1, y1, x2, y2, x1, y1, x2, y2)
297
192
    }
298

            
299
    // If the segment has directionality, returns two vectors (v1x, v1y, v2x, v2y); otherwise,
300
    // returns None.  The vectors are the tangents at the beginning and at the end of the segment,
301
    // respectively.  A segment does not have directionality if it is degenerate (i.e. a single
302
    // point) or a zero-length segment, i.e. where all four control points are coincident (the first
303
    // and last control points may coincide, but the others may define a loop - thus nonzero length)
304
384
    fn get_directionalities(&self) -> Option<(f64, f64, f64, f64)> {
305
384
        match *self {
306
1
            Segment::Degenerate { .. } => None,
307

            
308
            Segment::LineOrCurve {
309
383
                x1,
310
383
                y1,
311
383
                x2,
312
383
                y2,
313
383
                x3,
314
383
                y3,
315
383
                x4,
316
383
                y4,
317
            } => {
318
383
                let coincide_1_and_2 = points_equal(x1, y1, x2, y2);
319
383
                let coincide_1_and_3 = points_equal(x1, y1, x3, y3);
320
383
                let coincide_1_and_4 = points_equal(x1, y1, x4, y4);
321
383
                let coincide_2_and_3 = points_equal(x2, y2, x3, y3);
322
383
                let coincide_2_and_4 = points_equal(x2, y2, x4, y4);
323
383
                let coincide_3_and_4 = points_equal(x3, y3, x4, y4);
324

            
325
383
                if coincide_1_and_2 && coincide_1_and_3 && coincide_1_and_4 {
326
20
                    None
327
363
                } else if coincide_1_and_2 && coincide_1_and_3 {
328
3
                    Some((x4 - x1, y4 - y1, x4 - x3, y4 - y3))
329
360
                } else if coincide_1_and_2 && coincide_3_and_4 {
330
3
                    Some((x4 - x1, y4 - y1, x4 - x1, y4 - y1))
331
357
                } else if coincide_2_and_3 && coincide_2_and_4 {
332
3
                    Some((x2 - x1, y2 - y1, x4 - x1, y4 - y1))
333
354
                } else if coincide_1_and_2 {
334
3
                    Some((x3 - x1, y3 - y1, x4 - x3, y4 - y3))
335
351
                } else if coincide_3_and_4 {
336
3
                    Some((x2 - x1, y2 - y1, x4 - x2, y4 - y2))
337
                } else {
338
348
                    Some((x2 - x1, y2 - y1, x4 - x3, y4 - y3))
339
                }
340
            }
341
        }
342
384
    }
343
}
344

            
345
2298
fn points_equal(x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
346
2298
    x1.approx_eq_cairo(x2) && y1.approx_eq_cairo(y2)
347
2298
}
348

            
349
enum SegmentState {
350
    Initial,
351
    NewSubpath,
352
    InSubpath,
353
    ClosedSubpath,
354
}
355

            
356
10
#[derive(Debug, PartialEq)]
357
5
struct Segments(Vec<Segment>);
358

            
359
impl Deref for Segments {
360
    type Target = [Segment];
361

            
362
743
    fn deref(&self) -> &[Segment] {
363
743
        &self.0
364
743
    }
365
}
366

            
367
// This converts a path builder into a vector of curveto-like segments.
368
// Each segment can be:
369
//
370
// 1. Segment::Degenerate => the segment is actually a single point (x, y)
371
//
372
// 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective
373
// lineto that results from a closepath).
374
// We have the following points:
375
//       P1 = (x1, y1)
376
//       P2 = (x2, y2)
377
//       P3 = (x3, y3)
378
//       P4 = (x4, y4)
379
//
380
// The start and end points are P1 and P4, respectively.
381
// The tangent at the start point is given by the vector (P2 - P1).
382
// The tangent at the end point is given by the vector (P4 - P3).
383
// The tangents also work if the segment refers to a lineto (they will
384
// both just point in the same direction).
385
impl From<&Path> for Segments {
386
69
    fn from(path: &Path) -> Segments {
387
        let mut last_x: f64;
388
        let mut last_y: f64;
389

            
390
69
        let mut cur_x: f64 = 0.0;
391
69
        let mut cur_y: f64 = 0.0;
392
69
        let mut subpath_start_x: f64 = 0.0;
393
69
        let mut subpath_start_y: f64 = 0.0;
394

            
395
69
        let mut segments = Vec::new();
396
69
        let mut state = SegmentState::Initial;
397

            
398
340
        for path_command in path.iter() {
399
271
            last_x = cur_x;
400
271
            last_y = cur_y;
401

            
402
271
            match path_command {
403
77
                PathCommand::MoveTo(x, y) => {
404
77
                    cur_x = x;
405
77
                    cur_y = y;
406

            
407
77
                    subpath_start_x = cur_x;
408
77
                    subpath_start_y = cur_y;
409

            
410
77
                    match state {
411
70
                        SegmentState::Initial | SegmentState::InSubpath => {
412
                            // Ignore the very first moveto in a sequence (Initial state),
413
                            // or if we were already drawing within a subpath, start
414
                            // a new subpath.
415
70
                            state = SegmentState::NewSubpath;
416
                        }
417

            
418
                        SegmentState::NewSubpath => {
419
                            // We had just begun a new subpath (i.e. from a moveto) and we got
420
                            // another moveto?  Output a stray point for the
421
                            // previous moveto.
422
                            segments.push(Segment::degenerate(last_x, last_y));
423
                            state = SegmentState::NewSubpath;
424
                        }
425

            
426
7
                        SegmentState::ClosedSubpath => {
427
                            // Cairo outputs a moveto after every closepath, so that subsequent
428
                            // lineto/curveto commands will start at the closed vertex.
429
                            // We don't want to actually emit a point (a degenerate segment) in
430
                            // that artificial-moveto case.
431
                            //
432
                            // We'll reset to the Initial state so that a subsequent "real"
433
                            // moveto will be handled as the beginning of a new subpath, or a
434
                            // degenerate point, as usual.
435
7
                            state = SegmentState::Initial;
436
                        }
437
                    }
438
                }
439

            
440
145
                PathCommand::LineTo(x, y) => {
441
145
                    cur_x = x;
442
145
                    cur_y = y;
443

            
444
145
                    segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
445

            
446
145
                    state = SegmentState::InSubpath;
447
145
                }
448

            
449
19
                PathCommand::CurveTo(curve) => {
450
                    let CubicBezierCurve {
451
19
                        pt1: (x2, y2),
452
19
                        pt2: (x3, y3),
453
19
                        to,
454
                    } = curve;
455
19
                    cur_x = to.0;
456
19
                    cur_y = to.1;
457

            
458
19
                    segments.push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
459

            
460
19
                    state = SegmentState::InSubpath;
461
19
                }
462

            
463
4
                PathCommand::Arc(arc) => {
464
4
                    cur_x = arc.to.0;
465
4
                    cur_y = arc.to.1;
466

            
467
4
                    match arc.center_parameterization() {
468
                        ArcParameterization::CenterParameters {
469
4
                            center,
470
4
                            radii,
471
4
                            theta1,
472
4
                            delta_theta,
473
                        } => {
474
4
                            let rot = arc.x_axis_rotation;
475
4
                            let theta2 = theta1 + delta_theta;
476
4
                            let n_segs = (delta_theta / (PI * 0.5 + 0.001)).abs().ceil() as u32;
477
4
                            let d_theta = delta_theta / f64::from(n_segs);
478

            
479
                            let segment1 =
480
4
                                arc_segment(center, radii, rot, theta1, theta1 + d_theta);
481
                            let segment2 =
482
4
                                arc_segment(center, radii, rot, theta2 - d_theta, theta2);
483

            
484
4
                            let (x2, y2) = segment1.pt1;
485
4
                            let (x3, y3) = segment2.pt2;
486
4
                            segments
487
4
                                .push(Segment::curve(last_x, last_y, x2, y2, x3, y3, cur_x, cur_y));
488

            
489
4
                            state = SegmentState::InSubpath;
490
4
                        }
491
                        ArcParameterization::LineTo => {
492
                            segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
493

            
494
                            state = SegmentState::InSubpath;
495
                        }
496
                        ArcParameterization::Omit => {}
497
                    }
498
                }
499

            
500
26
                PathCommand::ClosePath => {
501
26
                    cur_x = subpath_start_x;
502
26
                    cur_y = subpath_start_y;
503

            
504
26
                    segments.push(Segment::line(last_x, last_y, cur_x, cur_y));
505

            
506
26
                    state = SegmentState::ClosedSubpath;
507
                }
508
            }
509
        }
510

            
511
69
        if let SegmentState::NewSubpath = state {
512
            // Output a lone point if we started a subpath with a moveto
513
            // command, but there are no subsequent commands.
514
            segments.push(Segment::degenerate(cur_x, cur_y));
515
        };
516

            
517
69
        Segments(segments)
518
69
    }
519
}
520

            
521
// The SVG spec 1.1 says http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes
522
// Certain line-capping and line-joining situations and markers
523
// require that a path segment have directionality at its start and
524
// end points. Zero-length path segments have no directionality. In
525
// these cases, the following algorithm is used to establish
526
// directionality:  to determine the directionality of the start
527
// point of a zero-length path segment, go backwards in the path
528
// data specification within the current subpath until you find a
529
// segment which has directionality at its end point (e.g., a path
530
// segment with non-zero length) and use its ending direction;
531
// otherwise, temporarily consider the start point to lack
532
// directionality. Similarly, to determine the directionality of the
533
// end point of a zero-length path segment, go forwards in the path
534
// data specification within the current subpath until you find a
535
// segment which has directionality at its start point (e.g., a path
536
// segment with non-zero length) and use its starting direction;
537
// otherwise, temporarily consider the end point to lack
538
// directionality. If the start point has directionality but the end
539
// point doesn't, then the end point uses the start point's
540
// directionality. If the end point has directionality but the start
541
// point doesn't, then the start point uses the end point's
542
// directionality. Otherwise, set the directionality for the path
543
// segment's start and end points to align with the positive x-axis
544
// in user space.
545
impl Segments {
546
173
    fn find_incoming_angle_backwards(&self, start_index: usize) -> Option<Angle> {
547
        // "go backwards ... within the current subpath until ... segment which has directionality
548
        // at its end point"
549
182
        for segment in self[..=start_index].iter().rev() {
550
181
            match *segment {
551
                Segment::Degenerate { .. } => {
552
                    return None; // reached the beginning of the subpath as we ran into a standalone point
553
                }
554

            
555
181
                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
556
172
                    Some((_, _, v2x, v2y)) => {
557
172
                        return Some(Angle::from_vector(v2x, v2y));
558
                    }
559
                    None => {
560
                        continue;
561
                    }
562
                },
563
            }
564
        }
565

            
566
1
        None
567
173
    }
568

            
569
186
    fn find_outgoing_angle_forwards(&self, start_index: usize) -> Option<Angle> {
570
        // "go forwards ... within the current subpath until ... segment which has directionality at
571
        // its start point"
572
195
        for segment in &self[start_index..] {
573
192
            match *segment {
574
                Segment::Degenerate { .. } => {
575
                    return None; // reached the end of a subpath as we ran into a standalone point
576
                }
577

            
578
192
                Segment::LineOrCurve { .. } => match segment.get_directionalities() {
579
183
                    Some((v1x, v1y, _, _)) => {
580
183
                        return Some(Angle::from_vector(v1x, v1y));
581
                    }
582
                    None => {
583
                        continue;
584
                    }
585
                },
586
            }
587
        }
588

            
589
3
        None
590
186
    }
591
}
592

            
593
// From SVG's marker-start, marker-mid, marker-end properties
594
11
#[derive(Debug, Copy, Clone, PartialEq)]
595
enum MarkerType {
596
    Start,
597
    Middle,
598
    End,
599
}
600

            
601
194
fn emit_marker_by_node(
602
    viewport: &Viewport,
603
    draw_ctx: &mut DrawingCtx,
604
    acquired_nodes: &mut AcquiredNodes<'_>,
605
    marker: &layout::Marker,
606
    xpos: f64,
607
    ypos: f64,
608
    computed_angle: Angle,
609
    line_width: f64,
610
    clipping: bool,
611
    marker_type: MarkerType,
612
) -> Result<BoundingBox, InternalRenderingError> {
613
194
    match acquired_nodes.acquire_ref(marker.node_ref.as_ref().unwrap()) {
614
194
        Ok(acquired) => {
615
194
            let node = acquired.get();
616

            
617
            let marker_elt = borrow_element_as!(node, Marker);
618

            
619
194
            marker_elt.render(
620
                node,
621
                acquired_nodes,
622
                viewport,
623
                draw_ctx,
624
                xpos,
625
                ypos,
626
                computed_angle,
627
                line_width,
628
                clipping,
629
                marker_type,
630
                marker,
631
            )
632
194
        }
633

            
634
        Err(e) => {
635
            rsvg_log!(draw_ctx.session(), "could not acquire marker: {}", e);
636
            Ok(draw_ctx.empty_bbox())
637
        }
638
    }
639
194
}
640

            
641
#[derive(Debug, Copy, Clone, PartialEq)]
642
enum MarkerEndpoint {
643
    Start,
644
    End,
645
}
646

            
647
237
fn emit_marker<E>(
648
    segment: &Segment,
649
    endpoint: MarkerEndpoint,
650
    marker_type: MarkerType,
651
    orient: Angle,
652
    emit_fn: &mut E,
653
) -> Result<BoundingBox, InternalRenderingError>
654
where
655
    E: FnMut(MarkerType, f64, f64, Angle) -> Result<BoundingBox, InternalRenderingError>,
656
{
657
474
    let (x, y) = match *segment {
658
        Segment::Degenerate { x, y } => (x, y),
659

            
660
237
        Segment::LineOrCurve { x1, y1, x4, y4, .. } => match endpoint {
661
173
            MarkerEndpoint::Start => (x1, y1),
662
64
            MarkerEndpoint::End => (x4, y4),
663
        },
664
    };
665

            
666
237
    emit_fn(marker_type, x, y, orient)
667
237
}
668

            
669
948601
pub fn render_markers_for_shape(
670
    shape: &Shape,
671
    viewport: &Viewport,
672
    draw_ctx: &mut DrawingCtx,
673
    acquired_nodes: &mut AcquiredNodes<'_>,
674
    clipping: bool,
675
) -> Result<BoundingBox, InternalRenderingError> {
676
948601
    let path = match &shape.path {
677
        layout::Path::Validated {
678
948601
            path,
679
            extents: Some(_),
680
            ..
681
        } => path,
682
        layout::Path::Validated { extents: None, .. } => return Ok(draw_ctx.empty_bbox()),
683
        layout::Path::Invalid(_) => return Ok(draw_ctx.empty_bbox()),
684
    };
685

            
686
948601
    if shape.stroke.width.approx_eq_cairo(0.0) {
687
19
        return Ok(draw_ctx.empty_bbox());
688
    }
689

            
690
948582
    if shape.marker_start.node_ref.is_none()
691
948367
        && shape.marker_mid.node_ref.is_none()
692
948344
        && shape.marker_end.node_ref.is_none()
693
    {
694
948090
        return Ok(draw_ctx.empty_bbox());
695
    }
696

            
697
62
    emit_markers_for_path(
698
62
        path,
699
62
        draw_ctx.empty_bbox(),
700
290
        &mut |marker_type: MarkerType, x: f64, y: f64, computed_angle: Angle| {
701
228
            let marker = match marker_type {
702
62
                MarkerType::Start => &shape.marker_start,
703
104
                MarkerType::Middle => &shape.marker_mid,
704
62
                MarkerType::End => &shape.marker_end,
705
            };
706

            
707
228
            if marker.node_ref.is_some() {
708
194
                emit_marker_by_node(
709
194
                    viewport,
710
194
                    draw_ctx,
711
194
                    acquired_nodes,
712
194
                    marker,
713
                    x,
714
                    y,
715
                    computed_angle,
716
194
                    shape.stroke.width,
717
194
                    clipping,
718
                    marker_type,
719
                )
720
            } else {
721
34
                Ok(draw_ctx.empty_bbox())
722
            }
723
228
        },
724
    )
725
948171
}
726

            
727
64
fn emit_markers_for_path<E>(
728
    path: &Path,
729
    empty_bbox: BoundingBox,
730
    emit_fn: &mut E,
731
) -> Result<BoundingBox, InternalRenderingError>
732
where
733
    E: FnMut(MarkerType, f64, f64, Angle) -> Result<BoundingBox, InternalRenderingError>,
734
{
735
    enum SubpathState {
736
        NoSubpath,
737
        InSubpath,
738
    }
739

            
740
64
    let mut bbox = empty_bbox;
741

            
742
    // Convert the path to a list of segments and bare points
743
64
    let segments = Segments::from(path);
744

            
745
64
    let mut subpath_state = SubpathState::NoSubpath;
746

            
747
237
    for (i, segment) in segments.iter().enumerate() {
748
173
        match *segment {
749
            Segment::Degenerate { .. } => {
750
                if let SubpathState::InSubpath = subpath_state {
751
                    assert!(i > 0);
752

            
753
                    // Got a lone point after a subpath; render the subpath's end marker first
754
                    let angle = segments
755
                        .find_incoming_angle_backwards(i - 1)
756
                        .unwrap_or_else(|| Angle::new(0.0));
757
                    let marker_bbox = emit_marker(
758
                        &segments[i - 1],
759
                        MarkerEndpoint::End,
760
                        MarkerType::End,
761
                        angle,
762
                        emit_fn,
763
                    )?;
764
                    bbox.insert(&marker_bbox);
765
                }
766

            
767
                // Render marker for the lone point; no directionality
768
                let marker_bbox = emit_marker(
769
                    segment,
770
                    MarkerEndpoint::Start,
771
                    MarkerType::Middle,
772
                    Angle::new(0.0),
773
                    emit_fn,
774
                )?;
775
                bbox.insert(&marker_bbox);
776

            
777
                subpath_state = SubpathState::NoSubpath;
778
            }
779

            
780
            Segment::LineOrCurve { .. } => {
781
                // Not a degenerate segment
782
173
                match subpath_state {
783
                    SubpathState::NoSubpath => {
784
64
                        let angle = segments
785
                            .find_outgoing_angle_forwards(i)
786
1
                            .unwrap_or_else(|| Angle::new(0.0));
787
64
                        let marker_bbox = emit_marker(
788
                            segment,
789
64
                            MarkerEndpoint::Start,
790
64
                            MarkerType::Start,
791
                            angle,
792
                            emit_fn,
793
                        )?;
794
64
                        bbox.insert(&marker_bbox);
795

            
796
64
                        subpath_state = SubpathState::InSubpath;
797
64
                    }
798

            
799
                    SubpathState::InSubpath => {
800
109
                        assert!(i > 0);
801

            
802
109
                        let incoming = segments.find_incoming_angle_backwards(i - 1);
803
109
                        let outgoing = segments.find_outgoing_angle_forwards(i);
804

            
805
109
                        let angle = match (incoming, outgoing) {
806
107
                            (Some(incoming), Some(outgoing)) => incoming.bisect(outgoing),
807
2
                            (Some(incoming), _) => incoming,
808
                            (_, Some(outgoing)) => outgoing,
809
                            _ => Angle::new(0.0),
810
                        };
811

            
812
109
                        let marker_bbox = emit_marker(
813
                            segment,
814
109
                            MarkerEndpoint::Start,
815
109
                            MarkerType::Middle,
816
109
                            angle,
817
                            emit_fn,
818
                        )?;
819
109
                        bbox.insert(&marker_bbox);
820
                    }
821
                }
822
            }
823
        }
824
    }
825

            
826
    // Finally, render the last point
827
64
    if !segments.is_empty() {
828
64
        let segment = &segments[segments.len() - 1];
829
64
        if let Segment::LineOrCurve { .. } = *segment {
830
64
            let incoming = segments
831
64
                .find_incoming_angle_backwards(segments.len() - 1)
832
1
                .unwrap_or_else(|| Angle::new(0.0));
833

            
834
            let angle = {
835
64
                if let PathCommand::ClosePath = path.iter().nth(segments.len()).unwrap() {
836
13
                    let outgoing = segments
837
                        .find_outgoing_angle_forwards(0)
838
                        .unwrap_or_else(|| Angle::new(0.0));
839
13
                    incoming.bisect(outgoing)
840
                } else {
841
51
                    incoming
842
                }
843
            };
844

            
845
64
            let marker_bbox = emit_marker(
846
                segment,
847
64
                MarkerEndpoint::End,
848
64
                MarkerType::End,
849
64
                angle,
850
                emit_fn,
851
            )?;
852
64
            bbox.insert(&marker_bbox);
853
        }
854
    }
855

            
856
64
    Ok(bbox)
857
64
}
858

            
859
#[cfg(test)]
860
mod parser_tests {
861
    use super::*;
862

            
863
    #[test]
864
2
    fn parsing_invalid_marker_units_yields_error() {
865
1
        assert!(MarkerUnits::parse_str("").is_err());
866
1
        assert!(MarkerUnits::parse_str("foo").is_err());
867
2
    }
868

            
869
    #[test]
870
2
    fn parses_marker_units() {
871
1
        assert_eq!(
872
1
            MarkerUnits::parse_str("userSpaceOnUse").unwrap(),
873
            MarkerUnits::UserSpaceOnUse
874
        );
875
1
        assert_eq!(
876
1
            MarkerUnits::parse_str("strokeWidth").unwrap(),
877
            MarkerUnits::StrokeWidth
878
        );
879
2
    }
880

            
881
    #[test]
882
2
    fn parsing_invalid_marker_orient_yields_error() {
883
1
        assert!(MarkerOrient::parse_str("").is_err());
884
1
        assert!(MarkerOrient::parse_str("blah").is_err());
885
1
        assert!(MarkerOrient::parse_str("45blah").is_err());
886
2
    }
887

            
888
    #[test]
889
2
    fn parses_marker_orient() {
890
1
        assert_eq!(MarkerOrient::parse_str("auto").unwrap(), MarkerOrient::Auto);
891
1
        assert_eq!(
892
1
            MarkerOrient::parse_str("auto-start-reverse").unwrap(),
893
            MarkerOrient::AutoStartReverse
894
        );
895

            
896
1
        assert_eq!(
897
1
            MarkerOrient::parse_str("0").unwrap(),
898
1
            MarkerOrient::Angle(Angle::new(0.0))
899
        );
900
1
        assert_eq!(
901
1
            MarkerOrient::parse_str("180").unwrap(),
902
1
            MarkerOrient::Angle(Angle::from_degrees(180.0))
903
        );
904
1
        assert_eq!(
905
1
            MarkerOrient::parse_str("180deg").unwrap(),
906
1
            MarkerOrient::Angle(Angle::from_degrees(180.0))
907
        );
908
1
        assert_eq!(
909
1
            MarkerOrient::parse_str("-400grad").unwrap(),
910
1
            MarkerOrient::Angle(Angle::from_degrees(-360.0))
911
        );
912
1
        assert_eq!(
913
1
            MarkerOrient::parse_str("1rad").unwrap(),
914
1
            MarkerOrient::Angle(Angle::new(1.0))
915
        );
916
2
    }
917
}
918

            
919
#[cfg(test)]
920
mod directionality_tests {
921
    use super::*;
922
    use crate::path_builder::PathBuilder;
923

            
924
    // Single open path; the easy case
925
1
    fn setup_open_path() -> Segments {
926
1
        let mut builder = PathBuilder::default();
927

            
928
1
        builder.move_to(10.0, 10.0);
929
1
        builder.line_to(20.0, 10.0);
930
1
        builder.line_to(20.0, 20.0);
931

            
932
1
        Segments::from(&builder.into_path())
933
1
    }
934

            
935
    #[test]
936
2
    fn path_to_segments_handles_open_path() {
937
2
        let expected_segments: Segments = Segments(vec![
938
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
939
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
940
        ]);
941

            
942
2
        assert_eq!(setup_open_path(), expected_segments);
943
2
    }
944

            
945
1
    fn setup_multiple_open_subpaths() -> Segments {
946
1
        let mut builder = PathBuilder::default();
947

            
948
1
        builder.move_to(10.0, 10.0);
949
1
        builder.line_to(20.0, 10.0);
950
1
        builder.line_to(20.0, 20.0);
951

            
952
1
        builder.move_to(30.0, 30.0);
953
1
        builder.line_to(40.0, 30.0);
954
1
        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
955
1
        builder.line_to(80.0, 90.0);
956

            
957
1
        Segments::from(&builder.into_path())
958
1
    }
959

            
960
    #[test]
961
2
    fn path_to_segments_handles_multiple_open_subpaths() {
962
2
        let expected_segments: Segments = Segments(vec![
963
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
964
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
965
1
            Segment::line(30.0, 30.0, 40.0, 30.0),
966
1
            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
967
1
            Segment::line(70.0, 70.0, 80.0, 90.0),
968
        ]);
969

            
970
2
        assert_eq!(setup_multiple_open_subpaths(), expected_segments);
971
2
    }
972

            
973
    // Closed subpath; must have a line segment back to the first point
974
1
    fn setup_closed_subpath() -> Segments {
975
1
        let mut builder = PathBuilder::default();
976

            
977
1
        builder.move_to(10.0, 10.0);
978
1
        builder.line_to(20.0, 10.0);
979
1
        builder.line_to(20.0, 20.0);
980
1
        builder.close_path();
981

            
982
1
        Segments::from(&builder.into_path())
983
1
    }
984

            
985
    #[test]
986
2
    fn path_to_segments_handles_closed_subpath() {
987
2
        let expected_segments: Segments = Segments(vec![
988
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
989
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
990
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
991
        ]);
992

            
993
2
        assert_eq!(setup_closed_subpath(), expected_segments);
994
2
    }
995

            
996
    // Multiple closed subpaths; each must have a line segment back to their
997
    // initial points, with no degenerate segments between subpaths.
998
1
    fn setup_multiple_closed_subpaths() -> Segments {
999
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(10.0, 10.0);
1
        builder.line_to(20.0, 10.0);
1
        builder.line_to(20.0, 20.0);
1
        builder.close_path();
1
        builder.move_to(30.0, 30.0);
1
        builder.line_to(40.0, 30.0);
1
        builder.curve_to(50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
1
        builder.line_to(80.0, 90.0);
1
        builder.close_path();
1
        Segments::from(&builder.into_path())
1
    }
    #[test]
2
    fn path_to_segments_handles_multiple_closed_subpaths() {
2
        let expected_segments: Segments = Segments(vec![
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
1
            Segment::line(30.0, 30.0, 40.0, 30.0),
1
            Segment::curve(40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
1
            Segment::line(70.0, 70.0, 80.0, 90.0),
1
            Segment::line(80.0, 90.0, 30.0, 30.0),
        ]);
2
        assert_eq!(setup_multiple_closed_subpaths(), expected_segments);
2
    }
    // A lineto follows the first closed subpath, with no moveto to start the second subpath.
    // The lineto must start at the first point of the first subpath.
1
    fn setup_no_moveto_after_closepath() -> Segments {
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(10.0, 10.0);
1
        builder.line_to(20.0, 10.0);
1
        builder.line_to(20.0, 20.0);
1
        builder.close_path();
1
        builder.line_to(40.0, 30.0);
1
        Segments::from(&builder.into_path())
1
    }
    #[test]
2
    fn path_to_segments_handles_no_moveto_after_closepath() {
2
        let expected_segments: Segments = Segments(vec![
1
            Segment::line(10.0, 10.0, 20.0, 10.0),
1
            Segment::line(20.0, 10.0, 20.0, 20.0),
1
            Segment::line(20.0, 20.0, 10.0, 10.0),
1
            Segment::line(10.0, 10.0, 40.0, 30.0),
        ]);
2
        assert_eq!(setup_no_moveto_after_closepath(), expected_segments);
2
    }
    // Sequence of moveto; should generate degenerate points.
    // This test is not enabled right now!  We create the
    // path fixtures with Cairo, and Cairo compresses
    // sequences of moveto into a single one.  So, we can't
    // really test this, as we don't get the fixture we want.
    //
    // Eventually we'll probably have to switch librsvg to
    // its own internal path representation which should
    // allow for unelided path commands, and which should
    // only build a cairo_path_t for the final rendering step.
    //
    // fn setup_sequence_of_moveto () -> Segments {
    // let mut builder = PathBuilder::default ();
    //
    // builder.move_to (10.0, 10.0);
    // builder.move_to (20.0, 20.0);
    // builder.move_to (30.0, 30.0);
    // builder.move_to (40.0, 40.0);
    //
    // Segments::from(&builder.into_path())
    // }
    //
    // #[test]
    // fn path_to_segments_handles_sequence_of_moveto () {
    // let expected_segments: Segments = Segments(vec! [
    // Segment::degenerate(10.0, 10.0),
    // Segment::degenerate(20.0, 20.0),
    // Segment::degenerate(30.0, 30.0),
    // Segment::degenerate(40.0, 40.0),
    // ]);
    //
    // assert_eq!(setup_sequence_of_moveto(), expected_segments);
    // }
    #[test]
2
    fn degenerate_segment_has_no_directionality() {
1
        let s = Segment::degenerate(1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
2
    }
    #[test]
2
    fn line_segment_has_directionality() {
1
        let s = Segment::line(1.0, 2.0, 3.0, 4.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((2.0, 2.0), (v2x, v2y));
2
    }
    #[test]
2
    fn line_segment_with_coincident_ends_has_no_directionality() {
1
        let s = Segment::line(1.0, 2.0, 1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
2
    }
    #[test]
2
    fn curve_has_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 20.0, 33.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 3.0), (v1x, v1y));
1
        assert_eq!((12.0, 20.0), (v2x, v2y));
2
    }
    #[test]
2
    fn curves_with_loops_and_coincident_ends_have_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-4.0, -4.0), (v2x, v2y));
1
        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-2.0, -2.0), (v2x, v2y));
1
        let s = Segment::curve(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 1.0, 2.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((2.0, 2.0), (v1x, v1y));
1
        assert_eq!((-2.0, -2.0), (v2x, v2y));
2
    }
    #[test]
2
    fn curve_with_coincident_control_points_has_no_directionality() {
1
        let s = Segment::curve(1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0);
1
        assert!(s.get_directionalities().is_none());
2
    }
    #[test]
2
    fn curve_with_123_coincident_has_directionality() {
1
        let s = Segment::curve(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 40.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((20.0, 40.0), (v1x, v1y));
1
        assert_eq!((20.0, 40.0), (v2x, v2y));
2
    }
    #[test]
2
    fn curve_with_234_coincident_has_directionality() {
1
        let s = Segment::curve(20.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((-20.0, -40.0), (v1x, v1y));
1
        assert_eq!((-20.0, -40.0), (v2x, v2y));
2
    }
    #[test]
2
    fn curve_with_12_34_coincident_has_directionality() {
1
        let s = Segment::curve(20.0, 40.0, 20.0, 40.0, 60.0, 70.0, 60.0, 70.0);
1
        let (v1x, v1y, v2x, v2y) = s.get_directionalities().unwrap();
1
        assert_eq!((40.0, 30.0), (v1x, v1y));
1
        assert_eq!((40.0, 30.0), (v2x, v2y));
2
    }
}
#[cfg(test)]
mod marker_tests {
    use super::*;
    use crate::path_builder::PathBuilder;
    #[test]
2
    fn emits_for_open_subpath() {
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(0.0, 0.0);
1
        builder.line_to(1.0, 0.0);
1
        builder.line_to(1.0, 1.0);
1
        builder.line_to(0.0, 1.0);
1
        let mut v = Vec::new();
1
        assert!(emit_markers_for_path(
1
            &builder.into_path(),
1
            BoundingBox::new(),
5
            &mut |marker_type: MarkerType,
                  x: f64,
                  y: f64,
                  computed_angle: Angle|
             -> Result<BoundingBox, InternalRenderingError> {
4
                v.push((marker_type, x, y, computed_angle));
4
                Ok(BoundingBox::new())
4
            }
        )
        .is_ok());
2
        assert_eq!(
            v,
2
            vec![
1
                (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1
                (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1
                (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1
                (MarkerType::End, 0.0, 1.0, Angle::from_vector(-1.0, 0.0)),
            ]
        );
2
    }
    #[test]
2
    fn emits_for_closed_subpath() {
1
        let mut builder = PathBuilder::default();
1
        builder.move_to(0.0, 0.0);
1
        builder.line_to(1.0, 0.0);
1
        builder.line_to(1.0, 1.0);
1
        builder.line_to(0.0, 1.0);
1
        builder.close_path();
1
        let mut v = Vec::new();
1
        assert!(emit_markers_for_path(
1
            &builder.into_path(),
1
            BoundingBox::new(),
6
            &mut |marker_type: MarkerType,
                  x: f64,
                  y: f64,
                  computed_angle: Angle|
             -> Result<BoundingBox, InternalRenderingError> {
5
                v.push((marker_type, x, y, computed_angle));
5
                Ok(BoundingBox::new())
5
            }
        )
        .is_ok());
2
        assert_eq!(
            v,
2
            vec![
1
                (MarkerType::Start, 0.0, 0.0, Angle::new(0.0)),
1
                (MarkerType::Middle, 1.0, 0.0, Angle::from_vector(1.0, 1.0)),
1
                (MarkerType::Middle, 1.0, 1.0, Angle::from_vector(-1.0, 1.0)),
1
                (MarkerType::Middle, 0.0, 1.0, Angle::from_vector(-1.0, -1.0)),
1
                (MarkerType::End, 0.0, 0.0, Angle::from_vector(1.0, -1.0)),
            ]
        );
2
    }
}