Export: warnings name an option the chosen format doesn't read

`document.export`, `document.serialize` and `document.exportSelection`
accept a key that only another format takes. `{"format": "pdf",
"quality": 80}` wrote the PDF and gave no warning about `quality`.

The reply's `warnings` now name each such key, in the wording of a
save's note: "`quality` was not used: PDF files don't read it".

- The format's own options, the commands' own keys, null values and an
  exporter plug-in's format get no note.
- The note names the kind of file written. A legacy `.ai` file
  (`aiVersion: "7"`) reads `outlineText`, `hiddenObjects`,
  `transparentImages`, the artboard choice and `useArtboards`, and
  beside `aiVersion` no other option. The other `.ai` files don't read
  those three options or `preserveEditing`, which is always on for
  them.
- An export doesn't read `separateArtboards`, or `range` for a
  `vectorcraft` or `template` file. A save reads them to write each
  artboard to a file of its own.
- In the app, an export that automation asked for (`app.export` with a
  path, `file.exportAs` run as a command) gives the same notes in its
  reply and in the status bar. A Background Export replies before the
  file is written, and that reply has the notes. An export that a
  person starts from the File menu gets none.
- Export As gives a template export no `useArtboards` or `range`: a
  template doesn't read them.
- `vectorcraft-cli convert` sends `outlineText` only with
  `--outline-text`, and `hiddenLayers` only to the formats that read
  it. A conversion then warns only about an option the command line
  gave.

`document.save` gives its notes as before: it does not apply the `.ai`
rules above.

Docs: docs/mcp.md, docs/control-protocol.md,
docs/target-app-parity.md, the params text of the three commands.
This commit is contained in:
dotCooCoo
2026-11-13 06:58:28 -08:00
parent 1dbdbce91f
commit d04a19ac53
17 changed files with 353 additions and 27 deletions
+21
View File
@@ -177,6 +177,27 @@ fn convert_reports_import_warnings() {
assert!(w.iter().any(|w| w.as_str().unwrap().contains("nowhere-to-be-found.png")), "{out}");
}
/// A conversion sends each format only the options it reads, so its warnings name an unused
/// option only when the command line gave one: `--outline-text` for a format without it.
#[test]
fn convert_warns_about_an_unused_option_only_when_one_was_given() {
let svg = tmp("unused-option.svg");
std::fs::write(&svg, r#"<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40"><rect width="20" height="10"/></svg>"#).unwrap();
let unused = |out: &Value| -> Vec<String> {
let w = out["warnings"].as_array().unwrap_or_else(|| panic!("{out}"));
w.iter().filter_map(Value::as_str).filter(|w| w.contains("was not used")).map(str::to_string).collect()
};
for ext in ["png", "jpg", "pdf", "ai", "eps", "psd", "dxf", "svg", "txt", "vectorcraft", "vctemplate"] {
let path = tmp(&format!("unused-option.{ext}"));
let out: Value = serde_json::from_str(ok(&["convert", &svg, &path]).trim()).unwrap();
assert!(unused(&out).is_empty(), "{ext}: {out}");
}
let out: Value = serde_json::from_str(ok(&["convert", &svg, &tmp("unused-option-outlined.svg"), "--outline-text"]).trim()).unwrap();
assert!(unused(&out).is_empty(), "{out}");
let out: Value = serde_json::from_str(ok(&["convert", &svg, &tmp("unused-option-outlined.png"), "--outline-text"]).trim()).unwrap();
assert_eq!(unused(&out), ["`outlineText` was not used: PNG files don't read it"], "{out}");
}
#[test]
fn convert_writes_legacy_ai_with_its_options() {
// Two artboards, a shown rectangle and a hidden one.