Merge pull request #6937 from dotCooCoo/fix/artboard-new-subpoint-sides

Artboards: artboard.new keeps a side below 1 pt as given
This commit is contained in:
BFlat
2026-11-13 03:29:11 +07:00
committed by GitHub
2 changed files with 27 additions and 4 deletions
+13 -4
View File
@@ -850,14 +850,23 @@ fn move_node(s: &mut Session, p: &Value) -> Result<Value> {
ok()
}
fn rect_from(p: &Value, default: Rect) -> Rect {
/// The rectangle from `x`, `y`, `width` and `height`, each defaulting to `default`'s. Sides are kept as given.
fn rect_unclamped(p: &Value, default: Rect) -> Rect {
let x = f64_or(p, "x", default.x0);
let y = f64_or(p, "y", default.y0);
let w = f64_or(p, "width", default.width()).max(1.0);
let h = f64_or(p, "height", default.height()).max(1.0);
let w = f64_or(p, "width", default.width());
let h = f64_or(p, "height", default.height());
Rect::new(x, y, x + w, y + h)
}
/// [`rect_unclamped`] with each supplied side raised to at least 1 pt, as `artboard.setProps` uses it.
/// A side left out keeps `default`'s, even below 1 pt.
fn rect_from(p: &Value, default: Rect) -> Rect {
let r = rect_unclamped(p, default);
let floor = |key: &str, side: f64| if p.get(key).and_then(Value::as_f64).is_some() { side.max(1.0) } else { side };
Rect::new(r.x0, r.y0, r.x0 + floor("width", r.width()), r.y0 + floor("height", r.height()))
}
/// An artboard command's `color` param, its background color: None when absent, `Some(None)` for
/// null (no color), else `Some(Some(color))`.
fn artboard_color(p: &Value, cmd: &str) -> Result<Option<Option<vectorcraft_color::Color>>> {
@@ -942,7 +951,7 @@ fn artboard_new(s: &mut Session, p: &Value) -> Result<Value> {
src + side.offset(src)
}
None if p.get("beside").is_some_and(|v| !v.is_null()) => return Err(bad("artboard.new", "`beside` needs `side`")),
None => rect_from(p, def),
None => rect_unclamped(p, def),
};
// A size taken from another artboard (`side`, or width or height left out) keeps to the limit too.
if !super::newdoc::side_in_range(r.width()) || !super::newdoc::side_in_range(r.height()) {
+14
View File
@@ -291,6 +291,20 @@ fn file_new_and_artboard_new_take_the_same_largest_side() {
assert!(err.contains(in_meters), "{p}: {err}");
}
assert_eq!(boards(&s).len(), 2);
// A side below 1 pt is kept as given, as file.new keeps it, and a size taken from such an artboard keeps it too.
let mut s = new_doc(json!({"width": 0.5, "height": 0.25}));
assert_eq!(boards(&s), [Rect::new(0.0, 0.0, 0.5, 0.25)]);
s.execute("artboard.new", &json!({"x": 0, "y": 10, "width": 0.5, "height": 0.75})).unwrap();
s.execute("artboard.new", &json!({"x": 0, "y": 20})).unwrap();
assert_eq!(boards(&s)[1..], [Rect::new(0.0, 10.0, 0.5, 10.75), Rect::new(0.0, 20.0, 0.5, 20.75)]);
// artboard.setProps keeps such sides when it changes something else, on a locked artboard too,
// and still raises a supplied side to 1 pt.
s.execute("artboard.setProps", &json!({"index": 1, "name": "Small"})).unwrap();
s.execute("artboard.setProps", &json!({"index": 2, "locked": true})).unwrap();
s.execute("artboard.setProps", &json!({"index": 2, "name": "Small locked"})).unwrap();
s.execute("artboard.setProps", &json!({"index": 0, "height": 0.5})).unwrap();
assert_eq!(boards(&s), [Rect::new(0.0, 0.0, 0.5, 1.0), Rect::new(0.0, 10.0, 0.5, 10.75), Rect::new(0.0, 20.0, 0.5, 20.75)]);
}
#[test]