Fix #1387: a .ai whose PDF part is only text opens from it

This commit is contained in:
bflatastic
2026-10-12 19:50:53 +07:00
parent bfb173e967
commit 7f5d8a3db1
8 changed files with 88 additions and 18 deletions
+8 -6
View File
@@ -196,8 +196,9 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
b.warnings = notes;
let cache = InterpreterCache::new();
let mut x = 0.0;
// Every page only a placeholder (text) over private data: the file's art isn't in its PDF part.
let mut placeholder = true;
// Every page only text over private data: a file saved without its PDF part shows a notice
// there, not its art. A page that states where its art is isn't that notice.
let (mut only_text, mut art_boxes) = (true, false);
let mut slots: Vec<Slot> = vec![];
let mut group_art: HashMap<usize, Vec<Arc<Node>>> = HashMap::new();
let mut nesting = Nesting::new();
@@ -266,7 +267,8 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
b.warn(PAST_PAGE_NOTE);
}
let children: Vec<Arc<Node>> = parts.iter().flat_map(|(_, v)| v.iter().cloned()).collect();
placeholder &= ai && only_text(&children);
only_text &= ai && is_only_text(&children);
art_boxes |= crate::pages::states_art(page);
let mut right = ab.x1;
if opts.crop == CropTo::Bounding
&& let Some(art) = vectorcraft_doc::live::nodes_bounds(&children)
@@ -308,7 +310,7 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
}
}
}
if placeholder {
if only_text && !art_boxes {
return Err(PdfError::PlaceholderOnly);
}
// A group the pages mark but draw nothing in is an empty layer, as the editor had it.
@@ -380,7 +382,7 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
}
}
}
Ok(ImportReport { document: doc, warnings, native: crate::editing::editing_in(&pdf) })
Ok(ImportReport { document: doc, warnings, native: crate::editing::editing_in(&pdf), only_text })
}
/// The characters of a CID-keyed CFF font embedded without a ToUnicode map (as macOS writes
@@ -604,7 +606,7 @@ fn drop_page_fill(parts: &mut Vec<(Option<usize>, Vec<Arc<Node>>)>, page: Rect)
}
/// Is this art text and nothing else (in groups and clips), with some text?
fn only_text(nodes: &[Arc<Node>]) -> bool {
fn is_only_text(nodes: &[Arc<Node>]) -> bool {
fn walk(nodes: &[Arc<Node>], text: &mut bool) -> bool {
nodes.iter().all(|n| match &n.kind {
NodeKind::Path { clipping: true, .. } => true,
+3
View File
@@ -163,6 +163,9 @@ pub struct ImportReport {
pub warnings: Vec<String>,
/// The native document the PDF carries (written with Preserve Editing), if any.
pub native: Option<Editing>,
/// Every page shows only text, over an editor's private data: that data, where it reads,
/// is the file's art (a file saved without its PDF part shows a notice there instead).
pub only_text: bool,
}
/// Result of an export with non-fatal warnings (features approximated or dropped).
+8
View File
@@ -197,6 +197,14 @@ pub(crate) fn has_private_data(page: &Page<'_>) -> bool {
piece_info_has(page, &[b"AIPrivateData"])
}
/// Does `page` state where its art is: an art, trim or bleed box (ISO 32000-1 §14.11.2, the
/// extent of the page's meaningful content and of the finished page)? A page written from an
/// artboard has them; the notice a file saved without its PDF part shows instead of its art is
/// the same page whatever the document, with no art to state the place of.
pub(crate) fn states_art(page: &Page<'_>) -> bool {
[&b"ArtBox"[..], b"TrimBox", b"BleedBox"].iter().any(|k| page.raw().get::<[f32; 4]>(*k).is_some())
}
/// Does `page`'s `PieceInfo` hold a key starting with one of `prefixes`, up to four dictionaries in?
fn piece_info_has(page: &Page<'_>, prefixes: &[&[u8]]) -> bool {
fn walk(d: &Dict<'_>, prefixes: &[&[u8]], depth: u32) -> bool {