Merge pull request #5992 from mockydevs/fix/ai-symbols-before-art-styles

EPS/.ai editing data: symbols defined before the art styles take them
This commit is contained in:
BFlat
2026-11-07 10:14:38 +08:00
committed by GitHub
3 changed files with 39 additions and 5 deletions
+17 -4
View File
@@ -250,7 +250,7 @@ pub fn read_beside(data: &[u8], large_canvas: bool, ps: &[u8]) -> Result<Structu
/// content, the prolog, resources, sections it skips (such as symbols and the text document) or
/// what follows the trailer. The palette the reader keeps is the last one outside those and the
/// sections it reads on their own.
fn swatch_names(data: &[u8]) -> ShownNames {
fn swatch_names(data: &[u8]) -> (ShownNames, Option<&[u8]>) {
/// The sections the reader reads on their own whose definitions and colors it keeps.
const READ_APART: &[&str] = &["BeginPattern", "BeginBrushPattern", "BeginPluginObject", "BeginArtStyles", "BeginArtStyleList"];
/// Whether the marker `m` ends what began with `begin` and ends with `end`, `depth` of its kind
@@ -277,6 +277,8 @@ fn swatch_names(data: &[u8]) -> ShownNames {
let mut skip: Option<(&str, &str, usize, Option<usize>)> = None;
let mut section: Option<(&str, &str, usize, bool)> = None;
let (mut palette_at, mut palette) = (None, None);
// Where the first art styles section starts, and that section.
let (mut styles_at, mut styles) = (None, None);
// A pattern's name is the first string of its definition.
let mut head = false;
// ASCII85 data in comment lines is skipped past, as the reader skips it.
@@ -320,6 +322,9 @@ fn swatch_names(data: &[u8]) -> ShownNames {
skip = Some(("", "%EndResource", 0, None));
} else if let Some((begin, end, depth, _)) = section.as_mut() {
if closes(begin, end, depth, m) {
if let Some(at) = styles_at.take().filter(|_| *begin == "BeginArtStyles") {
styles = Some(lex.slice(at, before));
}
section = None;
}
} else if m == "BeginPalette" {
@@ -327,6 +332,9 @@ fn swatch_names(data: &[u8]) -> ShownNames {
} else if let Some((begin, end)) = types::SKIPPED.iter().find(|(b, _)| *b == m) {
section = Some((begin, end, 0, READ_APART.contains(begin)));
head = m == "BeginPattern";
if m == "BeginArtStyles" && styles.is_none() {
styles_at = Some(lex.offset());
}
} else if matches!(m, "%PageTrailer" | "%Trailer") {
break;
}
@@ -358,7 +366,7 @@ fn swatch_names(data: &[u8]) -> ShownNames {
}
palette.own_names().for_each(|n| names.reserve(n));
colors.names.iter().for_each(|n| names.reserve(n));
names
(names, styles)
}
/// What the brushes and brush art read so far give a brushed stroke.
@@ -597,10 +605,15 @@ pub(crate) fn is_look_slot(name: Option<&str>) -> bool {
impl<'a> Reader<'a> {
/// A reader of the editing data `data` as a whole, with the names its gradients and patterns
/// get in the document found first ([`swatch_names`]).
/// get in the document and its art styles found first ([`swatch_names`]): the symbols defined
/// before the art styles section use them too.
fn with_names(data: &'a [u8]) -> Self {
let mut r = Self::new(data);
r.global_names = swatch_names(data);
let (names, styles) = swatch_names(data);
r.global_names = names;
if let Some(section) = styles {
r.styles = super::style::art_styles(section);
}
r
}
+19
View File
@@ -5114,6 +5114,25 @@ fn an_object_with_a_style_this_reads_comes_in_as_itself() {
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
}
#[test]
fn a_symbol_defined_before_the_art_styles_reads_its_styled_objects() {
// GUARDS: objects in a symbol defined ahead of `%AI9_BeginArtStyles` (as Illustrator 24 writes
// its editing data) found no style and came in as their drawn look.
let hidden_rect = rect_path(0.0, 0.0, 10.0, 10.0).replace('\n', "\n%_");
let styled = format!("u\n{RED}{}f\nU\n%_{hidden_rect}f\n1 (Blue) XW\n", rect_path(0.0, 0.0, 10.0, 10.0));
let r = read_ok(&format!("{}{ART_STYLES}{}", symbols(&symbol("Tag", &styled, 1)), layer("")));
let art = &r.doc.symbols.first().expect("the symbol").art;
let object = match &art.kind {
NodeKind::Group { children, .. } if children.len() == 1 => &children[0],
_ => art,
};
assert!(matches!(object.kind, NodeKind::Path { .. }), "the object, not its look: {art:#?}");
assert_eq!(object.appearance.fill().and_then(|f| f.paint.color()), Some(Color::rgb(0.0, 0.0, 1.0)));
let effects: Vec<_> = object.appearance.effects.iter().map(|e| e.id.as_str()).collect();
assert_eq!(effects, ["stylize.dropShadow"]);
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
}
/// An art style named `name` (its bytes as written) that fills with colour `xa` (`c m y k r g b Xa`).
fn fill_style(name: &str, xa: &str) -> String {
fill_style_with(name, xa, "")
File diff suppressed because one or more lines are too long