mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 12:13:19 +00:00
EPS/.ai editing data: symbols defined before the art styles take them
Illustrator 24 writes its symbol definitions ahead of %AI9_BeginArtStyles, so a styled object inside a symbol found no style and came in as its drawn look. The pre-pass that finds swatch names now also keeps the first top-level art styles section, which the reader parses before the art.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user