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