mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 13:17:19 -06:00
fix(ai-import): put placed files where they draw closer to the page
The corner placement of placed files was tried only when the first placement failed the comparison with the page. A small logo in the wrong place differs too little to fail, so it stayed there. When the corner placement draws closer to the page and still matches it, use it.
This commit is contained in:
@@ -247,7 +247,22 @@ pub(super) fn build_from(
|
||||
let unseen = prune_unseen(&frame, &mut view, &mut doc);
|
||||
let unseen_note = |n: usize| (n > 0).then(|| format!("{n} {UNSEEN_TEXT}"));
|
||||
match compare(&frame, &mut view, &doc) {
|
||||
Ok(note) => notes.extend(unseen_note(unseen).into_iter().chain(note)),
|
||||
// Placed files at the other point their data gives, where that draws closer to the
|
||||
// page: a small image in the wrong place differs too little to fail.
|
||||
Ok(note) => {
|
||||
let closer = other.and_then(|mut other| {
|
||||
let unseen = prune_unseen(&frame, &mut view, &mut other);
|
||||
let closer = view.difference(&other, frame.rect).any < view.difference(&doc, frame.rect).any;
|
||||
closer.then(|| compare(&frame, &mut view, &other).ok()).flatten().map(|note| (other, unseen, note))
|
||||
});
|
||||
match closer {
|
||||
Some((other, unseen, note)) => {
|
||||
doc = other;
|
||||
notes.extend(unseen_note(unseen).into_iter().chain(note));
|
||||
}
|
||||
None => notes.extend(unseen_note(unseen).into_iter().chain(note)),
|
||||
}
|
||||
}
|
||||
// Placed files at the other point their data gives, where that draws as the page.
|
||||
Err(why) => match other.map(|mut other| {
|
||||
let unseen = prune_unseen(&frame, &mut view, &mut other);
|
||||
|
||||
@@ -1728,6 +1728,31 @@ fn a_placed_file_at_its_boxs_corner_matches_the_page() {
|
||||
assert!([b.x0 - 30.0, b.y0 - 30.0, b.x1 - 70.0, b.y1 - 70.0].iter().all(|v| v.abs() < 0.01), "on the page's square: {b:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_small_placed_file_goes_where_it_draws_closer_to_the_page() {
|
||||
// A 2 × 2 placed file (a small logo on a large page) whose box corner the matrix takes to
|
||||
// (30, 30)–(32, 32), as the page draws it. The template box's canvas point puts it at
|
||||
// (10, 10)–(12, 12): on the page too, and small enough that the layers pass the comparison
|
||||
// there all the same.
|
||||
let place = format!(
|
||||
"%AI5_BeginPlace\n%%FileType: 1\n[1 0 0 -1 -7970 8070] 8000 8000 8002 8002 4 4 0\n(logo.png)`\n\
|
||||
%AI26_BeginPlacedObjectPreview\n2 2 8 32 17 {}\n%AI26_EndPlacedObjectPreview\n~\n%AI5_EndPlace\n",
|
||||
"\x7f\0\0\0".repeat(4)
|
||||
);
|
||||
let data = native(&layer("Jig", true, &place)).replace("%AI5_NumLayers", "%AI3_TemplateBox: 211.5 -211.5 211.5 -211.5\n%AI5_NumLayers");
|
||||
let mut page = Document::new(100.0, 100.0);
|
||||
let id = page.alloc_id();
|
||||
let path = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(30.0, 30.0, 32.0, 32.0));
|
||||
let grey = vectorcraft_doc::FillLayer::new(vectorcraft_color::Paint::solid(vectorcraft_color::Color::gray(0.5)));
|
||||
let n = Node::path(id, path, vectorcraft_doc::Appearance { items: vec![vectorcraft_doc::AppearanceItem::Fill(grey)], ..Default::default() });
|
||||
let l = page.layers[0].id;
|
||||
page.insert(Some(l), 0, n).unwrap();
|
||||
let (d, w, read) = crate::layered_ai(&private(&data), page, vec![], false, false, 1.0);
|
||||
assert!(read, "{w:?}");
|
||||
let b = d.layers[0].children().unwrap()[0].geometric_bounds().unwrap();
|
||||
assert!([b.x0 - 30.0, b.y0 - 30.0, b.x1 - 32.0, b.y1 - 32.0].iter().all(|v| v.abs() < 0.01), "on the page's square: {b:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_a_placed_file_covers_only_at_its_first_placement_is_kept() {
|
||||
const OPAQUE: &str = "<opaque>";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-30 · **Change:** minor (`.ai` import: a page's type inside placed files needs no text object) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-30 · **Change:** minor (`.ai` import: placed files go where they draw closer to the page) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
Every format Illustrator opens, places, saves or exports, against VectorCraft's format table (`FORMATS`,
|
||||
`OPEN_EXTS`, `PLACE_EXTS` in `crates/engine/src/cmd/fileio/formats.rs`). Illustrator's list comes from its public
|
||||
@@ -132,6 +132,7 @@ the app's own editing copy of a file (an EPS after its `%%EOF`, a `.ai`'s `AIPri
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | `.ai` import: a placed (linked) file whose data gives two placements is put at the corner of its box when that draws closer to the page and the layers still match it, not only when the first placement fails the comparison: a small logo in the wrong place differed too little to fail, and stayed there. Test `import::tests_native::a_small_placed_file_goes_where_it_draws_closer_to_the_page`; scores unchanged |
|
||||
| 2026-10-30 | minor | `.ai` import: a file whose layers hold placed (linked) PDFs and no text object comes in from its layers when all of its page's type lies inside the placed files (at either of their placements): the PDF part draws each placed file, type and all, where the layers show its preview, so that type needs no text object. Such a file came in as its PDF part, saying its layers had no text objects for its page's text; type outside the placed files still needs one. Own-fixture test in `import::tests_native`; scores unchanged |
|
||||
| 2026-10-30 | minor | `.ai`/EPS editing data: a gradient mesh takes the transparency (`Xy`) written right after its `/End X!`, before its `Xd` and `XW`, as a group takes the one after its `U`; it was read as the next object's, so a Multiply shadow mesh painted opaque white over the art under it, where the file's page shows the shadow. A user's file now draws such a mesh as its page does. Own-fixture test in `import::ai::tests`; scores unchanged |
|
||||
| 2026-10-30 | minor | `.ai`/EPS editing data: a gradient mesh corner whose handle weights are 0 (one or both; the others are 1) is read like one whose weights are 1, so a mesh with such corners no longer leaves the whole file coming in as its page or PDF part; other weights still leave the mesh unread. Checked against the pages of two users' files: one opens from its layers 0.6% from its page, the other with 728 live meshes instead of its PDF part's 65,558 patches. Test `import::ai::mesh::tests::corners_with_handle_weights_of_zero_are_read`; scores unchanged |
|
||||
|
||||
Reference in New Issue
Block a user