.ai import: Scatter brushes in Tints open in Tints, and brush art drops its frame

A Scatter brush in a .ai or EPS file's editing data always came into
the Brushes panel keeping its art's colors. A Scatter brush whose last
15 numbers start with 1 now opens in Tints, and one whose numbers start
with 0 keeps its art's colors. Other values are not read and keep the
art's colors. The field before the art's name does not decide it: an
object whose first field is 1 is read by its 15 numbers, and a Scatter
object with 8 numbers is still left out.

A brush's art usually comes in a group after its frame, an unpainted
rectangle around the art. The reader kept that rectangle as part of
the brush's art. It now leaves out a frame at the art's bounds (within
1/1000 pt): an unnamed, unpainted rectangle with its four corners in
order, closed, or open with its first corner again at the end, and no
settings of its own (shown, unlocked, at full opacity in Normal, with
no effects, note or other attributes). A wider frame stays in the art,
and so do a path that crosses the corners, an open path along three
sides and a rectangle with settings of its own. A group left with one
object becomes that object, unless it has a state or look of its own
(hidden, locked, isolated, knockout, an opacity, a blend mode, a name,
a mask, a note or another setting, or an appearance); then it stays a
group without the frame. The rule applies to every brush art section.

Existing files with such brushes now open with Tints and without the
extra frame path in the brush's art. With an exact frame, the pieces
that a Scatter or Art brush draws along a path stay where they were.

The Scatter, Art and Pattern brush colorization options, Tints among
them, are described on pages 281-282 of
https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf#page=281

Tests cover Scatter brushes whose numbers start with 1 and 0, with a
first field of 1 and 2, and an 8-number object. They cover frames kept
or left out (exact, wider, open, filled, named, crossing the corners,
along three sides, with a note, Show Center or hidden), a group's own
state and note kept, and the pieces a Scatter and an Art brush draw
along a path with and without the frame. The import fuzz test reads
brush art after frames of any corners and paint.
This commit is contained in:
dotCooCoo
2026-11-08 13:23:23 +07:00
committed by bflatastic
parent 1ea4eefa80
commit c85b286994
6 changed files with 264 additions and 7 deletions
+12 -3
View File
@@ -3799,8 +3799,9 @@ proptest! {
}
/// Brush objects of every kind with any parameters, the brush manager's order with any names,
/// and brush art sections nested or cut short: read with the layers, or as a library without
/// them, the brushes kept within their ranges.
/// and brush art sections nested or cut short, their art alone or in a group after a frame of
/// any corners, painted or not, open or closed, the group ended or not: read with the layers,
/// or as a library without them, the brushes kept within their ranges.
#[test]
fn ai_brushes_never_panic(
kind in 0usize..7,
@@ -3809,6 +3810,7 @@ proptest! {
nest in 0usize..6,
cut in any::<bool>(),
layers in any::<bool>(),
frame in prop::option::of((prop::collection::vec(prop_oneof![arb_num(), Just("0".to_string()), Just("4".to_string())], 4), prop::sample::select(vec!["n", "N", "f", "S"]), any::<bool>())),
) {
use vectorcraft_testkit::ai;
let kinds = ["Adobe Calligraphic Brush Tool", "Adobe Scatter Brush Tool", "Adobe ArtOnPath Brush Tool", "Adobe PatternOnPath Brush Tool", "Adobe dBrush Brush Tool", "Adobe Brush Manager Order", "Other"];
@@ -3822,7 +3824,14 @@ proptest! {
for _ in 0..nest {
art.push_str("%AI8_BeginBrushPattern\n(A)\n");
}
art.push_str("0 g\n0 0 m\n4 0 L\n4 4 L\nf\n");
match &frame {
Some((c, op, ended)) => {
let (x0, y0, x1, y1) = (&c[0], &c[1], &c[2], &c[3]);
let end = if *ended { "U\n" } else { "" };
art.push_str(&format!("u\n{x0} {y0} m\n{x1} {y0} L\n{x1} {y1} L\n{x0} {y1} L\n{x0} {y0} L\n{op}\n0 g\n0 0 m\n4 0 L\n4 4 L\nf\n{end}"));
}
None => art.push_str("0 g\n0 0 m\n4 0 L\n4 4 L\nf\n"),
}
for _ in 0..nest.saturating_sub(usize::from(cut)) {
art.push_str("%AI8_EndBrushPattern\n");
}