mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 11:11:30 +00:00
Merge pull request #5794 from dotCooCoo/fix/image-trace-batch-options
Reject malformed Batch Trace Folder options
This commit is contained in:
@@ -47,7 +47,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Batch Trace Folder",
|
||||
["Object", "Plug-ins", "Document Tools"],
|
||||
None,
|
||||
"{folder, destinationFolder, preset?: name (imageTrace.presets; default: Default), expand?: false (true: trace and expand, as Make and Expand), format?: vectorcraft (default)|svg|pdf|ai|eps, includeSubdirs?: false} open each image in folder (png, jpg, tif, bmp, gif, webp, psd), select it, trace it with the preset, save it into destinationFolder as <name>.<ext> (-2, -3… added when that name is taken, so no file is replaced) and close it again, whether it traced or not; the document that was active stays active. Caps: 500 images, 8 folder levels; a folder with more than 1,000 entries is refused. Not on the web (no file system) → {traced: [{path, output}], failed: [{path, error}], format} (#4921)",
|
||||
"{folder, destinationFolder, preset?: name (imageTrace.presets; default: Default), expand?: false (true: trace and expand, as Make and Expand), format?: vectorcraft (default)|svg|pdf|ai|eps, includeSubdirs?: false} open each image in folder (png, jpg, tif, bmp, gif, webp, psd), select it, trace it with the preset, save it into destinationFolder as <name>.<ext> (-2, -3… added when that name is taken, so no file is replaced) and close it again, whether it traced or not; the document that was active stays active. Optional settings use their defaults only when omitted; null and values of the wrong type return an error before any image is opened. Caps: 500 images, 8 folder levels; a folder with more than 1,000 entries is refused. Not on the web (no file system) → {traced: [{path, output}], failed: [{path, error}], format} (#4921)",
|
||||
always,
|
||||
batch_trace::batch_trace_folder
|
||||
),
|
||||
|
||||
@@ -8,9 +8,9 @@ use serde_json::json;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use super::super::super::fileio;
|
||||
use super::super::super::{Result, Session, bad};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use super::super::super::{bool_or, str_param};
|
||||
use super::super::super::str_param;
|
||||
use super::super::super::{Result, Session, bad};
|
||||
|
||||
/// The command id.
|
||||
pub(super) const ID: &str = "addon.document.batchTraceFolder";
|
||||
@@ -69,20 +69,39 @@ fn images(folder: &str, deep: bool, depth: usize, out: &mut Vec<String>) -> Resu
|
||||
pub(super) fn batch_trace_folder(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let folder = str_param(p, "folder").map(fileio::absolute_path).ok_or_else(|| bad(ID, "missing `folder`"))?;
|
||||
let dest = str_param(p, "destinationFolder").map(fileio::absolute_path).ok_or_else(|| bad(ID, "missing `destinationFolder`"))?;
|
||||
let format = str_param(p, "format").unwrap_or("vectorcraft");
|
||||
let format = match p.get("format") {
|
||||
None => "vectorcraft",
|
||||
Some(Value::String(value)) => value.as_str(),
|
||||
Some(_) => return Err(bad(ID, "`format` must be a string")),
|
||||
};
|
||||
let preset = match p.get("preset") {
|
||||
None => "Default",
|
||||
Some(Value::String(value)) => value.as_str(),
|
||||
Some(_) => return Err(bad(ID, "`preset` must be a string")),
|
||||
};
|
||||
let expand = match p.get("expand") {
|
||||
None => false,
|
||||
Some(Value::Bool(value)) => *value,
|
||||
Some(_) => return Err(bad(ID, "`expand` must be true or false")),
|
||||
};
|
||||
let include_subdirs = match p.get("includeSubdirs") {
|
||||
None => false,
|
||||
Some(Value::Bool(value)) => *value,
|
||||
Some(_) => return Err(bad(ID, "`includeSubdirs` must be true or false")),
|
||||
};
|
||||
if !FORMATS.contains(&format) {
|
||||
return Err(bad(ID, format!("`format` must be one of {}", FORMATS.join(", "))));
|
||||
}
|
||||
let preset = str_param(p, "preset").unwrap_or("Default").to_string();
|
||||
let preset = preset.to_string();
|
||||
let presets = s.execute("imageTrace.presets", &json!({}))?;
|
||||
if !presets["presets"].as_array().into_iter().flatten().any(|q| q["name"].as_str().is_some_and(|n| n.eq_ignore_ascii_case(&preset))) {
|
||||
return Err(bad(ID, format!("no tracing preset `{preset}` (see imageTrace.presets)")));
|
||||
}
|
||||
let trace = if bool_or(p, "expand", false) { "imageTrace.makeAndExpand" } else { "imageTrace.make" };
|
||||
let trace = if expand { "imageTrace.makeAndExpand" } else { "imageTrace.make" };
|
||||
fileio::may_read(&folder)?;
|
||||
fileio::may_write(&dest)?;
|
||||
let mut files = vec![];
|
||||
images(&folder, bool_or(p, "includeSubdirs", false), 0, &mut files)?;
|
||||
images(&folder, include_subdirs, 0, &mut files)?;
|
||||
files.sort();
|
||||
if files.is_empty() {
|
||||
return Err(bad(ID, "no images in the folder"));
|
||||
|
||||
@@ -333,6 +333,35 @@ fn batch_trace_folder_traces_and_saves_each_image() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Batch Trace Folder rejects optional values with the wrong type before opening images.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn batch_trace_folder_rejects_wrong_typed_options() {
|
||||
const C: &str = "addon.document.batchTraceFolder";
|
||||
let dir = folder("batch-trace-bad-options");
|
||||
let src = dir.join("in");
|
||||
let out = dir.join("out");
|
||||
std::fs::create_dir_all(&src).unwrap();
|
||||
let mut s = Session::new();
|
||||
for (key, value) in [
|
||||
("format", json!(42)),
|
||||
("format", Value::Null),
|
||||
("preset", json!(false)),
|
||||
("preset", Value::Null),
|
||||
("expand", json!("true")),
|
||||
("expand", Value::Null),
|
||||
("includeSubdirs", json!(1)),
|
||||
("includeSubdirs", Value::Null),
|
||||
] {
|
||||
let mut params = json!({"folder": src, "destinationFolder": out});
|
||||
params[key] = value;
|
||||
let error = s.execute(C, ¶ms).unwrap_err();
|
||||
assert!(error.to_string().contains(&format!("`{key}`")), "{key}: {error}");
|
||||
assert!(!out.exists(), "invalid options must not create a destination folder");
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// A folder too big to list in full is refused rather than traced in part.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
|
||||
+4
-2
@@ -7078,8 +7078,10 @@ image in `folder` (PNG, JPEG, TIFF, BMP, GIF, WebP, PSD; with `includeSubdirs`,
|
||||
`preset` (an `imageTrace.presets` name; Default by default; with `expand`, as Make and Expand), saves it into
|
||||
`destinationFolder` as `<name>.<ext>` in `format` (`vectorcraft` by default; `svg`, `pdf`, `ai`, `eps`), with `-2`,
|
||||
`-3`… added when the name is taken so no file is replaced, and closes it again whether it traced or not; the active
|
||||
document stays active. Images that can't be traced are listed in `failed` and the others are still traced. Up to 500
|
||||
images and 8 folder levels (a folder with more than 1,000 entries is refused); not on the web. The desktop app's Batch
|
||||
document stays active. Images that can't be traced are listed in `failed` and the others are still traced. Optional
|
||||
`format` and `preset` accept strings; `expand` and `includeSubdirs` accept true or false. Null and values of another
|
||||
type return an error before any image is opened. Up to 500 images and 8 folder levels (a folder with more than 1,000
|
||||
entries is refused); not on the web. The desktop app's Batch
|
||||
Trace Folder asks for both folders (#4921). → `{traced: [{path, output}], failed: [{path, error}], format}`
|
||||
|
||||
```json
|
||||
|
||||
@@ -1639,7 +1639,7 @@ A VectorCraft addition, not a feature of the reference app, so no area score mov
|
||||
|
||||
## Document Tools add-on: Batch Trace Folder (2026-11-04)
|
||||
|
||||
A VectorCraft addition (it replaces a script some users ran), so no area score moves. Object › Plug-ins › Document Tools › Batch Trace Folder (`addon.document.batchTraceFolder`) asks for a folder of images and a destination folder, traces each image with a preset (optionally expanded) and saves it there under its own name as `.vectorcraft`, `.svg`, `.pdf`, `.ai` or `.eps`, without replacing files, closing each image's document again and reporting the images it couldn't trace (#4921). A folder with more than 1,000 entries is refused rather than traced in part. Tests: `cmd::addons::document::tests::batch_trace_folder_traces_and_saves_each_image` and `batch_trace_folder_refuses_a_folder_it_cannot_list_in_full` (engine), `addons::document::tests::batch_trace_folder_asks_for_both_folders` (ui-egui).
|
||||
A VectorCraft addition (it replaces a script some users ran), so no area score moves. Object › Plug-ins › Document Tools › Batch Trace Folder (`addon.document.batchTraceFolder`) asks for a folder of images and a destination folder, traces each image with a preset (optionally expanded) and saves it there under its own name as `.vectorcraft`, `.svg`, `.pdf`, `.ai` or `.eps`, without replacing files, closing each image's document again and reporting the images it couldn't trace (#4921). A folder with more than 1,000 entries is refused rather than traced in part. Optional settings reject null and values of the wrong type before opening images. Tests: `cmd::addons::document::tests::batch_trace_folder_traces_and_saves_each_image`, `batch_trace_folder_refuses_a_folder_it_cannot_list_in_full`, and `batch_trace_folder_rejects_wrong_typed_options` (engine), `addons::document::tests::batch_trace_folder_asks_for_both_folders` (ui-egui).
|
||||
|
||||
## Color Effects add-on: Find Nearest Swatch (2026-11-01)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user