Place: Link's tooltip treats a PostScript .ai as a file it embeds

The Place dialog classed every .pdf, .ai and .ait path as a document
that Link places as one locked object. A .ai saved without PDF
compatibility is PostScript, and file.place embeds it as editable art
with Link on or off, so the tooltip was wrong for it. The dialog now
reads the file's header with the same PostScript check file.place uses,
and describes such a file as it does other files Link does not keep
linked.
This commit is contained in:
dotCooCoo
2026-10-22 21:52:23 +07:00
committed by bflatastic
parent 9a72eadc51
commit 22b6f2a89c
2 changed files with 30 additions and 6 deletions
+13 -6
View File
@@ -155,6 +155,7 @@ fn open_dialog(app: &mut VectorcraftApp, files: Vec<Value>) {
let units = app.session.active().map(|d| d.doc.units).unwrap_or_default();
let info: Vec<String> = files.iter().map(|f| summary(app, f, units)).collect();
let one_object = app.session.active().is_some_and(|d| d.selection.objects.len() == 1);
let linked: Vec<bool> = files.iter().map(|f| is_vectorcraft_file(app, f)).collect();
app.ui.dialog = Some(crate::state::Dialog::new(
crate::dialogs::place::KIND,
json!({
@@ -163,18 +164,24 @@ fn open_dialog(app: &mut VectorcraftApp, files: Vec<Value>) {
"template": false,
"replace": false,
"__replace": files.len() == 1 && one_object,
"__documents": files.iter().any(is_vectorcraft_file),
"__others": !files.iter().all(is_vectorcraft_file),
"__documents": linked.contains(&true),
"__others": linked.contains(&false),
"__info": info,
}),
));
}
/// Is dialog file entry `file` a VectorCraft document on disk (what Link places as one locked
/// object), or a PDF or `.ai` file on disk?
fn is_vectorcraft_file(file: &Value) -> bool {
let linked = ["vectorcraft", vectorcraft_format::LEGACY_EXTENSION, "vctemplate", "pdf", "ai", "ait"];
file.get("path").and_then(Value::as_str).is_some_and(|p| linked.contains(&fileio::extension(p).as_str()))
/// object), or a PDF or `.ai` file on disk? A `.ai` saved without PDF compatibility is PostScript,
/// which `file.place` embeds whether Link is on or not.
fn is_vectorcraft_file(app: &VectorcraftApp, file: &Value) -> bool {
let Some(path) = file.get("path").and_then(Value::as_str) else { return false };
let native = ["vectorcraft", vectorcraft_format::LEGACY_EXTENSION, "vctemplate"];
match fileio::extension(path).as_str() {
e if native.contains(&e) => true,
"pdf" | "ai" | "ait" => app.services.read.as_ref().and_then(|r| r(path).ok()).is_none_or(|b| !vectorcraft_pdf::is_postscript(&b)),
_ => false,
}
}
/// The engine params naming `file` (a dialog file entry).
+17
View File
@@ -659,6 +659,23 @@ fn the_place_dialog_links_a_pdf_page_and_embeds_it_with_link_off() {
assert_eq!(st.doc.node(st.selection.objects[0]).unwrap().kind_label(), "Clip Group");
}
#[test]
fn the_place_dialog_does_not_offer_a_postscript_ai_as_a_locked_object() {
let mut app = app();
let ps = b"%!PS-Adobe-3.0\n%%BoundingBox: 0 0 100 100\n%%EndComments\n1 0 0 setrgbcolor 10 10 50 50 rectfill\nshowpage\n%%EOF\n";
let path = temp_file("legacy.ai", ps);
// file.place embeds a .ai saved without PDF compatibility, even with Link on.
let r = app.session.execute("file.place", &json!({"path": path, "link": true})).unwrap();
assert_eq!(r["linked"], json!(false), "{r}");
let st = app.session.active().unwrap();
assert!(!matches!(st.doc.node(st.selection.objects[0]).unwrap().kind, NodeKind::PlacedDocument(_)));
// So Link's tooltip does not promise one locked object for it.
place_picked(&mut app, &path);
let d = app.ui.dialog.as_ref().expect("the Place dialog");
assert!(!d.bool("__documents") && d.bool("__others"), "{:?}", d.fields);
assert!(!crate::dialogs::place::link_tip(d).contains("locked object"));
}
/// Tool key `key` as the Control bar's Apply (Enter) and Cancel (Escape) send it.
fn tool_key(app: &mut VectorcraftApp, key: vectorcraft_tools::ToolKey) {
let view = app.view_info();