mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
Merge PR #7455: photocraft-cli run: refuse an --out that is the input file unless --in-place (#7437)
This commit is contained in:
@@ -32,9 +32,10 @@ USAGE:
|
||||
only Gray and CMYK shrink) or progressive.
|
||||
photocraft-cli info <file> [--compact]
|
||||
Print the document as JSON (size, mode, depth, layer tree).
|
||||
photocraft-cli run (<file> | --new <json>) --cmd <id> [--params <json>] [--cmd …] [--out <file>] [--format <ext>] [--quality <1-100>] [--tiff-layers] [--tiff-compression <none|lzw|zip>] [--tga-bits <24|32>] [--dpx-bits <8|10|12|16>] [--dds-compression <none|bc1|bc2|bc3>] [--dds-mipmaps] [--jpeg-format <baseline|optimized|progressive>]
|
||||
photocraft-cli run (<file> | --new <json>) --cmd <id> [--params <json>] [--cmd …] [--out <file>] [--format <ext>] [--quality <1-100>] [--in-place] [--tiff-layers] [--tiff-compression <none|lzw|zip>] [--tga-bits <24|32>] [--dpx-bits <8|10|12|16>] [--dds-compression <none|bc1|bc2|bc3>] [--dds-mipmaps] [--jpeg-format <baseline|optimized|progressive>]
|
||||
Open a file, run engine commands in order, save the result. Each --params
|
||||
applies to the preceding --cmd. Prints each command's JSON result.
|
||||
applies to the preceding --cmd. Prints each command's JSON result. An --out that is the
|
||||
<file> is refused, as the result would replace the original; --in-place allows it.
|
||||
photocraft-cli batch --actions <actions.json|set.atn> [--action <name>] --in <dir> --out <dir> [--format <ext>] [--quality <1-100>] [--in-place] [--tiff-layers] [--tiff-compression <none|lzw|zip>] [--tga-bits <24|32>] [--dpx-bits <8|10|12|16>] [--dds-compression <none|bc1|bc2|bc3>] [--dds-mipmaps] [--jpeg-format <baseline|optimized|progressive>]
|
||||
Apply an action list to every image in a directory. A Photoshop Actions file (.atn) is a
|
||||
set: its action --action names plays, as below. JSON steps are [id, params] pairs,
|
||||
@@ -99,7 +100,7 @@ const SUBCOMMANDS: &[Subcommand] = &[
|
||||
"--tiff-compression",
|
||||
"--jpeg-format",
|
||||
],
|
||||
bare: &["--tiff-layers", "--dds-mipmaps"],
|
||||
bare: &["--in-place", "--tiff-layers", "--dds-mipmaps"],
|
||||
run: run_cmds,
|
||||
},
|
||||
Subcommand {
|
||||
@@ -373,6 +374,10 @@ fn run_cmds(a: &Args, out: &mut dyn Write, err: &mut dyn Write) -> R {
|
||||
let mut reported_fonts = std::collections::HashSet::<String>::new();
|
||||
match (a.positional.as_slice(), a.get("--new")) {
|
||||
([file], None) => {
|
||||
// Refused before the file is read or any command runs.
|
||||
if let Some(out) = a.get("--out") {
|
||||
refuse_overwriting_input(a, file, out)?;
|
||||
}
|
||||
let o = h.open(Path::new(file)).map_err(|e| e.to_string())?;
|
||||
let ws: Vec<String> = serde_json::from_value(o["warnings"].clone()).unwrap_or_default();
|
||||
reported_fonts.extend(ws.iter().cloned());
|
||||
|
||||
@@ -563,6 +563,46 @@ fn tga_bits_choose_the_targa_depth() {
|
||||
}
|
||||
}
|
||||
|
||||
/// #7437: `run <file> --out` to that same file, however `--out` is spelt, is refused before the
|
||||
/// file is read or any command runs, unless `--in-place` asks for it (as `batch` does, #492).
|
||||
#[test]
|
||||
fn run_refuses_to_write_over_its_input() {
|
||||
let d = tmp("run-in-place");
|
||||
write_png(&d.join("in.png"), 8, 4, 7);
|
||||
let before = std::fs::read(d.join("in.png")).unwrap();
|
||||
let run = |out: &Path, extra: &[&str]| {
|
||||
bin().current_dir(&d).args(["run", "in.png", "--cmd", "image.adjustments.invert", "--out"]).arg(out).args(extra).output().unwrap()
|
||||
};
|
||||
let mut outs = vec![PathBuf::from("in.png"), PathBuf::from("./in.png"), d.join("in.png"), d.join(".").join("in.png")];
|
||||
std::fs::hard_link(d.join("in.png"), d.join("hard.png")).unwrap();
|
||||
outs.push(PathBuf::from("hard.png"));
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::os::unix::fs::symlink(d.join("in.png"), d.join("soft.png")).unwrap();
|
||||
outs.push(PathBuf::from("soft.png"));
|
||||
}
|
||||
for out in &outs {
|
||||
for extra in [&[][..], &["--format", "png"][..]] {
|
||||
let o = run(out, extra);
|
||||
let (stdout, err) = (String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr));
|
||||
assert_eq!(o.status.code(), Some(1), "--out {} {extra:?}: {err}", out.display());
|
||||
assert!(err.contains("is the input file") && err.contains("--in-place"), "{err}");
|
||||
assert!(stdout.is_empty(), "--out {} {extra:?} ran commands: {stdout}", out.display());
|
||||
assert_eq!(std::fs::read(d.join("in.png")).unwrap(), before, "--out {} {extra:?} changed the original", out.display());
|
||||
}
|
||||
}
|
||||
// Another file is written as before, leaving the original alone.
|
||||
ok(bin().current_dir(&d).args(["run", "in.png", "--cmd", "image.adjustments.invert", "--out", "out.png"]));
|
||||
assert!(d.join("out.png").is_file());
|
||||
assert_eq!(std::fs::read(d.join("in.png")).unwrap(), before);
|
||||
// `--in-place` opts in: the inverted result replaces the original, which still opens.
|
||||
ok(bin().current_dir(&d).args(["run", "in.png", "--cmd", "image.adjustments.invert", "--out", "in.png", "--in-place"]));
|
||||
assert_ne!(std::fs::read(d.join("in.png")).unwrap(), before, "--in-place left the original unchanged");
|
||||
let (out, _) = ok(bin().current_dir(&d).args(["info", "in.png", "--compact"]));
|
||||
let info: Value = serde_json::from_str(&out).unwrap();
|
||||
assert_eq!((info["width"].as_u64(), info["height"].as_u64()), (Some(8), Some(4)), "{info}");
|
||||
}
|
||||
|
||||
/// `--jpeg-format` picks the JPEG's scans: progressive writes a progressive frame (SOF2). One it
|
||||
/// doesn't name is refused and nothing is written.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user