Commit Graph
3400 Commits
Author SHA1 Message Date
dotCooCoo 72c46b8aa2 Path ops: boolean contour passes test the shapes covering each face instead of a flag per shape
The contour passes of the two-path boolean operations, Offset Path and
the many-shape boolean (`boolean_n`, used by compound shapes and
Intertwine) built a flag for every input shape for each winding they
tested. They now test the list of inputs that cover the face, ascending,
refilled in one buffer per pass, so a test costs the size of the cover
rather than the number of shapes. `boolean_n` and `try_boolean_n` take
that list; the compound shape and Intertwine tests read it directly.

The contours, their order and the output are the same: a new test
compares every pass's test with the flag form on overlapping, nested,
touching and apart squares and on 72 overlapping squares, and another
checks that a pass over 100 squares apart hands its test one input at
most.
2026-11-07 10:28:25 -08:00
M Balazs PC fe3b9b3a4d .ai/EPS import: the parity assessment's evidence and revision history record the tab stop fixes (#6147) 2026-11-07 19:25:54 +01:00
M Balazs PC d8134e8bd6 Merge branch 'fix/ai-type-leading-forced-breaks' into fix/ai-right-aligned-tabs 2026-11-07 19:25:54 +01:00
M Balazs PC 1b364b955d .ai/EPS import: the parity assessment's evidence and revision history record the line layout fixes (#6137) 2026-11-07 19:25:43 +01:00
M Balazs PC b46d321c2b .ai/EPS import: the parity assessment and roadmap record the tab stop fixes (#6147) 2026-11-07 19:25:01 +01:00
dotCooCoo 6c285d3042 .ai save: the editing copy keeps live effects
The editing copy of a PDF-compatible .ai wrote an object's live effects
as their result (geometry effects) or left them out (the others).
Objects with effects the .ai reader reads are now written as art styles:

- An object is written as its drawn look (a group of its vector art as
  its geometry effects leave it, without shadows, glows or feathers,
  with the object's transparency after it), then the object itself on
  %_ lines (its points painted with nothing, or with its gradient fill
  placed on the outline it draws; a group with its members), its name
  and opacity mask, and
  1 (style) XW naming its style.
- Styles are written in a non-printing block after the symbols, in
  %AI9_BeginArtStyles: a chain of the stack of fills and strokes (with
  the contents where the appearance puts them), the effects and the
  object's transparency. Objects of the same appearance share a style.
- The effects written are the ones the reader reads on objects, with
  their settings in the units it reads them in: Drop Shadow, Inner Glow,
  Outer Glow, Feather, Round Corners, Scribble, Convert to Shape, Offset
  Path, Outline Object, Outline Stroke and Pathfinder > Add.
- Each style is read back before it is written, by a new reader
  function, read_art_style, which runs the reader's style parser and its
  reading of a graphic style with the object's own gradient. An object
  whose style reads back differently (several fills or strokes, fills
  or strokes with transparency or effects of their own, pattern fills,
  other effects), a filled object that fills by the even-odd rule (a
  style's fill fills by the non-zero rule), a group whose effects change
  its members' outlines while a member is written on another outline
  (strokes as filled outlines, pattern fills as tiles, gradients that
  fit their object), or one in a container whose own
  effects change its members, is written as its drawn look with the
  warning, and a style that is not written leaves no gradient
  definition behind. Graphic styles and links to them are left out with
  a warning. No art style is written on a large canvas.

Appearance attributes:
https://helpx.adobe.com/illustrator/using/appearance-attributes.html

Tests: the round trip of objects with each effect, a gradient fill, a
group with transparency and a mask, Pathfinder > Add and a hidden
effect; the same bytes on a second write; the forms written; the
objects the reader would not read; a large canvas; the warning for
graphic styles; filled even-odd objects; a gradient placed on the
outline Offset Path draws; a style that is not written; effects in a
pattern a drawn look uses; the reader's check of a style; fuzz cases
for damaged written styles.

Docs: docs/file-format-parity.md.
2026-11-07 10:17:51 -08:00
M Balazs PC 36511e1bbd Merge branch 'fix/ai-type-leading-forced-breaks' into fix/ai-right-aligned-tabs 2026-11-07 19:10:42 +01:00
M Balazs PC c0341c3fff Type: a paragraph ending in a soft return justifies the line the soft return ends
A paragraph that ends in a soft return ends with the empty line after it,
so the line the soft return ends is justified like any other; it was taken
for the paragraph's last line and left aligned.
2026-11-07 19:10:21 +01:00
M Balazs PC 88bc2ac06a .ai/EPS import: type keeps its paragraphs' tab stops
Key 30 of a paragraph's attributes holds its tab stops: each its position
(key 0) and alignment (key 1: 0 left, 1 center, 2 right, 3 decimal).
They were not read, so tabs fell on the default stops. At most 1,024 are
read a paragraph and 2^20 copied into a story's paragraphs, past which the
default stops apply with a warning. The fuzz test feeds hostile paragraph
attributes: alignments, auto leading, hyphenation options and tab stops.
2026-11-07 18:49:20 +01:00
M Balazs PC a4e315796b Type: right-aligned area type sets the text after a line's last tab against the edge
In right-aligned area type, the text before a line's last tab sits at its
stops, measured from the left edge, and the text after it (none when the
tab ends the line) against the right edge. The line was laid out from the
left and aligned as a whole, which moved its start off the left edge and
its tabs' text off their stops. Illustrator's own layout in a user's .ai
sets its price lists so. Centred and justified lines are aligned as a
whole as before.
2026-11-07 18:49:19 +01:00
dotCooCoo db61a47435 Docs: the region-building line gives the latest Divide timings and more of the commands that use it
The line names trapping, Intertwine and the Pathfinder effects next to
Divide, Hard Mix, Soft Mix, Shape Builder and Flatten Transparency, and
gives six alternating runs of each build of Divide on 3,000 overlapping
rectangles, timed on this branch's final code.
2026-11-07 09:16:05 -08:00
dotCooCoo 3fcd157f17 Path ops: region building lists the shapes covering each region instead of a flag per shape
Finding the distinct regions of an arrangement built a flag for every
input shape at each edge and kept them in a sorted set, so it cost edges
times shapes in time and memory. Each coverage is now the sorted list of
the shapes that cover it, deduplicated in a hash set and sorted into the
order the flag masks had, so regions, their order and the output of
Divide, Hard Mix, Soft Mix, Shape Builder and the flattener are the same.
The face lookup and Pathfinder merging take the same lists.

Tests compare the lists and the regions with the flag masks for
overlapping, nested, touching and disjoint shapes and for 72 shapes.
2026-11-07 08:44:50 -08:00
M Balazs PC f2c1df1ec3 .ai/EPS import: the parity note says what still differs in the file it was checked on 2026-11-07 17:43:42 +01:00
dotCooCoo f8e0639d14 .ai save: the editing copy keeps symbols and their instances
The editing copy of a PDF-compatible .ai wrote every symbol instance as
its art. Symbols are now written as symbols, in the form the .ai reader
reads them:

- Every symbol is defined in the setup, in a non-printing block after the
  patterns: %AI14_BeginSymbol, its name, its art in the symbol's own
  space, its registration point as AI13PatternRegistrationType and
  %AI10_EndSymbol, each after the symbols its art uses. The nine points
  are numbered 1 to 9 row by row from the top left, in the order the
  scripting reference lists them. A definition is written the same
  wherever its first instance lies. The symbol list after the palette
  keeps the Symbols panel's order. A hidden root of a symbol's art is
  written shown, as the canvas draws it.
- Each instance is a /SymbolInstance with its symbol's name and the matrix
  that places the art (to ten decimals), followed by its name, opacity
  mask, opacity and blending mode as a group's. In a symbol's art, the
  matrices of instances, gradients and images are written in the
  canvas's space about the canvas point of the ruler origin.
- Pattern fills in a symbol's art are written as their tiles cut to the
  shape. A symbol whose definition holds such tiles, from its own
  objects, a brushed stroke's brush art, a live object, an opacity mask,
  a nested symbol or an inline graphic, has its instances written as
  their art.
- Instances with a fill, appearance changes, isolation, knockout or
  knockout shape of their own, instances of symbols with 9-slice guides
  or pattern paint, instances in pattern tiles or in art cut to a
  clipping object, instances scaled almost flat, instances that turn,
  mirror or scale symbols holding type, live objects, instances written
  as their art, shapes with rounded live corners or strokes written as
  outlines or brush art (the canvas works these out again where the
  instance puts them), instances that scale their symbol more than 100
  times, nested instances included (numbers are written to four
  decimals), and instances on the layers of a large canvas are written
  as their art (the symbols are still defined). Symbols past the 4,096
  the reader keeps are left out and their instances written as art; an
  instance of a symbol the document doesn't have is an empty group.
  9-slice guides, export types and the dynamic type are left out, and
  linked images and type in a symbol's art are embedded and written as
  outlines. Each comes with a warning.

The writer takes the reader's limit of 4,096 definitions (MAX_SYMBOLS)
and its symbol_art_member check, which now are pub(crate).

The editingData option's description lists what the copy now holds,
type written as text included.

Symbols and their instances:
https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/edit-symbols.html
https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-and-place-symbols.html
The nine registration points, in the order the scripting reference lists
them:
https://web.archive.org/web/20180219005134/https://wwwimages.adobe.com/content/dam/acom/en/devnet/illustrator/pdf/Illustrator-Scripting-Reference-JavaScript-cs6.pdf#page=252

Tests: the round trip of symbols, nested symbols and instances in every
state, symbols whose art is a group with a state of its own, images and
gradients in a symbol's art, every registration point, symbol names with
special characters, duplicate and missing symbols, a symbol first used
deep in opacity masks, symbols past the reader's count (one found only
while its holder's art is written included), a large canvas, symbols
with pattern fills (directly, nested, in an opacity mask, through an
inline graphic and through a brushed stroke), scaled instances of
symbols with an outside stroke, rounded live corners, type, a blend, a
tiny or thin object, an ellipse or an instance of a symbol with 9-slice
guides, a symbol whose root is hidden, instance matrices from a fixed
seed, a second write of what is read back giving the same bytes, the
forms written, the instances written as art and the options reported.

Docs: docs/file-format-parity.md, README.md, ROADMAP.md, docs/gaps.md,
docs/roadmap.md and docs/target-app-parity.md describe the editing copy
a saved .ai holds.
2026-11-07 08:35:54 -08:00
M Balazs PC b4199559f2 .ai/EPS import: cite Illustrator's documentation of auto leading 2026-11-07 17:17:00 +01:00
M Balazs PC eb03eacba6 .ai/EPS import: type keeps auto leading, hyphenation options and the file's last lines
- Auto leading stays auto: each line takes the paragraph's share (key 7)
  of the largest type on it. The leading was the first two lines'
  distance for every line, so 10 pt lines under a 15 pt heading came in
  18 pt apart.
- The paragraph's hyphenation options (keys 10 to 15) are read.
- Area type of one frame whose last lines the file sets, but which don't
  fit here (the fallback font, or a descent past the frame's bottom),
  gets a frame taller from its bottom (vectorcraft_text::fit_source_lines),
  with a warning.

A user's .ai whose type differed from its page by 6.6%, so that it opened
from its PDF part with each line its own text object, now opens from its
layers.
2026-11-07 16:57:21 +01:00
M Balazs PC 0f0e707e60 Type: a line a soft return ends is justified as a wrapped line is
In Justify Left, Center or Right, only the paragraph's last line keeps its
alignment; a line that a soft return (U+2028) ends is spread to the frame's
edges like any wrapped line. Illustrator's own layout in a user's .ai sets
such lines justified, and VectorCraft left them ragged.
2026-11-07 16:57:21 +01:00
vangel76andClaude Sonnet 5.5 128d362730 Merge upstream/main into fix/2414-svg-classes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 16:21:40 +01:00
ruchirguitar 349bdfa867 5949: step numeric fields on wheel hover 2026-11-07 19:57:54 +05:30
dotCooCoo 2a32d1bc65 .ai import: symbol libraries open with their symbols
A .ai or EPS whose editing data defines symbols but has no layers came
in as its PDF part or page without symbols, and Other Library in the
Symbols panel listed none. Such a file, and a file read as its PDF part
or page because its layers can't be used, now gets the symbols its
editing data defines before its first layer, as a file read from its
layers does: File > Open shows them in the Symbols panel in the order
of the symbol list, and Other Library lists them in a library panel.

- Each symbol keeps its registration point. A symbol that holds an
  instance of a symbol the file doesn't define is left out, and an
  object linked to a listed graphic style keeps the link.
- The symbols' art takes ids of the document, the art of opacity masks
  in it included, and the images it shows come with it. Instances
  drawn too deep are removed with the usual note.
- The global and spot colors the art is linked to come in as swatches.
  A color whose name another swatch has takes that swatch's place when
  it is a default swatch of a file without a palette; otherwise it
  comes in under a free variant of the name ("Ink 2"), which the art
  then names.
- Symbol art keeps the editing data's units, as in a file read from its
  layers, so in a file in larger units its linked graphic styles are
  drawn at the scaled size and the symbol art is not.
- The art of the patterns a palette or brush brings into a file read as
  its PDF part or page now also takes new ids for its opacity masks.

Part of #3349
2026-11-07 05:29:38 -08:00
MarekSpodBiedry 6162c853b9 Crop Image: cut from the pixels only when the rect is on the pixel grid, and format put_pixels (#5534) 2026-11-07 13:42:44 +01:00
MarekSpodBiedry cc75aa4916 Merge remote-tracking branch 'origin/main' into review-5560 2026-11-07 13:41:41 +01:00
dotCooCoo 5a9a08fcb1 Graphs: a series keeps the object opacity and blend modes set on one of its marks
An object opacity or blend mode set on a path directly in a graph series, and
the blend mode of its topmost visible fill or stroke, were lost when the graph
was drawn again. The graph now stores them per series next to the row opacity
it already stores, and puts them on every path directly in the series when it
draws the series again.

- transparency.set and object.setProps store the object's opacity and blend
  mode. A value the mark already has still replaces the value another mark of
  the series stored.
- appearance.clear, appearance.transfer, appearance.copyFrom with Transparency
  picked up, graphicStyle.apply without add and graphicStyle.redefine store
  the object's opacity and blend mode they leave on the mark.
- The other Appearance and style commands store them when they change them
  on a mark.
- Every command that stores the opacity of the topmost visible fill or stroke
  also stores its blend mode. A Filled Lines band's outline takes the stored
  stroke opacity and blend mode and keeps its black 0.25 pt stroke.
- An opacity and blend mode are stored as a pair from the edited mark:
  setting only the opacity of one mark also stores that mark's blend mode.
- A graph design's copy in a series keeps its own values.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-07 04:28:27 -08:00
Don Omondi 53f60823b2 EPS import: release only the image decoder's own buffers from the memory budget
What a data source procedure allocates stays counted: only the sample planes,
the decoder's copies of the strings a procedure returns, and the split buffers
of an interleaved masked image stop counting once the image is decoded.
2026-11-07 07:10:41 -05:00
dotCooCoo 434cf3e692 Graphs: graphic styles and Default and Swap Fill and Stroke update a series' stored paint
Applying a graphic style, Redefine Graphic Style, Default Fill and Stroke
and Swap Fill and Stroke on a path in a graph series changed the path
but not the paint the graph stores for the series. When the graph was
drawn again, the series went back to its earlier paint.

These commands now store the mark's topmost visible fill and stroke on
the graph, in the same undo step as the command:

- graphicStyle.apply stores the paint, weight and opacity of both kinds
  of row. With `add` it stores only the kinds of row that are visible
  in the style.
- graphicStyle.redefine stores the paint, weight and opacity of the
  marks still linked to the style.
- paint.default stores the white fill and the 1 pt black stroke, both
  opaque.
- paint.swap stores the swapped paints and the stroke weight and keeps
  the stored opacity. It swaps the topmost rows even when they are
  hidden, and a hidden row it swaps is not stored.

The rule for each kind of mark is the one docs/mcp.md gives for the
Appearance commands. docs/mcp.md also says what the graph does not
store from a graphic style and that a series group given to a graphic
style command is not stored. The params text, docs/mcp.md and the
Graphs line in docs/target-app-parity.md list the commands.
2026-11-07 02:54:42 -08:00
ruchirguitar e6a899857f M2.1: split disconnected path pieces after deletion (#3380) 2026-11-07 15:20:14 +05:30
dotCooCoo 0bc150c51e UI: save the Help Bar setting to preference files that lack it
A preferences file from before the Help Bar setting gets `help_bar` from
`status_bar` when it is read. A save that changed only the status bar
left `help_bar` out of the file, so the next start hid the Help Bar with
the status bar. A save now writes `help_bar` whenever the file lacks it,
and keeps a value another window saved.

Docs: the UI evidence list, the UI checklist and the M14 row record
Window > Help Bar.
2026-11-07 00:18:45 -08:00
dotCooCoo 9fe7aec49c Graphs: Appearance commands and the Eyedropper update a series' stored paint
paint.setFill, paint.setStroke, paint.none, Apply Fill to Stroke and Apply
Stroke to Fill stored a series' paint on the graph. appearance.setItem, the
Eyedropper, appearance.transfer, Clear Appearance, Reduce to Basic Appearance
and the add, remove, move and duplicate row commands stored no paint or weight.
An Eyedropper pick of a blue fill at 40% onto a red column drew red at 40%
after the next redraw.

These commands and Show All Hidden Attributes now store the paint and weight
of the topmost visible fill and stroke of a path directly in a series group,
in the same undo step. A color, weight or opacity that a command writes to that
row replaces the stored value even when the mark's own value does not change. A
row that has no paint before and after the command does not change the stored
paint. These commands store no stroke for a series that draws Filled Lines
bands, and a band no longer takes its color from an empty stored stroke.

The stored opacity also comes from the topmost visible row: hiding a top row
stores the row under it, and an opacity set on a hidden top row is not stored.

Follows #5501.
2026-11-07 00:07:40 -08:00
dotCooCoo 73e216bd9d Docs: the parameter-key description names the export commands as an exception
The earlier paragraph said document.export takes any key when its description names none. The export commands refuse an unknown option key, so the paragraph now lists them as an exception and keeps document.save as the example that takes any key.
2026-11-07 00:01:56 -08:00
dotCooCoo 6ffcf800c8 Export: the export commands refuse an unknown option key
document.export, document.exportSelection, document.serialize, app.export
and file.export took {noSuchOption: true} and wrote the file with no
warning. They now refuse a key that no format lists in document.formats,
and name the nearest key when one is close. document.exportPdf,
document.pdfSettings and pdf.preset.save refuse a key PDF doesn't take,
document.exportEps one EPS doesn't take and document.exportDxf one DXF
doesn't take. Each refuses an unknown key inside an option object it takes,
such as marks.useDocumentBleed, and names its path.

A key that only another format reads, such as {format: pdf, quality: 80},
is still ignored: docs/mcp.md says one object can carry several formats'
options. The check runs for callers (the control channel, MCP, the CLI).
A journal or a recorded action isn't checked, and a saved PDF preset with
a key that is no longer read still loads.

document.formats now also lists PDF's thumbnails, fastWebView and
viewAfterSaving.
2026-11-06 23:09:15 -08:00
dotCooCoo 72af76d418 Artboards: a Cut or Copy with the Artboard tool replays on its artboards
edit.copy and edit.cut chose the artboard route by the tool chosen, which
the journal does not record. A replay in a new session acted on the objects
selected there, or stopped with "nothing selected" when there were none.

With the Artboard tool chosen, both now record selectedArtboards and art in
the journal entry. A given selectedArtboards runs the artboard route with
any tool and needs only a document. A recorded action leaves both values
out, so it follows the tool chosen when it plays. Cut and Copy of objects
record nothing new.
2026-11-06 21:35:41 -08:00
Don Omondi 5817cabb3a Merge remote-tracking branch 'upstream/main' into fix/eps-memory-cap
# Conflicts:
#	docs/file-format-parity.md
2026-11-07 00:12:50 -05:00
Don Omondi fbccefe17e Merge remote-tracking branch 'upstream/main' into fix/eps-sampled-function-shading
# Conflicts:
#	docs/file-format-parity.md
2026-11-07 00:12:44 -05:00
Don Omondi f906de86ff Merge remote-tracking branch 'upstream/main' into feat/ai-import-stacked-fills
# Conflicts:
#	crates/eps/src/import/ai/tests.rs
#	docs/file-format-parity.md
2026-11-07 00:12:40 -05:00
Don Omondi e6e8dde874 Docs: keep this branch's entries that the merge with main dropped 2026-11-07 00:12:00 -05:00
Don Omondi fc7e1bf0bd Docs: keep this branch's entries that the merge with main dropped 2026-11-07 00:11:56 -05:00
Don Omondi 21dc2bceff Merge remote-tracking branch 'upstream/main' into fix/ai-import-brushes
# Conflicts:
#	crates/eps/src/import/ai/brush.rs
#	docs/file-format-parity.md
#	docs/target-app-parity.md
2026-11-07 00:09:24 -05:00
Don Omondi d1b45aec9f Merge remote-tracking branch 'upstream/main' into fix/eps01-editing-overlay
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
2026-11-07 00:09:19 -05:00
Helen Kwok f9bf685cbd vectorcraft-web: window.vectorcraft takes its request functions from craft-web-api
request, execute, commands and screenshot move onto the shared crate (storytold/craft-libs#413): a timeout on every call, the screenshot's PNG also in data, and docInspect and uiInspect beside them. A call the page gave up on gives up its request if the app has not started it, so the app skips it. openFile, captureDownloads, files, readFile and inspect stay VectorCraft's own, on the same object.
2026-11-07 09:18:00 +07:00
dotCooCoo fe9d62e05e .ai import: legacy type keeps its writing direction, Tate-chu-yoko runs and upright Roman characters
The legacy reader skipped `TV`, `Tv`, `Ty` and `TY` of section 15.3.4 of
the version 7 file format specification, so type set with `1 TV` opened
horizontal.

- `TV` sets a text object's writing direction. VectorCraft keeps one
  direction for each text object, the one in force at its first
  character, with a warning when a later character has another.
- `Ty` marks a Kumi block as a Tate-chu-yoko run, which is VectorCraft's
  own mapping, and `TY` does the same in horizontal type. A `TY` block in
  vertical type opens unmarked. Two blocks with nothing between them open
  as one block.
- `Tv` sets Standard Vertical Roman Alignment.

All four are modal, and an operand other than 0 or 1 leaves the value as
it was.

Docs: docs/file-format-parity.md.
2026-11-07 09:17:53 +07:00
dotCooCoo c592b31a1e Type: Tabs indent markers and Justification follow the whole story
After Select All in threaded text, the Tabs panel's indent markers read
the indents of the edited frame alone. When another frame's paragraphs
had a different left indent, a marker drag changed them by the edited
frame's values, or left them alone. The markers now take every
paragraph of every frame in the story, so each paragraph keeps its own
left indent or first-line position.

The Justification dialog kept the frames of the story from the moment
it opened. OK now acts on the frames in the edited frame's thread when
it is pressed, so a frame that left the thread while the dialog was
open is not changed, and a frame that joined it is.
2026-11-07 09:17:48 +07:00
dotCooCoo 40a46e539d Type: story-wide panel changes follow the story's current thread
After Select All in threaded text, the panels read the frames from the
list saved at Select All. A frame that had left the thread still took
Paragraph, Tabs, Justification and Text Styles changes, a frame added
later did not, and when the first saved frame had left, Character,
OpenType and font changes went to that frame alone.

text_story now reads the frames of the edited frame's thread from the
document. Locked and hidden frames stay in the story.
2026-11-07 09:17:46 +07:00
Don Omondi ed00a8870e EPS/.ai editing data: a transparency set inside a clipping group stays after it
Q restores the painting state but not the transparency (Xy): the writer
sets Xy only when the next object's state differs from the last one it
wrote. Restored at Q, type after a clipping group following a Screen glow
drew in Screen and nearly vanished on a light background.
2026-11-07 09:00:20 +07:00
Don Omondi 95ac8d7cc7 EPS/.ai editing data: blends with no spine written come in live
A blend whose spine was never edited has only its keys in the editing
data, no spine part. Its spine is the straight lines between the keys'
centres in the record's order; read as such, the blend comes in live
instead of as its drawn steps.
2026-11-07 09:00:15 +07:00
Don Omondi 2626791d7d 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.
2026-11-07 08:59:55 +07:00
Don Omondi 06179bbf19 Merge remote-tracking branch 'upstream/main' into fix/eps-memory-cap
# Conflicts:
#	docs/file-format-parity.md
2026-11-06 20:58:27 -05:00
Don Omondi 1b1af20685 Merge remote-tracking branch 'upstream/main' into fix/eps-sampled-function-shading
# Conflicts:
#	docs/file-format-parity.md
2026-11-06 20:58:27 -05:00
dotCooCoo 104d9562d4 Preferences: prefs.set refuses a missing or unusable recovery or plug-ins folder
prefs.set did not check the folder given as recoveryFolder or pluginsFolder,
beyond the automation roots check, so a control or MCP caller could save a
folder that does not exist, a file, or a folder the app cannot use. Only the
Preferences dialog checked the folder itself.

The dialog's folder checks move to the engine, and prefs.set runs them after the
automation roots check. A folder that fails returns the same error the dialog
shows, and no preference changes. An empty value, an omitted value and a folder
the preference already holds are not checked. The Wasm build skips the check, as
the dialog did. The dialog's OK now gets the check from prefs.set, and Choose…
calls the moved check directly.
2026-11-07 08:38:20 +07:00
dotCooCoo 2e8e8a1909 Offset Path: the spurious-contour check tests only the source segments near each contour
The check tested one box per source segment for every candidate hole or
island. It now searches a tree of the segments' bounding boxes, built once
when the first contour is checked. The answer for each contour is the same,
so the output paths do not change. MAX_OFFSET_SEGMENT_PAIRS does not change.
2026-11-07 08:38:12 +07:00
dotCooCoo c7c8e5428d Artboards: the Artboard tool's canvas menu cuts, copies, pastes, renames, rearranges and exports artboards
With the Artboard tool, the canvas context menu lists the entries of the
public Use the artboard context menu page in its order, in place of the
entries for objects and the view. Lock Artboards and Unlock Artboards are
two submenus. An entry that cannot run is left out, and Undo and Redo stay
dimmed. A right-click on an artboard that is not selected selects it alone
first, and the menu measures the art for Delete Empty Artboards and Fit to
Artwork Bounds once, at the right-click.

Cut, Copy, Duplicate Artboards, Rename, Delete Artboards, Lock Selected,
Unlock Selected, Fit to Artwork Bounds, Switch Orientation and Export act on
the artboards the tool has selected, else the active one. docs/mcp.md lists
what the other entries act on.

- artboard.rename renames in one undo step and numbers several names. The
  menu's Rename opens a dialog that runs it, and the Artboards panel's name
  field runs it too.
- artboard.switchOrientation swaps width and height and keeps the top-left
  corner, skipping square and locked artboards.
- artboard.selectAll chooses the tool and selects every artboard.
- edit.paste takes artboardsAt, and the menu's Paste centers copied
  artboards on the right-click's point.
- The menu's Cut and Copy run artboard.cut and artboard.copy. In the desktop
  app those commands publish to the system clipboard, and the menu shows the
  Cut and Copy shortcuts for them.
- artboard.fitToArt and the panel's name field record no undo step when
  nothing changes.

artboard.duplicate, copy, cut, rename and switchOrientation take their
artboards from the tool's selection when a call names none. Each now records
them in the journal as selectedArtboards, which a replay reads and a recorded
action leaves out. The copy commands also record art. switchOrientation
records scaleArt, and it takes patterns and lockedAndHidden and records what
its artboard.setProps step resolved for them.

While the Artboard tool is in use, a command chosen from any menu or the
command palette moves the tool's selection as the Artboards panel does:
artboard.new makes the new artboard active, artboard.duplicate selects the
copies, and artboard.delete and artboard.deleteEmpty make a remaining
artboard active.
2026-11-07 08:37:44 +07:00