Export for Screens: the params text and the format error list every format a row writes

The document.exportForScreens params text listed png, png8, jpg, webp,
gif, svg, svgz and pdf for a row's format, and its settings keys left
out svgz. A row of another format VectorCraft writes, such as eps, was
refused with "Export for Screens writes png, png8, jpg, webp, gif, svg,
svgz or pdf". Rows of tiff, bmp, tga and psd are written too, and the
Export for Screens dialog offers TIFF rows.

The params text, its settings keys and the error now list the 12
formats a row writes: png, png8, jpg, webp, gif, tiff, bmp, tga, psd,
svg, svgz and pdf. Which formats a row accepts does not change.
This commit is contained in:
dotCooCoo
2026-10-16 02:56:34 +07:00
committed by bflatastic
parent 53c59f06de
commit 66dbe40ea6
3 changed files with 39 additions and 2 deletions
+1 -1
View File
@@ -133,7 +133,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Export for Screens",
["File", "Export"],
None,
"{folder?, zip?: false (one store-only .zip of every file: no folder → {name, dataBase64, bytes, files: [name…]}; with a folder it is written there → {path, bytes, files}), artboards?: [index…] | range?: \"1-3\" (default all), assets?: [asset id…] (instead of artboards: each asset's art alone, cropped to its bounds and named after the asset; see assets.list), fullDocument?: false (one file per format instead: a PDF of every artboard, other formats the bounds of all visible art, named after the document), includeBleed?: false (artboards grown by the document's bleed), subfolders?: false (each row's files in a sub-folder: its folder, else its size for raster (1x, 2x, 100w…) or its format (SVG, PDF)), preset?: mobile (PNG 1x, 2x, 3x) | density (PNG 0.75x–4x in ldpi…xxxhdpi sub-folders) instead of formats (turns subfolders on), formats?: [{format: png|png8|jpg|webp|gif|svg|svgz|pdf, scale?: 1 | \"2x\" | \"100w\" | \"100h\" | \"72ppi\", width?: px | height?: px | ppi? (raster only; width, then height, then ppi win over scale), suffix?: (raster default: @2x, @100w, @100h, none at 1x; vector formats drop size suffixes), folder?: (its sub-folder), quality?: (jpg 0–100), …the format's options (antiAlias, background, preset (pdf)…)}], settings?: {png|png8|jpg|webp|gif|svg|pdf: {…options for every row of that format}} (rows' own options win), prefix?, antiAlias?: none|art|type (raster rows without their own), openLocation?: (remembered; the app shows the files after exporting)} one file per artboard and format (a PDF holds its artboard alone; artboards with the same name, in any case, get -2, -3…; an unnamed one is Artboard-N) → {files: [path…]}; no folder → {files: [{name, dataBase64}]} (names include sub-folders: 2x/Icon@2x.png). The params (but zip and assets) are remembered in the document (document.exportSettings; not an undo step)",
"{folder?, zip?: false (one store-only .zip of every file: no folder → {name, dataBase64, bytes, files: [name…]}; with a folder it is written there → {path, bytes, files}), artboards?: [index…] | range?: \"1-3\" (default all), assets?: [asset id…] (instead of artboards: each asset's art alone, cropped to its bounds and named after the asset; see assets.list), fullDocument?: false (one file per format instead: a PDF of every artboard, other formats the bounds of all visible art, named after the document), includeBleed?: false (artboards grown by the document's bleed), subfolders?: false (each row's files in a sub-folder: its folder, else its size for raster (1x, 2x, 100w…) or its format (SVG, PDF)), preset?: mobile (PNG 1x, 2x, 3x) | density (PNG 0.75x–4x in ldpi…xxxhdpi sub-folders) instead of formats (turns subfolders on), formats?: [{format: png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf, scale?: 1 | \"2x\" | \"100w\" | \"100h\" | \"72ppi\", width?: px | height?: px | ppi? (raster only; width, then height, then ppi win over scale), suffix?: (raster default: @2x, @100w, @100h, none at 1x; vector formats drop size suffixes), folder?: (its sub-folder), quality?: (jpg 0–100), …the format's options (antiAlias, background, preset (pdf)…)}], settings?: {png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf: {…options for every row of that format}} (rows' own options win), prefix?, antiAlias?: none|art|type (raster rows without their own), openLocation?: (remembered; the app shows the files after exporting)} one file per artboard and format (a PDF holds its artboard alone; artboards with the same name, in any case, get -2, -3…; an unnamed one is Artboard-N) → {files: [path…]}; no folder → {files: [{name, dataBase64}]} (names include sub-folders: 2x/Icon@2x.png). The params (but zip and assets) are remembered in the document (document.exportSettings; not an undo step)",
has_doc,
screens::export_for_screens
),
+1 -1
View File
@@ -170,7 +170,7 @@ impl Row {
}
let format = writable(C, Some(str_param(row, "format").unwrap_or("png")), None)?;
if matches!(format.id, "vectorcraft" | "template" | "ai" | "txt" | "dxf" | "eps" | "emf" | "wmf") {
return Err(bad(C, "Export for Screens writes png, png8, jpg, webp, gif, svg, svgz or pdf"));
return Err(bad(C, "Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz or pdf"));
}
// Vector formats have no pixel size: it doesn't apply and adds no @2x suffix (not even one
// left over from a raster row switched to SVG or PDF).
@@ -225,6 +225,43 @@ fn format_settings_apply_to_their_rows_and_rows_win() {
assert!(f[2].1.len() < f[3].1.len(), "quality 5 is smaller than quality 100");
}
/// The format ids `text` lists as `a|b|c` after `start`, up to `end`, sorted.
fn listed(text: &str, start: &str, end: char) -> Vec<String> {
let (_, rest) = text.split_once(start).unwrap_or_else(|| panic!("no `{start}` in {text}"));
let mut ids: Vec<String> = rest.split(end).next().unwrap_or_default().split('|').map(|f| f.trim().to_string()).collect();
ids.sort();
ids
}
/// The params text (a row's `format` and the keys of `settings`) and the refusal of a format a row
/// can't have list exactly the formats a row writes.
#[test]
fn the_params_and_the_refusal_list_every_format_a_row_writes() {
let mut s = session(1);
let (mut written, mut refusals) = (vec![], vec![]);
for f in FORMATS.iter().filter(|f| f.write) {
match s.execute("document.exportForScreens", &json!({"formats": [{"format": f.id}]})) {
Ok(r) => {
assert_eq!(files(&r).len(), 1, "{}", f.id);
written.push(f.id.to_string());
}
Err(crate::EngineError::BadParams { msg, .. }) => refusals.push(msg),
Err(e) => panic!("{}: {e}", f.id),
}
}
written.sort();
let params = crate::cmd::find_command("document.exportForScreens").unwrap().params;
assert_eq!(listed(params, "[{format: ", ','), written, "a row's format");
assert_eq!(listed(params, "settings?: {", ':'), written, "the keys of settings");
assert!(!refusals.is_empty());
for msg in refusals {
let list = msg.strip_prefix("Export for Screens writes ").unwrap_or_else(|| panic!("{msg}"));
let mut ids: Vec<String> = list.replace(" or ", ", ").split(", ").map(str::to_string).collect();
ids.sort();
assert_eq!(ids, written, "{msg}");
}
}
#[test]
fn zip_downloads_round_trip() {
let mut s = session(2);