1use float_cmp::approx_eq;
4use gio::prelude::*;
5use pango::prelude::FontMapExt;
6use regex::{Captures, Regex};
7use std::cell::RefCell;
8use std::convert::TryFrom;
9use std::rc::Rc;
10use std::{borrow::Cow, sync::OnceLock};
11
12use crate::accept_language::UserLanguage;
13use crate::bbox::BoundingBox;
14use crate::cairo_path::CairoPath;
15use crate::color::{Color, color_to_rgba};
16use crate::coord_units::CoordUnits;
17use crate::document::{AcquiredNodes, NodeId, RenderingOptions};
18use crate::dpi::Dpi;
19use crate::element::{DrawResult, Element, ElementData};
20use crate::error::{AcquireError, ImplementationLimit, InternalRenderingError, InvalidTransform};
21use crate::filters::{self, FilterPlan, FilterSpec, InputRequirements};
22use crate::float_eq_cairo::ApproxEqCairo;
23use crate::gradient::{GradientVariant, SpreadMethod, UserSpaceGradient};
24use crate::layout::{
25 Filter, Group, Image, Layer, LayerKind, LayoutViewport, Shape, StackingContext, Stroke, Text,
26 TextSpan,
27};
28use crate::length::*;
29use crate::limits;
30use crate::marker;
31use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
32use crate::paint_server::{PaintSource, UserSpacePaintSource};
33use crate::pattern::UserSpacePattern;
34use crate::properties::{
35 ClipRule, ComputedValues, FillRule, ImageRendering, MaskType, MixBlendMode, Opacity,
36 PaintTarget, ShapeRendering, StrokeLinecap, StrokeLinejoin, TextRendering,
37};
38use crate::rect::{IRect, Rect, rect_to_transform};
39use crate::rsvg_log;
40use crate::session::Session;
41use crate::surface_utils::shared_surface::{
42 ExclusiveImageSurface, Interpolation, SharedImageSurface, SurfaceType,
43};
44use crate::transform::{Transform, ValidTransform};
45use crate::unit_interval::UnitInterval;
46use crate::viewbox::ViewBox;
47use crate::{borrow_element_as, is_element_of_type};
48
49pub struct FontOptions {
53 options: cairo::FontOptions,
54}
55
56struct PathHelper<'a> {
60 cr: &'a cairo::Context,
61 transform: ValidTransform,
62 cairo_path: &'a CairoPath,
63 has_path: Option<bool>,
64}
65
66impl<'a> PathHelper<'a> {
67 pub fn new(
68 cr: &'a cairo::Context,
69 transform: ValidTransform,
70 cairo_path: &'a CairoPath,
71 ) -> Self {
72 PathHelper {
73 cr,
74 transform,
75 cairo_path,
76 has_path: None,
77 }
78 }
79
80 pub fn set(&mut self) -> Result<(), Box<InternalRenderingError>> {
81 match self.has_path {
82 Some(false) | None => {
83 self.has_path = Some(true);
84 self.cr.set_matrix(self.transform.into());
85 self.cairo_path.to_cairo_context(self.cr)
86 }
87 Some(true) => Ok(()),
88 }
89 }
90
91 pub fn unset(&mut self) {
92 match self.has_path {
93 Some(true) | None => {
94 self.has_path = Some(false);
95 self.cr.new_path();
96 }
97 Some(false) => {}
98 }
99 }
100}
101
102#[derive(Clone, Copy)]
104pub struct Viewport {
105 pub dpi: Dpi,
106
107 pub vbox: ViewBox,
109
110 pub transform: ValidTransform,
112}
113
114impl Viewport {
115 pub fn new(dpi: Dpi, view_box_width: f64, view_box_height: f64) -> Viewport {
118 Viewport {
119 dpi,
120 vbox: ViewBox::from(Rect::from_size(view_box_width, view_box_height)),
121 transform: Default::default(),
122 }
123 }
124
125 pub fn with_units(&self, units: CoordUnits) -> Viewport {
136 match units {
137 CoordUnits::ObjectBoundingBox => Viewport {
138 dpi: self.dpi,
139 vbox: ViewBox::from(Rect::from_size(1.0, 1.0)),
140 transform: self.transform,
141 },
142
143 CoordUnits::UserSpaceOnUse => Viewport {
144 dpi: self.dpi,
145 vbox: self.vbox,
146 transform: self.transform,
147 },
148 }
149 }
150
151 pub fn with_view_box(&self, width: f64, height: f64) -> Viewport {
153 Viewport {
154 dpi: self.dpi,
155 vbox: ViewBox::from(Rect::from_size(width, height)),
156 transform: self.transform,
157 }
158 }
159
160 pub fn with_explicit_transform(&self, transform: ValidTransform) -> Viewport {
165 Viewport {
166 dpi: self.dpi,
167 vbox: self.vbox,
168 transform,
169 }
170 }
171
172 pub fn with_composed_transform(
173 &self,
174 transform: ValidTransform,
175 ) -> Result<Viewport, InvalidTransform> {
176 let composed_transform =
177 ValidTransform::try_from((*self.transform).pre_transform(&transform))?;
178
179 Ok(Viewport {
180 dpi: self.dpi,
181 vbox: self.vbox,
182 transform: composed_transform,
183 })
184 }
185
186 pub fn empty_bbox(&self) -> Box<BoundingBox> {
187 Box::new(BoundingBox::new().with_transform(*self.transform))
188 }
189}
190
191#[derive(Clone)]
193pub 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
202pub 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 recursion_depth: u16,
219
220 stack_ptr: *const u8,
226}
227
228pub enum DrawingMode {
229 LimitToStack { node: Node, root: Node },
230
231 OnlyNode(Node),
232}
233
234#[derive(Copy, Clone)]
243pub enum SvgNesting {
244 Standalone,
245 ReferencedFromImageElement,
246}
247
248pub fn draw_tree(
252 session: Session,
253 mode: DrawingMode,
254 cr: &cairo::Context,
255 viewport_rect: Rect,
256 config: RenderingConfiguration,
257 acquired_nodes: &mut AcquiredNodes<'_>,
258) -> DrawResult {
259 let (drawsub_stack, node) = match mode {
260 DrawingMode::LimitToStack { node, root } => (node.ancestors().collect(), root),
261
262 DrawingMode::OnlyNode(node) => (Vec::new(), node),
263 };
264
265 let cascaded = CascadedValues::new_from_node(&node);
266
267 let user_transform = Transform::from(cr.matrix());
270 let mut user_bbox = Box::new(BoundingBox::new().with_transform(user_transform));
271
272 let transform = user_transform.pre_translate(viewport_rect.x0, viewport_rect.y0);
287
288 let valid_transform = ValidTransform::try_from(transform)?;
292 cr.set_matrix(valid_transform.into());
293
294 let viewport_rect = viewport_rect.translate((-viewport_rect.x0, -viewport_rect.y0));
296 let initial_viewport = Viewport {
297 dpi: config.dpi,
298 vbox: ViewBox::from(viewport_rect),
299 transform: valid_transform,
300 };
301
302 let mut draw_ctx = DrawingCtx::new(session, cr, &initial_viewport, config, drawsub_stack);
303
304 let content_bbox = draw_ctx.draw_node_from_stack(
305 &node,
306 acquired_nodes,
307 &cascaded,
308 &initial_viewport,
309 false,
310 )?;
311
312 user_bbox.insert(&content_bbox);
313
314 if draw_ctx.is_rendering_cancelled() {
315 Err(InternalRenderingError::Cancelled)?
316 } else {
317 Ok(user_bbox)
318 }
319}
320
321pub fn with_saved_cr<O, F>(cr: &cairo::Context, f: F) -> Result<O, Box<InternalRenderingError>>
322where
323 F: FnOnce() -> Result<O, Box<InternalRenderingError>>,
324{
325 cr.save()?;
326 match f() {
327 Ok(o) => {
328 cr.restore()?;
329 Ok(o)
330 }
331
332 Err(e) => Err(e),
333 }
334}
335
336impl Drop for DrawingCtx {
337 fn drop(&mut self) {
338 self.cr_stack.borrow_mut().pop();
339 }
340}
341
342const CAIRO_TAG_LINK: &str = "Link";
343
344impl DrawingCtx {
345 pub fn new(
346 session: Session,
347 cr: &cairo::Context,
348 initial_viewport: &Viewport,
349 config: RenderingConfiguration,
350 drawsub_stack: Vec<Node>,
351 ) -> DrawingCtx {
352 let stack_variable: u8 = 42;
353
354 DrawingCtx {
355 session,
356 initial_viewport: *initial_viewport,
357 cr_stack: Rc::new(RefCell::new(Vec::new())),
358 cr: cr.clone(),
359 drawsub_stack,
360 config,
361 recursion_depth: 0,
362
363 stack_ptr: &stack_variable,
370 }
371 }
372
373 fn nested(&self, cr: cairo::Context) -> Box<DrawingCtx> {
384 let cr_stack = self.cr_stack.clone();
385
386 cr_stack.borrow_mut().push(self.cr.clone());
387
388 Box::new(DrawingCtx {
389 session: self.session.clone(),
390 initial_viewport: self.initial_viewport,
391 cr_stack,
392 cr,
393 drawsub_stack: self.drawsub_stack.clone(),
394 config: self.config.clone(),
395 recursion_depth: self.recursion_depth,
396 stack_ptr: self.stack_ptr,
397 })
398 }
399
400 pub fn session(&self) -> &Session {
401 &self.session
402 }
403
404 pub fn print_stack_depth(&self, place_name: &str) {
405 let stack_variable: u8 = 42;
406
407 let current_stack_ptr = &stack_variable;
408
409 let stack_size = unsafe { self.stack_ptr.byte_offset_from(current_stack_ptr) };
410 rsvg_log!(
411 self.session,
412 "{place_name}: recursion_depth={}, stack_depth={stack_size}",
413 self.recursion_depth
414 );
415 }
416
417 pub fn rendering_options(&self, svg_nesting: SvgNesting) -> RenderingOptions {
419 RenderingOptions {
420 dpi: self.config.dpi,
421 cancellable: self.config.cancellable.clone(),
422 user_language: self.config.user_language.clone(),
423 svg_nesting,
424 testing: self.config.testing,
425 }
426 }
427
428 pub fn user_language(&self) -> &UserLanguage {
429 &self.config.user_language
430 }
431
432 pub fn toplevel_viewport(&self) -> Rect {
433 *self.initial_viewport.vbox
434 }
435
436 fn get_transform_for_stacking_ctx(
439 &self,
440 viewport: &Viewport,
441 stacking_ctx: &StackingContext,
442 clipping: bool,
443 ) -> Result<ValidTransform, Box<InternalRenderingError>> {
444 if stacking_ctx.should_isolate() && !clipping {
445 let affines = CompositingAffines::new(
446 *viewport.transform,
447 *self.initial_viewport.transform,
448 self.cr_stack.borrow().len(),
449 );
450
451 Ok(ValidTransform::try_from(affines.for_temporary_surface)?)
452 } else {
453 Ok(viewport.transform)
454 }
455 }
456
457 pub fn svg_nesting(&self) -> SvgNesting {
458 self.config.svg_nesting
459 }
460
461 pub fn is_measuring(&self) -> bool {
462 self.config.measuring
463 }
464
465 pub fn is_testing(&self) -> bool {
466 self.config.testing
467 }
468
469 fn size_for_temporary_surface(&self) -> (i32, i32) {
470 let rect = self.toplevel_viewport();
471
472 let (viewport_width, viewport_height) = (rect.width(), rect.height());
473
474 let (width, height) = self
475 .initial_viewport
476 .transform
477 .transform_distance(viewport_width, viewport_height);
478
479 (width.ceil().abs() as i32, height.ceil().abs() as i32)
482 }
483
484 pub fn create_surface_for_toplevel_viewport(
485 &self,
486 ) -> Result<cairo::ImageSurface, Box<InternalRenderingError>> {
487 let (w, h) = self.size_for_temporary_surface();
488
489 Ok(cairo::ImageSurface::create(cairo::Format::ARgb32, w, h)?)
490 }
491
492 fn create_similar_surface_for_toplevel_viewport(
493 &self,
494 surface: &cairo::Surface,
495 ) -> Result<cairo::Surface, Box<InternalRenderingError>> {
496 let (w, h) = self.size_for_temporary_surface();
497
498 Ok(cairo::Surface::create_similar(
499 surface,
500 cairo::Content::ColorAlpha,
501 w,
502 h,
503 )?)
504 }
505
506 fn push_new_viewport(
512 &self,
513 current_viewport: &Viewport,
514 layout_viewport: &LayoutViewport,
515 ) -> Option<Viewport> {
516 let LayoutViewport {
517 geometry,
518 vbox,
519 preserve_aspect_ratio,
520 overflow,
521 } = *layout_viewport;
522
523 if !overflow.overflow_allowed() || (vbox.is_some() && preserve_aspect_ratio.is_slice()) {
524 clip_to_rectangle(&self.cr, ¤t_viewport.transform, &geometry);
525 }
526
527 preserve_aspect_ratio
528 .viewport_to_viewbox_transform(vbox, &geometry)
529 .unwrap_or_else(|_e| {
530 match vbox {
531 None => unreachable!(
532 "viewport_to_viewbox_transform only returns errors when vbox != None"
533 ),
534 Some(v) => {
535 rsvg_log!(
536 self.session,
537 "ignoring viewBox ({}, {}, {}, {}) since it is not usable",
538 v.x0,
539 v.y0,
540 v.width(),
541 v.height()
542 );
543 }
544 }
545 None
546 })
547 .and_then(|t| {
548 let transform =
549 ValidTransform::try_from(current_viewport.transform.pre_transform(&t)).ok()?;
550
551 Some(Viewport {
552 dpi: self.config.dpi,
553 vbox: vbox.unwrap_or(current_viewport.vbox),
554 transform,
555 })
556 })
557 }
558
559 fn clip_to_node(
560 &mut self,
561 clip_node: &Option<Node>,
562 acquired_nodes: &mut AcquiredNodes<'_>,
563 viewport: &Viewport,
564 bbox: &BoundingBox,
565 ) -> Result<(), Box<InternalRenderingError>> {
566 if clip_node.is_none() {
567 return Ok(());
568 }
569
570 let node = clip_node.as_ref().unwrap();
571 let units = borrow_element_as!(node, ClipPath).get_units();
572
573 if let Ok(transform) = rect_to_transform(&bbox.rect, units) {
574 let cascaded = CascadedValues::new_from_node(node);
575 let values = cascaded.get();
576
577 let node_transform = values.transform().post_transform(&transform);
578 let transform_for_clip = ValidTransform::try_from(node_transform)?;
579
580 let clip_viewport = viewport.with_composed_transform(transform_for_clip)?;
581
582 for child in node.children().filter(|c| {
583 c.is_element() && element_can_be_used_inside_clip_path(&c.borrow_element())
584 }) {
585 child.draw(
586 acquired_nodes,
587 &CascadedValues::clone_with_node(&cascaded, &child),
588 &clip_viewport,
589 self,
590 true,
591 )?;
592 }
593
594 self.cr.clip();
595 }
596
597 Ok(())
598 }
599
600 fn generate_cairo_mask(
601 &mut self,
602 mask_node: &Node,
603 viewport: &Viewport,
604 transform: Transform,
605 bbox: &BoundingBox,
606 acquired_nodes: &mut AcquiredNodes<'_>,
607 ) -> Result<Option<cairo::ImageSurface>, Box<InternalRenderingError>> {
608 if bbox.rect.is_none() {
609 return Ok(None);
612 }
613
614 let _mask_acquired = match acquired_nodes.acquire_ref(mask_node) {
615 Ok(n) => n,
616
617 Err(AcquireError::CircularReference(_)) => {
618 rsvg_log!(self.session, "circular reference in element {}", mask_node);
619 return Ok(None);
620 }
621
622 _ => unreachable!(),
623 };
624
625 let mask_element = mask_node.borrow_element();
626 let mask = borrow_element_as!(mask_node, Mask);
627
628 let cascaded = CascadedValues::new_from_node(mask_node);
629 let values = cascaded.get();
630
631 let mask_units = mask.get_units();
632
633 let mask_rect = {
634 let params = NormalizeParams::new(values, &viewport.with_units(mask_units));
635 mask.get_rect(¶ms)
636 };
637
638 let transform_for_mask =
639 ValidTransform::try_from(values.transform().post_transform(&transform))?;
640
641 let bbtransform = if let Ok(t) = rect_to_transform(&bbox.rect, mask_units)
642 .map_err(|_: ()| InvalidTransform)
643 .and_then(ValidTransform::try_from)
644 {
645 t
646 } else {
647 return Ok(None);
648 };
649
650 let mask_content_surface = self.create_surface_for_toplevel_viewport()?;
651
652 {
655 let mask_cr = cairo::Context::new(&mask_content_surface)?;
656
657 let clip_rect = (*bbtransform).transform_rect(&mask_rect);
658 clip_to_rectangle(&mask_cr, &transform_for_mask, &clip_rect);
659
660 let mask_viewport = if mask.get_content_units() == CoordUnits::ObjectBoundingBox {
661 viewport
662 .with_units(mask.get_content_units())
663 .with_explicit_transform(transform_for_mask)
664 .with_composed_transform(bbtransform)?
665 } else {
666 viewport
667 .with_units(mask.get_content_units())
668 .with_explicit_transform(transform_for_mask)
669 };
670
671 let mut mask_draw_ctx = self.nested(mask_cr);
672
673 let stacking_ctx = Box::new(StackingContext::new(
674 self.session(),
675 acquired_nodes,
676 &mask_element,
677 Transform::identity(),
678 None,
679 values,
680 ));
681
682 rsvg_log!(self.session, "(mask {}", mask_element);
683
684 let res = mask_draw_ctx.with_discrete_layer(
685 &stacking_ctx,
686 acquired_nodes,
687 &mask_viewport,
688 None,
689 false,
690 &mut |an, dc, new_viewport| {
691 mask_node.draw_children(an, &cascaded, new_viewport, dc, false)
692 },
693 );
694
695 rsvg_log!(self.session, ")");
696
697 res?;
698 }
699
700 let tmp = SharedImageSurface::wrap(mask_content_surface, SurfaceType::SRgb)?;
701
702 let mask_result = match values.mask_type() {
703 MaskType::Luminance => tmp.to_luminance_mask()?,
704 MaskType::Alpha => tmp.extract_alpha(IRect::from_size(tmp.width(), tmp.height()))?,
705 };
706
707 let mask = mask_result.into_image_surface()?;
708
709 Ok(Some(mask))
710 }
711
712 fn is_rendering_cancelled(&self) -> bool {
713 match &self.config.cancellable {
714 None => false,
715 Some(cancellable) => cancellable.is_cancelled(),
716 }
717 }
718
719 fn check_cancellation(&self) -> Result<(), Box<InternalRenderingError>> {
724 if self.is_rendering_cancelled() {
725 return Err(Box::new(InternalRenderingError::Cancelled));
726 }
727
728 Ok(())
729 }
730
731 fn check_layer_nesting_depth(&self) -> Result<(), Box<InternalRenderingError>> {
732 if self.recursion_depth > limits::MAX_LAYER_NESTING_DEPTH {
733 return Err(Box::new(InternalRenderingError::LimitExceeded(
734 ImplementationLimit::MaximumLayerNestingDepthExceeded,
735 )));
736 }
737
738 Ok(())
739 }
740
741 fn filter_current_surface(
742 &mut self,
743 acquired_nodes: &mut AcquiredNodes<'_>,
744 filter: &Filter,
745 viewport: &Viewport,
746 element_name: &str,
747 bbox: &BoundingBox,
748 ) -> Result<cairo::Surface, Box<InternalRenderingError>> {
749 let surface_to_filter = SharedImageSurface::copy_from_surface(
750 &cairo::ImageSurface::try_from(self.cr.target()).unwrap(),
751 )?;
752
753 let stroke_paint_source = Rc::new(filter.stroke_paint_source.to_user_space(
754 &bbox.rect,
755 viewport,
756 &filter.normalize_values,
757 ));
758 let fill_paint_source = Rc::new(filter.fill_paint_source.to_user_space(
759 &bbox.rect,
760 viewport,
761 &filter.normalize_values,
762 ));
763
764 let user_space_params = NormalizeParams::from_values(
769 &filter.normalize_values,
770 &viewport.with_units(CoordUnits::UserSpaceOnUse),
771 );
772
773 let filtered_surface = self
774 .run_filters(
775 viewport,
776 surface_to_filter,
777 filter,
778 acquired_nodes,
779 element_name,
780 &user_space_params,
781 stroke_paint_source,
782 fill_paint_source,
783 bbox,
784 )?
785 .into_image_surface()?;
786
787 let generic_surface: &cairo::Surface = &filtered_surface; Ok(generic_surface.clone())
790 }
791
792 fn draw_in_optional_new_viewport(
793 &mut self,
794 acquired_nodes: &mut AcquiredNodes<'_>,
795 viewport: &Viewport,
796 layout_viewport: &Option<LayoutViewport>,
797 draw_fn: &mut dyn FnMut(&mut AcquiredNodes<'_>, &mut DrawingCtx, &Viewport) -> DrawResult,
798 ) -> DrawResult {
799 if let Some(layout_viewport) = layout_viewport.as_ref() {
800 if let Some(new_viewport) = self.push_new_viewport(viewport, layout_viewport) {
801 draw_fn(acquired_nodes, self, &new_viewport)
802 } else {
803 Ok(viewport.empty_bbox())
804 }
805 } else {
806 draw_fn(acquired_nodes, self, viewport)
807 }
808 }
809
810 fn draw_layer_internal(
811 &mut self,
812 stacking_ctx: &StackingContext,
813 acquired_nodes: &mut AcquiredNodes<'_>,
814 viewport: &Viewport,
815 layout_viewport: Option<LayoutViewport>,
816 clipping: bool,
817 draw_fn: &mut dyn FnMut(&mut AcquiredNodes<'_>, &mut DrawingCtx, &Viewport) -> DrawResult,
818 ) -> DrawResult {
819 self.print_stack_depth("DrawingCtx::draw_layer_internal entry");
820
821 let stacking_ctx_transform = ValidTransform::try_from(stacking_ctx.transform)?;
822
823 let viewport = viewport.with_composed_transform(stacking_ctx_transform)?;
824
825 if clipping {
826 self.draw_in_optional_new_viewport(acquired_nodes, &viewport, &layout_viewport, draw_fn)
827 } else {
828 with_saved_cr(&self.cr.clone(), || {
829 self.link_tag_begin(&stacking_ctx.link_target);
830
831 if let Some(rect) = stacking_ctx.clip_rect.as_ref() {
832 clip_to_rectangle(&self.cr, &viewport.transform, rect);
833 }
834
835 self.clip_to_node(
837 &stacking_ctx.clip_in_user_space,
838 acquired_nodes,
839 &viewport,
840 &viewport.empty_bbox(),
841 )?;
842
843 let res = if stacking_ctx.should_isolate() {
844 self.print_stack_depth("DrawingCtx::draw_layer_internal should_isolate=true");
845
846 let affines = Box::new(CompositingAffines::new(
849 *viewport.transform,
850 *self.initial_viewport.transform,
851 self.cr_stack.borrow().len(),
852 ));
853
854 let cr = match stacking_ctx.filter {
857 None => cairo::Context::new(
858 &self
859 .create_similar_surface_for_toplevel_viewport(&self.cr.target())?,
860 )?,
861 Some(_) => {
862 cairo::Context::new(self.create_surface_for_toplevel_viewport()?)?
863 }
864 };
865
866 let transform_for_temporary_surface =
867 ValidTransform::try_from(affines.for_temporary_surface)?;
868
869 let (source_surface, mut res, bbox) = {
870 let mut temporary_draw_ctx = self.nested(cr.clone());
871
872 let viewport_for_temporary_surface = Viewport::with_explicit_transform(
873 &viewport,
874 transform_for_temporary_surface,
875 );
876
877 let res = temporary_draw_ctx.draw_in_optional_new_viewport(
880 acquired_nodes,
881 &viewport_for_temporary_surface,
882 &layout_viewport,
883 draw_fn,
884 );
885
886 let bbox = if let Ok(ref bbox) = res {
887 bbox.clone()
888 } else {
889 Box::new(
890 BoundingBox::new().with_transform(*transform_for_temporary_surface),
891 )
892 };
893
894 if let Some(ref filter) = stacking_ctx.filter {
895 let filtered_surface = temporary_draw_ctx.filter_current_surface(
896 acquired_nodes,
897 filter,
898 &viewport_for_temporary_surface,
899 &stacking_ctx.element_name,
900 &bbox,
901 )?;
902
903 (filtered_surface, res, bbox)
908 } else {
909 (temporary_draw_ctx.cr.target(), res, bbox)
910 }
911 };
912
913 self.cr
916 .set_matrix(ValidTransform::try_from(affines.compositing)?.into());
917 self.cr.set_source_surface(&source_surface, 0.0, 0.0)?;
918
919 let transform_for_clip =
922 ValidTransform::try_from(affines.outside_temporary_surface)?;
923
924 let viewport_for_clip = viewport.with_explicit_transform(transform_for_clip);
925 self.cr.set_matrix(transform_for_clip.into());
926
927 self.clip_to_node(
928 &stacking_ctx.clip_in_object_space,
929 acquired_nodes,
930 &viewport_for_clip,
931 &bbox,
932 )?;
933
934 if let Some(ref mask_node) = stacking_ctx.mask {
937 self.print_stack_depth("DrawingCtx::draw_layer_internal creating mask");
938
939 res = res.and_then(|bbox| {
940 self.generate_cairo_mask(
941 mask_node,
942 &viewport,
943 affines.for_temporary_surface,
944 &bbox,
945 acquired_nodes,
946 )
947 .and_then(|mask_surf| {
948 if let Some(surf) = mask_surf {
949 self.cr.push_group();
950
951 self.cr.set_matrix(
952 ValidTransform::try_from(affines.compositing)?.into(),
953 );
954 self.cr.mask_surface(&surf, 0.0, 0.0)?;
955
956 Ok(self.cr.pop_group_to_source()?)
957 } else {
958 Ok(())
959 }
960 })
961 .map(|_: ()| bbox)
962 });
963 }
964
965 {
966 self.cr
969 .set_matrix(ValidTransform::try_from(affines.compositing)?.into());
970 self.cr.set_operator(stacking_ctx.mix_blend_mode.into());
971
972 let Opacity(UnitInterval(opacity)) = stacking_ctx.opacity;
973
974 if opacity < 1.0 {
975 self.cr.paint_with_alpha(opacity)?;
976 } else {
977 self.cr.paint()?;
978 }
979 }
980
981 res
982 } else {
983 self.draw_in_optional_new_viewport(
984 acquired_nodes,
985 &viewport,
986 &layout_viewport,
987 draw_fn,
988 )
989 };
990
991 self.link_tag_end(&stacking_ctx.link_target);
992
993 res
994 })
995 }
996 }
997
998 pub fn with_discrete_layer(
999 &mut self,
1000 stacking_ctx: &StackingContext,
1001 acquired_nodes: &mut AcquiredNodes<'_>,
1002 viewport: &Viewport,
1003 layout_viewport: Option<LayoutViewport>,
1004 clipping: bool,
1005 draw_fn: &mut dyn FnMut(&mut AcquiredNodes<'_>, &mut DrawingCtx, &Viewport) -> DrawResult,
1006 ) -> DrawResult {
1007 self.check_cancellation()?;
1008
1009 self.recursion_depth += 1;
1010 self.print_stack_depth("DrawingCtx::with_discrete_layer");
1011
1012 match self.check_layer_nesting_depth() {
1013 Ok(()) => {
1014 let res = self.draw_layer_internal(
1015 stacking_ctx,
1016 acquired_nodes,
1017 viewport,
1018 layout_viewport,
1019 clipping,
1020 draw_fn,
1021 );
1022
1023 self.recursion_depth -= 1;
1024 res
1025 }
1026
1027 Err(e) => Err(e),
1028 }
1029 }
1030
1031 fn with_alpha(
1033 &mut self,
1034 opacity: UnitInterval,
1035 draw_fn: &mut dyn FnMut(&mut DrawingCtx) -> DrawResult,
1036 ) -> DrawResult {
1037 let res;
1038 let UnitInterval(o) = opacity;
1039 if o < 1.0 {
1040 self.cr.push_group();
1041 res = draw_fn(self);
1042 self.cr.pop_group_to_source()?;
1043 self.cr.paint_with_alpha(o)?;
1044 } else {
1045 res = draw_fn(self);
1046 }
1047
1048 res
1049 }
1050
1051 fn link_tag_begin(&mut self, link_target: &Option<String>) {
1053 if let Some(ref link_target) = *link_target {
1054 let attributes = format!("uri='{}'", escape_link_target(link_target));
1055
1056 let cr = self.cr.clone();
1057 cr.tag_begin(CAIRO_TAG_LINK, &attributes);
1058 }
1059 }
1060
1061 fn link_tag_end(&mut self, link_target: &Option<String>) {
1063 if link_target.is_some() {
1064 self.cr.tag_end(CAIRO_TAG_LINK);
1065 }
1066 }
1067
1068 fn make_filter_plan(
1069 &mut self,
1070 acquired_nodes: &mut AcquiredNodes<'_>,
1071 specs: &[FilterSpec],
1072 source_image_width: i32,
1073 source_image_height: i32,
1074 stroke_paint_source: Rc<UserSpacePaintSource>,
1075 fill_paint_source: Rc<UserSpacePaintSource>,
1076 viewport: &Viewport,
1077 ) -> Result<Rc<FilterPlan>, Box<InternalRenderingError>> {
1078 let requirements = InputRequirements::new_from_filter_specs(specs);
1079
1080 let background_image =
1081 if requirements.needs_background_image || requirements.needs_background_alpha {
1082 Some(self.get_snapshot(source_image_width, source_image_height)?)
1083 } else {
1084 None
1085 };
1086
1087 let stroke_paint_image = if requirements.needs_stroke_paint_image {
1088 Some(self.get_paint_source_surface(
1089 source_image_width,
1090 source_image_height,
1091 acquired_nodes,
1092 &stroke_paint_source,
1093 viewport,
1094 )?)
1095 } else {
1096 None
1097 };
1098
1099 let fill_paint_image = if requirements.needs_fill_paint_image {
1100 Some(self.get_paint_source_surface(
1101 source_image_width,
1102 source_image_height,
1103 acquired_nodes,
1104 &fill_paint_source,
1105 viewport,
1106 )?)
1107 } else {
1108 None
1109 };
1110
1111 Ok(Rc::new(FilterPlan::new(
1112 self.session(),
1113 *viewport,
1114 requirements,
1115 background_image,
1116 stroke_paint_image,
1117 fill_paint_image,
1118 )?))
1119 }
1120
1121 fn run_filters(
1122 &mut self,
1123 viewport: &Viewport,
1124 surface_to_filter: SharedImageSurface,
1125 filter: &Filter,
1126 acquired_nodes: &mut AcquiredNodes<'_>,
1127 node_name: &str,
1128 user_space_params: &NormalizeParams,
1129 stroke_paint_source: Rc<UserSpacePaintSource>,
1130 fill_paint_source: Rc<UserSpacePaintSource>,
1131 node_bbox: &BoundingBox,
1132 ) -> Result<SharedImageSurface, Box<InternalRenderingError>> {
1133 let session = self.session();
1134
1135 let filter_specs = filter
1144 .filter_list
1145 .iter()
1146 .map(|filter_value| {
1147 filter_value.to_filter_spec(
1148 acquired_nodes,
1149 user_space_params,
1150 filter.current_color,
1151 viewport,
1152 session,
1153 node_name,
1154 )
1155 })
1156 .collect::<Result<Vec<FilterSpec>, _>>();
1157
1158 match filter_specs {
1159 Ok(specs) => {
1160 let plan = self.make_filter_plan(
1161 acquired_nodes,
1162 &specs,
1163 surface_to_filter.width(),
1164 surface_to_filter.height(),
1165 stroke_paint_source,
1166 fill_paint_source,
1167 viewport,
1168 )?;
1169
1170 specs.iter().try_fold(surface_to_filter, |surface, spec| {
1173 filters::render(plan.clone(), spec, surface, acquired_nodes, self, node_bbox)
1174 })
1175 }
1176
1177 Err(e) => {
1178 rsvg_log!(
1179 self.session,
1180 "not rendering filter list on node {} because it was in error: {}",
1181 node_name,
1182 e
1183 );
1184 Ok(surface_to_filter)
1186 }
1187 }
1188 }
1189
1190 fn set_pattern(
1191 &mut self,
1192 pattern: &UserSpacePattern,
1193 acquired_nodes: &mut AcquiredNodes<'_>,
1194 viewport: &Viewport,
1195 ) -> Result<bool, Box<InternalRenderingError>> {
1196 if approx_eq!(f64, pattern.width, 0.0) || approx_eq!(f64, pattern.height, 0.0) {
1198 return Ok(false);
1199 }
1200
1201 let pattern_node_acquired = match pattern.acquire_pattern_node(acquired_nodes) {
1203 Ok(n) => n,
1204
1205 Err(AcquireError::CircularReference(ref node)) => {
1206 rsvg_log!(self.session, "circular reference in element {}", node);
1207 return Ok(false);
1208 }
1209
1210 _ => unreachable!(),
1211 };
1212
1213 let pattern_node = pattern_node_acquired.get();
1214
1215 let taffine = viewport.transform.pre_transform(&pattern.transform);
1216
1217 let mut scwscale = (taffine.xx.powi(2) + taffine.xy.powi(2)).sqrt();
1218 let mut schscale = (taffine.yx.powi(2) + taffine.yy.powi(2)).sqrt();
1219
1220 let pw: i32 = (pattern.width * scwscale) as i32;
1221 let ph: i32 = (pattern.height * schscale) as i32;
1222
1223 if pw < 1 || ph < 1 {
1224 return Ok(false);
1225 }
1226
1227 scwscale = f64::from(pw) / pattern.width;
1228 schscale = f64::from(ph) / pattern.height;
1229
1230 let (affine, caffine) = if scwscale.approx_eq_cairo(1.0) && schscale.approx_eq_cairo(1.0) {
1232 (pattern.coord_transform, pattern.content_transform)
1233 } else {
1234 (
1235 pattern
1236 .coord_transform
1237 .pre_scale(1.0 / scwscale, 1.0 / schscale),
1238 pattern.content_transform.post_scale(scwscale, schscale),
1239 )
1240 };
1241
1242 let surface = self
1244 .cr
1245 .target()
1246 .create_similar(cairo::Content::ColorAlpha, pw, ph)?;
1247
1248 let cr_pattern = cairo::Context::new(&surface)?;
1249
1250 let transform = ValidTransform::try_from(caffine)?;
1253 cr_pattern.set_matrix(transform.into());
1254
1255 {
1258 let mut pattern_draw_ctx = self.nested(cr_pattern);
1259
1260 let pattern_viewport = viewport
1261 .with_view_box(pattern.width, pattern.height)
1262 .with_explicit_transform(transform);
1263
1264 let pattern_cascaded = CascadedValues::new_from_node(pattern_node);
1265 let pattern_values = pattern_cascaded.get();
1266
1267 let elt = pattern_node.borrow_element();
1268
1269 let stacking_ctx = Box::new(StackingContext::new(
1270 self.session(),
1271 acquired_nodes,
1272 &elt,
1273 Transform::identity(),
1274 None,
1275 pattern_values,
1276 ));
1277
1278 pattern_draw_ctx
1279 .with_alpha(pattern.opacity, &mut |dc| {
1280 dc.with_discrete_layer(
1281 &stacking_ctx,
1282 acquired_nodes,
1283 &pattern_viewport,
1284 None,
1285 false,
1286 &mut |an, dc, new_viewport| {
1287 pattern_node.draw_children(
1288 an,
1289 &pattern_cascaded,
1290 new_viewport,
1291 dc,
1292 false,
1293 )
1294 },
1295 )
1296 })
1297 .map(|_| ())?;
1298 }
1299
1300 let pattern = cairo::SurfacePattern::create(&surface);
1302
1303 if let Some(m) = affine.invert() {
1304 pattern.set_matrix(ValidTransform::try_from(m)?.into());
1305 pattern.set_extend(cairo::Extend::Repeat);
1306 pattern.set_filter(cairo::Filter::Best);
1307 self.cr.set_source(&pattern)?;
1308 }
1309
1310 Ok(true)
1311 }
1312
1313 fn set_paint_source(
1314 &mut self,
1315 paint_source: &UserSpacePaintSource,
1316 acquired_nodes: &mut AcquiredNodes<'_>,
1317 viewport: &Viewport,
1318 ) -> Result<bool, Box<InternalRenderingError>> {
1319 match *paint_source {
1320 UserSpacePaintSource::Gradient(ref gradient, _c) => {
1321 set_gradient_on_cairo(&self.cr, gradient)?;
1322 Ok(true)
1323 }
1324 UserSpacePaintSource::Pattern(ref pattern, ref c) => {
1325 if self.set_pattern(pattern, acquired_nodes, viewport)? {
1326 Ok(true)
1327 } else if let Some(c) = c {
1328 set_source_color_on_cairo(&self.cr, c);
1329 Ok(true)
1330 } else {
1331 Ok(false)
1332 }
1333 }
1334 UserSpacePaintSource::SolidColor(ref c) => {
1335 set_source_color_on_cairo(&self.cr, c);
1336 Ok(true)
1337 }
1338 UserSpacePaintSource::None => Ok(false),
1339 }
1340 }
1341
1342 pub fn get_paint_source_surface(
1344 &mut self,
1345 width: i32,
1346 height: i32,
1347 acquired_nodes: &mut AcquiredNodes<'_>,
1348 paint_source: &UserSpacePaintSource,
1349 viewport: &Viewport,
1350 ) -> Result<SharedImageSurface, Box<InternalRenderingError>> {
1351 let mut surface = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
1352
1353 surface.draw(&mut |cr| {
1354 let mut temporary_draw_ctx = self.nested(cr);
1355
1356 let had_paint_server =
1359 temporary_draw_ctx.set_paint_source(paint_source, acquired_nodes, viewport)?;
1360 if had_paint_server {
1361 temporary_draw_ctx.cr.paint()?;
1362 }
1363
1364 Ok(())
1365 })?;
1366
1367 Ok(surface.share()?)
1368 }
1369
1370 pub fn draw_layer(
1371 &mut self,
1372 layer: &Layer,
1373 acquired_nodes: &mut AcquiredNodes<'_>,
1374 clipping: bool,
1375 viewport: &Viewport,
1376 ) -> DrawResult {
1377 match &layer.kind {
1378 LayerKind::Shape(shape) => self.draw_shape(
1379 shape,
1380 &layer.stacking_ctx,
1381 acquired_nodes,
1382 clipping,
1383 viewport,
1384 ),
1385 LayerKind::Text(text) => self.draw_text(
1386 text,
1387 &layer.stacking_ctx,
1388 acquired_nodes,
1389 clipping,
1390 viewport,
1391 ),
1392 LayerKind::Image(image) => self.draw_image(
1393 image,
1394 &layer.stacking_ctx,
1395 acquired_nodes,
1396 clipping,
1397 viewport,
1398 ),
1399 LayerKind::Group(group) => self.draw_group(
1400 group,
1401 &layer.stacking_ctx,
1402 acquired_nodes,
1403 clipping,
1404 viewport,
1405 ),
1406 }
1407 }
1408
1409 fn draw_shape(
1410 &mut self,
1411 shape: &Shape,
1412 stacking_ctx: &StackingContext,
1413 acquired_nodes: &mut AcquiredNodes<'_>,
1414 clipping: bool,
1415 viewport: &Viewport,
1416 ) -> DrawResult {
1417 self.with_discrete_layer(
1418 stacking_ctx,
1419 acquired_nodes,
1420 viewport,
1421 None,
1422 clipping,
1423 &mut |an, dc, new_viewport| {
1424 dc.paint_shape(shape, stacking_ctx, an, clipping, new_viewport)
1425 },
1426 )
1427 }
1428
1429 fn paint_shape(
1430 &mut self,
1431 shape: &Shape,
1432 stacking_ctx: &StackingContext,
1433 acquired_nodes: &mut AcquiredNodes<'_>,
1434 clipping: bool,
1435 viewport: &Viewport,
1436 ) -> DrawResult {
1437 let cr = self.cr.clone();
1438
1439 let transform = self.get_transform_for_stacking_ctx(viewport, stacking_ctx, clipping)?;
1440 let mut path_helper = PathHelper::new(&cr, transform, &shape.path.cairo_path);
1441
1442 if clipping {
1443 if stacking_ctx.is_visible {
1444 cr.set_fill_rule(cairo::FillRule::from(shape.clip_rule));
1445 path_helper.set()?;
1446 }
1447 return Ok(viewport.empty_bbox());
1448 }
1449
1450 cr.set_antialias(cairo::Antialias::from(shape.shape_rendering));
1451
1452 setup_cr_for_stroke(&cr, &shape.stroke);
1453
1454 cr.set_fill_rule(cairo::FillRule::from(shape.fill_rule));
1455
1456 path_helper.set()?;
1457 let bbox = compute_stroke_and_fill_box(
1458 &cr,
1459 &shape.stroke,
1460 &shape.stroke_paint,
1461 &self.initial_viewport,
1462 )?;
1463
1464 if stacking_ctx.is_visible {
1465 for &target in &shape.paint_order.targets {
1466 match target {
1469 PaintTarget::Fill => {
1470 path_helper.set()?;
1471 let had_paint_server =
1472 self.set_paint_source(&shape.fill_paint, acquired_nodes, viewport)?;
1473 if had_paint_server {
1474 cr.fill_preserve()?;
1475 }
1476 }
1477
1478 PaintTarget::Stroke => {
1479 path_helper.set()?;
1480 if shape.stroke.non_scaling {
1481 cr.set_matrix(self.initial_viewport.transform.into());
1482 }
1483
1484 let had_paint_server =
1485 self.set_paint_source(&shape.stroke_paint, acquired_nodes, viewport)?;
1486 if had_paint_server {
1487 cr.stroke_preserve()?;
1488 }
1489 }
1490
1491 PaintTarget::Markers => {
1492 path_helper.unset();
1493 marker::render_markers_for_shape(
1494 shape,
1495 viewport,
1496 self,
1497 acquired_nodes,
1498 clipping,
1499 )?;
1500 }
1501 }
1502 }
1503 }
1504
1505 path_helper.unset();
1506 Ok(bbox)
1507 }
1508
1509 fn paint_surface_from_image(
1510 &mut self,
1511 image: &Image,
1512 viewport: &Viewport,
1513 ) -> Result<(), cairo::Error> {
1514 let cr = &self.cr;
1515
1516 let ptn = image.surface.to_cairo_pattern();
1524 ptn.set_extend(cairo::Extend::Pad);
1525
1526 let interpolation = Interpolation::from(image.image_rendering);
1527
1528 ptn.set_filter(cairo::Filter::from(interpolation));
1529 cr.set_matrix(viewport.transform.into());
1530 cr.set_source(&ptn)?;
1531
1532 let image_size_rect = Rect::from_size(
1533 f64::from(image.surface.width()),
1534 f64::from(image.surface.height()),
1535 );
1536
1537 clip_to_rectangle(cr, &viewport.transform, &image_size_rect);
1539
1540 cr.paint()
1541 }
1542
1543 fn draw_image(
1544 &mut self,
1545 image: &Image,
1546 stacking_ctx: &StackingContext,
1547 acquired_nodes: &mut AcquiredNodes<'_>,
1548 clipping: bool,
1549 viewport: &Viewport,
1550 ) -> DrawResult {
1551 let image_width = image.surface.width();
1552 let image_height = image.surface.height();
1553 if clipping || image.rect.is_empty() || image_width == 0 || image_height == 0 {
1554 return Ok(viewport.empty_bbox());
1555 }
1556
1557 let image_size_rect = Rect::from_size(f64::from(image_width), f64::from(image_height));
1558
1559 let vbox = ViewBox::from(image_size_rect);
1560
1561 let bounds = Box::new(viewport.empty_bbox().with_rect(image.rect));
1564
1565 let layout_viewport = LayoutViewport {
1566 vbox: Some(vbox),
1567 geometry: image.rect,
1568 preserve_aspect_ratio: image.aspect,
1569 overflow: image.overflow,
1570 };
1571
1572 if stacking_ctx.is_visible {
1573 self.with_discrete_layer(
1574 stacking_ctx,
1575 acquired_nodes,
1576 viewport,
1577 Some(layout_viewport),
1578 clipping,
1579 &mut |_an, dc, new_viewport| {
1580 dc.paint_surface_from_image(image, new_viewport)?;
1581
1582 Ok(bounds.clone())
1583 },
1584 )
1585 } else {
1586 Ok(bounds)
1587 }
1588 }
1589
1590 fn draw_group(
1591 &mut self,
1592 _group: &Group,
1593 _stacking_ctx: &StackingContext,
1594 _acquired_nodes: &mut AcquiredNodes<'_>,
1595 _clipping: bool,
1596 _viewport: &Viewport,
1597 ) -> DrawResult {
1598 unimplemented!();
1599 }
1614
1615 fn draw_text_span(
1616 &mut self,
1617 span: &TextSpan,
1618 acquired_nodes: &mut AcquiredNodes<'_>,
1619 clipping: bool,
1620 viewport: &Viewport,
1621 ) -> DrawResult {
1622 let path = pango_layout_to_cairo_path(span.x, span.y, &span.layout, span.gravity)?;
1623 if path.is_empty() {
1624 return Ok(viewport.empty_bbox());
1628 }
1629
1630 let can_use_text_as_path = self.cr.target().type_() != cairo::SurfaceType::Pdf;
1634
1635 self.cr
1636 .set_antialias(cairo::Antialias::from(span.text_rendering));
1637
1638 setup_cr_for_stroke(&self.cr, &span.stroke);
1639
1640 self.cr.set_matrix(viewport.transform.into());
1641
1642 if clipping {
1643 path.to_cairo_context(&self.cr)?;
1644 return Ok(viewport.empty_bbox());
1645 }
1646
1647 path.to_cairo_context(&self.cr)?;
1648 let bbox = compute_stroke_and_fill_box(
1649 &self.cr,
1650 &span.stroke,
1651 &span.stroke_paint,
1652 &self.initial_viewport,
1653 )?;
1654 self.cr.new_path();
1655
1656 if span.is_visible {
1657 self.link_tag_begin(&span.link_target);
1658
1659 for &target in &span.paint_order.targets {
1660 match target {
1661 PaintTarget::Fill => {
1662 let had_paint_server =
1663 self.set_paint_source(&span.fill_paint, acquired_nodes, viewport)?;
1664
1665 if had_paint_server {
1666 if can_use_text_as_path {
1667 path.to_cairo_context(&self.cr)?;
1668 self.cr.fill()?;
1669 self.cr.new_path();
1670 } else {
1671 self.cr.move_to(span.x, span.y);
1672
1673 let matrix = self.cr.matrix();
1674
1675 let rotation_from_gravity = span.gravity.to_rotation();
1676 if !rotation_from_gravity.approx_eq_cairo(0.0) {
1677 self.cr.rotate(-rotation_from_gravity);
1678 }
1679
1680 pangocairo::functions::update_layout(&self.cr, &span.layout);
1681 pangocairo::functions::show_layout(&self.cr, &span.layout);
1682
1683 self.cr.set_matrix(matrix);
1684 }
1685 }
1686 }
1687
1688 PaintTarget::Stroke => {
1689 let had_paint_server =
1690 self.set_paint_source(&span.stroke_paint, acquired_nodes, viewport)?;
1691
1692 if had_paint_server {
1693 path.to_cairo_context(&self.cr)?;
1694 self.cr.stroke()?;
1695 self.cr.new_path();
1696 }
1697 }
1698
1699 PaintTarget::Markers => {}
1700 }
1701 }
1702
1703 self.link_tag_end(&span.link_target);
1704 }
1705
1706 Ok(bbox)
1707 }
1708
1709 fn draw_text(
1710 &mut self,
1711 text: &Text,
1712 stacking_ctx: &StackingContext,
1713 acquired_nodes: &mut AcquiredNodes<'_>,
1714 clipping: bool,
1715 viewport: &Viewport,
1716 ) -> DrawResult {
1717 self.with_discrete_layer(
1718 stacking_ctx,
1719 acquired_nodes,
1720 viewport,
1721 None,
1722 clipping,
1723 &mut |an, dc, new_viewport| dc.paint_text_spans(text, an, clipping, new_viewport),
1724 )
1725 }
1726
1727 fn paint_text_spans(
1728 &mut self,
1729 text: &Text,
1730 acquired_nodes: &mut AcquiredNodes<'_>,
1731 clipping: bool,
1732 viewport: &Viewport,
1733 ) -> DrawResult {
1734 let mut bbox = viewport.empty_bbox();
1735
1736 for span in &text.spans {
1737 let span_bbox = self.draw_text_span(span, acquired_nodes, clipping, viewport)?;
1738 bbox.insert(&span_bbox);
1739 }
1740
1741 Ok(bbox)
1742 }
1743
1744 pub fn get_snapshot(
1745 &self,
1746 width: i32,
1747 height: i32,
1748 ) -> Result<SharedImageSurface, Box<InternalRenderingError>> {
1749 let mut surface = ExclusiveImageSurface::new(width, height, SurfaceType::SRgb)?;
1765
1766 surface.draw(&mut |cr| {
1767 for (depth, draw) in self.cr_stack.borrow().iter().enumerate() {
1772 let affines = CompositingAffines::new(
1773 Transform::from(draw.matrix()),
1774 *self.initial_viewport.transform,
1775 depth,
1776 );
1777
1778 cr.set_matrix(ValidTransform::try_from(affines.for_snapshot)?.into());
1779 cr.set_source_surface(draw.target(), 0.0, 0.0)?;
1780 cr.paint()?;
1781 }
1782
1783 Ok(())
1784 })?;
1785
1786 Ok(surface.share()?)
1787 }
1788
1789 pub fn draw_node_to_surface(
1790 &mut self,
1791 node: &Node,
1792 acquired_nodes: &mut AcquiredNodes<'_>,
1793 cascaded: &CascadedValues<'_>,
1794 transform: ValidTransform,
1795 width: i32,
1796 height: i32,
1797 ) -> Result<SharedImageSurface, Box<InternalRenderingError>> {
1798 let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
1799
1800 let save_cr = self.cr.clone();
1801
1802 {
1803 let cr = cairo::Context::new(&surface)?;
1804 cr.set_matrix(transform.into());
1805
1806 self.cr = cr;
1807 let viewport = Viewport {
1808 dpi: self.config.dpi,
1809 transform,
1810 vbox: ViewBox::from(Rect::from_size(f64::from(width), f64::from(height))),
1811 };
1812
1813 let _ = self.draw_node_from_stack(node, acquired_nodes, cascaded, &viewport, false)?;
1815 }
1816
1817 self.cr = save_cr;
1818
1819 Ok(SharedImageSurface::wrap(surface, SurfaceType::SRgb)?)
1820 }
1821
1822 pub fn draw_node_from_stack(
1823 &mut self,
1824 node: &Node,
1825 acquired_nodes: &mut AcquiredNodes<'_>,
1826 cascaded: &CascadedValues<'_>,
1827 viewport: &Viewport,
1828 clipping: bool,
1829 ) -> DrawResult {
1830 self.print_stack_depth("DrawingCtx::draw_node_from_stack");
1831
1832 let stack_top = self.drawsub_stack.pop();
1833
1834 let draw = if let Some(ref top) = stack_top {
1835 top == node
1836 } else {
1837 true
1838 };
1839
1840 let res = if draw {
1841 node.draw(acquired_nodes, cascaded, viewport, self, clipping)
1842 } else {
1843 Ok(viewport.empty_bbox())
1844 };
1845
1846 if let Some(top) = stack_top {
1847 self.drawsub_stack.push(top);
1848 }
1849
1850 res
1851 }
1852
1853 pub fn draw_from_use_node(
1854 &mut self,
1855 node: &Node,
1856 acquired_nodes: &mut AcquiredNodes<'_>,
1857 values: &ComputedValues,
1858 use_rect: Rect,
1859 link: &NodeId,
1860 clipping: bool,
1861 viewport: &Viewport,
1862 fill_paint: Rc<PaintSource>,
1863 stroke_paint: Rc<PaintSource>,
1864 ) -> DrawResult {
1865 let _self_acquired = match acquired_nodes.acquire_ref(node) {
1873 Ok(n) => n,
1874
1875 Err(AcquireError::CircularReference(circular)) => {
1876 rsvg_log!(self.session, "circular reference in element {}", circular);
1877 return Err(Box::new(InternalRenderingError::CircularReference(
1878 circular,
1879 )));
1880 }
1881
1882 _ => unreachable!(),
1883 };
1884
1885 let acquired = match acquired_nodes.acquire(link) {
1886 Ok(acquired) => acquired,
1887
1888 Err(AcquireError::CircularReference(circular)) => {
1889 rsvg_log!(
1890 self.session,
1891 "circular reference from {} to element {}",
1892 node,
1893 circular
1894 );
1895 return Err(Box::new(InternalRenderingError::CircularReference(
1896 circular,
1897 )));
1898 }
1899
1900 Err(AcquireError::MaxReferencesExceeded) => {
1901 return Err(Box::new(InternalRenderingError::LimitExceeded(
1902 ImplementationLimit::TooManyReferencedElements,
1903 )));
1904 }
1905
1906 Err(AcquireError::InvalidLinkType(_)) => unreachable!(),
1907
1908 Err(AcquireError::LinkNotFound(node_id)) => {
1909 rsvg_log!(
1910 self.session,
1911 "element {} references nonexistent \"{}\"",
1912 node,
1913 node_id
1914 );
1915 return Ok(viewport.empty_bbox());
1916 }
1917 };
1918
1919 if use_rect.is_empty() {
1922 return Ok(viewport.empty_bbox());
1923 }
1924
1925 let child = acquired.get();
1926
1927 if clipping && !element_can_be_used_inside_use_inside_clip_path(&child.borrow_element()) {
1928 return Ok(viewport.empty_bbox());
1929 }
1930
1931 let use_transform = ValidTransform::try_from(values.transform())?;
1932 let use_viewport = viewport.with_composed_transform(use_transform)?;
1933
1934 let use_element = node.borrow_element();
1935
1936 let defines_a_viewport = if is_element_of_type!(child, Symbol) {
1937 let symbol = borrow_element_as!(child, Symbol);
1938 Some((symbol.get_viewbox(), symbol.get_preserve_aspect_ratio()))
1939 } else if is_element_of_type!(child, Svg) {
1940 let svg = borrow_element_as!(child, Svg);
1941 Some((svg.get_viewbox(), svg.get_preserve_aspect_ratio()))
1942 } else {
1943 None
1944 };
1945
1946 let res = if let Some((vbox, preserve_aspect_ratio)) = defines_a_viewport {
1947 let elt = child.borrow_element();
1952
1953 let child_values = elt.get_computed_values();
1954
1955 let stacking_ctx = Box::new(StackingContext::new(
1956 self.session(),
1957 acquired_nodes,
1958 &use_element,
1959 Transform::identity(),
1960 None,
1961 values,
1962 ));
1963
1964 let layout_viewport = LayoutViewport {
1965 vbox,
1966 geometry: use_rect,
1967 preserve_aspect_ratio,
1968 overflow: child_values.overflow(),
1969 };
1970
1971 self.with_discrete_layer(
1972 &stacking_ctx,
1973 acquired_nodes,
1974 &use_viewport,
1975 Some(layout_viewport),
1976 clipping,
1977 &mut |an, dc, new_viewport| {
1978 child.draw_children(
1979 an,
1980 &CascadedValues::new_from_values(
1981 child,
1982 values,
1983 Some(fill_paint.clone()),
1984 Some(stroke_paint.clone()),
1985 ),
1986 new_viewport,
1987 dc,
1988 clipping,
1989 )
1990 },
1991 )
1992 } else {
1993 let stacking_ctx = Box::new(StackingContext::new(
1996 self.session(),
1997 acquired_nodes,
1998 &use_element,
1999 Transform::new_translate(use_rect.x0, use_rect.y0),
2000 None,
2001 values,
2002 ));
2003
2004 self.with_discrete_layer(
2005 &stacking_ctx,
2006 acquired_nodes,
2007 &use_viewport,
2008 None,
2009 clipping,
2010 &mut |an, dc, new_viewport| {
2011 child.draw(
2012 an,
2013 &CascadedValues::new_from_values(
2014 child,
2015 values,
2016 Some(fill_paint.clone()),
2017 Some(stroke_paint.clone()),
2018 ),
2019 new_viewport,
2020 dc,
2021 clipping,
2022 )
2023 },
2024 )
2025 };
2026
2027 if let Ok(bbox) = res {
2028 let mut res_bbox = Box::new(BoundingBox::new().with_transform(*viewport.transform));
2029 res_bbox.insert(&bbox);
2030 Ok(res_bbox)
2031 } else {
2032 res
2033 }
2034 }
2035
2036 pub fn get_font_options(&self) -> FontOptions {
2040 let mut options = cairo::FontOptions::new().unwrap();
2041 if self.config.testing {
2042 options.set_antialias(cairo::Antialias::Gray);
2043 }
2044
2045 options.set_hint_style(cairo::HintStyle::None);
2046 options.set_hint_metrics(cairo::HintMetrics::Off);
2047
2048 FontOptions { options }
2049 }
2050}
2051
2052impl From<ImageRendering> for Interpolation {
2053 fn from(r: ImageRendering) -> Interpolation {
2054 match r {
2055 ImageRendering::Pixelated
2056 | ImageRendering::CrispEdges
2057 | ImageRendering::OptimizeSpeed => Interpolation::Nearest,
2058
2059 ImageRendering::Smooth
2060 | ImageRendering::OptimizeQuality
2061 | ImageRendering::HighQuality
2062 | ImageRendering::Auto => Interpolation::Smooth,
2063 }
2064 }
2065}
2066
2067pub fn create_pango_context(font_options: &FontOptions) -> pango::Context {
2069 let font_map = pangocairo::FontMap::default();
2070 let context = font_map.create_context();
2071
2072 context.set_round_glyph_positions(false);
2073
2074 pangocairo::functions::context_set_font_options(&context, Some(&font_options.options));
2075
2076 pangocairo::functions::context_set_resolution(&context, 72.0);
2093
2094 context
2095}
2096
2097pub fn set_source_color_on_cairo(cr: &cairo::Context, color: &Color) {
2098 let rgba = color_to_rgba(color);
2099
2100 cr.set_source_rgba(
2101 f64::from(rgba.red) / 255.0,
2102 f64::from(rgba.green) / 255.0,
2103 f64::from(rgba.blue) / 255.0,
2104 f64::from(rgba.alpha),
2105 );
2106}
2107
2108fn set_gradient_on_cairo(
2109 cr: &cairo::Context,
2110 gradient: &UserSpaceGradient,
2111) -> Result<(), Box<InternalRenderingError>> {
2112 let g = match gradient.variant {
2113 GradientVariant::Linear { x1, y1, x2, y2 } => {
2114 cairo::Gradient::clone(&cairo::LinearGradient::new(x1, y1, x2, y2))
2115 }
2116
2117 GradientVariant::Radial {
2118 cx,
2119 cy,
2120 r,
2121 fx,
2122 fy,
2123 fr,
2124 } => cairo::Gradient::clone(&cairo::RadialGradient::new(fx, fy, fr, cx, cy, r)),
2125 };
2126
2127 g.set_matrix(ValidTransform::try_from(gradient.transform)?.into());
2128 g.set_extend(cairo::Extend::from(gradient.spread));
2129
2130 for stop in &gradient.stops {
2131 let UnitInterval(stop_offset) = stop.offset;
2132
2133 let rgba = color_to_rgba(&stop.color);
2134
2135 g.add_color_stop_rgba(
2136 stop_offset,
2137 f64::from(rgba.red) / 255.0,
2138 f64::from(rgba.green) / 255.0,
2139 f64::from(rgba.blue) / 255.0,
2140 f64::from(rgba.alpha),
2141 );
2142 }
2143
2144 Ok(cr.set_source(&g)?)
2145}
2146
2147fn pango_layout_to_cairo(
2150 x: f64,
2151 y: f64,
2152 layout: &pango::Layout,
2153 gravity: pango::Gravity,
2154 cr: &cairo::Context,
2155) {
2156 let rotation_from_gravity = gravity.to_rotation();
2157 let rotation = if !rotation_from_gravity.approx_eq_cairo(0.0) {
2158 Some(-rotation_from_gravity)
2159 } else {
2160 None
2161 };
2162
2163 cr.move_to(x, y);
2164
2165 let matrix = cr.matrix();
2166 if let Some(rot) = rotation {
2167 cr.rotate(rot);
2168 }
2169
2170 pangocairo::functions::update_layout(cr, layout);
2171 pangocairo::functions::layout_path(cr, layout);
2172 cr.set_matrix(matrix);
2173}
2174
2175fn pango_layout_to_cairo_path(
2177 x: f64,
2178 y: f64,
2179 layout: &pango::Layout,
2180 gravity: pango::Gravity,
2181) -> Result<CairoPath, Box<InternalRenderingError>> {
2182 let surface = cairo::RecordingSurface::create(cairo::Content::ColorAlpha, None)?;
2183 let cr = cairo::Context::new(&surface)?;
2184
2185 pango_layout_to_cairo(x, y, layout, gravity, &cr);
2186
2187 let cairo_path = cr.copy_path()?;
2188 Ok(CairoPath::from_cairo(cairo_path))
2189}
2190
2191fn element_can_be_used_inside_clip_path(element: &Element) -> bool {
2193 use ElementData::*;
2194
2195 matches!(
2196 element.element_data,
2197 Circle(_)
2198 | Ellipse(_)
2199 | Line(_)
2200 | Path(_)
2201 | Polygon(_)
2202 | Polyline(_)
2203 | Rect(_)
2204 | Text(_)
2205 | Use(_)
2206 )
2207}
2208
2209fn element_can_be_used_inside_use_inside_clip_path(element: &Element) -> bool {
2211 use ElementData::*;
2212
2213 matches!(
2214 element.element_data,
2215 Circle(_) | Ellipse(_) | Line(_) | Path(_) | Polygon(_) | Polyline(_) | Rect(_) | Text(_)
2216 )
2217}
2218
2219#[derive(Debug)]
2220struct CompositingAffines {
2221 pub outside_temporary_surface: Transform,
2222 #[allow(unused)]
2223 pub initial: Transform,
2224 pub for_temporary_surface: Transform,
2225 pub compositing: Transform,
2226 pub for_snapshot: Transform,
2227}
2228
2229impl CompositingAffines {
2230 fn new(current: Transform, initial: Transform, cr_stack_depth: usize) -> CompositingAffines {
2231 let is_topmost_temporary_surface = cr_stack_depth == 0;
2232
2233 let initial_inverse = initial.invert().unwrap();
2234
2235 let outside_temporary_surface = if is_topmost_temporary_surface {
2236 current
2237 } else {
2238 current.post_transform(&initial_inverse)
2239 };
2240
2241 let (scale_x, scale_y) = initial.transform_distance(1.0, 1.0);
2242
2243 let for_temporary_surface = if is_topmost_temporary_surface {
2244 current
2245 .post_transform(&initial_inverse)
2246 .post_scale(scale_x, scale_y)
2247 } else {
2248 current
2249 };
2250
2251 let compositing = if is_topmost_temporary_surface {
2252 initial.pre_scale(1.0 / scale_x, 1.0 / scale_y)
2253 } else {
2254 Transform::identity()
2255 };
2256
2257 let for_snapshot = compositing.invert().unwrap();
2258
2259 CompositingAffines {
2260 outside_temporary_surface,
2261 initial,
2262 for_temporary_surface,
2263 compositing,
2264 for_snapshot,
2265 }
2266 }
2267}
2268
2269fn compute_stroke_and_fill_extents(
2270 cr: &cairo::Context,
2271 stroke: &Stroke,
2272 stroke_paint_source: &UserSpacePaintSource,
2273 initial_viewport: &Viewport,
2274) -> Result<PathExtents, Box<InternalRenderingError>> {
2275 let backup_tolerance = cr.tolerance();
2279 cr.set_tolerance(1.0);
2280
2281 let (x0, y0, x1, y1) = cr.fill_extents()?;
2290 let fill_extents = if x0 != 0.0 || y0 != 0.0 || x1 != 0.0 || y1 != 0.0 {
2291 Some(Rect::new(x0, y0, x1, y1))
2292 } else {
2293 None
2294 };
2295
2296 let stroke_extents = if !stroke.width.approx_eq_cairo(0.0)
2308 && !matches!(stroke_paint_source, UserSpacePaintSource::None)
2309 {
2310 let backup_matrix = if stroke.non_scaling {
2311 let matrix = cr.matrix();
2312 cr.set_matrix(initial_viewport.transform.into());
2313 Some(matrix)
2314 } else {
2315 None
2316 };
2317 let (x0, y0, x1, y1) = cr.stroke_extents()?;
2318 if let Some(matrix) = backup_matrix {
2319 cr.set_matrix(matrix);
2320 }
2321 Some(Rect::new(x0, y0, x1, y1))
2322 } else {
2323 None
2324 };
2325
2326 let (x0, y0, x1, y1) = cr.path_extents()?;
2329 let path_extents = Some(Rect::new(x0, y0, x1, y1));
2330
2331 cr.set_tolerance(backup_tolerance);
2334
2335 Ok(PathExtents {
2336 path_only: path_extents,
2337 fill: fill_extents,
2338 stroke: stroke_extents,
2339 })
2340}
2341
2342fn compute_stroke_and_fill_box(
2343 cr: &cairo::Context,
2344 stroke: &Stroke,
2345 stroke_paint_source: &UserSpacePaintSource,
2346 initial_viewport: &Viewport,
2347) -> DrawResult {
2348 let extents =
2349 compute_stroke_and_fill_extents(cr, stroke, stroke_paint_source, initial_viewport)?;
2350
2351 let ink_rect = match (extents.fill, extents.stroke) {
2352 (None, None) => None,
2353 (Some(f), None) => Some(f),
2354 (None, Some(s)) => Some(s),
2355 (Some(f), Some(s)) => Some(f.union(&s)),
2356 };
2357
2358 let mut bbox = BoundingBox::new().with_transform(Transform::from(cr.matrix()));
2359
2360 if let Some(rect) = extents.path_only {
2361 bbox = bbox.with_rect(rect);
2362 }
2363
2364 if let Some(ink_rect) = ink_rect {
2365 bbox = bbox.with_ink_rect(ink_rect);
2366 }
2367
2368 Ok(Box::new(bbox))
2369}
2370
2371fn setup_cr_for_stroke(cr: &cairo::Context, stroke: &Stroke) {
2372 cr.set_line_width(stroke.width);
2373 cr.set_miter_limit(stroke.miter_limit.0);
2374 cr.set_line_cap(cairo::LineCap::from(stroke.line_cap));
2375 cr.set_line_join(cairo::LineJoin::from(stroke.line_join));
2376
2377 let total_length: f64 = stroke.dashes.iter().sum();
2378
2379 if total_length > 0.0 {
2380 cr.set_dash(&stroke.dashes, stroke.dash_offset);
2381 } else {
2382 cr.set_dash(&[], 0.0);
2383 }
2384}
2385
2386fn escape_link_target(value: &str) -> Cow<'_, str> {
2388 let regex = {
2389 static REGEX: OnceLock<Regex> = OnceLock::new();
2390 REGEX.get_or_init(|| Regex::new(r"['\\]").unwrap())
2391 };
2392
2393 regex.replace_all(value, |caps: &Captures<'_>| {
2394 match caps.get(0).unwrap().as_str() {
2395 "'" => "\\'".to_owned(),
2396 "\\" => "\\\\".to_owned(),
2397 _ => unreachable!(),
2398 }
2399 })
2400}
2401
2402fn clip_to_rectangle(cr: &cairo::Context, transform: &ValidTransform, r: &Rect) {
2403 cr.set_matrix((*transform).into());
2404
2405 cr.rectangle(r.x0, r.y0, r.width(), r.height());
2406 cr.clip();
2407}
2408
2409impl From<SpreadMethod> for cairo::Extend {
2410 fn from(s: SpreadMethod) -> cairo::Extend {
2411 match s {
2412 SpreadMethod::Pad => cairo::Extend::Pad,
2413 SpreadMethod::Reflect => cairo::Extend::Reflect,
2414 SpreadMethod::Repeat => cairo::Extend::Repeat,
2415 }
2416 }
2417}
2418
2419impl From<StrokeLinejoin> for cairo::LineJoin {
2420 fn from(j: StrokeLinejoin) -> cairo::LineJoin {
2421 match j {
2422 StrokeLinejoin::Miter => cairo::LineJoin::Miter,
2423 StrokeLinejoin::Round => cairo::LineJoin::Round,
2424 StrokeLinejoin::Bevel => cairo::LineJoin::Bevel,
2425 }
2426 }
2427}
2428
2429impl From<StrokeLinecap> for cairo::LineCap {
2430 fn from(j: StrokeLinecap) -> cairo::LineCap {
2431 match j {
2432 StrokeLinecap::Butt => cairo::LineCap::Butt,
2433 StrokeLinecap::Round => cairo::LineCap::Round,
2434 StrokeLinecap::Square => cairo::LineCap::Square,
2435 }
2436 }
2437}
2438
2439impl From<MixBlendMode> for cairo::Operator {
2440 fn from(m: MixBlendMode) -> cairo::Operator {
2441 use cairo::Operator;
2442
2443 match m {
2444 MixBlendMode::Normal => Operator::Over,
2445 MixBlendMode::Multiply => Operator::Multiply,
2446 MixBlendMode::Screen => Operator::Screen,
2447 MixBlendMode::Overlay => Operator::Overlay,
2448 MixBlendMode::Darken => Operator::Darken,
2449 MixBlendMode::Lighten => Operator::Lighten,
2450 MixBlendMode::ColorDodge => Operator::ColorDodge,
2451 MixBlendMode::ColorBurn => Operator::ColorBurn,
2452 MixBlendMode::HardLight => Operator::HardLight,
2453 MixBlendMode::SoftLight => Operator::SoftLight,
2454 MixBlendMode::Difference => Operator::Difference,
2455 MixBlendMode::Exclusion => Operator::Exclusion,
2456 MixBlendMode::Hue => Operator::HslHue,
2457 MixBlendMode::Saturation => Operator::HslSaturation,
2458 MixBlendMode::Color => Operator::HslColor,
2459 MixBlendMode::Luminosity => Operator::HslLuminosity,
2460 }
2461 }
2462}
2463
2464impl From<ClipRule> for cairo::FillRule {
2465 fn from(c: ClipRule) -> cairo::FillRule {
2466 match c {
2467 ClipRule::NonZero => cairo::FillRule::Winding,
2468 ClipRule::EvenOdd => cairo::FillRule::EvenOdd,
2469 }
2470 }
2471}
2472
2473impl From<FillRule> for cairo::FillRule {
2474 fn from(f: FillRule) -> cairo::FillRule {
2475 match f {
2476 FillRule::NonZero => cairo::FillRule::Winding,
2477 FillRule::EvenOdd => cairo::FillRule::EvenOdd,
2478 }
2479 }
2480}
2481
2482impl From<ShapeRendering> for cairo::Antialias {
2483 fn from(sr: ShapeRendering) -> cairo::Antialias {
2484 match sr {
2485 ShapeRendering::Auto | ShapeRendering::GeometricPrecision => cairo::Antialias::Default,
2486 ShapeRendering::OptimizeSpeed | ShapeRendering::CrispEdges => cairo::Antialias::None,
2487 }
2488 }
2489}
2490
2491impl From<TextRendering> for cairo::Antialias {
2492 fn from(tr: TextRendering) -> cairo::Antialias {
2493 match tr {
2494 TextRendering::Auto
2495 | TextRendering::OptimizeLegibility
2496 | TextRendering::GeometricPrecision => cairo::Antialias::Default,
2497 TextRendering::OptimizeSpeed => cairo::Antialias::None,
2498 }
2499 }
2500}
2501
2502impl From<cairo::Matrix> for Transform {
2503 #[inline]
2504 fn from(m: cairo::Matrix) -> Self {
2505 Self::new_unchecked(m.xx(), m.yx(), m.xy(), m.yy(), m.x0(), m.y0())
2506 }
2507}
2508
2509impl From<ValidTransform> for cairo::Matrix {
2510 #[inline]
2511 fn from(t: ValidTransform) -> cairo::Matrix {
2512 cairo::Matrix::new(t.xx, t.yx, t.xy, t.yy, t.x0, t.y0)
2513 }
2514}
2515
2516pub struct PathExtents {
2521 pub path_only: Option<Rect>,
2523
2524 pub fill: Option<Rect>,
2526
2527 pub stroke: Option<Rect>,
2529}