mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 21:10:40 +00:00
Fix #6135: opening a .ai with editing data skips the Import PDF dialog
The editing data of a .ai or .ait holds every artboard, so a multi-page file has no pages to pick. Opening one now goes straight to it, as a file carrying its VectorCraft document already does. The dialog stays for PDFs, a .ai that is only a PDF, a password, and Place.
This commit is contained in:
@@ -114,17 +114,19 @@ pub(super) fn show_preview(ui: &mut egui::Ui, tex: Option<&egui::TextureHandle>,
|
||||
|
||||
/// Open `bytes` (or place them with the `file.place` params `place`) through the dialog when the
|
||||
/// file is a PDF (or `.ai`/`.ait`) with several pages or a password; false when there's nothing to
|
||||
/// ask (the caller goes on), including a file that carries its VectorCraft document when opening.
|
||||
/// ask (the caller goes on), including, when opening, a file that carries its VectorCraft document
|
||||
/// and a `.ai`/`.ait` with its editing data.
|
||||
pub fn offer(app: &mut VectorcraftApp, name: &str, bytes: &[u8], path: Option<String>, place: Option<&Value>) -> bool {
|
||||
if !fileio::detect(name, bytes).is_some_and(|f| matches!(f.id, "pdf" | "ai" | "ait")) {
|
||||
let Some(format) = fileio::detect(name, bytes).map(|f| f.id).filter(|id| matches!(*id, "pdf" | "ai" | "ait")) else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// A file carrying its VectorCraft document (a saved .ai, or a PDF saved with Preserve Editing)
|
||||
// opens as that document, every artboard and layer: there are no pages to pick. Placing still
|
||||
// asks, since a place takes one page.
|
||||
// opens as that document, every artboard and layer: there are no pages to pick. So does a .ai
|
||||
// or .ait with its editing data, which holds them all (#6135); a PDF with editing data still
|
||||
// asks, as its pages are what it is. Placing still asks, since a place takes one page.
|
||||
// The file is read once for both checks.
|
||||
let opened = vectorcraft_pdf::open_pdf(bytes, None);
|
||||
if place.is_none() && opened.as_ref().is_ok_and(|o| o.editing().is_some()) {
|
||||
if place.is_none() && opened.as_ref().is_ok_and(|o| o.editing().is_some() || (format != "pdf" && o.has_illustrator_data())) {
|
||||
return false;
|
||||
}
|
||||
let (pages, locked) = match opened.map(|o| o.page_count()) {
|
||||
|
||||
@@ -197,3 +197,46 @@ fn placing_a_saved_ai_still_asks_for_the_page() {
|
||||
assert!(import_pdf::offer(&mut app, "two.ai", &bytes, None, Some(&json!({}))));
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("mode"), "place");
|
||||
}
|
||||
|
||||
/// Two pages, 100 and 200 pt wide; with `editing_data` the first page has the keys of an editor's
|
||||
/// copy of the art.
|
||||
fn two_pages(editing_data: bool, password: Option<&str>) -> Vec<u8> {
|
||||
let mut first = PdfPage::new(100.0, 100.0, "0 g 0 0 10 10 re f");
|
||||
if editing_data {
|
||||
first.entries = "/PieceInfo << /Illustrator << /Private << /AIPrivateData1 7 /NumBlock 1 >> >> >>".into();
|
||||
}
|
||||
pdf(&[first, PdfPage::new(200.0, 100.0, "1 0 0 rg 0 0 30 30 re f")], password)
|
||||
}
|
||||
|
||||
/// #6135: the editing data of a `.ai` or `.ait` holds every artboard, so opening one asks nothing
|
||||
/// and opens them all.
|
||||
#[test]
|
||||
fn opening_an_ai_with_editing_data_opens_every_artboard_without_asking() {
|
||||
let mut app = app();
|
||||
let bytes = two_pages(true, None);
|
||||
io::open_bytes(&mut app, "two.ai", &bytes, Some("/tmp/two.ai".into())).unwrap();
|
||||
assert!(app.ui.dialog.is_none(), "no Import PDF dialog");
|
||||
assert_eq!(app.session.documents().len(), 2);
|
||||
assert_eq!(artboard_widths(&app), [100.0, 200.0]);
|
||||
assert!(!import_pdf::offer(&mut app, "two.ait", &bytes, None, None), "a template too");
|
||||
assert!(app.ui.dialog.is_none());
|
||||
}
|
||||
|
||||
/// #6135: the dialog stays for what still has something to ask: a PDF, even one with editing data
|
||||
/// keys, a `.ai` that is only a PDF, a password, and Place.
|
||||
#[test]
|
||||
fn the_import_dialog_stays_where_there_is_something_to_ask() {
|
||||
for (name, bytes) in [("two.pdf", two_pages(true, None)), ("two.ai", two_pages(false, None))] {
|
||||
let mut app = app();
|
||||
assert!(import_pdf::offer(&mut app, name, &bytes, None, None), "{name}");
|
||||
assert_eq!((app.ui.dialog.as_ref().unwrap().str("mode"), field(&app, "pageCount")), ("open".into(), json!(2)), "{name}");
|
||||
}
|
||||
|
||||
let mut app = app();
|
||||
assert!(import_pdf::offer(&mut app, "locked.ai", &two_pages(true, Some("pw")), None, None));
|
||||
assert_eq!(field(&app, "__locked"), true);
|
||||
|
||||
let mut app = self::app();
|
||||
assert!(import_pdf::offer(&mut app, "two.ai", &two_pages(true, None), None, Some(&json!({}))));
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("mode"), "place");
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ the two can still win that race.
|
||||
| `engine.execute` `file.exportSelection` | `{multiple?}` | File › Export Selection…: collects the selected objects as one asset (`assets.add`; one per object with `multiple: true`) and opens `exportForScreens` on its Assets tab with just them checked → `{assets}`. `document.exportSelection` still exports the selection in one go, without the dialog |
|
||||
| CSS Properties panel | `window.panel {panel: "cssProperties"}`; `engine.execute` `css.copy` / `css.exportFile` | the panel shows `css.selection` of the selection (after Generate CSS, while nothing is selected, `css.generate`); its menu holds the options the commands take (`units`, `position`, `dimensions`, `unnamed`, `rasterize`). `css.copy {scope?, …options}` copies the CSS to the clipboard → `{css, rules}`; `css.exportFile {path?, scope?, …options}` writes `css.export` to `path` (else a picked file; the web downloads it) with the pictures of rasterized art next to it → `{path, rules, images, warnings}`, and the status bar shows the first of `warnings` as Export As shows an export's notes |
|
||||
|
||||
| `ui.dialog.*` on `importPdf` | `{field: "page" \| "allPages" \| "range" \| "cropTo" \| "password", value}` | opening (`app.open`, `file.open`) a PDF or `.ai` with several pages or a password, or placing one (`file.place` without `page`), opens the Import PDF dialog (`mode`: `open` or `place`; `pageCount`). While `__locked` is true, set `password` and `ui.dialog.confirm` to unlock it (a one-page file then opens or is placed). Then set `allPages: false` and `range` ("2-3, 5") to open some pages, or `page` to place one, and `cropTo` (`bounding`, `art`, `crop`, `trim`, `bleed`, `media`); `ui.dialog.confirm` opens or places them (placing keeps the other `file.place` params, such as `link`, `template` and `at`, in `__place`). `document.open {pages, cropTo, password}` and `file.place {page, crop, password}` do the same without the dialog |
|
||||
| `ui.dialog.*` on `importPdf` | `{field: "page" \| "allPages" \| "range" \| "cropTo" \| "password", value}` | opening (`app.open`, `file.open`) a PDF or `.ai` with several pages or a password (a `.ai` or `.ait` with its editing data, or a file carrying its VectorCraft document, opens whole without it unless it has a password), or placing one (`file.place` without `page`), opens the Import PDF dialog (`mode`: `open` or `place`; `pageCount`). While `__locked` is true, set `password` and `ui.dialog.confirm` to unlock it (a one-page file then opens or is placed). Then set `allPages: false` and `range` ("2-3, 5") to open some pages, or `page` to place one, and `cropTo` (`bounding`, `art`, `crop`, `trim`, `bleed`, `media`); `ui.dialog.confirm` opens or places them (placing keeps the other `file.place` params, such as `link`, `template` and `at`, in `__place`). `document.open {pages, cropTo, password}` and `file.place {page, crop, password}` do the same without the dialog |
|
||||
|
||||
Swatch editors: `engine.execute` with `ui.swatchOptions {name}` opens the `swatchOptions` dialog for a colour swatch
|
||||
(fields `name`, `spot`, `global`, `mode`: `gray`/`rgb`/`hsb`/`lab`/`cmyk`/`web`, `color`: `"#rrggbb"` or a colour object,
|
||||
|
||||
Reference in New Issue
Block a user