Merge pull request #2948 from dotCooCoo/fix/open-pages-naming-every-page

PDF/.ai import: a .ai saved without PDF compatibility opens when pages names every page
This commit is contained in:
BFlat
2026-10-19 00:52:03 +07:00
committed by GitHub
3 changed files with 25 additions and 3 deletions
+10 -2
View File
@@ -194,6 +194,12 @@ fn from_editing_data_alone(bytes: &[u8], opts: &LoadOptions) -> Option<Result<(D
)
}
/// Do `opts` read every page of the PDF `bytes`: no `pages`, or a range that names each one?
fn every_page(bytes: &[u8], opts: &LoadOptions) -> bool {
let Some(range) = &opts.pages else { return true };
vectorcraft_pdf::info(bytes, opts.password.as_deref()).is_ok_and(|i| parse_range(range, i.pages.len()).is_ok_and(|p| p.len() == i.pages.len()))
}
/// [`load`] with `document.open` options (the PDF pages, box and password; the DXF options).
pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded> {
let format = detect(name, bytes).ok_or_else(|| match super::unsupported(&super::extension(name)) {
@@ -239,9 +245,11 @@ pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded>
let alone = r.only_text.then(|| from_editing_data_alone(bytes, opts)).flatten().and_then(|read| read.ok());
Ok(alone.unwrap_or_else(|| through_editing_data(bytes, opts, r.document, r.warnings, r.user_unit)))
}
// Saved without its PDF part: the editing copy is all the file has.
// Saved without its PDF part: the editing copy is all the file has (`pages` that
// name every page read it as the whole file).
Err(e) if e.to_string() == vectorcraft_pdf::PdfError::PlaceholderOnly.to_string() => {
from_editing_data_alone(bytes, opts).unwrap_or(Err(e))
let whole = LoadOptions { pages: None, ..opts.clone() };
from_editing_data_alone(bytes, if every_page(bytes, opts) { &whole } else { opts }).unwrap_or(Err(e))
}
Err(e) => Err(e),
})?
@@ -229,6 +229,19 @@ fn an_ai_file_saved_without_its_pdf_part_opens_from_its_editing_data() {
assert!(e.contains("placeholder"), "{e}");
}
#[test]
fn an_ai_file_saved_without_its_pdf_part_opens_with_pages_naming_every_page() {
let file = ai::ai(&compressed(&ai::sample_data()), PLACEHOLDER);
for pages in [json!("1"), json!(1), json!("1-")] {
let mut s = Session::new();
open(&mut s, "art.ai", &file, json!({"pages": pages})).unwrap();
check_sample(&s);
}
// Without editing data, the placeholder still isn't opened as the art.
let e = open(&mut Session::new(), "plain.ai", &ai::ai(b"", PLACEHOLDER), json!({"pages": "1"})).unwrap_err().to_string();
assert!(e.contains("placeholder"), "{e}");
}
/// A page of text alone, as text-only art has in its PDF part: no path is painted.
const TEXT_ART: &str = "BT /F1 36 Tf 10 40 Td (Text only) Tj ET";
+2 -1
View File
@@ -666,7 +666,8 @@ page) opens from its editing data alone: its type is made from the file's text d
area type and type on a path), and what can't be is left out with a warning. Non-native art (the content of a placed
PDF, which Illustrator shows but doesn't edit and keeps as a PDF inside the editing data) is drawn by the PDF importer,
as a group named "Non-native art" fitted to the object's box; one whose PDF can't be read is left out with a warning.
Without editing data it says it can't be opened.
Without editing data it says it can't be opened. `pages` that name every page of such a file also open it from its
editing data.
What a PDF holds comes in as editable art: soft masks become opacity masks (an alpha mask as a white copy of its art;
the backdrop colour gives Clip, an inverting transfer function Invert), transparency groups keep isolation and knockout,