mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 21:17:48 +00:00
Merge pull request #6912 from dotCooCoo/fix/new-document-side-limit
New Document: file.new and artboard.new take the same largest artboard side
This commit is contained in:
@@ -23,7 +23,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New…",
|
||||
["File"],
|
||||
None,
|
||||
"{preset?: a name from file.newPresets (start from it; the other params change it), name?|title?: document name (default Untitled-N), width?: pt=612|\"210 mm\", height?: pt=792, units?: \"Pixels\"|\"Points\"|\"Picas\"|\"Inches\"|\"Millimeters\"|\"Centimeters\"|\"Feet\"|\"Yards\"|\"Meters\"|\"Feet & Inches\" (default: prefs unitsGeneral, as print presets; screen presets: Pixels), orientation?: \"portrait\"|\"landscape\" (swaps width and height to match), artboards?: n (1–1000), artboardLayout?: {layout?: \"gridByRow\"|\"gridByColumn\"|\"row\"|\"column\", columns?: n (default: all in one row), spacing?: pt=20, rightToLeft?: bool}, bleed?: pt|[top, bottom, left, right]|{top?, …} (0–72 pt), backgroundContents?: \"transparent\"|\"white\" (a white artboard background, not an object), colorMode?: \"rgb\"|\"cmyk\" (a CMYK document starts with CMYK swatches and stores the colours applied to it as CMYK), rasterEffectsPpi?: 72|150|300 (1–2400), previewMode?: \"default\"|\"pixel\"|\"overprint\" (overprint turns Overprint Preview on, pixel Pixel Preview in the app; default leaves both), alignNewObjectsToPixelGrid?: bool (Advanced Options › Align New Objects to Pixel Grid: paths drawn in the document are pixel-aligned; default on for the Mobile and Web presets, else off; document.setup changes it later), created?: Unix seconds|null (File Info's created date; default now, recorded in the journal so a replay matches), size?: a preset's size label (a preset file.newPresets lists can be passed back; ignored)} → {index, previewMode}; the size is remembered in file.newPresets' Recent",
|
||||
"{preset?: a name from file.newPresets (start from it; the other params change it), name?|title?: document name (default Untitled-N), width?: pt=612|\"210 mm\", height?: pt=792 (each more than 0 and at most 3,999,856 pt, as artboard.new takes them), units?: \"Pixels\"|\"Points\"|\"Picas\"|\"Inches\"|\"Millimeters\"|\"Centimeters\"|\"Feet\"|\"Yards\"|\"Meters\"|\"Feet & Inches\" (default: prefs unitsGeneral, as print presets; screen presets: Pixels), orientation?: \"portrait\"|\"landscape\" (swaps width and height to match), artboards?: n (1–1000), artboardLayout?: {layout?: \"gridByRow\"|\"gridByColumn\"|\"row\"|\"column\", columns?: n (default: all in one row), spacing?: pt=20, rightToLeft?: bool}, bleed?: pt|[top, bottom, left, right]|{top?, …} (0–72 pt), backgroundContents?: \"transparent\"|\"white\" (a white artboard background, not an object), colorMode?: \"rgb\"|\"cmyk\" (a CMYK document starts with CMYK swatches and stores the colours applied to it as CMYK), rasterEffectsPpi?: 72|150|300 (1–2400), previewMode?: \"default\"|\"pixel\"|\"overprint\" (overprint turns Overprint Preview on, pixel Pixel Preview in the app; default leaves both), alignNewObjectsToPixelGrid?: bool (Advanced Options › Align New Objects to Pixel Grid: paths drawn in the document are pixel-aligned; default on for the Mobile and Web presets, else off; document.setup changes it later), created?: Unix seconds|null (File Info's created date; default now, recorded in the journal so a replay matches), size?: a preset's size label (a preset file.newPresets lists can be passed back; ignored)} → {index, previewMode}; the size is remembered in file.newPresets' Recent",
|
||||
always,
|
||||
super::newdoc::file_new
|
||||
),
|
||||
|
||||
@@ -128,7 +128,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New Artboard",
|
||||
["Window", "Artboards"],
|
||||
None,
|
||||
"{x?, y?, width?, height?, name?, color?: color (its background color, as artboard.setProps takes it, #rrggbbaa and #rgba refused; default: none), guideLabel?: name | \"#rrggbb\" | null (its guide label, as artboard.setProps takes it; default: none), showCenterMark?: bool, showCrossHairs?: bool, showVideoSafeAreas?: bool (as artboard.setProps takes them; default false), videoRulerPixelAspectRatio?: number 0.1–10 (as artboard.setProps takes it; default 1), side?: \"left\"|\"right\"|\"top\"|\"bottom\" (the size of artboard `beside`, 20 pt from that side of it; refused with x, y, width or height), beside?: n (the artboard `side` places it by; default the last)} (default place: right of the last)",
|
||||
"{x?, y?, width?, height? (each more than 0 and at most 3,999,856 pt, as file.new takes them), name?, color?: color (its background color, as artboard.setProps takes it, #rrggbbaa and #rgba refused; default: none), guideLabel?: name | \"#rrggbb\" | null (its guide label, as artboard.setProps takes it; default: none), showCenterMark?: bool, showCrossHairs?: bool, showVideoSafeAreas?: bool (as artboard.setProps takes them; default false), videoRulerPixelAspectRatio?: number 0.1–10 (as artboard.setProps takes it; default 1), side?: \"left\"|\"right\"|\"top\"|\"bottom\" (the size of artboard `beside`, 20 pt from that side of it; refused with x, y, width or height), beside?: n (the artboard `side` places it by; default the last)} (default place: right of the last)",
|
||||
has_doc,
|
||||
artboard_new
|
||||
),
|
||||
@@ -926,6 +926,9 @@ fn artboard_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let guide_label = artboard_guide_label(p, "artboard.new")?.flatten();
|
||||
check_switches(p, &["showCenterMark", "showCrossHairs", "showVideoSafeAreas"], "artboard.new")?;
|
||||
check_numbers(p, &["x", "y", "width", "height"], "artboard.new")?;
|
||||
if ["width", "height"].iter().filter_map(|k| p.get(*k).and_then(Value::as_f64)).any(|side| !super::newdoc::side_in_range(side)) {
|
||||
return Err(super::newdoc::side_error("artboard.new", d0.units));
|
||||
}
|
||||
let r = match artboard_side(p, "artboard.new")? {
|
||||
Some(side) => {
|
||||
if ["x", "y", "width", "height"].iter().any(|k| p.get(*k).is_some_and(|v| !v.is_null())) {
|
||||
@@ -941,6 +944,10 @@ fn artboard_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
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),
|
||||
};
|
||||
// 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()) {
|
||||
return Err(super::newdoc::side_error("artboard.new", d0.units));
|
||||
}
|
||||
let given = artboard_str(p, "name", "artboard.new")?.map(str::trim).filter(|n| !n.is_empty());
|
||||
let (show_center_mark, show_cross_hairs) = (bool_or(p, "showCenterMark", false), bool_or(p, "showCrossHairs", false));
|
||||
let show_video_safe_areas = bool_or(p, "showVideoSafeAreas", false);
|
||||
|
||||
@@ -241,8 +241,8 @@ impl DocSettings {
|
||||
if let Some(h) = length("height", self.units)? {
|
||||
self.height = h;
|
||||
}
|
||||
if !(self.width > 0.0 && self.height > 0.0) || self.width > MAX_SIDE || self.height > MAX_SIDE {
|
||||
return Err(bad(cmd, "width/height must be positive and within the canvas"));
|
||||
if !side_in_range(self.width) || !side_in_range(self.height) {
|
||||
return Err(side_error(cmd, self.units));
|
||||
}
|
||||
match p.get("orientation").and_then(Value::as_str) {
|
||||
None => {}
|
||||
@@ -359,8 +359,29 @@ impl DocSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// Largest artboard side `file.new` accepts, points.
|
||||
const MAX_SIDE: f64 = 16383.0 * 10.0;
|
||||
/// Largest artboard side `file.new` and `artboard.new` accept, points: the span of the canvas
|
||||
/// ([`crate::MAX_COORD`] from the origin) less the largest bleed on both sides, 3,999,856 pt.
|
||||
pub(crate) const MAX_SIDE: f64 = crate::MAX_COORD - 2.0 * vectorcraft_doc::setup::MAX_BLEED;
|
||||
|
||||
/// Is `side` an artboard side `file.new` and `artboard.new` accept: more than 0 and at most [`MAX_SIDE`]?
|
||||
pub(crate) fn side_in_range(side: f64) -> bool {
|
||||
side > 0.0 && side <= MAX_SIDE
|
||||
}
|
||||
|
||||
/// The error of an artboard side outside [`side_in_range`]: the limit in points, and in `units` as
|
||||
/// well when they aren't points, rounded down to the decimals [`Unit::number`] shows so the value
|
||||
/// shown is one the commands take.
|
||||
pub(crate) fn side_error(cmd: &str, units: Unit) -> crate::EngineError {
|
||||
let limit = match units {
|
||||
Unit::Points => format!("{MAX_SIDE} pt"),
|
||||
u => {
|
||||
let step = if matches!(u, Unit::Pixels | Unit::Picas | Unit::Q | Unit::Ha) { 1e3 } else { 1e4 };
|
||||
let shown = (u.from_pt(MAX_SIDE) * step).floor() / step;
|
||||
format!("{MAX_SIDE} pt ({} {})", u.number(u.to_pt(shown)), u.suffix())
|
||||
}
|
||||
};
|
||||
bad(cmd, format!("width and height must be more than 0 and at most {limit}"))
|
||||
}
|
||||
/// Largest artboard spacing, points.
|
||||
const MAX_SPACING: f64 = 1.0e5;
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ fn a_saved_preset_with_values_out_of_range_reads_in_range() {
|
||||
saved,
|
||||
[
|
||||
(1.0, 1.0, 1.0, 0.0, vec![0.0, 0.0, 0.0, 9.0]),
|
||||
(163830.0, 163830.0, 1.0, 1.0e5, vec![72.0, 72.0, 72.0, 0.0]),
|
||||
(3_999_856.0, 200000.0, 1.0, 1.0e5, vec![72.0, 72.0, 72.0, 0.0]),
|
||||
(10.0, 10.0, 1000.0, 0.0, vec![0.0; 4]),
|
||||
]
|
||||
);
|
||||
@@ -242,7 +242,7 @@ fn a_saved_preset_with_values_out_of_range_reads_in_range() {
|
||||
s.execute("file.new", &json!({"preset": "Low"})).unwrap();
|
||||
assert_eq!((boards(&s), s.doc().unwrap().doc.setup.bleed), (vec![Rect::new(0.0, 0.0, 1.0, 1.0)], [0.0, 0.0, 0.0, 9.0]));
|
||||
s.execute("file.new", &json!({"preset": "High"})).unwrap();
|
||||
assert_eq!((boards(&s), s.doc().unwrap().doc.setup.bleed), (vec![Rect::new(0.0, 0.0, 163830.0, 163830.0)], [72.0, 72.0, 72.0, 0.0]));
|
||||
assert_eq!((boards(&s), s.doc().unwrap().doc.setup.bleed), (vec![Rect::new(0.0, 0.0, 3_999_856.0, 200000.0)], [72.0, 72.0, 72.0, 0.0]));
|
||||
s.execute("file.new", &json!({"preset": "Many"})).unwrap();
|
||||
assert_eq!(boards(&s).len(), 1000);
|
||||
let custom = presets(&mut s, "Recent").into_iter().find(|p| p["name"] == "Custom").unwrap();
|
||||
@@ -250,6 +250,49 @@ fn a_saved_preset_with_values_out_of_range_reads_in_range() {
|
||||
assert_eq!(boards(&s).iter().map(|r| r.x0).collect::<Vec<_>>(), [0.0, 100.0, 200.0], "side by side, not overlapping");
|
||||
}
|
||||
|
||||
/// `file.new` and `artboard.new` take the same largest artboard side (the canvas less the largest
|
||||
/// bleed on both sides) and refuse a side just past it, or of 0 or less, with an error that names
|
||||
/// the limit in points and, when the document's units aren't points, in those units too.
|
||||
#[test]
|
||||
fn file_new_and_artboard_new_take_the_same_largest_side() {
|
||||
let max = 3_999_856.0;
|
||||
let in_meters = "at most 3999856 pt (1411.0603 m)";
|
||||
let s = new_doc(json!({"width": max, "height": 170078}));
|
||||
assert_eq!(boards(&s), [Rect::new(0.0, 0.0, max, 170078.0)]);
|
||||
let mut s = Session::new();
|
||||
let err = s.execute("file.new", &json!({"width": max + 1.0, "height": 100})).unwrap_err().to_string();
|
||||
assert!(err.contains("width and height must be more than 0 and at most 3999856 pt") && !err.contains('('), "{err}");
|
||||
let err = s.execute("file.new", &json!({"width": 100, "height": "1412 m", "units": "Meters"})).unwrap_err().to_string();
|
||||
assert!(err.contains(in_meters), "{err}");
|
||||
// The value in other units is rounded down, so it is one file.new takes.
|
||||
let err = s.execute("file.new", &json!({"width": 100, "height": "56000 in", "units": "Inches"})).unwrap_err().to_string();
|
||||
assert!(err.contains("at most 3999856 pt (55553.5555 in)"), "{err}");
|
||||
assert!(s.execute("file.new", &json!({"width": 0, "height": 100})).is_err());
|
||||
assert!(s.doc().is_err(), "no document was made");
|
||||
s.execute("file.new", &json!({"width": "55553.5555 in", "height": 100, "units": "Inches"})).unwrap();
|
||||
|
||||
let mut s = new_doc(json!({"units": "Meters"}));
|
||||
s.execute("artboard.new", &json!({"x": -2_000_000, "y": 0, "width": max, "height": 100})).unwrap();
|
||||
assert_eq!(boards(&s)[1], Rect::new(-2_000_000.0, 0.0, max - 2_000_000.0, 100.0));
|
||||
for p in [
|
||||
json!({"x": -2_000_000, "y": 0, "width": max + 1.0, "height": 100}),
|
||||
json!({"x": 0, "y": 2000, "width": 100, "height": max + 1.0}),
|
||||
json!({"width": 0}),
|
||||
json!({"height": -1}),
|
||||
] {
|
||||
let err = s.execute("artboard.new", &p).unwrap_err().to_string();
|
||||
assert!(err.contains(in_meters), "{p}: {err}");
|
||||
}
|
||||
assert_eq!(boards(&s).len(), 2, "the refused artboards were not added");
|
||||
// A size taken from a wider artboard is refused too.
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "x": -2_000_000, "y": 0, "width": max + 1.0, "height": 100})).unwrap();
|
||||
for p in [json!({}), json!({"x": 0, "y": 5000, "height": 100}), json!({"side": "bottom", "beside": 1})] {
|
||||
let err = s.execute("artboard.new", &p).unwrap_err().to_string();
|
||||
assert!(err.contains(in_meters), "{p}: {err}");
|
||||
}
|
||||
assert_eq!(boards(&s).len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_presets_live_in_the_preferences() {
|
||||
let mut s = Session::new();
|
||||
|
||||
@@ -8144,7 +8144,7 @@ antiAlias Sharp शार्प
|
||||
@msg width must be positive width: चौड़ाई सकारात्मक होना चाहिए
|
||||
@msg width out of range सीमा से बाहर चौड़ाई
|
||||
@msg width/height must be > 0 चौड़ाई / ऊंचाई होना चाहिए > 0
|
||||
@msg width/height must be positive and within the canvas चौड़ाई / ऊंचाई सकारात्मक होना चाहिए और कैनवास के भीतर होना चाहिए
|
||||
@msg width and height must be more than 0 and at most {limit} चौड़ाई और ऊंचाई 0 से अधिक और अधिकतम {limit} होनी चाहिए
|
||||
@msg workspace name can't be empty कार्यक्षेत्र का नाम खाली नहीं हो सकता है
|
||||
@msg x and y must be finite x और y को परिमित होना चाहिए
|
||||
@msg x and y must be finite numbers x और y होना चाहिए परिमित संख्या
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -8394,7 +8394,7 @@ digits Hindi Hindi
|
||||
@msg width must be positive width måste vara positiv
|
||||
@msg width out of range bredden ligger utanför tillåtet omfång
|
||||
@msg width/height must be > 0 bredd/höjd måste vara > 0
|
||||
@msg width/height must be positive and within the canvas bredd/höjd måste vara positiva och inom arbetsytan
|
||||
@msg width and height must be more than 0 and at most {limit} bredd och höjd måste vara större än 0 och högst {limit}
|
||||
@msg x and y must be finite numbers x och y måste vara ändliga tal
|
||||
@msg x and y must be given together x och y måste anges tillsammans
|
||||
@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone behöver ett ankare: docka bredvid en panel med anchor eller i en ny dockkolumn med side (Start eller End)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
+5
-1
@@ -5496,7 +5496,11 @@ value outside that range is read as the nearer end), `previewMode` and `alignNew
|
||||
`file.newPresets {category?}` lists the categories and presets (Recent: the last sizes used; Saved: the user's presets,
|
||||
kept in the preferences by `file.newPresets.save` and removed by `file.newPresets.delete`). A listed preset can be
|
||||
passed straight back to `file.new`. Print presets and sizes without `units` start in `unitsGeneral`; screen presets
|
||||
(mobile, web, video, social) in Pixels. A saved or Recent preset whose size (up to 163,830 pt), `artboards` (1 to
|
||||
(mobile, web, video, social) in Pixels. `file.new` and `artboard.new` take the same artboard sizes: each side more than 0
|
||||
and at most 3,999,856 pt (the 4,000,000 pt the canvas reaches from the origin, less a 72 pt bleed on both sides). A side outside that range is
|
||||
refused with `width and height must be more than 0 and at most 3999856 pt`, followed by the limit in the document's
|
||||
units in parentheses when they are not points, such as `(1411.0603 m)`, and no document or artboard is made.
|
||||
A saved or Recent preset whose size (up to 3,999,856 pt), `artboards` (1 to
|
||||
1,000), spacing (0 to 100,000 pt) or bleed (0 to 72 pt a side) lies outside those ranges is read with the nearer end of
|
||||
each range, and a size of 0 or less is read as 1 pt.
|
||||
The desktop app's `file.newDialog` (Cmd/Ctrl+N) opens the New Document dialog on the first Recent entry, or on the
|
||||
|
||||
Reference in New Issue
Block a user