mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 19:57:05 -06:00
Artboards: Artboard Options shows video safe areas and sets the pixel aspect ratio that View › Rulers › Show Video Rulers counts the artboard in
Squashed from #3831 onto main.
This commit is contained in:
@@ -243,6 +243,8 @@ impl Builder {
|
||||
locked: false,
|
||||
show_center_mark: false,
|
||||
show_cross_hairs: false,
|
||||
show_video_safe_areas: false,
|
||||
video_ruler_pixel_aspect_ratio: 1.0,
|
||||
color: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -340,7 +340,17 @@ pub(super) fn single_artboard(doc: &Document, rect: Rect, name: &str) -> Documen
|
||||
|
||||
/// `d` with one artboard, `rect`.
|
||||
pub(crate) fn with_single_artboard(mut d: Document, rect: Rect, name: &str) -> Document {
|
||||
d.artboards = vec![Artboard { id: 1, name: name.into(), rect, locked: false, show_center_mark: false, show_cross_hairs: false, color: None }];
|
||||
d.artboards = vec![Artboard {
|
||||
id: 1,
|
||||
name: name.into(),
|
||||
rect,
|
||||
locked: false,
|
||||
show_center_mark: false,
|
||||
show_cross_hairs: false,
|
||||
show_video_safe_areas: false,
|
||||
video_ruler_pixel_aspect_ratio: 1.0,
|
||||
color: None,
|
||||
}];
|
||||
d
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,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; default: none), showCenterMark?: bool, showCrossHairs?: bool (as artboard.setProps takes them; default false)} (default place: right of the last)",
|
||||
"{x?, y?, width?, height?, name?, color?: color (its background color, 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)} (default place: right of the last)",
|
||||
has_doc,
|
||||
artboard_new
|
||||
),
|
||||
@@ -143,7 +143,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Artboard Options…",
|
||||
["Window", "Artboards"],
|
||||
None,
|
||||
"{index | artboards: [{index, name?, color?, locked?, showCenterMark?, showCrossHairs?, x?, y?, width?, height?}…] (several artboards in one undo step, each with its own fields; the art inside each before any of them changes goes with it, and an object inside several goes with the first listed that moves or scales its art), name?, color?: color (\"#rrggbb\", {c,m,y,k}, … as the paint commands take: the background color the canvas draws the artboard's paper in, multiplied with the Simulate Colored Paper color while that is on; exports and print leave it out) | null (no background color), locked?: bool (the Artboards panel's lock column: a locked artboard keeps its rectangle (a new x, y, width or height is an error unless locked is false), can't be moved, deleted or cut, and Rearrange and Fit to Artwork Bounds leave it as it is; the art on it (each object of a layer or sublayer whose bounds with strokes overlap it, a group as one object) acts as locked art until it is unlocked: clicks, marquees, select.set, select.all and the tools pass over it (art drawn or pasted onto it stays selected and editable until a selection leaves it out or it is moved off the locked artboards), it is deselected, and when another artboard moves or scales it stays unless lockedAndHidden), showCenterMark?: bool (Artboard Options' Show Center Mark: the canvas draws a mark at the artboard's center), showCrossHairs?: bool (Show Cross Hairs: the canvas draws a line from the middle of each side through the center; both are screen-only, as the artboard's edges are: exports and print leave them out), x?, y?, width?, height?, moveArt?: bool (default false; a position-only change moves the contained art with its artboard), scaleArt?: bool (Scale Artwork with Artboard: resized, the artboard takes the art fully inside it (locked and hidden art only with lockedAndHidden?: bool, default prefs moveLockedWithArtboard) and its guides from its old rectangle onto the new one, each side by its own ratio; strokes/corners/patterns as object.scale), strokes?: bool, corners?: bool, patterns?: bool (the three as object.scale, for art that moves or scales with the artboard), lockedAndHidden?: bool (locked and hidden art moves along; default: the preference; recorded in the journal so a replay matches)} → null, or with scaleArt {scaled: the art scaled}",
|
||||
"{index | artboards: [{index, name?, color?, locked?, showCenterMark?, showCrossHairs?, showVideoSafeAreas?, videoRulerPixelAspectRatio?, x?, y?, width?, height?}…] (several artboards in one undo step, each with its own fields; the art inside each before any of them changes goes with it, and an object inside several goes with the first listed that moves or scales its art), name?, color?: color (\"#rrggbb\", {c,m,y,k}, … as the paint commands take: the background color the canvas draws the artboard's paper in, multiplied with the Simulate Colored Paper color while that is on; exports and print leave it out) | null (no background color), locked?: bool (the Artboards panel's lock column: a locked artboard keeps its rectangle (a new x, y, width or height is an error unless locked is false), can't be moved, deleted or cut, and Rearrange and Fit to Artwork Bounds leave it as it is; the art on it (each object of a layer or sublayer whose bounds with strokes overlap it, a group as one object) acts as locked art until it is unlocked: clicks, marquees, select.set, select.all and the tools pass over it (art drawn or pasted onto it stays selected and editable until a selection leaves it out or it is moved off the locked artboards), it is deselected, and when another artboard moves or scales it stays unless lockedAndHidden), showCenterMark?: bool (Artboard Options' Show Center Mark: the canvas draws a mark at the artboard's center), showCrossHairs?: bool (Show Cross Hairs: the canvas draws a line from the middle of each side through the center), showVideoSafeAreas?: bool (Show Video Safe Areas: the canvas draws the action-safe area 3.5% and the title-safe area 5% in from each edge, as EBU R 95 sets them; the three are screen-only, as the artboard's edges are: exports and print leave them out), videoRulerPixelAspectRatio?: number 0.1–10 (Video Ruler Pixel Aspect Ratio: the width of one video pixel over its height, which View › Rulers › Show Video Rulers counts the artboard in; default 1, square pixels), x?, y?, width?, height?, moveArt?: bool (default false; a position-only change moves the contained art with its artboard), scaleArt?: bool (Scale Artwork with Artboard: resized, the artboard takes the art fully inside it (locked and hidden art only with lockedAndHidden?: bool, default prefs moveLockedWithArtboard) and its guides from its old rectangle onto the new one, each side by its own ratio; strokes/corners/patterns as object.scale), strokes?: bool, corners?: bool, patterns?: bool (the three as object.scale, for art that moves or scales with the artboard), lockedAndHidden?: bool (locked and hidden art moves along; default: the preference; recorded in the journal so a replay matches)} → null, or with scaleArt {scaled: the art scaled}",
|
||||
has_doc,
|
||||
artboard_set
|
||||
),
|
||||
@@ -813,19 +813,44 @@ fn artboard_color(p: &Value, cmd: &str) -> Result<Option<Option<vectorcraft_colo
|
||||
}
|
||||
}
|
||||
|
||||
/// An artboard command's `videoRulerPixelAspectRatio` param: None when absent, else a number within
|
||||
/// [`vectorcraft_doc::VIDEO_PIXEL_ASPECT_RANGE`].
|
||||
fn video_pixel_aspect(p: &Value, cmd: &str) -> Result<Option<f64>> {
|
||||
match p.get("videoRulerPixelAspectRatio") {
|
||||
None => Ok(None),
|
||||
Some(v) => v
|
||||
.as_f64()
|
||||
.filter(|r| vectorcraft_doc::VIDEO_PIXEL_ASPECT_RANGE.contains(r))
|
||||
.map(Some)
|
||||
.ok_or_else(|| bad(cmd, "videoRulerPixelAspectRatio must be a number from 0.1 to 10")),
|
||||
}
|
||||
}
|
||||
|
||||
fn artboard_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let d0 = &s.doc()?.doc;
|
||||
let last = d0.artboards.last().map(|a| a.rect).unwrap_or(Rect::new(0.0, 0.0, 612.0, 792.0));
|
||||
let def = Rect::new(last.x1 + 20.0, last.y0, last.x1 + 20.0 + last.width(), last.y1);
|
||||
let r = rect_from(p, def);
|
||||
let color = artboard_color(p, "artboard.new")?.flatten();
|
||||
check_switches(p, &["showCenterMark", "showCrossHairs"], "artboard.new")?;
|
||||
check_switches(p, &["showCenterMark", "showCrossHairs", "showVideoSafeAreas"], "artboard.new")?;
|
||||
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);
|
||||
let video_ruler_pixel_aspect_ratio = video_pixel_aspect(p, "artboard.new")?.unwrap_or(1.0);
|
||||
let idx = s.edit("New Artboard", |d, _| {
|
||||
let id = d.next_artboard_id();
|
||||
let given = str_param(p, "name").map(str::trim).filter(|n| !n.is_empty());
|
||||
let name = given.map_or_else(|| format!("Artboard {}", d.artboards.len() + 1), str::to_string);
|
||||
d.artboards.push(Artboard { id, name, rect: r, locked: false, show_center_mark, show_cross_hairs, color });
|
||||
d.artboards.push(Artboard {
|
||||
id,
|
||||
name,
|
||||
rect: r,
|
||||
locked: false,
|
||||
show_center_mark,
|
||||
show_cross_hairs,
|
||||
show_video_safe_areas,
|
||||
video_ruler_pixel_aspect_ratio,
|
||||
color,
|
||||
});
|
||||
Ok(d.artboards.len() - 1)
|
||||
})?;
|
||||
Ok(json!({ "index": idx }))
|
||||
@@ -848,9 +873,10 @@ fn artboard_delete(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
ok()
|
||||
}
|
||||
|
||||
/// One artboard's part of `artboard.setProps`: its index, new rectangle, name, color, lock and
|
||||
/// display marks (Show Center Mark, Show Cross Hairs), how far it moved, whether it was resized,
|
||||
/// the map its art and guides take when they go with it (`fit` when it scales them), and that art.
|
||||
/// One artboard's part of `artboard.setProps`: its index, new rectangle, name, color, lock,
|
||||
/// display marks (Show Center Mark, Show Cross Hairs, Show Video Safe Areas) and Video Ruler Pixel
|
||||
/// Aspect Ratio, how far it moved, whether it was resized, the map its art and guides take when
|
||||
/// they go with it (`fit` when it scales them), and that art.
|
||||
struct ArtboardEdit<'a> {
|
||||
index: usize,
|
||||
now: Rect,
|
||||
@@ -859,6 +885,8 @@ struct ArtboardEdit<'a> {
|
||||
lock: Option<bool>,
|
||||
center_mark: Option<bool>,
|
||||
cross_hairs: Option<bool>,
|
||||
safe_areas: Option<bool>,
|
||||
pixel_aspect: Option<f64>,
|
||||
delta: vectorcraft_geom::Vec2,
|
||||
resized: bool,
|
||||
fit: Option<Affine>,
|
||||
@@ -876,7 +904,7 @@ fn artboard_set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
};
|
||||
check_switches(p, &["moveArt", "scaleArt", "strokes", "corners", "patterns", "lockedAndHidden"], "artboard.setProps")?;
|
||||
for q in &entries {
|
||||
check_switches(q, &["locked", "showCenterMark", "showCrossHairs"], "artboard.setProps")?;
|
||||
check_switches(q, &["locked", "showCenterMark", "showCrossHairs", "showVideoSafeAreas"], "artboard.setProps")?;
|
||||
check_numbers(q, &["x", "y", "width", "height"], "artboard.setProps")?;
|
||||
}
|
||||
// Scale Artwork with Artboard: resized, an artboard takes the art fully inside it (as Move
|
||||
@@ -900,6 +928,7 @@ fn artboard_set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let now = rect_from(q, was);
|
||||
let lock = q.get("locked").and_then(Value::as_bool);
|
||||
let (center_mark, cross_hairs) = (q.get("showCenterMark").and_then(Value::as_bool), q.get("showCrossHairs").and_then(Value::as_bool));
|
||||
let (safe_areas, pixel_aspect) = (q.get("showVideoSafeAreas").and_then(Value::as_bool), video_pixel_aspect(q, "artboard.setProps")?);
|
||||
// A locked artboard keeps its rectangle unless this unlocks it; its name can change.
|
||||
let reshaped = [now.x0 - was.x0, now.y0 - was.y0, now.x1 - was.x1, now.y1 - was.y1].iter().any(|d| d.abs() > 1e-6);
|
||||
if reshaped && lock != Some(false) {
|
||||
@@ -925,6 +954,8 @@ fn artboard_set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
lock,
|
||||
center_mark,
|
||||
cross_hairs,
|
||||
safe_areas,
|
||||
pixel_aspect,
|
||||
delta,
|
||||
resized,
|
||||
fit,
|
||||
@@ -961,6 +992,12 @@ fn artboard_set(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if let Some(c) = e.cross_hairs {
|
||||
a.show_cross_hairs = c;
|
||||
}
|
||||
if let Some(v) = e.safe_areas {
|
||||
a.show_video_safe_areas = v;
|
||||
}
|
||||
if let Some(r) = e.pixel_aspect {
|
||||
a.video_ruler_pixel_aspect_ratio = r;
|
||||
}
|
||||
let id = a.id;
|
||||
if let Some(xf) = e.transform {
|
||||
let mut sc = if e.fit.is_some_and(|xf| Scaling::factor(xf).is_some()) { scaling } else { Scaling::default() };
|
||||
|
||||
@@ -975,6 +975,8 @@ fn convert_to_artboards(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
locked: false,
|
||||
show_center_mark: false,
|
||||
show_cross_hairs: false,
|
||||
show_video_safe_areas: false,
|
||||
video_ruler_pixel_aspect_ratio: 1.0,
|
||||
color: None,
|
||||
});
|
||||
converted.push(id);
|
||||
|
||||
@@ -341,6 +341,8 @@ impl DocSettings {
|
||||
locked: false,
|
||||
show_center_mark: false,
|
||||
show_cross_hairs: false,
|
||||
show_video_safe_areas: false,
|
||||
video_ruler_pixel_aspect_ratio: 1.0,
|
||||
color: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -129,18 +129,26 @@ pub fn document(s: &Session) -> Value {
|
||||
}
|
||||
|
||||
/// An artboard's name and rectangle, its background color (`#rrggbb`) when it has one,
|
||||
/// `locked: true` when it is locked, and `showCenterMark: true` and `showCrossHairs: true` when
|
||||
/// it shows them.
|
||||
/// `locked: true` when it is locked, `showCenterMark: true`, `showCrossHairs: true` and
|
||||
/// `showVideoSafeAreas: true` when it shows them, and `videoRulerPixelAspectRatio` when it isn't 1.
|
||||
fn artboard_summary(a: &vectorcraft_doc::Artboard) -> Value {
|
||||
let mut v = json!({"name": a.name, "x": a.rect.x0, "y": a.rect.y0, "width": a.rect.width(), "height": a.rect.height()});
|
||||
if let Some(c) = a.color {
|
||||
v["color"] = json!(c.to_hex());
|
||||
}
|
||||
for (key, on) in [("locked", a.locked), ("showCenterMark", a.show_center_mark), ("showCrossHairs", a.show_cross_hairs)] {
|
||||
for (key, on) in [
|
||||
("locked", a.locked),
|
||||
("showCenterMark", a.show_center_mark),
|
||||
("showCrossHairs", a.show_cross_hairs),
|
||||
("showVideoSafeAreas", a.show_video_safe_areas),
|
||||
] {
|
||||
if on {
|
||||
v[key] = json!(true);
|
||||
}
|
||||
}
|
||||
if a.video_ruler_pixel_aspect_ratio != 1.0 {
|
||||
v["videoRulerPixelAspectRatio"] = json!(a.video_ruler_pixel_aspect_ratio);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
|
||||
@@ -916,21 +916,147 @@ fn artboard_display_marks_round_trip_through_the_native_format() {
|
||||
assert!(back.artboards.iter().all(|a| !a.show_center_mark && !a.show_cross_hairs));
|
||||
}
|
||||
|
||||
/// Show Center Mark and Show Cross Hairs are screen-only: SVG and PDF exports (and print, which
|
||||
/// prints the PDF) come out the same with them on as with them off.
|
||||
/// Show Center Mark, Show Cross Hairs, Show Video Safe Areas and the Video Ruler Pixel Aspect Ratio
|
||||
/// are screen-only: SVG and PDF exports (and print, which prints the PDF) come out the same with
|
||||
/// them on as with them off.
|
||||
#[test]
|
||||
fn artboard_display_marks_stay_out_of_exports() {
|
||||
let mut s = session();
|
||||
rect(&mut s);
|
||||
let doc = |s: &Session| (*s.doc().unwrap().doc).clone();
|
||||
let off = doc(&s);
|
||||
s.execute("artboard.setProps", &json!({"index": 0, "showCenterMark": true, "showCrossHairs": true})).unwrap();
|
||||
s.execute(
|
||||
"artboard.setProps",
|
||||
&json!({"index": 0, "showCenterMark": true, "showCrossHairs": true, "showVideoSafeAreas": true, "videoRulerPixelAspectRatio": 2}),
|
||||
)
|
||||
.unwrap();
|
||||
let on = doc(&s);
|
||||
assert_ne!(on.artboards, off.artboards);
|
||||
assert_eq!(vectorcraft_svg::export(&on, &Default::default()), vectorcraft_svg::export(&off, &Default::default()));
|
||||
assert_eq!(crate::export_pdf(&on, &Default::default()).unwrap(), crate::export_pdf(&off, &Default::default()).unwrap());
|
||||
}
|
||||
|
||||
/// Artboard Options' video display options: `artboard.setProps {showVideoSafeAreas,
|
||||
/// videoRulerPixelAspectRatio}` sets each on that artboard only, in one undo step, also for a
|
||||
/// locked artboard and in an `artboards` list. A change without them leaves them, a duplicate
|
||||
/// keeps them, `artboard.new` takes them, `inspect_document` reports them, and a value that isn't
|
||||
/// true or false, or a ratio outside 0.1–10, is refused and changes nothing.
|
||||
#[test]
|
||||
fn artboard_video_display_options_are_set_in_one_undo_step() {
|
||||
let mut s = session();
|
||||
s.execute("artboard.new", &json!({})).unwrap();
|
||||
let video = |s: &Session, i: usize| {
|
||||
let a = &s.doc().unwrap().doc.artboards[i];
|
||||
(a.show_video_safe_areas, a.video_ruler_pixel_aspect_ratio)
|
||||
};
|
||||
let undo = |s: &Session| s.doc().unwrap().history.undo.len();
|
||||
assert_eq!((video(&s, 0), video(&s, 1)), ((false, 1.0), (false, 1.0)), "off and square by default");
|
||||
let u0 = undo(&s);
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "showVideoSafeAreas": true})).unwrap();
|
||||
assert_eq!((video(&s, 0), video(&s, 1)), ((false, 1.0), (true, 1.0)), "only that artboard");
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "videoRulerPixelAspectRatio": 0.9})).unwrap();
|
||||
assert_eq!(video(&s, 1), (true, 0.9));
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "name": "Video"})).unwrap();
|
||||
assert_eq!(video(&s, 1), (true, 0.9), "a change without them leaves them");
|
||||
assert_eq!(undo(&s), u0 + 3, "one undo step each");
|
||||
for was in [(true, 0.9), (true, 1.0), (false, 1.0)] {
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(video(&s, 1), was);
|
||||
}
|
||||
s.execute("edit.redo", &json!({})).unwrap();
|
||||
assert_eq!(video(&s, 1), (true, 1.0));
|
||||
// A locked artboard keeps its rectangle, not its video display options.
|
||||
s.execute("artboard.setProps", &json!({"index": 0, "locked": true})).unwrap();
|
||||
s.execute("artboard.setProps", &json!({"index": 0, "videoRulerPixelAspectRatio": 2})).unwrap();
|
||||
assert_eq!(video(&s, 0), (false, 2.0));
|
||||
let u = undo(&s);
|
||||
s.execute(
|
||||
"artboard.setProps",
|
||||
&json!({"artboards": [{"index": 0, "showVideoSafeAreas": true}, {"index": 1, "showVideoSafeAreas": false, "videoRulerPixelAspectRatio": 1.2}]}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!((video(&s, 0), video(&s, 1)), ((true, 2.0), (false, 1.2)), "each listed artboard takes its own");
|
||||
assert_eq!(undo(&s), u + 1, "in one undo step");
|
||||
let doc = crate::inspect::document(&s);
|
||||
assert_eq!(doc["artboards"][0]["showVideoSafeAreas"], json!(true));
|
||||
assert_eq!(
|
||||
(doc["artboards"][0]["videoRulerPixelAspectRatio"].clone(), doc["artboards"][1]["videoRulerPixelAspectRatio"].clone()),
|
||||
(json!(2.0), json!(1.2))
|
||||
);
|
||||
assert!(doc["artboards"][1].get("showVideoSafeAreas").is_none(), "{doc}");
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "videoRulerPixelAspectRatio": 1})).unwrap();
|
||||
assert!(crate::inspect::document(&s)["artboards"][1].get("videoRulerPixelAspectRatio").is_none(), "square pixels aren't reported");
|
||||
let copy = s.execute("artboard.duplicate", &json!({"index": 0})).unwrap()["index"].as_u64().unwrap() as usize;
|
||||
assert_eq!(video(&s, copy), (true, 2.0), "a duplicate keeps them");
|
||||
let made = s.execute("artboard.new", &json!({"showVideoSafeAreas": true, "videoRulerPixelAspectRatio": 0.1})).unwrap()["index"].as_u64().unwrap()
|
||||
as usize;
|
||||
assert_eq!(video(&s, made), (true, 0.1), "a new artboard takes them too");
|
||||
let before = s.doc().unwrap().doc.clone();
|
||||
for bad in [json!("yes"), json!(1)] {
|
||||
assert!(s.execute("artboard.setProps", &json!({"index": 1, "showVideoSafeAreas": bad})).is_err());
|
||||
assert!(s.execute("artboard.new", &json!({"showVideoSafeAreas": bad})).is_err());
|
||||
}
|
||||
for bad in [json!(0.09), json!(10.5), json!(0), json!(-1), json!("2"), json!(null)] {
|
||||
assert!(s.execute("artboard.setProps", &json!({"index": 1, "videoRulerPixelAspectRatio": bad})).is_err(), "{bad}");
|
||||
assert!(s.execute("artboard.new", &json!({"videoRulerPixelAspectRatio": bad})).is_err(), "{bad}");
|
||||
}
|
||||
assert!(
|
||||
s.execute(
|
||||
"artboard.setProps",
|
||||
&json!({"artboards": [{"index": 0, "showVideoSafeAreas": false}, {"index": 1, "videoRulerPixelAspectRatio": 11}]})
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(*s.doc().unwrap().doc, *before, "a bad value changes nothing");
|
||||
s.execute("artboard.setProps", &json!({"index": 1, "videoRulerPixelAspectRatio": 10})).unwrap();
|
||||
assert_eq!(video(&s, 1), (false, 10.0), "10 is in range");
|
||||
}
|
||||
|
||||
/// The native format keeps Show Video Safe Areas and the Video Ruler Pixel Aspect Ratio. A
|
||||
/// document whose artboards hide the safe areas and have square pixels is written byte for byte as
|
||||
/// before they could be set, a file written without the two keys opens with the safe areas off
|
||||
/// and square pixels, and a file's ratio outside 0.1–10 opens brought into that range.
|
||||
#[test]
|
||||
fn artboard_video_display_options_round_trip_through_the_native_format() {
|
||||
let mut s = session();
|
||||
s.execute("artboard.new", &json!({})).unwrap();
|
||||
let save = |s: &Session| vectorcraft_format::save(&s.doc().unwrap().doc, false);
|
||||
let before = save(&s);
|
||||
s.execute(
|
||||
"artboard.setProps",
|
||||
&json!({"artboards": [{"index": 0, "showVideoSafeAreas": true}, {"index": 1, "videoRulerPixelAspectRatio": 0.9}]}),
|
||||
)
|
||||
.unwrap();
|
||||
let bytes = save(&s);
|
||||
let saved: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
|
||||
let ab = |i: usize| saved["document"]["artboards"][i].clone();
|
||||
assert_eq!(ab(0)["show_video_safe_areas"], json!(true));
|
||||
assert!(ab(0).get("video_ruler_pixel_aspect_ratio").is_none(), "square pixels aren't written: {}", ab(0));
|
||||
assert_eq!(ab(1)["video_ruler_pixel_aspect_ratio"], json!(0.9));
|
||||
assert!(ab(1).get("show_video_safe_areas").is_none(), "hidden safe areas aren't written: {}", ab(1));
|
||||
let back = vectorcraft_format::load(&bytes).unwrap();
|
||||
assert_eq!(back.artboards, s.doc().unwrap().doc.artboards);
|
||||
assert_eq!((back.artboards[0].show_video_safe_areas, back.artboards[1].video_ruler_pixel_aspect_ratio), (true, 0.9));
|
||||
s.execute("artboard.setProps", &json!({"artboards": [{"index": 0, "showVideoSafeAreas": false}, {"index": 1, "videoRulerPixelAspectRatio": 1}]}))
|
||||
.unwrap();
|
||||
assert_eq!(save(&s), before, "off and square again, the file is as it was");
|
||||
// A file whose artboards have no such keys (older writers) opens with them off and square.
|
||||
let mut old: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
|
||||
for a in old["document"]["artboards"].as_array_mut().unwrap() {
|
||||
let a = a.as_object_mut().unwrap();
|
||||
a.remove("show_video_safe_areas");
|
||||
a.remove("video_ruler_pixel_aspect_ratio");
|
||||
}
|
||||
let back = vectorcraft_format::load(&serde_json::to_vec(&old).unwrap()).unwrap();
|
||||
assert!(back.artboards.iter().all(|a| !a.show_video_safe_areas && a.video_ruler_pixel_aspect_ratio == 1.0));
|
||||
for (written, opens) in [(json!(0), 0.1), (json!(-3), 0.1), (json!(1e9), 10.0), (json!(2.5), 2.5)] {
|
||||
let mut file: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
|
||||
file["document"]["artboards"][1]["video_ruler_pixel_aspect_ratio"] = written.clone();
|
||||
let back = vectorcraft_format::load(&serde_json::to_vec(&file).unwrap()).unwrap();
|
||||
assert_eq!(back.artboards[1].video_ruler_pixel_aspect_ratio, opens, "{written}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swatch_groups_duplicate_sort() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user