Merge pull request #5487 from dotCooCoo/fix/text-range-stroke

Fix range text stroke weight defaults and scaling
This commit is contained in:
BFlat
2026-11-04 12:45:26 +07:00
committed by GitHub
3 changed files with 25 additions and 1 deletions
+8 -1
View File
@@ -351,6 +351,8 @@ pub(crate) struct CharChange {
stroke: Option<Paint>,
/// Weight (`strokeWidth`), cap, join, miter limit and dashes of the character stroke.
stroke_opts: StrokeChange,
/// Document points per text-space point for a stroke edit.
stroke_scale: Option<f64>,
underline: Option<bool>,
strikethrough: Option<bool>,
all_caps: Option<bool>,
@@ -641,6 +643,7 @@ impl CharChange {
fill: paint_param(p, "fill", cmd)?,
stroke: paint_param(p, "stroke", cmd)?,
stroke_opts,
stroke_scale: None,
underline: flag("underline"),
strikethrough: flag("strikethrough"),
all_caps: flag("allCaps"),
@@ -723,6 +726,7 @@ impl CharChange {
self.tcy_up_down = self.tcy_up_down.map(|v| local_type_value(t, v, (-1296.0, 1296.0), false, cmd)).transpose()?;
self.tcy_left_right = self.tcy_left_right.map(|v| local_type_value(t, v, (-1296.0, 1296.0), false, cmd)).transpose()?;
self.h_scale = self.h_scale.map(|v| local_type_value(t, v, (1.0, 10000.0), true, cmd)).transpose()?;
self.stroke_scale = Some(vectorcraft_doc::Scaling::factor(t.xf).filter(|k| k.is_finite() && *k > 1e-12).unwrap_or(1.0));
Ok(())
}
@@ -809,10 +813,13 @@ impl CharChange {
st.fill = v.clone();
}
if let Some(v) = &self.stroke {
if !v.is_none() {
super::gradient::run_stroke_weight(st);
}
st.stroke = v.clone();
}
if !self.stroke_opts.is_empty() {
self.stroke_opts.apply_char(st);
self.stroke_opts.apply_char_scaled(st, self.stroke_scale.unwrap_or(1.0));
}
if let Some(v) = self.underline {
st.underline = v;
+15
View File
@@ -91,6 +91,21 @@ fn range_stroke_options_split_runs() {
assert!(s.execute("text.setRangeStyle", &json!({"id": id.0, "strokeOptions": 3})).is_err());
}
#[test]
fn range_stroke_gains_a_default_weight_and_scales_explicit_weight() {
let mut s = session();
let id = NodeId(run(&mut s, "text.create", json!({"x": 10, "y": 50, "text": "ABCD"}))["id"].as_u64().unwrap());
let weights = |s: &Session| text(s, id).runs.iter().map(|r| r.style.stroke_width).collect::<Vec<_>>();
run(&mut s, "text.setRangeStyle", json!({"id": id.0, "start": 0, "end": 2, "stroke": "#ff0000"}));
assert_eq!(weights(&s), [1.0, 0.0], "a newly painted range has a visible default stroke");
run(&mut s, "object.scale", json!({"ids": [id.0], "sx": 200, "sy": 200}));
run(&mut s, "text.setRangeStyle", json!({"id": id.0, "start": 0, "end": 2, "strokeWidth": 2.0}));
assert_eq!(weights(&s), [1.0, 0.0], "two document points become one text-space point at 200% scale");
run(&mut s, "text.setRangeStyle", json!({"id": id.0, "start": 2, "end": 4, "stroke": "#0000ff", "strokeWidth": 4.0}));
assert_eq!(weights(&s), [1.0, 2.0], "a different range receives its scaled weight");
}
#[test]
fn eyedropper_and_outlines_carry_the_character_stroke() {
let mut s = session();
+2
View File
@@ -2404,6 +2404,8 @@ dash?, dashOffset?, alignDashes?}}`. `text.setStyle {ids?, stroke?, strokeWidth?
and weight of the characters of each object given (without `start` and `end`: all of its text), with its other keys in
one undo step; `strokeWidth` is in document points, as `stroke.set` gives the weight of scaled type. A `stroke` color
without `strokeWidth` gives characters that have no stroke weight 1 pt, as `paint.setStroke` does.
`text.setRangeStyle` does the same for its range, and its `strokeWidth` and `strokeOptions` lengths are in document
points too. The 1 pt default of both commands is in the type's own units, so on type scaled to 200% it draws 2 pt wide.
`align: "inside"|"outside"` keeps the characters' stroke on that side of the
glyph outlines, drawn at twice the weight and clipped, while the type stays editable (beyond Illustrator); SVG export
writes the text live and that stroke as glyph outlines over it. `inspect_document` reports each object's stroke as `strokeOptions` (type: its first