diff --git a/crates/eps/src/import/native.rs b/crates/eps/src/import/native.rs index c7f193c2b..465cec773 100644 --- a/crates/eps/src/import/native.rs +++ b/crates/eps/src/import/native.rs @@ -1005,7 +1005,7 @@ fn build(data: &[u8], visible: Option<&Document>, page: Page, outlined: bool, tr Err(why) => return Err(why), } } - None => unmark(&mut doc.layers, true, &mut vec![]), + None => unmark(&mut doc.layers, true, true, &mut vec![]), } doc.limit_depth_noting(&mut notes); if small.is_some() { @@ -1047,12 +1047,13 @@ const UNSEEN_TEXT: &str = "text objects that the file keeps but its page doesn't /// How much more of the page (0 to 1) the layers may differ without a text object and still look no worse. const UNSEEN_MARGIN: f64 = 5e-5; -/// Clear the marks in `nodes` and say which marked objects show, by id. -fn unmark(nodes: &mut [Arc], shown: bool, found: &mut Vec<(NodeId, Option)>) { +/// Clear the marks in `nodes` and say which marked objects show and print, by id. +fn unmark(nodes: &mut [Arc], shown: bool, prints: bool, found: &mut Vec<(NodeId, Option)>) { for n in nodes { let shown = shown && n.visible; + let prints = prints && !matches!(n.kind, NodeKind::Layer { printable: false, .. }); if let Some(name) = n.name.as_deref().and_then(|m| m.strip_prefix(MADE)) { - if shown { + if shown && prints { found.push((n.id, n.visual_bounds())); } let name = (!name.is_empty()).then(|| name.to_string()); @@ -1060,7 +1061,7 @@ fn unmark(nodes: &mut [Arc], shown: bool, found: &mut Vec<(NodeId, Option< } else if !matches!(n.kind, NodeKind::Compound { .. }) && let Some(c) = Arc::make_mut(n).children_mut() { - unmark(c, shown, found); + unmark(c, shown, prints, found); } } } @@ -1080,13 +1081,13 @@ fn take_out(nodes: &mut Vec>, id: NodeId, hide: bool) -> bool { nodes.iter_mut().any(|n| !matches!(n.kind, NodeKind::Compound { .. }) && Arc::make_mut(n).children_mut().is_some_and(|c| take_out(c, id, hide))) } -/// The text objects made from the file's text document that show on the page's area, where the page -/// has no type to match them, and that the layers look no worse without, are not drawn by the app -/// that wrote the file (which paints every shown text object; it keeps the type of what was turned -/// to outlines): they are taken out. How many. +/// The text objects made from the file's text document that show and print on the page's area, +/// where the page has no type to match them, and that the layers look no worse without, are not +/// drawn by the app that wrote the file (which paints every shown text object; it keeps the type +/// of what was turned to outlines): they are taken out. How many. fn prune_unseen(frame: &Frame<'_>, view: &mut PageView, doc: &mut Document) -> usize { let mut found = vec![]; - unmark(&mut doc.layers, true, &mut found); + unmark(&mut doc.layers, true, true, &mut found); let rect = frame.rect; if found.is_empty() || found.len() > MAX_WEIGHED { return 0; diff --git a/crates/eps/src/import/tests_native.rs b/crates/eps/src/import/tests_native.rs index 84217c4b2..b7f9a67fe 100644 --- a/crates/eps/src/import/tests_native.rs +++ b/crates/eps/src/import/tests_native.rs @@ -628,6 +628,24 @@ fn a_shown_text_object_on_the_page_that_the_page_doesnt_draw_is_left_out() { assert!(r.warnings.iter().any(|w| w.contains("doesn't draw")), "{:?}", r.warnings); } +/// A text object on a layer that shows and doesn't print, which the page doesn't draw, isn't taken +/// out: it comes in from the text document, on its layer. +#[test] +fn a_shown_text_object_on_a_layer_that_doesnt_print_stays_on_its_layer() { + let doc = text_document(&[("Hi there\r", 0, (8200.0, 8180.0), (0.0, 0.0), 0.0)]); + // The layer's options with its fourth flag, printing, off. + let tips = layer("Tips", true, &text_object(0)).replace("1 1 1 1 0 0 1 0 79", "1 1 1 0 0 0 1 0 79"); + let art = native_with_text(&(layer("Art", true, &square(10, 10)) + &tips), &doc); + let (d, w, read) = crate::layered_ai(&private(&art), pdf_part(10.0, 86.0), vec![], false, false, 1.0); + assert!(read, "{w:?}"); + assert_eq!(names(&d), ["Art", "Tips"], "{w:?}"); + let tips = &d.layers[1]; + assert!(tips.visible && matches!(tips.kind, NodeKind::Layer { printable: false, .. })); + let texts: Vec = text_of(tips).iter().map(|t| t.plain_text()).collect(); + assert_eq!(texts, ["Hi there"], "{w:?}"); + assert!(!w.iter().any(|w| w.contains("doesn't draw")), "{w:?}"); +} + #[test] fn pieces_of_two_lines_the_page_draws_alternately_go_each_to_its_own_text() { // "Hello" is drawn as "He" and "llo", with "abcd" ("ab", "cd") drawn between them. diff --git a/docs/mcp.md b/docs/mcp.md index f9e0d2886..2581f7744 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -546,7 +546,8 @@ strokes, effects or a brush comes in as its drawn look (a group named after it). document, whole and editable: point type, area type (a story in several frames as threaded type) and type on a path, with its fonts, size, leading, tracking, scaling, baseline shift, fill and stroke (grey, RGB or CMYK), alignment, indents and paragraph spacing. Where the text document can't say, type that shows is the page's, in its text object's -place. A file whose editing data has symbols, +place. A text object on a layer that shows and doesn't print keeps the type the text document makes for it, on that +layer, even where the page doesn't draw it. A file whose editing data has symbols, pattern fills, placed files or anything else the reader doesn't read on a layer that shows, or whose layers look different from its page, opens as before: an EPS as its printed page, a `.ai` file from its PDF content (where plain groups open ungrouped), with a warning saying why. Layers look different when they draw an object the page doesn't (or diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 1eae3e6f3..280a2b6bd 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -578,6 +578,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur SVG import's style cascade, used for hidden objects, non-scaling strokes and type, tests each element only against the `