Merge pull request #5886 from dotCooCoo/fix/ai-no-layers-active-artboard

.ai import: a file whose editing data has no layers also opens on its active artboard
This commit is contained in:
BFlat
2026-11-06 14:47:49 +08:00
committed by GitHub
3 changed files with 59 additions and 5 deletions
+15 -3
View File
@@ -103,7 +103,8 @@ fn layer_tree(nodes: &[Arc<Node>]) -> Vec<Arc<Node>> {
/// data was decompressed but its layers can't be read, and its swatches, brushes, graphic styles and
/// variables those of the editing data's palette ([`use_palette`]), brushes ([`use_brushes`]), styles
/// ([`use_styles`]) and variable library ([`use_variables`]) when it was decompressed: a graphic
/// style library, which has no layers, opens with its styles. `outlined`: the PDF part's type was
/// style library, which has no layers, opens with its styles, and editing data with no layers opens
/// on the page of its active artboard under the checks of `place_pages`. `outlined`: the PDF part's type was
/// read as outlines (a file whose type shows then comes in as its PDF part).
/// `trust`: layers that draw differently from the PDF part are used all the same, with a warning.
/// `unit`: the size in points of a unit of the PDF part's user space, its first page's `UserUnit`,
@@ -134,10 +135,14 @@ pub fn layered_ai_from(
};
if !has_layers(&data) {
let (mut visible, mut warnings) = (visible, warnings);
let (boards, active) = ai::artboards(&data);
if board_scale(&boards, &visible, unit).is_some() {
visible.opened_artboard = active_page(active, &visible);
}
let have = visible.patterns.len();
use_palette(&data, &mut visible);
use_brushes(&data, &mut visible, have, &mut warnings);
use_styles(&data, &mut visible, false, unit, None);
use_styles(&data, &mut visible, false, unit, Some(&boards));
use_variables(&data, &mut visible, &mut warnings);
return (visible, warnings, false);
}
@@ -347,7 +352,7 @@ fn place_pages(boards: &[(String, Rect)], active: Option<usize>, doc: &mut Docum
for ((name, _), a) in boards.iter().zip(&mut doc.artboards) {
a.name.clone_from(name);
}
doc.opened_artboard = active.filter(|i| *i > 0).and_then(|i| doc.artboards.get(i)).map(|a| a.id);
doc.opened_artboard = active_page(active, doc);
let [(_, first), _, ..] = boards else { return };
// Art space (y up) → the document, the first artboard's top left at the origin, as `read` has it,
// `k` points a unit.
@@ -362,6 +367,13 @@ fn place_pages(boards: &[(String, Rect)], active: Option<usize>, doc: &mut Docum
}
}
/// The id of the page of `doc` (a `.ai` read as its PDF part: a page per artboard) for the artboard
/// at `active` among those of its editing data, to open the file on: `None` for the first, which a
/// file opens on anyway, or when `doc` has no such page.
fn active_page(active: Option<usize>, doc: &Document) -> Option<u32> {
active.filter(|i| *i > 0).and_then(|i| doc.artboards.get(i)).map(|a| a.id)
}
/// The objects in `layer` (and its sublayers) moved by the move of the page they are on.
fn place_art(layer: &mut Node, pages: &[Rect], moves: &[Vec2]) {
let Some(children) = layer.children_mut() else { return };
+43
View File
@@ -2335,6 +2335,49 @@ fn an_ai_file_read_as_its_pdf_part_keeps_its_active_artboard() {
assert_eq!(d.opened_artboard, Some(2));
}
/// A `.ai` whose editing data has artboards and no layers comes in as its PDF part, open on the
/// page of the artboard the editing data names as the active one when there is a page per artboard
/// of its size.
#[test]
fn an_ai_file_with_no_layers_keeps_its_active_artboard() {
let board = |x0: i32| {
format!(
"%_/Dictionary :\n%_{x0} 100 /RealPointRelToROrigin\n%_ (PositionPoint1) ,\n%_{} 0 /RealPointRelToROrigin\n%_ (PositionPoint2) ,\n%_; ,\n",
x0 + 100
)
};
let no_layers = |active: &str| {
native(&format!(
"%_/Document :\n%_/Dictionary :\n%_/Array :\n{}{}%_; (ArtboardArray) ,\n{active}%_; /NotRecorded ,\n%_;\n",
board(0),
board(120)
))
};
let mut pdf = pdf_part(10.0, 10.0);
let mut page = pdf.artboards[0].clone();
(page.id, page.rect) = (2, vectorcraft_geom::Rect::new(120.0, 0.0, 220.0, 100.0));
pdf.artboards.push(page.clone());
let opened = |pdf: &Document, active: &str| {
let data = no_layers(active);
assert!(!data.contains("%AI5_BeginLayer"));
let (d, w, read) = crate::layered_ai(&private(&data), pdf.clone(), vec![], false, false, 1.0);
assert!(!read && !w.iter().any(|w| w.contains("layers weren't read")), "{w:?}");
assert_eq!(d.artboards.iter().map(|a| a.rect).collect::<Vec<_>>(), pdf.artboards.iter().map(|a| a.rect).collect::<Vec<_>>());
d.opened_artboard
};
assert_eq!(opened(&pdf, "%_1 /Int (CropAreaActive) ,\n"), Some(2));
assert_eq!(opened(&pdf, "%_0 /Int (CropAreaActive) ,\n"), None);
assert_eq!(opened(&pdf, ""), None);
// Not a page per artboard, or a page of another size: the pages aren't the artboards.
let mut three = pdf.clone();
(page.id, page.rect) = (3, vectorcraft_geom::Rect::new(240.0, 0.0, 340.0, 100.0));
three.artboards.push(page);
assert_eq!(opened(&three, "%_1 /Int (CropAreaActive) ,\n"), None);
let mut other_size = pdf.clone();
other_size.artboards[1].rect = vectorcraft_geom::Rect::new(120.0, 0.0, 320.0, 100.0);
assert_eq!(opened(&other_size, "%_1 /Int (CropAreaActive) ,\n"), None);
}
#[test]
fn art_moved_with_its_page_moves_its_pattern_tile() {
// Two 100-point artboards, the second 50 below the first; an operator this doesn't know, so
File diff suppressed because one or more lines are too long