From dba1e6ae0ab77ee18446fb688bfb51cec67e6ac5 Mon Sep 17 00:00:00 2001 From: Velli20 Date: Sun, 18 Oct 2026 14:58:54 +0300 Subject: [PATCH] Paint uncolored tiling patterns in the color selected with the pattern The cell of a PaintType 2 tiling pattern was recorded on a fresh graphics state, so its shapes were painted black instead of in the color given to scn/SCN together with the pattern. Seed the recording's fill and stroke color with that color. Co-Authored-By: Claude Opus 5.5 --- .../src/canvas_graphics_state_ops.rs | 1 + crates/pdf-canvas/src/pdf_canvas.rs | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/pdf-canvas/src/canvas_graphics_state_ops.rs b/crates/pdf-canvas/src/canvas_graphics_state_ops.rs index e50a4cd8..d5e80921 100644 --- a/crates/pdf-canvas/src/canvas_graphics_state_ops.rs +++ b/crates/pdf-canvas/src/canvas_graphics_state_ops.rs @@ -153,6 +153,7 @@ impl GraphicsStateOps for PdfCanvas<'_, B> { .map(|resources| resources.get()) .transpose()?, None, + None, )?; let transform = self.current_state()?.transform; diff --git a/crates/pdf-canvas/src/pdf_canvas.rs b/crates/pdf-canvas/src/pdf_canvas.rs index 02ca0ed8..f9fb8ef3 100644 --- a/crates/pdf-canvas/src/pdf_canvas.rs +++ b/crates/pdf-canvas/src/pdf_canvas.rs @@ -181,6 +181,8 @@ impl<'a, B: CanvasBackend> PdfCanvas<'a, B> { /// - `bbox`: The content-space bounding box that sizes the recording surface. /// - `resources`: Optional resource dictionary for resolving fonts, patterns, etc. /// - `filter`: Optional filter function to skip certain operations. + /// - `cell_color`: Optional initial fill and stroke color, used by uncolored tiling + /// patterns whose cell is painted in the color selected with the pattern. /// /// # Errors /// @@ -193,14 +195,20 @@ impl<'a, B: CanvasBackend> PdfCanvas<'a, B> { bbox: &Rect, resources: Option>, filter: Option<&mut (dyn FnMut(&PdfOperatorVariant) -> bool + '_)>, + cell_color: Option, ) -> Result { let mut recording_canvas = RecordingCanvas::new(bbox.width(), bbox.height()); - let canvas_stack = vec![CanvasState { + let mut initial_state = CanvasState { transform: Transform::identity(), pattern_parent_transform: Transform::identity(), text_state: TextState::default(), ..Default::default() - }]; + }; + if let Some(color) = cell_color { + initial_state.paint.set_fill_color(color); + initial_state.paint.set_stroke_color(color); + } + let canvas_stack = vec![initial_state]; let mut other = PdfCanvas:: { current_path: None, @@ -403,6 +411,15 @@ impl<'a, B: CanvasBackend> PdfCanvas<'a, B> { PaintType::Uncolored => Some(&mut uncolored_filter), }; + // The cell of an uncolored pattern is painted in the color selected + // together with the pattern. + let paint = &self.current_state()?.paint; + let cell_color = match paint_type { + PaintType::Colored => None, + PaintType::Uncolored if for_stroke => Some(paint.stroke_color), + PaintType::Uncolored => Some(paint.fill_color), + }; + // Render the tiling content into a temporary canvas. let recording_canvas = self.record_content_stream( content_stream, @@ -410,6 +427,7 @@ impl<'a, B: CanvasBackend> PdfCanvas<'a, B> { &bbox, Some(resources.get()?), filter, + cell_color, )?; let step = [*x_step, *y_step];