From 23619155c8afef59f2a3b999c194ed2e9d6b05ae Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 23 Oct 2026 22:35:20 -0700 Subject: [PATCH] Outline Stroke: a Transform effect that scales strokes or moves patterns outlines as drawn main's reshape takes the document as its first argument, and since #885 a path whose Transform effect has Scale Strokes & Effects or Transform Patterns on is drawn through its art (has_transform_art). Outline Stroke, Expand's Stroke option and Flatten Transparency's Convert All Strokes to Outlines now take that art for such a path too, so a 4 pt stroke that the effect scales to 200% outlines at the 8 pt the canvas draws. --- crates/engine/src/cmd/pathops.rs | 11 ++++++----- crates/engine/src/tests_outlinestroke.rs | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/pathops.rs b/crates/engine/src/cmd/pathops.rs index e399d5390..7173e3245 100644 --- a/crates/engine/src/cmd/pathops.rs +++ b/crates/engine/src/cmd/pathops.rs @@ -720,17 +720,18 @@ pub(crate) fn outlined_stroke( /// stroke's own geometry effects then shape the path that stroke outlines; [`outlined_stroke`] /// gets the stroke without them. A fill with a random Transform effect paints the outline its /// geometry effects make of the path, on a copy of its own. A path whose Transform effect copies -/// it with a gradient becomes the art the canvas draws for it, each copy with its own gradient and -/// its strokes outlined before the art gets new ids. Random in a Transform effect takes its seed +/// it with a gradient, or has Scale Strokes & Effects or Transform Patterns on, becomes the art the +/// canvas draws for it, each copy with its own gradient, stroke width and pattern, and its strokes +/// outlined before the art gets new ids. Random in a Transform effect takes its seed /// from the object's id, so these effects give the outline the values the canvas drew. /// `None` when `l` has no stroke to outline. pub(crate) fn outline_strokes(d: &mut Document, brushes: &[vectorcraft_brush::Brush], l: &Node) -> Option { - use vectorcraft_render::effects::{GeomContext, apply_geometry_with, has_geometry, has_gradient_copies, is_geometry, reshape}; + use vectorcraft_render::effects::{GeomContext, apply_geometry_with, has_geometry, has_transform_art, is_geometry, reshape}; if !l.appearance.items.iter().any(|i| matches!(i, AppearanceItem::Stroke(st) if outlinable(st))) { return None; } - if has_gradient_copies(l) { - let mut art = reshape(l, None)?; + if has_transform_art(l) { + let mut art = reshape(Some(&*d), l, None)?; outline_member_strokes(d, brushes, &mut art); return Some(d.reid(&art)); } diff --git a/crates/engine/src/tests_outlinestroke.rs b/crates/engine/src/tests_outlinestroke.rs index 84428432a..7ea96d375 100644 --- a/crates/engine/src/tests_outlinestroke.rs +++ b/crates/engine/src/tests_outlinestroke.rs @@ -303,6 +303,23 @@ fn each_transform_copy_keeps_its_own_gradient() { assert_eq!(differ, none_differ); } +/// With Scale Strokes & Effects on, the canvas draws the stroke of a solid square that its +/// Transform effect scales to 200% at 8 pt; the outlined result draws the same. +#[test] +fn a_transform_that_scales_strokes_outlines_the_scaled_stroke() { + let scale = json!({"scaleH": 200, "scaleV": 200, "strokes": true}); + let mut differ = vec![]; + for (command, p) in outlining() { + let mut s = session(); + square_with(&mut s, json!({"color": "#33aa55"}), Value::Null, &[("distort.transform", scale.clone())]); + let before = render(&s); + outlined_by(&mut s, command, &p); + differ.push((command, differing(&before, &render(&s), 64))); + } + let none_differ: Vec<_> = outlining().map(|(command, _)| (command, 0)).into_iter().collect(); + assert_eq!(differ, none_differ); +} + /// Random in a Transform effect takes its seed from the object's id. The outline draws as the /// canvas drew the object with Random in a Transform effect that copies a gradient square, in a /// Transform effect on the stroke of such a square, and in a Transform effect on the fill of a