mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 18:27:02 -06:00
Merge pull request #2358 from dotCooCoo/fix/ai-text-on-nonprinting-layer
.ai import: text on a layer that doesn't print stays on that layer
This commit is contained in:
@@ -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<Node>], shown: bool, found: &mut Vec<(NodeId, Option<Rect>)>) {
|
||||
/// Clear the marks in `nodes` and say which marked objects show and print, by id.
|
||||
fn unmark(nodes: &mut [Arc<Node>], shown: bool, prints: bool, found: &mut Vec<(NodeId, Option<Rect>)>) {
|
||||
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<Node>], 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<Arc<Node>>, 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;
|
||||
|
||||
@@ -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<String> = 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.
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
@@ -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 `<style>` rules for its id, its classes and its element name, and the rules whose selector ends in none of these (`*`, `[x]`).
|
||||
A `.svgz` file copied in a file manager pastes as its art, as a copied `.svg` file does.
|
||||
A PDF saved with Illustrator's editing data opens through it as a PDF-compatible `.ai` does, with its layers and groups and with art reaching past its page unclipped; such a PDF holds the data in `AIPDFPrivateData` streams (part of #868). A shape a PDF or `.ai` paints with a gradient (a clip path round a shading) opens as one path filled with the gradient, not a clip group round a rectangle, so Shape Builder, Live Paint and Pathfinder work on its outline (#1304).
|
||||
In a `.ai` opened from its editing data, a text object on a layer that shows and doesn't print stays on that layer, made from the file's text document, even where the page doesn't draw it (`crates/eps/src/import/tests_native.rs`).
|
||||
A PDF page whose `UserUnit` (PDF 1.6) is above 1 opens at its size in points, its numbers times the `UserUnit` (one above 75,000 counts as 75,000), and `document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of such a page opens from that data at the page's size, with its layers, and when the layers can't be read its pages still take the artboard layout of the data, scaled the same way (part of #1482).
|
||||
Placed (linked) files in a `.ai`'s editing data come in as linked images in their place, each showing the low-resolution preview the file keeps of it until its file is found or relinked in the Links panel (a file found next to the document or at its path is read as the document opens). A file with them no longer falls back to its PDF part, so its guides, sublayers and hidden objects open (#1264; `crates/eps/src/import/ai/tests.rs`, `crates/engine/src/tests_links.rs`). In a file opened that way, a gradient with stops in the middle runs its whole ramp across its shape and a gradient object no longer passes its fill to the next path (#1390); text objects with an appearance keep their own text and frame, type of hidden layers stays hidden, and type on later artboards lands on its text objects (#1391); area type whose frame is too short for a line of the fallback font shows its first line, the frame made taller with a warning in the open result (#1392).
|
||||
- EPS and DXF in and out, EMF/WMF in and out; PNG (with PNG-8), JPEG, WebP, GIF, TIFF, BMP, Targa and layered PSD export; grayscale TIFFs with an alpha channel or other extra channels place, relink and open, the alpha as the image's transparency (#1466 in part; CCITT Group 3 and planar files with extra channels are not read); PSD and PSB files open and place, linked or embedded, as their merged image in every colour mode and depth (#918). EPS import runs a PostScript Level 3 interpreter written from the PostScript Language Reference: resource categories and `resourceforall`, local and global VM (`currentglobal`/`setglobal`/`gcheck`), `clipsave`/`cliprestore`, subarrays and substrings that share storage, the standard dictionaries in `systemdict`, `dictstack`, `internaldict`, executable filters and strings, `load` by any key, `//name` immediate names, `token`, `execform`, user paths, `cshow`, `rootfont`, `cachestatus`, colour rendering and halftone queries, file `status` and data read with `flushfile`, and `pdfmark` marks dropped (Discord feedback, #466, #469, #474). Masked images (`ImageType 3` in its three interleavings) and reusable streams are read, and an image's several data sources are read in turn, as the reference defines (procedures that read one file get their own rows: such images came in banded, #498). `restore` puts dictionaries back as they were at the `save`, so programs that count their saves in a dictionary read to their end (part of #505). Tiling patterns come in as pattern swatches, uncoloured ones too; Type 3 fonts' glyphs as their outlines, spaced by `ashow`/`widthshow`/`xshow`/`kshow`; mesh and function-based shadings (types 1, 4–7) as gradient meshes; sampled functions (type 0) and Flate/LZW predictors are read. It is tested with programs written from the specification in the ways common generators use these features; Inkscape/cairo EPS files open as vectors. When a file's PostScript still can't be read, it opens as its TIFF preview, its Windows metafile preview (as vectors) or its EPSI preview, and the warning (in the status bar, `document.open`/`app.open`/MCP `open_file` replies and `vectorcraft-cli info`) names the error, the operator that raised it and the procedures it ran in. Legacy PostScript `.ai` files (Illustrator 3–8, written with the prolog that defines their operators) keep their groups and nested groups (`u` … `U`), between and inside clipping groups; EPS and EMF/WMF import nest clips (and groups) at most 128 deep, so a hostile file can't exhaust the stack. DXF TEXT justified Fit or Aligned spans its two points (stretched, or scaled as a whole). PDF export writes type as real, searchable text in embedded subset fonts by default, as Illustrator's PDF presets do (Convert Text to Outlines is an option; printer's marks stay shapes), so text-heavy PDFs stay small (#733). DXF export writes each fill as its outlines followed by its hatch, so laser and cutter software, which skips hatches, gets filled art and type too (#546).
|
||||
|
||||
Reference in New Issue
Block a user