1
//! The main context structure which drives the drawing process.
2

            
3
use float_cmp::approx_eq;
4
use gio::prelude::*;
5
use pango::prelude::FontMapExt;
6
use regex::{Captures, Regex};
7
use std::cell::RefCell;
8
use std::convert::TryFrom;
9
use std::rc::Rc;
10
use std::{borrow::Cow, sync::OnceLock};
11

            
12
use crate::accept_language::UserLanguage;
13
use crate::bbox::BoundingBox;
14
use crate::cairo_path::CairoPath;
15
use crate::color::color_to_rgba;
16
use crate::coord_units::CoordUnits;
17
use crate::document::{AcquiredNodes, NodeId, RenderingOptions};
18
use crate::dpi::Dpi;
19
use crate::element::{Element, ElementData};
20
use crate::error::{AcquireError, ImplementationLimit, InternalRenderingError, InvalidTransform};
21
use crate::filters::{self, FilterPlan, FilterSpec, InputRequirements};
22
use crate::float_eq_cairo::ApproxEqCairo;
23
use crate::gradient::{GradientVariant, SpreadMethod, UserSpaceGradient};
24
use crate::layout::{
25
    Filter, Group, Image, Layer, LayerKind, LayoutViewport, Shape, StackingContext, Stroke, Text,
26
    TextSpan,
27
};
28
use crate::length::*;
29
use crate::limits;
30
use crate::marker;
31
use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
32
use crate::paint_server::{PaintSource, UserSpacePaintSource};
33
use crate::pattern::UserSpacePattern;
34
use crate::properties::{
35
    ClipRule, ComputedValues, FillRule, ImageRendering, MaskType, MixBlendMode, Opacity,
36
    PaintTarget, ShapeRendering, StrokeLinecap, StrokeLinejoin, TextRendering,
37
};
38
use crate::rect::{rect_to_transform, IRect, Rect};
39
use crate::rsvg_log;
40
use crate::session::Session;
41
use crate::surface_utils::shared_surface::{
42
    ExclusiveImageSurface, Interpolation, SharedImageSurface, SurfaceType,
43
};
44
use crate::transform::{Transform, ValidTransform};
45
use crate::unit_interval::UnitInterval;
46
use crate::viewbox::ViewBox;
47
use crate::{borrow_element_as, is_element_of_type};
48

            
49
/// Opaque font options for a DrawingCtx.
50
///
51
/// This is used for DrawingCtx::create_pango_context.
52
pub struct FontOptions {
53
    options: cairo::FontOptions,
54
}
55

            
56
/// Set path on the cairo context, or clear it.
57
/// This helper object keeps track whether the path has been set already,
58
/// so that it isn't recalculated every so often.
59
struct PathHelper<'a> {
60
    cr: &'a cairo::Context,
61
    transform: ValidTransform,
62
    cairo_path: &'a CairoPath,
63
    has_path: Option<bool>,
64
}
65

            
66
impl<'a> PathHelper<'a> {
67
18029100
    pub fn new(
68
18029100
        cr: &'a cairo::Context,
69
18029100
        transform: ValidTransform,
70
18029100
        cairo_path: &'a CairoPath,
71
18029100
    ) -> Self {
72
18029100
        PathHelper {
73
18029100
            cr,
74
18029100
            transform,
75
18029100
            cairo_path,
76
18029100
            has_path: None,
77
18029100
        }
78
18029100
    }
79

            
80
54085419
    pub fn set(&mut self) -> Result<(), InternalRenderingError> {
81
54085419
        match self.has_path {
82
            Some(false) | None => {
83
18033945
                self.has_path = Some(true);
84
18033945
                self.cr.set_matrix(self.transform.into());
85
18033945
                self.cairo_path.to_cairo_context(self.cr)
86
            }
87
36051474
            Some(true) => Ok(()),
88
        }
89
54085419
    }
90

            
91
36056528
    pub fn unset(&mut self) {
92
36056528
        match self.has_path {
93
18033223
            Some(true) | None => {
94
18033223
                self.has_path = Some(false);
95
18033223
                self.cr.new_path();
96
18033223
            }
97
18023305
            Some(false) => {}
98
        }
99
36056528
    }
100
}
101

            
102
/// Holds the size of the current viewport in the user's coordinate system.
103
#[derive(Clone, Copy)]
104
pub struct Viewport {
105
    pub dpi: Dpi,
106

            
107
    /// Corners of the current coordinate space.
108
    pub vbox: ViewBox,
109

            
110
    /// The viewport's coordinate system, or "user coordinate system" in SVG terms.
111
    pub transform: ValidTransform,
112
}
113

            
114
impl Viewport {
115
    /// FIXME: this is just used in Handle::with_height_to_user(), and in length.rs's test suite.
116
    /// Find a way to do this without involving a default identity transform.
117
2421
    pub fn new(dpi: Dpi, view_box_width: f64, view_box_height: f64) -> Viewport {
118
2421
        Viewport {
119
2421
            dpi,
120
2421
            vbox: ViewBox::from(Rect::from_size(view_box_width, view_box_height)),
121
2421
            transform: Default::default(),
122
2421
        }
123
2421
    }
124

            
125
    /// Creates a new viewport suitable for a certain kind of units.
126
    ///
127
    /// For `objectBoundingBox`, CSS lengths which are in percentages
128
    /// refer to the size of the current viewport.  Librsvg implements
129
    /// that by keeping the same current transformation matrix, and
130
    /// setting a viewport size of (1.0, 1.0).
131
    ///
132
    /// For `userSpaceOnUse`, we just duplicate the current viewport,
133
    /// since that kind of units means to use the current coordinate
134
    /// system unchanged.
135
9522996
    pub fn with_units(&self, units: CoordUnits) -> Viewport {
136
9522996
        match units {
137
7717
            CoordUnits::ObjectBoundingBox => Viewport {
138
7717
                dpi: self.dpi,
139
7717
                vbox: ViewBox::from(Rect::from_size(1.0, 1.0)),
140
7717
                transform: self.transform,
141
7717
            },
142

            
143
9515279
            CoordUnits::UserSpaceOnUse => Viewport {
144
9515279
                dpi: self.dpi,
145
9515279
                vbox: self.vbox,
146
9515279
                transform: self.transform,
147
9515279
            },
148
        }
149
9522996
    }
150

            
151
    /// Returns a viewport with a new size for normalizing `Length` values.
152
954161
    pub fn with_view_box(&self, width: f64, height: f64) -> Viewport {
153
954161
        Viewport {
154
954161
            dpi: self.dpi,
155
954161
            vbox: ViewBox::from(Rect::from_size(width, height)),
156
954161
            transform: self.transform,
157
954161
        }
158
954161
    }
159

            
160
    /// Copies the viewport, but just uses a new transform.
161
    ///
162
    /// This is used when we are about to draw in a temporary surface: the transform for
163
    /// that surface is computed independenly of the one in the current viewport.
164
977569
    pub fn with_explicit_transform(&self, transform: ValidTransform) -> Viewport {
165
977569
        Viewport {
166
977569
            dpi: self.dpi,
167
977569
            vbox: self.vbox,
168
977569
            transform,
169
977569
        }
170
977569
    }
171

            
172
47646319
    pub fn with_composed_transform(
173
47646319
        &self,
174
47646319
        transform: ValidTransform,
175
47646319
    ) -> Result<Viewport, InvalidTransform> {
176
47646319
        let composed_transform =
177
47646319
            ValidTransform::try_from((*self.transform).pre_transform(&transform))?;
178

            
179
47646319
        Ok(Viewport {
180
47646319
            dpi: self.dpi,
181
47646319
            vbox: self.vbox,
182
47646319
            transform: composed_transform,
183
47646319
        })
184
47646319
    }
185

            
186
66855186
    pub fn empty_bbox(&self) -> BoundingBox {
187
66855186
        BoundingBox::new().with_transform(*self.transform)
188
66855186
    }
189
}
190

            
191
/// Values that stay constant during rendering with a DrawingCtx.
192
#[derive(Clone)]
193
pub struct RenderingConfiguration {
194
    pub dpi: Dpi,
195
    pub cancellable: Option<gio::Cancellable>,
196
    pub user_language: UserLanguage,
197
    pub svg_nesting: SvgNesting,
198
    pub measuring: bool,
199
    pub testing: bool,
200
}
201

            
202
pub struct DrawingCtx {
203
    session: Session,
204

            
205
    initial_viewport: Viewport,
206

            
207
    cr_stack: Rc<RefCell<Vec<cairo::Context>>>,
208
    cr: cairo::Context,
209

            
210
    drawsub_stack: Vec<Node>,
211

            
212
    config: RenderingConfiguration,
213

            
214
    /// Depth of nested layers while drawing.
215
    ///
216
    /// We use this to set a hard limit on how many nested layers there can be, to avoid
217
    /// malicious SVGs that would cause unbounded stack consumption.
218
    recursion_depth: u16,
219
}
220

            
221
pub enum DrawingMode {
222
    LimitToStack { node: Node, root: Node },
223

            
224
    OnlyNode(Node),
225
}
226

            
227
/// Whether an SVG document is being rendered standalone or referenced from an `<image>` element.
228
///
229
/// Normally, the coordinate system used when rendering a toplevel SVG is determined from the
230
/// initial viewport and the `<svg>` element's `viewBox` and `preserveAspectRatio` attributes.
231
/// However, when an SVG document is referenced from an `<image>` element, as in `<image href="foo.svg"/>`,
232
/// its `preserveAspectRatio` needs to be ignored so that the one from the `<image>` element can
233
/// be used instead.  This lets the parent document (the one with the `<image>` element) specify
234
/// how it wants the child SVG to be scaled into the viewport.
235
#[derive(Copy, Clone)]
236
pub enum SvgNesting {
237
    Standalone,
238
    ReferencedFromImageElement,
239
}
240

            
241
/// The toplevel drawing routine.
242
///
243
/// This creates a DrawingCtx internally and starts drawing at the specified `node`.
244
20653
pub fn draw_tree(
245
20653
    session: Session,
246
20653
    mode: DrawingMode,
247
20653
    cr: &cairo::Context,
248
20653
    viewport_rect: Rect,
249
20653
    config: RenderingConfiguration,
250
20653
    acquired_nodes: &mut AcquiredNodes<'_>,
251
20653
) -> Result<BoundingBox, InternalRenderingError> {
252
20653
    let (drawsub_stack, node) = match mode {
253
20368
        DrawingMode::LimitToStack { node, root } => (node.ancestors().collect(), root),
254

            
255
285
        DrawingMode::OnlyNode(node) => (Vec::new(), node),
256
    };
257

            
258
20653
    let cascaded = CascadedValues::new_from_node(&node);
259
20653

            
260
20653
    // Preserve the user's transform and use it for the outermost bounding box.  All bounds/extents
261
20653
    // will be converted to this transform in the end.
262
20653
    let user_transform = Transform::from(cr.matrix());
263
20653
    let mut user_bbox = BoundingBox::new().with_transform(user_transform);
264
20653

            
265
20653
    // https://www.w3.org/TR/SVG2/coords.html#InitialCoordinateSystem
266
20653
    //
267
20653
    // "For the outermost svg element, the SVG user agent must
268
20653
    // determine an initial viewport coordinate system and an
269
20653
    // initial user coordinate system such that the two
270
20653
    // coordinates systems are identical. The origin of both
271
20653
    // coordinate systems must be at the origin of the SVG
272
20653
    // viewport."
273
20653
    //
274
20653
    // "... the initial viewport coordinate system (and therefore
275
20653
    // the initial user coordinate system) must have its origin at
276
20653
    // the top/left of the viewport"
277
20653

            
278
20653
    // Translate so (0, 0) is at the viewport's upper-left corner.
279
20653
    let transform = user_transform.pre_translate(viewport_rect.x0, viewport_rect.y0);
280

            
281
    // Here we exit immediately if the transform is not valid, since we are in the
282
    // toplevel drawing function.  Downstream cases would simply not render the current
283
    // element and ignore the error.
284
20653
    let valid_transform = ValidTransform::try_from(transform)?;
285
20653
    cr.set_matrix(valid_transform.into());
286
20653

            
287
20653
    // Per the spec, so the viewport has (0, 0) as upper-left.
288
20653
    let viewport_rect = viewport_rect.translate((-viewport_rect.x0, -viewport_rect.y0));
289
20653
    let initial_viewport = Viewport {
290
20653
        dpi: config.dpi,
291
20653
        vbox: ViewBox::from(viewport_rect),
292
20653
        transform: valid_transform,
293
20653
    };
294
20653

            
295
20653
    let mut draw_ctx = DrawingCtx::new(session, cr, &initial_viewport, config, drawsub_stack);
296

            
297
20653
    let content_bbox = draw_ctx.draw_node_from_stack(
298
20653
        &node,
299
20653
        acquired_nodes,
300
20653
        &cascaded,
301
20653
        &initial_viewport,
302
20653
        false,
303
20653
    )?;
304

            
305
20558
    user_bbox.insert(&content_bbox);
306
20558

            
307
20558
    if draw_ctx.is_rendering_cancelled() {
308
        Err(InternalRenderingError::Cancelled)
309
    } else {
310
20558
        Ok(user_bbox)
311
    }
312
20653
}
313

            
314
38159638
pub fn with_saved_cr<O, F>(cr: &cairo::Context, f: F) -> Result<O, InternalRenderingError>
315
38159638
where
316
38159638
    F: FnOnce() -> Result<O, InternalRenderingError>,
317
38159638
{
318
38159638
    cr.save()?;
319
38159638
    match f() {
320
38153349
        Ok(o) => {
321
38153349
            cr.restore()?;
322
38153349
            Ok(o)
323
        }
324

            
325
6289
        Err(e) => Err(e),
326
    }
327
38159638
}
328

            
329
impl Drop for DrawingCtx {
330
985493
    fn drop(&mut self) {
331
985493
        self.cr_stack.borrow_mut().pop();
332
985493
    }
333
}
334

            
335
const CAIRO_TAG_LINK: &str = "Link";
336

            
337
impl DrawingCtx {
338
20654
    pub fn new(
339
20654
        session: Session,
340
20654
        cr: &cairo::Context,
341
20654
        initial_viewport: &Viewport,
342
20654
        config: RenderingConfiguration,
343
20654
        drawsub_stack: Vec<Node>,
344
20654
    ) -> DrawingCtx {
345
20654
        DrawingCtx {
346
20654
            session,
347
20654
            initial_viewport: *initial_viewport,
348
20654
            cr_stack: Rc::new(RefCell::new(Vec::new())),
349
20654
            cr: cr.clone(),
350
20654
            drawsub_stack,
351
20654
            config,
352
20654
            recursion_depth: 0,
353
20654
        }
354
20654
    }
355

            
356
    /// Copies a `DrawingCtx` for temporary use on a Cairo surface.
357
    ///
358
    /// `DrawingCtx` maintains state using during the drawing process, and sometimes we
359
    /// would like to use that same state but on a different Cairo surface and context
360
    /// than the ones being used on `self`.  This function copies the `self` state into a
361
    /// new `DrawingCtx`, and ties the copied one to the supplied `cr`.
362
    ///
363
    /// Note that if this function is called, it means that a temporary surface is being used.
364
    /// That surface needs a viewport which starts with a special transform; see
365
    /// [`Viewport::with_explicit_transform`] and how it is used elsewhere.
366
964839
    fn nested(&self, cr: cairo::Context) -> Box<DrawingCtx> {
367
964839
        let cr_stack = self.cr_stack.clone();
368
964839

            
369
964839
        cr_stack.borrow_mut().push(self.cr.clone());
370
964839

            
371
964839
        Box::new(DrawingCtx {
372
964839
            session: self.session.clone(),
373
964839
            initial_viewport: self.initial_viewport,
374
964839
            cr_stack,
375
964839
            cr,
376
964839
            drawsub_stack: self.drawsub_stack.clone(),
377
964839
            config: self.config.clone(),
378
964839
            recursion_depth: self.recursion_depth,
379
964839
        })
380
964839
    }
381

            
382
131652885
    pub fn session(&self) -> &Session {
383
131652885
        &self.session
384
131652885
    }
385

            
386
    /// Returns the `RenderingOptions` being used for rendering.
387
285
    pub fn rendering_options(&self, svg_nesting: SvgNesting) -> RenderingOptions {
388
285
        RenderingOptions {
389
285
            dpi: self.config.dpi,
390
285
            cancellable: self.config.cancellable.clone(),
391
285
            user_language: self.config.user_language.clone(),
392
285
            svg_nesting,
393
285
            testing: self.config.testing,
394
285
        }
395
285
    }
396

            
397
361
    pub fn user_language(&self) -> &UserLanguage {
398
361
        &self.config.user_language
399
361
    }
400

            
401
33098
    pub fn toplevel_viewport(&self) -> Rect {
402
33098
        *self.initial_viewport.vbox
403
33098
    }
404

            
405
    /// Gets the transform that will be used on the target surface,
406
    /// whether using an isolated stacking context or not.
407
18029100
    fn get_transform_for_stacking_ctx(
408
18029100
        &self,
409
18029100
        viewport: &Viewport,
410
18029100
        stacking_ctx: &StackingContext,
411
18029100
        clipping: bool,
412
18029100
    ) -> Result<ValidTransform, InternalRenderingError> {
413
18029100
        if stacking_ctx.should_isolate() && !clipping {
414
7999
            let affines = CompositingAffines::new(
415
7999
                *viewport.transform,
416
7999
                *self.initial_viewport.transform,
417
7999
                self.cr_stack.borrow().len(),
418
7999
            );
419
7999

            
420
7999
            Ok(ValidTransform::try_from(affines.for_temporary_surface)?)
421
        } else {
422
18021101
            Ok(viewport.transform)
423
        }
424
18029100
    }
425

            
426
21641
    pub fn svg_nesting(&self) -> SvgNesting {
427
21641
        self.config.svg_nesting
428
21641
    }
429

            
430
20368
    pub fn is_measuring(&self) -> bool {
431
20368
        self.config.measuring
432
20368
    }
433

            
434
    pub fn is_testing(&self) -> bool {
435
        self.config.testing
436
    }
437

            
438
14098
    fn size_for_temporary_surface(&self) -> (i32, i32) {
439
14098
        let rect = self.toplevel_viewport();
440
14098

            
441
14098
        let (viewport_width, viewport_height) = (rect.width(), rect.height());
442
14098

            
443
14098
        let (width, height) = self
444
14098
            .initial_viewport
445
14098
            .transform
446
14098
            .transform_distance(viewport_width, viewport_height);
447
14098

            
448
14098
        // We need a size in whole pixels, so use ceil() to ensure the whole viewport fits
449
14098
        // into the temporary surface.
450
14098
        (width.ceil().abs() as i32, height.ceil().abs() as i32)
451
14098
    }
452

            
453
6878
    pub fn create_surface_for_toplevel_viewport(
454
6878
        &self,
455
6878
    ) -> Result<cairo::ImageSurface, InternalRenderingError> {
456
6878
        let (w, h) = self.size_for_temporary_surface();
457
6878

            
458
6878
        Ok(cairo::ImageSurface::create(cairo::Format::ARgb32, w, h)?)
459
6878
    }
460

            
461
7220
    fn create_similar_surface_for_toplevel_viewport(
462
7220
        &self,
463
7220
        surface: &cairo::Surface,
464
7220
    ) -> Result<cairo::Surface, InternalRenderingError> {
465
7220
        let (w, h) = self.size_for_temporary_surface();
466
7220

            
467
7220
        Ok(cairo::Surface::create_similar(
468
7220
            surface,
469
7220
            cairo::Content::ColorAlpha,
470
7220
            w,
471
7220
            h,
472
7220
        )?)
473
7220
    }
474

            
475
    /// Creates a new coordinate space inside a viewport and sets a clipping rectangle.
476
    ///
477
    /// Returns the new viewport with the new coordinate space, or `None` if the transform
478
    /// inside the new viewport turned out to be invalid.  In this case, the caller can simply
479
    /// not render the object in question.
480
23864
    fn push_new_viewport(
481
23864
        &self,
482
23864
        current_viewport: &Viewport,
483
23864
        layout_viewport: &LayoutViewport,
484
23864
    ) -> Option<Viewport> {
485
23864
        let LayoutViewport {
486
23864
            geometry,
487
23864
            vbox,
488
23864
            preserve_aspect_ratio,
489
23864
            overflow,
490
23864
        } = *layout_viewport;
491
23864

            
492
23864
        if !overflow.overflow_allowed() || (vbox.is_some() && preserve_aspect_ratio.is_slice()) {
493
3363
            clip_to_rectangle(&self.cr, &current_viewport.transform, &geometry);
494
20501
        }
495

            
496
23864
        preserve_aspect_ratio
497
23864
            .viewport_to_viewbox_transform(vbox, &geometry)
498
23864
            .unwrap_or_else(|_e| {
499
38
                match vbox {
500
                    None => unreachable!(
501
                        "viewport_to_viewbox_transform only returns errors when vbox != None"
502
                    ),
503
38
                    Some(v) => {
504
38
                        rsvg_log!(
505
38
                            self.session,
506
                            "ignoring viewBox ({}, {}, {}, {}) since it is not usable",
507
                            v.x0,
508
                            v.y0,
509
                            v.width(),
510
                            v.height()
511
                        );
512
                    }
513
                }
514
38
                None
515
23864
            })
516
23864
            .and_then(|t| {
517
23826
                let transform =
518
23826
                    ValidTransform::try_from(current_viewport.transform.pre_transform(&t)).ok()?;
519

            
520
23826
                Some(Viewport {
521
23826
                    dpi: self.config.dpi,
522
23826
                    vbox: vbox.unwrap_or(current_viewport.vbox),
523
23826
                    transform,
524
23826
                })
525
23864
            })
526
23864
    }
527

            
528
38153520
    fn clip_to_node(
529
38153520
        &mut self,
530
38153520
        clip_node: &Option<Node>,
531
38153520
        acquired_nodes: &mut AcquiredNodes<'_>,
532
38153520
        viewport: &Viewport,
533
38153520
        bbox: &BoundingBox,
534
38153520
    ) -> Result<(), InternalRenderingError> {
535
38153520
        if clip_node.is_none() {
536
38152665
            return Ok(());
537
855
        }
538
855

            
539
855
        let node = clip_node.as_ref().unwrap();
540
855
        let units = borrow_element_as!(node, ClipPath).get_units();
541

            
542
855
        if let Ok(transform) = rect_to_transform(&bbox.rect, units) {
543
817
            let cascaded = CascadedValues::new_from_node(node);
544
817
            let values = cascaded.get();
545
817

            
546
817
            let node_transform = values.transform().post_transform(&transform);
547
817
            let transform_for_clip = ValidTransform::try_from(node_transform)?;
548

            
549
798
            let clip_viewport = viewport.with_composed_transform(transform_for_clip)?;
550

            
551
2394
            for child in node.children().filter(|c| {
552
2394
                c.is_element() && element_can_be_used_inside_clip_path(&c.borrow_element())
553
2394
            }) {
554
798
                child.draw(
555
798
                    acquired_nodes,
556
798
                    &CascadedValues::clone_with_node(&cascaded, &child),
557
798
                    &clip_viewport,
558
798
                    self,
559
798
                    true,
560
798
                )?;
561
            }
562

            
563
798
            self.cr.clip();
564
38
        }
565

            
566
836
        Ok(())
567
38153520
    }
568

            
569
1159
    fn generate_cairo_mask(
570
1159
        &mut self,
571
1159
        mask_node: &Node,
572
1159
        viewport: &Viewport,
573
1159
        transform: Transform,
574
1159
        bbox: &BoundingBox,
575
1159
        acquired_nodes: &mut AcquiredNodes<'_>,
576
1159
    ) -> Result<Option<cairo::ImageSurface>, InternalRenderingError> {
577
1159
        if bbox.rect.is_none() {
578
            // The node being masked is empty / doesn't have a
579
            // bounding box, so there's nothing to mask!
580
19
            return Ok(None);
581
1140
        }
582

            
583
1140
        let _mask_acquired = match acquired_nodes.acquire_ref(mask_node) {
584
1140
            Ok(n) => n,
585

            
586
            Err(AcquireError::CircularReference(_)) => {
587
                rsvg_log!(self.session, "circular reference in element {}", mask_node);
588
                return Ok(None);
589
            }
590

            
591
            _ => unreachable!(),
592
        };
593

            
594
1140
        let mask_element = mask_node.borrow_element();
595
1140
        let mask = borrow_element_as!(mask_node, Mask);
596
1140

            
597
1140
        let cascaded = CascadedValues::new_from_node(mask_node);
598
1140
        let values = cascaded.get();
599
1140

            
600
1140
        let mask_units = mask.get_units();
601
1140

            
602
1140
        let mask_rect = {
603
1140
            let params = NormalizeParams::new(values, &viewport.with_units(mask_units));
604
1140
            mask.get_rect(&params)
605
        };
606

            
607
1140
        let transform_for_mask =
608
1140
            ValidTransform::try_from(values.transform().post_transform(&transform))?;
609

            
610
1140
        let bbtransform = if let Ok(t) = rect_to_transform(&bbox.rect, mask_units)
611
1140
            .map_err(|_: ()| InvalidTransform)
612
1140
            .and_then(ValidTransform::try_from)
613
        {
614
1121
            t
615
        } else {
616
19
            return Ok(None);
617
        };
618

            
619
1121
        let mask_content_surface = self.create_surface_for_toplevel_viewport()?;
620

            
621
        // Use a scope because mask_cr needs to release the
622
        // reference to the surface before we access the pixels
623
        {
624
1121
            let mask_cr = cairo::Context::new(&mask_content_surface)?;
625

            
626
1121
            let clip_rect = (*bbtransform).transform_rect(&mask_rect);
627
1121
            clip_to_rectangle(&mask_cr, &transform_for_mask, &clip_rect);
628

            
629
1121
            let mask_viewport = if mask.get_content_units() == CoordUnits::ObjectBoundingBox {
630
76
                viewport
631
76
                    .with_units(mask.get_content_units())
632
76
                    .with_explicit_transform(transform_for_mask)
633
76
                    .with_composed_transform(bbtransform)?
634
            } else {
635
1045
                viewport
636
1045
                    .with_units(mask.get_content_units())
637
1045
                    .with_explicit_transform(transform_for_mask)
638
            };
639

            
640
1121
            let mut mask_draw_ctx = self.nested(mask_cr);
641
1121

            
642
1121
            let stacking_ctx = Box::new(StackingContext::new(
643
1121
                self.session(),
644
1121
                acquired_nodes,
645
1121
                &mask_element,
646
1121
                Transform::identity(),
647
1121
                None,
648
1121
                values,
649
1121
            ));
650
1121

            
651
1121
            rsvg_log!(self.session, "(mask {}", mask_element);
652

            
653
1121
            let res = mask_draw_ctx.with_discrete_layer(
654
1121
                &stacking_ctx,
655
1121
                acquired_nodes,
656
1121
                &mask_viewport,
657
1121
                None,
658
1121
                false,
659
1121
                &mut |an, dc, new_viewport| {
660
1121
                    mask_node.draw_children(an, &cascaded, new_viewport, dc, false)
661
1121
                },
662
1121
            );
663
1121

            
664
1121
            rsvg_log!(self.session, ")");
665

            
666
1121
            res?;
667
        }
668

            
669
1102
        let tmp = SharedImageSurface::wrap(mask_content_surface, SurfaceType::SRgb)?;
670

            
671
1102
        let mask_result = match values.mask_type() {
672
1083
            MaskType::Luminance => tmp.to_luminance_mask()?,
673
19
            MaskType::Alpha => tmp.extract_alpha(IRect::from_size(tmp.width(), tmp.height()))?,
674
        };
675

            
676
1102
        let mask = mask_result.into_image_surface()?;
677

            
678
1102
        Ok(Some(mask))
679
1159
    }
680

            
681
38161956
    fn is_rendering_cancelled(&self) -> bool {
682
38161956
        match &self.config.cancellable {
683
38161937
            None => false,
684
19
            Some(cancellable) => cancellable.is_cancelled(),
685
        }
686
38161956
    }
687

            
688
    /// Checks whether the rendering has been cancelled in the middle.
689
    ///
690
    /// If so, returns an Err.  This is used from [`DrawingCtx::with_discrete_layer`] to
691
    /// exit early instead of proceeding with rendering.
692
38141398
    fn check_cancellation(&self) -> Result<(), InternalRenderingError> {
693
38141398
        if self.is_rendering_cancelled() {
694
19
            return Err(InternalRenderingError::Cancelled);
695
38141379
        }
696
38141379

            
697
38141379
        Ok(())
698
38141398
    }
699

            
700
38141379
    fn check_layer_nesting_depth(&self) -> Result<(), InternalRenderingError> {
701
38141379
        if self.recursion_depth > limits::MAX_LAYER_NESTING_DEPTH {
702
57
            return Err(InternalRenderingError::LimitExceeded(
703
57
                ImplementationLimit::MaximumLayerNestingDepthExceeded,
704
57
            ));
705
38141322
        }
706
38141322

            
707
38141322
        Ok(())
708
38141379
    }
709

            
710
5757
    fn filter_current_surface(
711
5757
        &mut self,
712
5757
        acquired_nodes: &mut AcquiredNodes<'_>,
713
5757
        filter: &Filter,
714
5757
        viewport: &Viewport,
715
5757
        element_name: &str,
716
5757
        bbox: &BoundingBox,
717
5757
    ) -> Result<cairo::Surface, InternalRenderingError> {
718
5757
        let surface_to_filter = SharedImageSurface::copy_from_surface(
719
5757
            &cairo::ImageSurface::try_from(self.cr.target()).unwrap(),
720
5757
        )?;
721

            
722
5757
        let stroke_paint_source = Rc::new(filter.stroke_paint_source.to_user_space(
723
5757
            &bbox.rect,
724
5757
            viewport,
725
5757
            &filter.normalize_values,
726
5757
        ));
727
5757
        let fill_paint_source = Rc::new(filter.fill_paint_source.to_user_space(
728
5757
            &bbox.rect,
729
5757
            viewport,
730
5757
            &filter.normalize_values,
731
5757
        ));
732
5757

            
733
5757
        // Filter functions (like "blend()", not the <filter> element) require
734
5757
        // being resolved in userSpaceonUse units, since that is the default
735
5757
        // for primitive_units.  So, get the corresponding NormalizeParams
736
5757
        // here and pass them down.
737
5757
        let user_space_params = NormalizeParams::from_values(
738
5757
            &filter.normalize_values,
739
5757
            &viewport.with_units(CoordUnits::UserSpaceOnUse),
740
5757
        );
741

            
742
5757
        let filtered_surface = self
743
5757
            .run_filters(
744
5757
                viewport,
745
5757
                surface_to_filter,
746
5757
                filter,
747
5757
                acquired_nodes,
748
5757
                element_name,
749
5757
                &user_space_params,
750
5757
                stroke_paint_source,
751
5757
                fill_paint_source,
752
5757
                bbox,
753
5757
            )?
754
5757
            .into_image_surface()?;
755

            
756
5757
        let generic_surface: &cairo::Surface = &filtered_surface; // deref to Surface
757
5757

            
758
5757
        Ok(generic_surface.clone())
759
5757
    }
760

            
761
38141284
    fn draw_in_optional_new_viewport(
762
38141284
        &mut self,
763
38141284
        acquired_nodes: &mut AcquiredNodes<'_>,
764
38141284
        viewport: &Viewport,
765
38141284
        layout_viewport: &Option<LayoutViewport>,
766
38141284
        draw_fn: &mut dyn FnMut(
767
38141284
            &mut AcquiredNodes<'_>,
768
38141284
            &mut DrawingCtx,
769
38141284
            &Viewport,
770
38141284
        ) -> Result<BoundingBox, InternalRenderingError>,
771
38141284
    ) -> Result<BoundingBox, InternalRenderingError> {
772
38141284
        if let Some(layout_viewport) = layout_viewport.as_ref() {
773
23864
            if let Some(new_viewport) = self.push_new_viewport(viewport, layout_viewport) {
774
23826
                draw_fn(acquired_nodes, self, &new_viewport)
775
            } else {
776
38
                Ok(viewport.empty_bbox())
777
            }
778
        } else {
779
38117420
            draw_fn(acquired_nodes, self, viewport)
780
        }
781
38141284
    }
782

            
783
38141322
    fn draw_layer_internal(
784
38141322
        &mut self,
785
38141322
        stacking_ctx: &StackingContext,
786
38141322
        acquired_nodes: &mut AcquiredNodes<'_>,
787
38141322
        viewport: &Viewport,
788
38141322
        layout_viewport: Option<LayoutViewport>,
789
38141322
        clipping: bool,
790
38141322
        draw_fn: &mut dyn FnMut(
791
38141322
            &mut AcquiredNodes<'_>,
792
38141322
            &mut DrawingCtx,
793
38141322
            &Viewport,
794
38141322
        ) -> Result<BoundingBox, InternalRenderingError>,
795
38141322
    ) -> Result<BoundingBox, InternalRenderingError> {
796
38141322
        let stacking_ctx_transform = ValidTransform::try_from(stacking_ctx.transform)?;
797

            
798
38141303
        let viewport = viewport.with_composed_transform(stacking_ctx_transform)?;
799

            
800
38141303
        let res = if clipping {
801
760
            self.draw_in_optional_new_viewport(acquired_nodes, &viewport, &layout_viewport, draw_fn)
802
        } else {
803
38140543
            with_saved_cr(&self.cr.clone(), || {
804
38140543
                if let Some(ref link_target) = stacking_ctx.link_target {
805
38
                    self.link_tag_begin(link_target);
806
38140505
                }
807

            
808
38140543
                let Opacity(UnitInterval(opacity)) = stacking_ctx.opacity;
809

            
810
38140543
                if let Some(rect) = stacking_ctx.clip_rect.as_ref() {
811
3496
                    clip_to_rectangle(&self.cr, &viewport.transform, rect);
812
38137047
                }
813

            
814
                // Here we are clipping in user space, so the bbox doesn't matter
815
38140543
                self.clip_to_node(
816
38140543
                    &stacking_ctx.clip_in_user_space,
817
38140543
                    acquired_nodes,
818
38140543
                    &viewport,
819
38140543
                    &viewport.empty_bbox(),
820
38140543
                )?;
821

            
822
38140524
                let should_isolate = stacking_ctx.should_isolate();
823

            
824
38140524
                let res = if should_isolate {
825
                    // Compute our assortment of affines
826

            
827
12977
                    let affines = Box::new(CompositingAffines::new(
828
12977
                        *viewport.transform,
829
12977
                        *self.initial_viewport.transform,
830
12977
                        self.cr_stack.borrow().len(),
831
12977
                    ));
832

            
833
                    // Create temporary surface and its cr
834

            
835
12977
                    let cr = match stacking_ctx.filter {
836
                        None => cairo::Context::new(
837
7220
                            &self
838
7220
                                .create_similar_surface_for_toplevel_viewport(&self.cr.target())?,
839
                        )?,
840
                        Some(_) => {
841
5757
                            cairo::Context::new(self.create_surface_for_toplevel_viewport()?)?
842
                        }
843
                    };
844

            
845
12977
                    let transform_for_temporary_surface =
846
12977
                        ValidTransform::try_from(affines.for_temporary_surface)?;
847

            
848
12977
                    let (source_surface, mut res, bbox) = {
849
12977
                        let mut temporary_draw_ctx = self.nested(cr.clone());
850
12977

            
851
12977
                        let viewport_for_temporary_surface = Viewport::with_explicit_transform(
852
12977
                            &viewport,
853
12977
                            transform_for_temporary_surface,
854
12977
                        );
855
12977

            
856
12977
                        // Draw!
857
12977

            
858
12977
                        let res = temporary_draw_ctx.draw_in_optional_new_viewport(
859
12977
                            acquired_nodes,
860
12977
                            &viewport_for_temporary_surface,
861
12977
                            &layout_viewport,
862
12977
                            draw_fn,
863
12977
                        );
864

            
865
12977
                        let bbox = if let Ok(ref bbox) = res {
866
12920
                            *bbox
867
                        } else {
868
57
                            BoundingBox::new().with_transform(*transform_for_temporary_surface)
869
                        };
870

            
871
12977
                        if let Some(ref filter) = stacking_ctx.filter {
872
5757
                            let filtered_surface = temporary_draw_ctx.filter_current_surface(
873
5757
                                acquired_nodes,
874
5757
                                filter,
875
5757
                                &viewport_for_temporary_surface,
876
5757
                                &stacking_ctx.element_name,
877
5757
                                &bbox,
878
5757
                            )?;
879

            
880
                            // FIXME: "res" was declared mutable above so that we could overwrite it
881
                            // with the result of filtering, so that if filtering produces an error,
882
                            // then the masking below wouldn't take place.  Test for that and fix this;
883
                            // we are *not* modifying res in case of error.
884
5757
                            (filtered_surface, res, bbox)
885
                        } else {
886
7220
                            (temporary_draw_ctx.cr.target(), res, bbox)
887
                        }
888
                    };
889

            
890
                    // Set temporary surface as source
891

            
892
12977
                    self.cr
893
12977
                        .set_matrix(ValidTransform::try_from(affines.compositing)?.into());
894
12977
                    self.cr.set_source_surface(&source_surface, 0.0, 0.0)?;
895

            
896
                    // Clip
897

            
898
12977
                    let transform_for_clip =
899
12977
                        ValidTransform::try_from(affines.outside_temporary_surface)?;
900

            
901
12977
                    let viewport_for_clip = viewport.with_explicit_transform(transform_for_clip);
902
12977
                    self.cr.set_matrix(transform_for_clip.into());
903
12977

            
904
12977
                    self.clip_to_node(
905
12977
                        &stacking_ctx.clip_in_object_space,
906
12977
                        acquired_nodes,
907
12977
                        &viewport_for_clip,
908
12977
                        &bbox,
909
12977
                    )?;
910

            
911
                    // Mask
912

            
913
12977
                    if let Some(ref mask_node) = stacking_ctx.mask {
914
1216
                        res = res.and_then(|bbox| {
915
1159
                            self.generate_cairo_mask(
916
1159
                                mask_node,
917
1159
                                &viewport,
918
1159
                                affines.for_temporary_surface,
919
1159
                                &bbox,
920
1159
                                acquired_nodes,
921
1159
                            )
922
1159
                            .and_then(|mask_surf| {
923
1140
                                if let Some(surf) = mask_surf {
924
1102
                                    self.cr.push_group();
925
1102

            
926
1102
                                    self.cr.set_matrix(
927
1102
                                        ValidTransform::try_from(affines.compositing)?.into(),
928
1102
                                    );
929
1102
                                    self.cr.mask_surface(&surf, 0.0, 0.0)?;
930

            
931
1102
                                    Ok(self.cr.pop_group_to_source()?)
932
                                } else {
933
38
                                    Ok(())
934
                                }
935
1159
                            })
936
1159
                            .map(|_: ()| bbox)
937
1216
                        });
938
11761
                    }
939

            
940
                    {
941
                        // Composite the temporary surface
942

            
943
12977
                        self.cr
944
12977
                            .set_matrix(ValidTransform::try_from(affines.compositing)?.into());
945
12977
                        self.cr.set_operator(stacking_ctx.mix_blend_mode.into());
946
12977

            
947
12977
                        if opacity < 1.0 {
948
5871
                            self.cr.paint_with_alpha(opacity)?;
949
                        } else {
950
7106
                            self.cr.paint()?;
951
                        }
952
                    }
953

            
954
12977
                    res
955
                } else {
956
38127547
                    self.draw_in_optional_new_viewport(
957
38127547
                        acquired_nodes,
958
38127547
                        &viewport,
959
38127547
                        &layout_viewport,
960
38127547
                        draw_fn,
961
38127547
                    )
962
                };
963

            
964
38140524
                if stacking_ctx.link_target.is_some() {
965
38
                    self.link_tag_end();
966
38140486
                }
967

            
968
38140524
                res
969
38140543
            })
970
        };
971

            
972
38141303
        res
973
38141322
    }
974

            
975
38141398
    pub fn with_discrete_layer(
976
38141398
        &mut self,
977
38141398
        stacking_ctx: &StackingContext,
978
38141398
        acquired_nodes: &mut AcquiredNodes<'_>,
979
38141398
        viewport: &Viewport,
980
38141398
        layout_viewport: Option<LayoutViewport>,
981
38141398
        clipping: bool,
982
38141398
        draw_fn: &mut dyn FnMut(
983
38141398
            &mut AcquiredNodes<'_>,
984
38141398
            &mut DrawingCtx,
985
38141398
            &Viewport,
986
38141398
        ) -> Result<BoundingBox, InternalRenderingError>,
987
38141398
    ) -> Result<BoundingBox, InternalRenderingError> {
988
38141398
        self.check_cancellation()?;
989

            
990
38141379
        self.recursion_depth += 1;
991
38141379

            
992
38141379
        match self.check_layer_nesting_depth() {
993
            Ok(()) => {
994
38141322
                let res = self.draw_layer_internal(
995
38141322
                    stacking_ctx,
996
38141322
                    acquired_nodes,
997
38141322
                    viewport,
998
38141322
                    layout_viewport,
999
38141322
                    clipping,
38141322
                    draw_fn,
38141322
                );
38141322

            
38141322
                self.recursion_depth -= 1;
38141322
                res
            }
57
            Err(e) => Err(e),
        }
38141398
    }
    /// Run the drawing function with the specified opacity
950494
    fn with_alpha(
950494
        &mut self,
950494
        opacity: UnitInterval,
950494
        draw_fn: &mut dyn FnMut(&mut DrawingCtx) -> Result<BoundingBox, InternalRenderingError>,
950494
    ) -> Result<BoundingBox, InternalRenderingError> {
950494
        let res;
950494
        let UnitInterval(o) = opacity;
950494
        if o < 1.0 {
19
            self.cr.push_group();
19
            res = draw_fn(self);
19
            self.cr.pop_group_to_source()?;
19
            self.cr.paint_with_alpha(o)?;
950475
        } else {
950475
            res = draw_fn(self);
950475
        }
950494
        res
950494
    }
    /// Start a Cairo tag for PDF links
114
    fn link_tag_begin(&mut self, link_target: &str) {
114
        let attributes = format!("uri='{}'", escape_link_target(link_target));
114

            
114
        let cr = self.cr.clone();
114
        cr.tag_begin(CAIRO_TAG_LINK, &attributes);
114
    }
    /// End a Cairo tag for PDF links
114
    fn link_tag_end(&mut self) {
114
        self.cr.tag_end(CAIRO_TAG_LINK);
114
    }
5700
    fn make_filter_plan(
5700
        &mut self,
5700
        acquired_nodes: &mut AcquiredNodes<'_>,
5700
        specs: &[FilterSpec],
5700
        source_image_width: i32,
5700
        source_image_height: i32,
5700
        stroke_paint_source: Rc<UserSpacePaintSource>,
5700
        fill_paint_source: Rc<UserSpacePaintSource>,
5700
        viewport: &Viewport,
5700
    ) -> Result<Rc<FilterPlan>, InternalRenderingError> {
5700
        let requirements = InputRequirements::new_from_filter_specs(specs);
5700
        let background_image =
5700
            if requirements.needs_background_image || requirements.needs_background_alpha {
209
                Some(self.get_snapshot(source_image_width, source_image_height)?)
            } else {
5491
                None
            };
5700
        let stroke_paint_image = if requirements.needs_stroke_paint_image {
114
            Some(self.get_paint_source_surface(
114
                source_image_width,
114
                source_image_height,
114
                acquired_nodes,
114
                &stroke_paint_source,
114
                viewport,
114
            )?)
        } else {
5586
            None
        };
5700
        let fill_paint_image = if requirements.needs_fill_paint_image {
133
            Some(self.get_paint_source_surface(
133
                source_image_width,
133
                source_image_height,
133
                acquired_nodes,
133
                &fill_paint_source,
133
                viewport,
133
            )?)
        } else {
5567
            None
        };
5700
        Ok(Rc::new(FilterPlan::new(
5700
            self.session(),
5700
            *viewport,
5700
            requirements,
5700
            background_image,
5700
            stroke_paint_image,
5700
            fill_paint_image,
5700
        )?))
5700
    }
5757
    fn run_filters(
5757
        &mut self,
5757
        viewport: &Viewport,
5757
        surface_to_filter: SharedImageSurface,
5757
        filter: &Filter,
5757
        acquired_nodes: &mut AcquiredNodes<'_>,
5757
        node_name: &str,
5757
        user_space_params: &NormalizeParams,
5757
        stroke_paint_source: Rc<UserSpacePaintSource>,
5757
        fill_paint_source: Rc<UserSpacePaintSource>,
5757
        node_bbox: &BoundingBox,
5757
    ) -> Result<SharedImageSurface, InternalRenderingError> {
5757
        let session = self.session();
5757

            
5757
        // We try to convert each item in the filter_list to a FilterSpec.
5757
        //
5757
        // However, the spec mentions, "If the filter references a non-existent object or
5757
        // the referenced object is not a filter element, then the whole filter chain is
5757
        // ignored." - https://www.w3.org/TR/filter-effects/#FilterProperty
5757
        //
5757
        // So, run through the filter_list and collect into a Result<Vec<FilterSpec>>.
5757
        // This will return an Err if any of the conversions failed.
5757
        let filter_specs = filter
5757
            .filter_list
5757
            .iter()
5814
            .map(|filter_value| {
5814
                filter_value.to_filter_spec(
5814
                    acquired_nodes,
5814
                    user_space_params,
5814
                    filter.current_color,
5814
                    viewport,
5814
                    session,
5814
                    node_name,
5814
                )
5814
            })
5757
            .collect::<Result<Vec<FilterSpec>, _>>();
5757

            
5757
        match filter_specs {
5700
            Ok(specs) => {
5700
                let plan = self.make_filter_plan(
5700
                    acquired_nodes,
5700
                    &specs,
5700
                    surface_to_filter.width(),
5700
                    surface_to_filter.height(),
5700
                    stroke_paint_source,
5700
                    fill_paint_source,
5700
                    viewport,
5700
                )?;
                // Start with the surface_to_filter, and apply each filter spec in turn;
                // the final result is our return value.
5719
                specs.iter().try_fold(surface_to_filter, |surface, spec| {
5719
                    filters::render(plan.clone(), spec, surface, acquired_nodes, self, node_bbox)
5719
                })
            }
57
            Err(e) => {
57
                rsvg_log!(
57
                    self.session,
57
                    "not rendering filter list on node {} because it was in error: {}",
57
                    node_name,
57
                    e
57
                );
                // just return the original surface without filtering it
57
                Ok(surface_to_filter)
            }
        }
5757
    }
9500627
    fn set_pattern(
9500627
        &mut self,
9500627
        pattern: &UserSpacePattern,
9500627
        acquired_nodes: &mut AcquiredNodes<'_>,
9500627
        viewport: &Viewport,
9500627
    ) -> Result<bool, InternalRenderingError> {
9500627
        // Bail out early if the pattern has zero size, per the spec
9500627
        if approx_eq!(f64, pattern.width, 0.0) || approx_eq!(f64, pattern.height, 0.0) {
171
            return Ok(false);
9500456
        }
        // Bail out early if this pattern has a circular reference
9500456
        let pattern_node_acquired = match pattern.acquire_pattern_node(acquired_nodes) {
9500456
            Ok(n) => n,
            Err(AcquireError::CircularReference(ref node)) => {
                rsvg_log!(self.session, "circular reference in element {}", node);
                return Ok(false);
            }
            _ => unreachable!(),
        };
9500456
        let pattern_node = pattern_node_acquired.get();
9500456

            
9500456
        let taffine = viewport.transform.pre_transform(&pattern.transform);
9500456

            
9500456
        let mut scwscale = (taffine.xx.powi(2) + taffine.xy.powi(2)).sqrt();
9500456
        let mut schscale = (taffine.yx.powi(2) + taffine.yy.powi(2)).sqrt();
9500456

            
9500456
        let pw: i32 = (pattern.width * scwscale) as i32;
9500456
        let ph: i32 = (pattern.height * schscale) as i32;
9500456

            
9500456
        if pw < 1 || ph < 1 {
8549962
            return Ok(false);
950494
        }
950494

            
950494
        scwscale = f64::from(pw) / pattern.width;
950494
        schscale = f64::from(ph) / pattern.height;
        // Apply the pattern transform
950494
        let (affine, caffine) = if scwscale.approx_eq_cairo(1.0) && schscale.approx_eq_cairo(1.0) {
437
            (pattern.coord_transform, pattern.content_transform)
        } else {
950057
            (
950057
                pattern
950057
                    .coord_transform
950057
                    .pre_scale(1.0 / scwscale, 1.0 / schscale),
950057
                pattern.content_transform.post_scale(scwscale, schscale),
950057
            )
        };
        // Draw to another surface
950494
        let surface = self
950494
            .cr
950494
            .target()
950494
            .create_similar(cairo::Content::ColorAlpha, pw, ph)?;
950494
        let cr_pattern = cairo::Context::new(&surface)?;
        // Set up transformations to be determined by the contents units
950494
        let transform = ValidTransform::try_from(caffine)?;
950494
        cr_pattern.set_matrix(transform.into());
950494

            
950494
        // Draw everything
950494

            
950494
        {
950494
            let mut pattern_draw_ctx = self.nested(cr_pattern);
950494

            
950494
            let pattern_viewport = viewport
950494
                .with_view_box(pattern.width, pattern.height)
950494
                .with_explicit_transform(transform);
950494

            
950494
            let pattern_cascaded = CascadedValues::new_from_node(pattern_node);
950494
            let pattern_values = pattern_cascaded.get();
950494

            
950494
            let elt = pattern_node.borrow_element();
950494

            
950494
            let stacking_ctx = Box::new(StackingContext::new(
950494
                self.session(),
950494
                acquired_nodes,
950494
                &elt,
950494
                Transform::identity(),
950494
                None,
950494
                pattern_values,
950494
            ));
950494

            
950494
            pattern_draw_ctx
950494
                .with_alpha(pattern.opacity, &mut |dc| {
950494
                    dc.with_discrete_layer(
950494
                        &stacking_ctx,
950494
                        acquired_nodes,
950494
                        &pattern_viewport,
950494
                        None,
950494
                        false,
950494
                        &mut |an, dc, new_viewport| {
950494
                            pattern_node.draw_children(
950494
                                an,
950494
                                &pattern_cascaded,
950494
                                new_viewport,
950494
                                dc,
950494
                                false,
950494
                            )
950494
                        },
950494
                    )
950494
                })
950494
                .map(|_| ())?;
        }
        // Set the final surface as a Cairo pattern into the Cairo context
950494
        let pattern = cairo::SurfacePattern::create(&surface);
950494
        if let Some(m) = affine.invert() {
950494
            pattern.set_matrix(ValidTransform::try_from(m)?.into());
950494
            pattern.set_extend(cairo::Extend::Repeat);
950494
            pattern.set_filter(cairo::Filter::Best);
950494
            self.cr.set_source(&pattern)?;
        }
950494
        Ok(true)
9500627
    }
36093711
    fn set_paint_source(
36093711
        &mut self,
36093711
        paint_source: &UserSpacePaintSource,
36093711
        acquired_nodes: &mut AcquiredNodes<'_>,
36093711
        viewport: &Viewport,
36093711
    ) -> Result<bool, InternalRenderingError> {
36093711
        match *paint_source {
2679
            UserSpacePaintSource::Gradient(ref gradient, _c) => {
2679
                set_gradient_on_cairo(&self.cr, gradient)?;
2679
                Ok(true)
            }
9500627
            UserSpacePaintSource::Pattern(ref pattern, ref c) => {
9500627
                if self.set_pattern(pattern, acquired_nodes, viewport)? {
950494
                    Ok(true)
8550133
                } else if let Some(c) = c {
171
                    set_source_color_on_cairo(&self.cr, c);
171
                    Ok(true)
                } else {
8549962
                    Ok(false)
                }
            }
18054598
            UserSpacePaintSource::SolidColor(ref c) => {
18054598
                set_source_color_on_cairo(&self.cr, c);
18054598
                Ok(true)
            }
8535807
            UserSpacePaintSource::None => Ok(false),
        }
36093711
    }
    /// Computes and returns a surface corresponding to the given paint server.
247
    pub fn get_paint_source_surface(
247
        &mut self,
247
        width: i32,
247
        height: i32,
247
        acquired_nodes: &mut AcquiredNodes<'_>,
247
        paint_source: &UserSpacePaintSource,
247
        viewport: &Viewport,
247
    ) -> Result<SharedImageSurface, InternalRenderingError> {
247
        let mut surface = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
247
        surface.draw(&mut |cr| {
247
            let mut temporary_draw_ctx = self.nested(cr);
            // FIXME: we are ignoring any error
247
            let had_paint_server =
247
                temporary_draw_ctx.set_paint_source(paint_source, acquired_nodes, viewport)?;
247
            if had_paint_server {
247
                temporary_draw_ctx.cr.paint()?;
            }
247
            Ok(())
247
        })?;
247
        Ok(surface.share()?)
247
    }
18048727
    pub fn draw_layer(
18048727
        &mut self,
18048727
        layer: &Layer,
18048727
        acquired_nodes: &mut AcquiredNodes<'_>,
18048727
        clipping: bool,
18048727
        viewport: &Viewport,
18048727
    ) -> Result<BoundingBox, InternalRenderingError> {
18048727
        match &layer.kind {
18029119
            LayerKind::Shape(shape) => self.draw_shape(
18029119
                shape,
18029119
                &layer.stacking_ctx,
18029119
                acquired_nodes,
18029119
                clipping,
18029119
                viewport,
18029119
            ),
17556
            LayerKind::Text(text) => self.draw_text(
17556
                text,
17556
                &layer.stacking_ctx,
17556
                acquired_nodes,
17556
                clipping,
17556
                viewport,
17556
            ),
2052
            LayerKind::Image(image) => self.draw_image(
2052
                image,
2052
                &layer.stacking_ctx,
2052
                acquired_nodes,
2052
                clipping,
2052
                viewport,
2052
            ),
            LayerKind::Group(group) => self.draw_group(
                group,
                &layer.stacking_ctx,
                acquired_nodes,
                clipping,
                viewport,
            ),
        }
18048727
    }
18029119
    fn draw_shape(
18029119
        &mut self,
18029119
        shape: &Shape,
18029119
        stacking_ctx: &StackingContext,
18029119
        acquired_nodes: &mut AcquiredNodes<'_>,
18029119
        clipping: bool,
18029119
        viewport: &Viewport,
18029119
    ) -> Result<BoundingBox, InternalRenderingError> {
18029119
        self.with_discrete_layer(
18029119
            stacking_ctx,
18029119
            acquired_nodes,
18029119
            viewport,
18029119
            None,
18029119
            clipping,
18029119
            &mut |an, dc, new_viewport| {
18029100
                let cr = dc.cr.clone();
18029100
                let transform =
18029100
                    dc.get_transform_for_stacking_ctx(new_viewport, stacking_ctx, clipping)?;
18029100
                let mut path_helper = PathHelper::new(&cr, transform, &shape.path.cairo_path);
18029100

            
18029100
                if clipping {
741
                    if stacking_ctx.is_visible {
722
                        cr.set_fill_rule(cairo::FillRule::from(shape.clip_rule));
722
                        path_helper.set()?;
19
                    }
741
                    return Ok(new_viewport.empty_bbox());
18028359
                }
18028359

            
18028359
                cr.set_antialias(cairo::Antialias::from(shape.shape_rendering));
18028359

            
18028359
                setup_cr_for_stroke(&cr, &shape.stroke);
18028359

            
18028359
                cr.set_fill_rule(cairo::FillRule::from(shape.fill_rule));
18028359

            
18028359
                path_helper.set()?;
18028359
                let bbox = compute_stroke_and_fill_box(
18028359
                    &cr,
18028359
                    &shape.stroke,
18028359
                    &shape.stroke_paint,
18028359
                    &dc.initial_viewport,
18028359
                )?;
18028359
                if stacking_ctx.is_visible {
72112676
                    for &target in &shape.paint_order.targets {
                        // fill and stroke operations will preserve the path.
                        // markers operation will clear the path.
54084507
                        match target {
                            PaintTarget::Fill => {
18028169
                                path_helper.set()?;
18028169
                                let had_paint_server =
18028169
                                    dc.set_paint_source(&shape.fill_paint, an, viewport)?;
18028169
                                if had_paint_server {
9466864
                                    cr.fill_preserve()?;
8561305
                                }
                            }
                            PaintTarget::Stroke => {
18028169
                                path_helper.set()?;
18028169
                                if shape.stroke.non_scaling {
19
                                    cr.set_matrix(dc.initial_viewport.transform.into());
18028150
                                }
18028169
                                let had_paint_server =
18028169
                                    dc.set_paint_source(&shape.stroke_paint, an, viewport)?;
18028169
                                if had_paint_server {
9521660
                                    cr.stroke_preserve()?;
8506509
                                }
                            }
                            PaintTarget::Markers => {
18028169
                                path_helper.unset();
18028169
                                marker::render_markers_for_shape(
18028169
                                    shape,
18028169
                                    new_viewport,
18028169
                                    dc,
18028169
                                    an,
18028169
                                    clipping,
18028169
                                )?;
                            }
                        }
                    }
190
                }
18028359
                path_helper.unset();
18028359
                Ok(bbox)
18029119
            },
18029119
        )
18029119
    }
2052
    fn paint_surface(
2052
        &mut self,
2052
        surface: &SharedImageSurface,
2052
        width: f64,
2052
        height: f64,
2052
        image_rendering: ImageRendering,
2052
        viewport: &Viewport,
2052
    ) -> Result<(), cairo::Error> {
2052
        let cr = self.cr.clone();
2052

            
2052
        // We need to set extend appropriately, so can't use cr.set_source_surface().
2052
        //
2052
        // If extend is left at its default value (None), then bilinear scaling uses
2052
        // transparency outside of the image producing incorrect results.
2052
        // For example, in svg1.1/filters-blend-01-b.svgthere's a completely
2052
        // opaque 100×1 image of a gradient scaled to 100×98 which ends up
2052
        // transparent almost everywhere without this fix (which it shouldn't).
2052
        let ptn = surface.to_cairo_pattern();
2052
        ptn.set_extend(cairo::Extend::Pad);
2052

            
2052
        let interpolation = Interpolation::from(image_rendering);
2052

            
2052
        ptn.set_filter(cairo::Filter::from(interpolation));
2052
        cr.set_matrix(viewport.transform.into());
2052
        cr.set_source(&ptn)?;
        // Clip is needed due to extend being set to pad.
2052
        clip_to_rectangle(&cr, &viewport.transform, &Rect::from_size(width, height));
2052

            
2052
        cr.paint()
2052
    }
2052
    fn draw_image(
2052
        &mut self,
2052
        image: &Image,
2052
        stacking_ctx: &StackingContext,
2052
        acquired_nodes: &mut AcquiredNodes<'_>,
2052
        clipping: bool,
2052
        viewport: &Viewport,
2052
    ) -> Result<BoundingBox, InternalRenderingError> {
2052
        let image_width = image.surface.width();
2052
        let image_height = image.surface.height();
2052
        if clipping || image.rect.is_empty() || image_width == 0 || image_height == 0 {
            return Ok(viewport.empty_bbox());
2052
        }
2052

            
2052
        let image_width = f64::from(image_width);
2052
        let image_height = f64::from(image_height);
2052
        let vbox = ViewBox::from(Rect::from_size(image_width, image_height));
2052

            
2052
        // The bounding box for <image> is decided by the values of the image's x, y, w, h
2052
        // and not by the final computed image bounds.
2052
        let bounds = viewport.empty_bbox().with_rect(image.rect);
2052

            
2052
        let layout_viewport = LayoutViewport {
2052
            vbox: Some(vbox),
2052
            geometry: image.rect,
2052
            preserve_aspect_ratio: image.aspect,
2052
            overflow: image.overflow,
2052
        };
2052

            
2052
        if stacking_ctx.is_visible {
2052
            self.with_discrete_layer(
2052
                stacking_ctx,
2052
                acquired_nodes,
2052
                viewport,
2052
                Some(layout_viewport),
2052
                clipping,
2052
                &mut |_an, dc, new_viewport| {
2052
                    dc.paint_surface(
2052
                        &image.surface,
2052
                        image_width,
2052
                        image_height,
2052
                        image.image_rendering,
2052
                        new_viewport,
2052
                    )?;
2052
                    Ok(bounds)
2052
                },
2052
            )
        } else {
            Ok(bounds)
        }
2052
    }
    fn draw_group(
        &mut self,
        _group: &Group,
        _stacking_ctx: &StackingContext,
        _acquired_nodes: &mut AcquiredNodes<'_>,
        _clipping: bool,
        _viewport: &Viewport,
    ) -> Result<BoundingBox, InternalRenderingError> {
        unimplemented!();
        /*
        self.with_discrete_layer(
            stacking_ctx,
            acquired_nodes,
            viewport,
            group.establish_viewport,
            clipping,
            &mut |an, dc, new_viewport| {
                for layer in &group.children {
                    dc.draw_layer(layer, an, clipping, &new_viewport)?;
                }
            },
        )
        */
    }
19893
    fn draw_text_span(
19893
        &mut self,
19893
        span: &TextSpan,
19893
        acquired_nodes: &mut AcquiredNodes<'_>,
19893
        clipping: bool,
19893
        viewport: &Viewport,
19893
    ) -> Result<BoundingBox, InternalRenderingError> {
19893
        let path = pango_layout_to_cairo_path(span.x, span.y, &span.layout, span.gravity)?;
19893
        if path.is_empty() {
            // Empty strings, or only-whitespace text, get turned into empty paths.
            // In that case, we really want to return "no bounds" rather than an
            // empty rectangle.
1121
            return Ok(viewport.empty_bbox());
18772
        }
18772

            
18772
        // #851 - We can't just render all text as paths for PDF; it
18772
        // needs the actual text content so text is selectable by PDF
18772
        // viewers.
18772
        let can_use_text_as_path = self.cr.target().type_() != cairo::SurfaceType::Pdf;
18772

            
18772
        self.cr
18772
            .set_antialias(cairo::Antialias::from(span.text_rendering));
18772

            
18772
        setup_cr_for_stroke(&self.cr, &span.stroke);
18772

            
18772
        self.cr.set_matrix(viewport.transform.into());
18772

            
18772
        if clipping {
19
            path.to_cairo_context(&self.cr)?;
19
            return Ok(viewport.empty_bbox());
18753
        }
18753

            
18753
        path.to_cairo_context(&self.cr)?;
18753
        let bbox = compute_stroke_and_fill_box(
18753
            &self.cr,
18753
            &span.stroke,
18753
            &span.stroke_paint,
18753
            &self.initial_viewport,
18753
        )?;
18753
        self.cr.new_path();
18753

            
18753
        if span.is_visible {
18563
            if let Some(ref link_target) = span.link_target {
76
                self.link_tag_begin(link_target);
18487
            }
74252
            for &target in &span.paint_order.targets {
55689
                match target {
                    PaintTarget::Fill => {
18563
                        let had_paint_server =
18563
                            self.set_paint_source(&span.fill_paint, acquired_nodes, viewport)?;
18563
                        if had_paint_server {
18506
                            if can_use_text_as_path {
18430
                                path.to_cairo_context(&self.cr)?;
18430
                                self.cr.fill()?;
18430
                                self.cr.new_path();
                            } else {
76
                                self.cr.move_to(span.x, span.y);
76

            
76
                                let matrix = self.cr.matrix();
76

            
76
                                let rotation_from_gravity = span.gravity.to_rotation();
76
                                if !rotation_from_gravity.approx_eq_cairo(0.0) {
                                    self.cr.rotate(-rotation_from_gravity);
76
                                }
76
                                pangocairo::functions::update_layout(&self.cr, &span.layout);
76
                                pangocairo::functions::show_layout(&self.cr, &span.layout);
76

            
76
                                self.cr.set_matrix(matrix);
                            }
57
                        }
                    }
                    PaintTarget::Stroke => {
18563
                        let had_paint_server =
18563
                            self.set_paint_source(&span.stroke_paint, acquired_nodes, viewport)?;
18563
                        if had_paint_server {
665
                            path.to_cairo_context(&self.cr)?;
665
                            self.cr.stroke()?;
665
                            self.cr.new_path();
17898
                        }
                    }
18563
                    PaintTarget::Markers => {}
                }
            }
18563
            if span.link_target.is_some() {
76
                self.link_tag_end();
18487
            }
190
        }
18753
        Ok(bbox)
19893
    }
17556
    fn draw_text(
17556
        &mut self,
17556
        text: &Text,
17556
        stacking_ctx: &StackingContext,
17556
        acquired_nodes: &mut AcquiredNodes<'_>,
17556
        clipping: bool,
17556
        viewport: &Viewport,
17556
    ) -> Result<BoundingBox, InternalRenderingError> {
17556
        self.with_discrete_layer(
17556
            stacking_ctx,
17556
            acquired_nodes,
17556
            viewport,
17556
            None,
17556
            clipping,
17556
            &mut |an, dc, new_viewport| {
17556
                let mut bbox = new_viewport.empty_bbox();
37449
                for span in &text.spans {
19893
                    let span_bbox = dc.draw_text_span(span, an, clipping, new_viewport)?;
19893
                    bbox.insert(&span_bbox);
                }
17556
                Ok(bbox)
17556
            },
17556
        )
17556
    }
209
    pub fn get_snapshot(
209
        &self,
209
        width: i32,
209
        height: i32,
209
    ) -> Result<SharedImageSurface, InternalRenderingError> {
        // TODO: as far as I can tell this should not render elements past the last (topmost) one
        // with enable-background: new (because technically we shouldn't have been caching them).
        // Right now there are no enable-background checks whatsoever.
        //
        // Addendum: SVG 2 has deprecated the enable-background property, and replaced it with an
        // "isolation" property from the CSS Compositing and Blending spec.
        //
        // Deprecation:
        //   https://www.w3.org/TR/filter-effects-1/#AccessBackgroundImage
        //
        // BackgroundImage, BackgroundAlpha in the "in" attribute of filter primitives:
        //   https://www.w3.org/TR/filter-effects-1/#attr-valuedef-in-backgroundimage
        //
        // CSS Compositing and Blending, "isolation" property:
        //   https://www.w3.org/TR/compositing-1/#isolation
209
        let mut surface = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
209
        surface.draw(&mut |cr| {
            // TODO: apparently DrawingCtx.cr_stack is just a way to store pairs of
            // (surface, transform).  Can we turn it into a DrawingCtx.surface_stack
            // instead?  See what CSS isolation would like to call that; are the pairs just
            // stacking contexts instead, or the result of rendering stacking contexts?
247
            for (depth, draw) in self.cr_stack.borrow().iter().enumerate() {
247
                let affines = CompositingAffines::new(
247
                    Transform::from(draw.matrix()),
247
                    *self.initial_viewport.transform,
247
                    depth,
247
                );
247

            
247
                cr.set_matrix(ValidTransform::try_from(affines.for_snapshot)?.into());
247
                cr.set_source_surface(draw.target(), 0.0, 0.0)?;
247
                cr.paint()?;
            }
209
            Ok(())
209
        })?;
209
        Ok(surface.share()?)
209
    }
475
    pub fn draw_node_to_surface(
475
        &mut self,
475
        node: &Node,
475
        acquired_nodes: &mut AcquiredNodes<'_>,
475
        cascaded: &CascadedValues<'_>,
475
        transform: ValidTransform,
475
        width: i32,
475
        height: i32,
475
    ) -> Result<SharedImageSurface, InternalRenderingError> {
475
        let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
475
        let save_cr = self.cr.clone();
        {
475
            let cr = cairo::Context::new(&surface)?;
475
            cr.set_matrix(transform.into());
475

            
475
            self.cr = cr;
475
            let viewport = Viewport {
475
                dpi: self.config.dpi,
475
                transform,
475
                vbox: ViewBox::from(Rect::from_size(f64::from(width), f64::from(height))),
475
            };
475

            
475
            // FIXME: if this returns an error, we will not restore the self.cr as per below
475
            let _ = self.draw_node_from_stack(node, acquired_nodes, cascaded, &viewport, false)?;
        }
475
        self.cr = save_cr;
475

            
475
        Ok(SharedImageSurface::wrap(surface, SurfaceType::SRgb)?)
475
    }
27757043
    pub fn draw_node_from_stack(
27757043
        &mut self,
27757043
        node: &Node,
27757043
        acquired_nodes: &mut AcquiredNodes<'_>,
27757043
        cascaded: &CascadedValues<'_>,
27757043
        viewport: &Viewport,
27757043
        clipping: bool,
27757043
    ) -> Result<BoundingBox, InternalRenderingError> {
27757043
        let stack_top = self.drawsub_stack.pop();
27757043
        let draw = if let Some(ref top) = stack_top {
42199
            top == node
        } else {
27714844
            true
        };
27757043
        let res = if draw {
27737530
            node.draw(acquired_nodes, cascaded, viewport, self, clipping)
        } else {
19513
            Ok(viewport.empty_bbox())
        };
27757043
        if let Some(top) = stack_top {
42199
            self.drawsub_stack.push(top);
27714844
        }
27757043
        res
27757043
    }
9507296
    pub fn draw_from_use_node(
9507296
        &mut self,
9507296
        node: &Node,
9507296
        acquired_nodes: &mut AcquiredNodes<'_>,
9507296
        values: &ComputedValues,
9507296
        use_rect: Rect,
9507296
        link: &NodeId,
9507296
        clipping: bool,
9507296
        viewport: &Viewport,
9507296
        fill_paint: Rc<PaintSource>,
9507296
        stroke_paint: Rc<PaintSource>,
9507296
    ) -> Result<BoundingBox, InternalRenderingError> {
        // <use> is an element that is used directly, unlike
        // <pattern>, which is used through a fill="url(#...)"
        // reference.  However, <use> will always reference another
        // element, potentially itself or an ancestor of itself (or
        // another <use> which references the first one, etc.).  So,
        // we acquire the <use> element itself so that circular
        // references can be caught.
9507296
        let _self_acquired = match acquired_nodes.acquire_ref(node) {
9504655
            Ok(n) => n,
2641
            Err(AcquireError::CircularReference(circular)) => {
2641
                rsvg_log!(self.session, "circular reference in element {}", circular);
2641
                return Err(InternalRenderingError::CircularReference(circular));
            }
            _ => unreachable!(),
        };
9504655
        let acquired = match acquired_nodes.acquire(link) {
9504161
            Ok(acquired) => acquired,
            Err(AcquireError::CircularReference(circular)) => {
                rsvg_log!(
                    self.session,
                    "circular reference from {} to element {}",
                    node,
                    circular
                );
                return Err(InternalRenderingError::CircularReference(circular));
            }
            Err(AcquireError::MaxReferencesExceeded) => {
19
                return Err(InternalRenderingError::LimitExceeded(
19
                    ImplementationLimit::TooManyReferencedElements,
19
                ));
            }
            Err(AcquireError::InvalidLinkType(_)) => unreachable!(),
475
            Err(AcquireError::LinkNotFound(node_id)) => {
475
                rsvg_log!(
475
                    self.session,
475
                    "element {} references nonexistent \"{}\"",
475
                    node,
475
                    node_id
475
                );
475
                return Ok(viewport.empty_bbox());
            }
        };
        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
9504161
        if use_rect.is_empty() {
            return Ok(viewport.empty_bbox());
9504161
        }
9504161

            
9504161
        let child = acquired.get();
9504161

            
9504161
        if clipping && !element_can_be_used_inside_use_inside_clip_path(&child.borrow_element()) {
19
            return Ok(viewport.empty_bbox());
9504142
        }
9504142
        let use_transform = ValidTransform::try_from(values.transform())?;
9504142
        let use_viewport = viewport.with_composed_transform(use_transform)?;
9504142
        let use_element = node.borrow_element();
9504142
        let defines_a_viewport = if is_element_of_type!(child, Symbol) {
171
            let symbol = borrow_element_as!(child, Symbol);
171
            Some((symbol.get_viewbox(), symbol.get_preserve_aspect_ratio()))
9503971
        } else if is_element_of_type!(child, Svg) {
19
            let svg = borrow_element_as!(child, Svg);
19
            Some((svg.get_viewbox(), svg.get_preserve_aspect_ratio()))
        } else {
9503952
            None
        };
9504142
        let res = if let Some((vbox, preserve_aspect_ratio)) = defines_a_viewport {
            // <symbol> and <svg> define a viewport, as described in the specification:
            // https://www.w3.org/TR/SVG2/struct.html#UseElement
            // https://gitlab.gnome.org/GNOME/librsvg/-/issues/875#note_1482705
190
            let elt = child.borrow_element();
190

            
190
            let child_values = elt.get_computed_values();
190

            
190
            let stacking_ctx = Box::new(StackingContext::new(
190
                self.session(),
190
                acquired_nodes,
190
                &use_element,
190
                Transform::identity(),
190
                None,
190
                values,
190
            ));
190

            
190
            let layout_viewport = LayoutViewport {
190
                vbox,
190
                geometry: use_rect,
190
                preserve_aspect_ratio,
190
                overflow: child_values.overflow(),
190
            };
190

            
190
            self.with_discrete_layer(
190
                &stacking_ctx,
190
                acquired_nodes,
190
                &use_viewport,
190
                Some(layout_viewport),
190
                clipping,
190
                &mut |an, dc, new_viewport| {
190
                    child.draw_children(
190
                        an,
190
                        &CascadedValues::new_from_values(
190
                            child,
190
                            values,
190
                            Some(fill_paint.clone()),
190
                            Some(stroke_paint.clone()),
190
                        ),
190
                        new_viewport,
190
                        dc,
190
                        clipping,
190
                    )
190
                },
190
            )
        } else {
            // otherwise the referenced node is not a <symbol>; process it generically
9503952
            let stacking_ctx = Box::new(StackingContext::new(
9503952
                self.session(),
9503952
                acquired_nodes,
9503952
                &use_element,
9503952
                Transform::new_translate(use_rect.x0, use_rect.y0),
9503952
                None,
9503952
                values,
9503952
            ));
9503952

            
9503952
            self.with_discrete_layer(
9503952
                &stacking_ctx,
9503952
                acquired_nodes,
9503952
                &use_viewport,
9503952
                None,
9503952
                clipping,
9503952
                &mut |an, dc, new_viewport| {
9503933
                    child.draw(
9503933
                        an,
9503933
                        &CascadedValues::new_from_values(
9503933
                            child,
9503933
                            values,
9503933
                            Some(fill_paint.clone()),
9503933
                            Some(stroke_paint.clone()),
9503933
                        ),
9503933
                        new_viewport,
9503933
                        dc,
9503933
                        clipping,
9503933
                    )
9503952
                },
9503952
            )
        };
9504142
        if let Ok(bbox) = res {
9503439
            let mut res_bbox = BoundingBox::new().with_transform(*viewport.transform);
9503439
            res_bbox.insert(&bbox);
9503439
            Ok(res_bbox)
        } else {
703
            res
        }
9507296
    }
    /// Extracts the font options for the current state of the DrawingCtx.
    ///
    /// You can use the font options later with create_pango_context().
17556
    pub fn get_font_options(&self) -> FontOptions {
17556
        let mut options = cairo::FontOptions::new().unwrap();
17556
        if self.config.testing {
16644
            options.set_antialias(cairo::Antialias::Gray);
16644
        }
17556
        options.set_hint_style(cairo::HintStyle::None);
17556
        options.set_hint_metrics(cairo::HintMetrics::Off);
17556

            
17556
        FontOptions { options }
17556
    }
}
impl From<ImageRendering> for Interpolation {
3325
    fn from(r: ImageRendering) -> Interpolation {
3325
        match r {
            ImageRendering::Pixelated
            | ImageRendering::CrispEdges
19
            | ImageRendering::OptimizeSpeed => Interpolation::Nearest,
            ImageRendering::Smooth
            | ImageRendering::OptimizeQuality
            | ImageRendering::HighQuality
3306
            | ImageRendering::Auto => Interpolation::Smooth,
        }
3325
    }
}
/// Create a Pango context with a particular configuration.
19931
pub fn create_pango_context(font_options: &FontOptions) -> pango::Context {
19931
    let font_map = pangocairo::FontMap::default();
19931
    let context = font_map.create_context();
19931

            
19931
    context.set_round_glyph_positions(false);
19931

            
19931
    pangocairo::functions::context_set_font_options(&context, Some(&font_options.options));
19931

            
19931
    // Pango says this about pango_cairo_context_set_resolution():
19931
    //
19931
    //     Sets the resolution for the context. This is a scale factor between
19931
    //     points specified in a #PangoFontDescription and Cairo units. The
19931
    //     default value is 96, meaning that a 10 point font will be 13
19931
    //     units high. (10 * 96. / 72. = 13.3).
19931
    //
19931
    // I.e. Pango font sizes in a PangoFontDescription are in *points*, not pixels.
19931
    // However, we are normalizing everything to userspace units, which amount to
19931
    // pixels.  So, we will use 72.0 here to make Pango not apply any further scaling
19931
    // to the size values we give it.
19931
    //
19931
    // An alternative would be to divide our font sizes by (dpi_y / 72) to effectively
19931
    // cancel out Pango's scaling, but it's probably better to deal with Pango-isms
19931
    // right here, instead of spreading them out through our Length normalization
19931
    // code.
19931
    pangocairo::functions::context_set_resolution(&context, 72.0);
19931

            
19931
    context
19931
}
18055586
pub fn set_source_color_on_cairo(cr: &cairo::Context, color: &cssparser::Color) {
18055586
    let rgba = color_to_rgba(color);
18055586

            
18055586
    cr.set_source_rgba(
18055586
        f64::from(rgba.red.unwrap_or(0)) / 255.0,
18055586
        f64::from(rgba.green.unwrap_or(0)) / 255.0,
18055586
        f64::from(rgba.blue.unwrap_or(0)) / 255.0,
18055586
        f64::from(rgba.alpha.unwrap_or(0.0)),
18055586
    );
18055586
}
2679
fn set_gradient_on_cairo(
2679
    cr: &cairo::Context,
2679
    gradient: &UserSpaceGradient,
2679
) -> Result<(), InternalRenderingError> {
2679
    let g = match gradient.variant {
2071
        GradientVariant::Linear { x1, y1, x2, y2 } => {
2071
            cairo::Gradient::clone(&cairo::LinearGradient::new(x1, y1, x2, y2))
        }
        GradientVariant::Radial {
608
            cx,
608
            cy,
608
            r,
608
            fx,
608
            fy,
608
            fr,
608
        } => cairo::Gradient::clone(&cairo::RadialGradient::new(fx, fy, fr, cx, cy, r)),
    };
2679
    g.set_matrix(ValidTransform::try_from(gradient.transform)?.into());
2679
    g.set_extend(cairo::Extend::from(gradient.spread));
10488
    for stop in &gradient.stops {
7809
        let UnitInterval(stop_offset) = stop.offset;
7809

            
7809
        let rgba = color_to_rgba(&stop.color);
7809

            
7809
        g.add_color_stop_rgba(
7809
            stop_offset,
7809
            f64::from(rgba.red.unwrap_or(0)) / 255.0,
7809
            f64::from(rgba.green.unwrap_or(0)) / 255.0,
7809
            f64::from(rgba.blue.unwrap_or(0)) / 255.0,
7809
            f64::from(rgba.alpha.unwrap_or(0.0)),
7809
        );
7809
    }
2679
    Ok(cr.set_source(&g)?)
2679
}
/// Converts a Pango layout to a Cairo path on the specified cr starting at (x, y).
/// Does not clear the current path first.
19893
fn pango_layout_to_cairo(
19893
    x: f64,
19893
    y: f64,
19893
    layout: &pango::Layout,
19893
    gravity: pango::Gravity,
19893
    cr: &cairo::Context,
19893
) {
19893
    let rotation_from_gravity = gravity.to_rotation();
19893
    let rotation = if !rotation_from_gravity.approx_eq_cairo(0.0) {
        Some(-rotation_from_gravity)
    } else {
19893
        None
    };
19893
    cr.move_to(x, y);
19893

            
19893
    let matrix = cr.matrix();
19893
    if let Some(rot) = rotation {
        cr.rotate(rot);
19893
    }
19893
    pangocairo::functions::update_layout(cr, layout);
19893
    pangocairo::functions::layout_path(cr, layout);
19893
    cr.set_matrix(matrix);
19893
}
/// Converts a Pango layout to a CairoPath starting at (x, y).
19893
fn pango_layout_to_cairo_path(
19893
    x: f64,
19893
    y: f64,
19893
    layout: &pango::Layout,
19893
    gravity: pango::Gravity,
19893
) -> Result<CairoPath, InternalRenderingError> {
19893
    let surface = cairo::RecordingSurface::create(cairo::Content::ColorAlpha, None)?;
19893
    let cr = cairo::Context::new(&surface)?;
19893
    pango_layout_to_cairo(x, y, layout, gravity, &cr);
19893
    let cairo_path = cr.copy_path()?;
19893
    Ok(CairoPath::from_cairo(cairo_path))
19893
}
// https://www.w3.org/TR/css-masking-1/#ClipPathElement
817
fn element_can_be_used_inside_clip_path(element: &Element) -> bool {
    use ElementData::*;
19
    matches!(
817
        element.element_data,
        Circle(_)
            | Ellipse(_)
            | Line(_)
            | Path(_)
            | Polygon(_)
            | Polyline(_)
            | Rect(_)
            | Text(_)
            | Use(_)
    )
817
}
// https://www.w3.org/TR/css-masking-1/#ClipPathElement
19
fn element_can_be_used_inside_use_inside_clip_path(element: &Element) -> bool {
    use ElementData::*;
19
    matches!(
19
        element.element_data,
        Circle(_) | Ellipse(_) | Line(_) | Path(_) | Polygon(_) | Polyline(_) | Rect(_) | Text(_)
    )
19
}
#[derive(Debug)]
struct CompositingAffines {
    pub outside_temporary_surface: Transform,
    #[allow(unused)]
    pub initial: Transform,
    pub for_temporary_surface: Transform,
    pub compositing: Transform,
    pub for_snapshot: Transform,
}
impl CompositingAffines {
21223
    fn new(current: Transform, initial: Transform, cr_stack_depth: usize) -> CompositingAffines {
21223
        let is_topmost_temporary_surface = cr_stack_depth == 0;
21223

            
21223
        let initial_inverse = initial.invert().unwrap();
21223
        let outside_temporary_surface = if is_topmost_temporary_surface {
12198
            current
        } else {
9025
            current.post_transform(&initial_inverse)
        };
21223
        let (scale_x, scale_y) = initial.transform_distance(1.0, 1.0);
21223
        let for_temporary_surface = if is_topmost_temporary_surface {
12198
            current
12198
                .post_transform(&initial_inverse)
12198
                .post_scale(scale_x, scale_y)
        } else {
9025
            current
        };
21223
        let compositing = if is_topmost_temporary_surface {
12198
            initial.pre_scale(1.0 / scale_x, 1.0 / scale_y)
        } else {
9025
            Transform::identity()
        };
21223
        let for_snapshot = compositing.invert().unwrap();
21223

            
21223
        CompositingAffines {
21223
            outside_temporary_surface,
21223
            initial,
21223
            for_temporary_surface,
21223
            compositing,
21223
            for_snapshot,
21223
        }
21223
    }
}
18047112
fn compute_stroke_and_fill_extents(
18047112
    cr: &cairo::Context,
18047112
    stroke: &Stroke,
18047112
    stroke_paint_source: &UserSpacePaintSource,
18047112
    initial_viewport: &Viewport,
18047112
) -> Result<PathExtents, InternalRenderingError> {
18047112
    // Dropping the precision of cairo's bezier subdivision, yielding 2x
18047112
    // _rendering_ time speedups, are these rather expensive operations
18047112
    // really needed here? */
18047112
    let backup_tolerance = cr.tolerance();
18047112
    cr.set_tolerance(1.0);
    // Bounding box for fill
    //
    // Unlike the case for stroke, for fills we always compute the bounding box.
    // In GNOME we have SVGs for symbolic icons where each icon has a bounding
    // rectangle with no fill and no stroke, and inside it there are the actual
    // paths for the icon's shape.  We need to be able to compute the bounding
    // rectangle's extents, even when it has no fill nor stroke.
18047112
    let (x0, y0, x1, y1) = cr.fill_extents()?;
18047112
    let fill_extents = if x0 != 0.0 || y0 != 0.0 || x1 != 0.0 || y1 != 0.0 {
9597508
        Some(Rect::new(x0, y0, x1, y1))
    } else {
8449604
        None
    };
    // Bounding box for stroke
    //
    // When presented with a line width of 0, Cairo returns a
    // stroke_extents rectangle of (0, 0, 0, 0).  This would cause the
    // bbox to include a lone point at the origin, which is wrong, as a
    // stroke of zero width should not be painted, per
    // https://www.w3.org/TR/SVG2/painting.html#StrokeWidth
    //
    // So, see if the stroke width is 0 and just not include the stroke in the
    // bounding box if so.
18047112
    let stroke_extents = if !stroke.width.approx_eq_cairo(0.0)
18046637
        && !matches!(stroke_paint_source, UserSpacePaintSource::None)
    {
9522287
        let backup_matrix = if stroke.non_scaling {
19
            let matrix = cr.matrix();
19
            cr.set_matrix(initial_viewport.transform.into());
19
            Some(matrix)
        } else {
9522268
            None
        };
9522287
        let (x0, y0, x1, y1) = cr.stroke_extents()?;
9522287
        if let Some(matrix) = backup_matrix {
19
            cr.set_matrix(matrix);
9522268
        }
9522287
        Some(Rect::new(x0, y0, x1, y1))
    } else {
8524825
        None
    };
    // objectBoundingBox
18047112
    let (x0, y0, x1, y1) = cr.path_extents()?;
18047112
    let path_extents = Some(Rect::new(x0, y0, x1, y1));
18047112

            
18047112
    // restore tolerance
18047112

            
18047112
    cr.set_tolerance(backup_tolerance);
18047112

            
18047112
    Ok(PathExtents {
18047112
        path_only: path_extents,
18047112
        fill: fill_extents,
18047112
        stroke: stroke_extents,
18047112
    })
18047112
}
18047112
fn compute_stroke_and_fill_box(
18047112
    cr: &cairo::Context,
18047112
    stroke: &Stroke,
18047112
    stroke_paint_source: &UserSpacePaintSource,
18047112
    initial_viewport: &Viewport,
18047112
) -> Result<BoundingBox, InternalRenderingError> {
18047112
    let extents =
18047112
        compute_stroke_and_fill_extents(cr, stroke, stroke_paint_source, initial_viewport)?;
18047112
    let ink_rect = match (extents.fill, extents.stroke) {
8445082
        (None, None) => None,
79743
        (Some(f), None) => Some(f),
4522
        (None, Some(s)) => Some(s),
9517765
        (Some(f), Some(s)) => Some(f.union(&s)),
    };
18047112
    let mut bbox = BoundingBox::new().with_transform(Transform::from(cr.matrix()));
18047112
    if let Some(rect) = extents.path_only {
18047112
        bbox = bbox.with_rect(rect);
18047112
    }
18047112
    if let Some(ink_rect) = ink_rect {
9602030
        bbox = bbox.with_ink_rect(ink_rect);
9602030
    }
18047112
    Ok(bbox)
18047112
}
18047131
fn setup_cr_for_stroke(cr: &cairo::Context, stroke: &Stroke) {
18047131
    cr.set_line_width(stroke.width);
18047131
    cr.set_miter_limit(stroke.miter_limit.0);
18047131
    cr.set_line_cap(cairo::LineCap::from(stroke.line_cap));
18047131
    cr.set_line_join(cairo::LineJoin::from(stroke.line_join));
18047131

            
18047131
    let total_length: f64 = stroke.dashes.iter().sum();
18047131

            
18047131
    if total_length > 0.0 {
2014
        cr.set_dash(&stroke.dashes, stroke.dash_offset);
18045117
    } else {
18045117
        cr.set_dash(&[], 0.0);
18045117
    }
18047131
}
/// escape quotes and backslashes with backslash
114
fn escape_link_target(value: &str) -> Cow<'_, str> {
114
    let regex = {
        static REGEX: OnceLock<Regex> = OnceLock::new();
114
        REGEX.get_or_init(|| Regex::new(r"['\\]").unwrap())
114
    };
114

            
114
    regex.replace_all(value, |caps: &Captures<'_>| {
        match caps.get(0).unwrap().as_str() {
            "'" => "\\'".to_owned(),
            "\\" => "\\\\".to_owned(),
            _ => unreachable!(),
        }
114
    })
114
}
10032
fn clip_to_rectangle(cr: &cairo::Context, transform: &ValidTransform, r: &Rect) {
10032
    cr.set_matrix((*transform).into());
10032

            
10032
    cr.rectangle(r.x0, r.y0, r.width(), r.height());
10032
    cr.clip();
10032
}
impl From<SpreadMethod> for cairo::Extend {
2679
    fn from(s: SpreadMethod) -> cairo::Extend {
2679
        match s {
2565
            SpreadMethod::Pad => cairo::Extend::Pad,
57
            SpreadMethod::Reflect => cairo::Extend::Reflect,
57
            SpreadMethod::Repeat => cairo::Extend::Repeat,
        }
2679
    }
}
impl From<StrokeLinejoin> for cairo::LineJoin {
18047131
    fn from(j: StrokeLinejoin) -> cairo::LineJoin {
18047131
        match j {
18044661
            StrokeLinejoin::Miter => cairo::LineJoin::Miter,
2432
            StrokeLinejoin::Round => cairo::LineJoin::Round,
38
            StrokeLinejoin::Bevel => cairo::LineJoin::Bevel,
        }
18047131
    }
}
impl From<StrokeLinecap> for cairo::LineCap {
18047131
    fn from(j: StrokeLinecap) -> cairo::LineCap {
18047131
        match j {
18043654
            StrokeLinecap::Butt => cairo::LineCap::Butt,
2869
            StrokeLinecap::Round => cairo::LineCap::Round,
608
            StrokeLinecap::Square => cairo::LineCap::Square,
        }
18047131
    }
}
impl From<MixBlendMode> for cairo::Operator {
12977
    fn from(m: MixBlendMode) -> cairo::Operator {
        use cairo::Operator;
12977
        match m {
12673
            MixBlendMode::Normal => Operator::Over,
19
            MixBlendMode::Multiply => Operator::Multiply,
19
            MixBlendMode::Screen => Operator::Screen,
19
            MixBlendMode::Overlay => Operator::Overlay,
19
            MixBlendMode::Darken => Operator::Darken,
19
            MixBlendMode::Lighten => Operator::Lighten,
19
            MixBlendMode::ColorDodge => Operator::ColorDodge,
19
            MixBlendMode::ColorBurn => Operator::ColorBurn,
19
            MixBlendMode::HardLight => Operator::HardLight,
19
            MixBlendMode::SoftLight => Operator::SoftLight,
38
            MixBlendMode::Difference => Operator::Difference,
19
            MixBlendMode::Exclusion => Operator::Exclusion,
19
            MixBlendMode::Hue => Operator::HslHue,
19
            MixBlendMode::Saturation => Operator::HslSaturation,
19
            MixBlendMode::Color => Operator::HslColor,
19
            MixBlendMode::Luminosity => Operator::HslLuminosity,
        }
12977
    }
}
impl From<ClipRule> for cairo::FillRule {
722
    fn from(c: ClipRule) -> cairo::FillRule {
722
        match c {
703
            ClipRule::NonZero => cairo::FillRule::Winding,
19
            ClipRule::EvenOdd => cairo::FillRule::EvenOdd,
        }
722
    }
}
impl From<FillRule> for cairo::FillRule {
18028359
    fn from(f: FillRule) -> cairo::FillRule {
18028359
        match f {
18025737
            FillRule::NonZero => cairo::FillRule::Winding,
2622
            FillRule::EvenOdd => cairo::FillRule::EvenOdd,
        }
18028359
    }
}
impl From<ShapeRendering> for cairo::Antialias {
18028359
    fn from(sr: ShapeRendering) -> cairo::Antialias {
18028359
        match sr {
18028112
            ShapeRendering::Auto | ShapeRendering::GeometricPrecision => cairo::Antialias::Default,
247
            ShapeRendering::OptimizeSpeed | ShapeRendering::CrispEdges => cairo::Antialias::None,
        }
18028359
    }
}
impl From<TextRendering> for cairo::Antialias {
18772
    fn from(tr: TextRendering) -> cairo::Antialias {
18772
        match tr {
            TextRendering::Auto
            | TextRendering::OptimizeLegibility
18772
            | TextRendering::GeometricPrecision => cairo::Antialias::Default,
            TextRendering::OptimizeSpeed => cairo::Antialias::None,
        }
18772
    }
}
impl From<cairo::Matrix> for Transform {
    #[inline]
18068012
    fn from(m: cairo::Matrix) -> Self {
18068012
        Self::new_unchecked(m.xx(), m.yx(), m.xy(), m.yy(), m.x0(), m.y0())
18068012
    }
}
impl From<ValidTransform> for cairo::Matrix {
    #[inline]
20029914
    fn from(t: ValidTransform) -> cairo::Matrix {
20029914
        cairo::Matrix::new(t.xx, t.yx, t.xy, t.yy, t.x0, t.y0)
20029914
    }
}
/// Extents for a path in its current coordinate system.
///
/// Normally you'll want to convert this to a BoundingBox, which has knowledge about just
/// what that coordinate system is.
pub struct PathExtents {
    /// Extents of the "plain", unstroked path, or `None` if the path is empty.
    pub path_only: Option<Rect>,
    /// Extents of just the fill, or `None` if the path is empty.
    pub fill: Option<Rect>,
    /// Extents for the stroked path, or `None` if the path is empty or zero-width.
    pub stroke: Option<Rect>,
}