Commit Graph
3742 Commits
Author SHA1 Message Date
dotCooCoo 1c4894b2f7 Brushes: Pattern Brush Options' OK leaves corner tiles that were not set
OK used to save None for a corner tile the dialog showed as None because
the brush had no art or source for it, so that corner stopped drawing the
other kind of corner's art once the brush got some. It also copied the
art a corner borrows from the other kind of corner when that corner kept
its art. Now OK saves a corner tile's choice only when it was set from its
menu or with `ui.dialog.set`, or when the art it borrows goes away.
2026-10-29 11:22:56 +07:00
dotCooCoo d4a30cc084 Brushes: Pattern Brush Options' corner tiles can be None or made from the side tile
Pattern Brush Options has a menu below the Outer Corner Tile and the Inner
Corner Tile. Each can be the brush's own corner art (Original), None, or one
of four tiles made from the side tile at each corner of the path:

- Auto-Centered stretches one side tile around the corner, centered on it.
- Auto-Between bends a side tile on each side into the corner, the two
  meeting on the corner's diagonal.
- Auto-Sliced lays a side tile on each side past the corner and masks it
  at the diagonal, so the two meet like a mitered frame.
- Auto-Overlap lays a side tile on each side past the corner, overlapping.

The tiles are straight at the corner and follow the path towards the side
tiles, so they meet a curve that runs into the corner. A side tile taller
than the corner can reach is stretched along until the two sides meet. On a
short path, a corner's tile is squeezed along so that it covers at most the
path up to an open path's end and half way to the next corner, and its two sides still meet on the
corner's diagonal.

The definition stores the choice as `outer_source` and `inner_source`
(`none`, `autoCentered`, `autoBetween`, `autoSliced`, `autoOverlap`), which
`brush.options` sets. Corner art wins over a source. A brush saved without
a source draws as before: a kind of corner without art takes the other
kind's art. None leaves that kind of corner empty.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:22:56 +07:00
dotCooCoo 3ea2bba6e3 Blend: Escape drops the Blend tool's first pick
After a click picked the first object with the Blend tool, Escape did
nothing: the pick stayed, and the next click on another object blended
the two. After a blend was made, Escape did not stop the next click
adding to it either.

Escape now drops the object picked first and stops the next click
adding to the blend just made, as a click on empty canvas does, so the
next click picks a new first object. The document and the selection
stay as they are, and no undo step is added.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:22:56 +07:00
bflatastic 1924ddd1dd Properties: one filter leaves out Lock and Isolate Group while they're off (keeps show() within the cognitive complexity lint #4299 added) 2026-10-29 11:22:51 +07:00
dotCooCoo 7d04096840 Transparency: Make Opacity Mask is off for a targeted layer in isolation mode 2026-10-29 11:20:08 +07:00
dotCooCoo dfe201dec3 Transparency: isolation mode and opacity mask editing can't be entered from each other
Enter Isolation Mode worked while an opacity mask was edited, and Edit
Opacity Mask and View Opacity Mask worked in isolation mode, so each
mode replaced the other's isolated art. In the reference app, isolation
mode can't be entered while a mask is edited, and mask editing can't be
entered in isolation mode.

object.isolate and perspective.editText, which shows type in isolation
mode, are now off while an opacity mask is edited, and a call returns
an error that ends "stop editing the opacity mask first". In isolation
mode transparency.editOpacityMask and transparency.viewOpacityMask are
off, and transparency.makeOpacityMask refuses one target, an object or
a layer, which it would take into mask editing; a call returns an error
that ends "exit isolation mode first". Make Opacity Mask with two
objects still masks one with the other. Pattern editing mode does not
count as isolation mode for these commands.

Object > Perspective > Edit Text, the canvas context menu and the
Transparency panel's menu and Make Mask button follow the commands'
enablement. A click or Alt-click on the mask thumbnail in isolation
mode shows the error and does not edit the mask. While a mask is
edited, the Contextual Task Bar leaves out Isolate, and the Properties
panel's Quick Actions leave out Isolate Group.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:20:08 +07:00
dotCooCoo e251bbf159 Contextual Task Bar: the Fill and Stroke chips show the proxies' paints
The chips drew the first selected object's fill and stroke. They now show
the paints the Fill and Stroke proxies show, a group's painted art
included, and a "?" when the selected objects' paints differ. A click on
either chip shows the Swatches panel through `window.panel.activate`, so
it also appears when the panel is floating, closed or in an expanded dock
column; an open flyout stays open. The progress doc describes the
reference app's bar in its own words.
2026-10-29 11:20:08 +07:00
dotCooCoo 93bbe36bc9 Contextual Task Bar: a Stroke chip beside Fill picks the stroke color
The task bar's Fill chip had no Stroke counterpart. A Stroke chip now sits
beside it, drawn as a ring with the selected object's stroke paint, as the
Control bar's Stroke chip is. A click on it brings the Stroke proxy forward
and opens the Swatches panel, as a click on the Fill chip does for the
Fill proxy. The chip is left out with the Artboard tool, as Fill is.

The bar's width estimate, which centers the bar under the selection, grows
by the chip's width. The drag test's first square moves 20 pt right so its
bar still starts inside the canvas.
2026-10-29 11:20:08 +07:00
bflatastic 0362ae5453 Object: import choice in object/arrange.rs (#4299 made the imports explicit) 2026-10-29 11:20:07 +07:00
dotCooCoo 02f2dd8c5a Object: Unlock All and Show All with Shift+Alt act only within the selected object's group
Squashed from #4439 onto main.
2026-10-29 11:18:38 +07:00
dotCooCoo a8c166a957 Selection tool: Escape cancels a move, scale, rotation, marquee or widget drag
Escape during a Selection tool move or Alt-drag copy, a bounding-box
scale or rotation, a marquee, or a drag of a corner widget, a pie widget
or the brackets of type on a path did nothing, and the release then
applied the drag. Only a ruler guide, mirror axis or grid repeat drag
and the loaded text took Escape.

Escape in any of these drags now cancels it, the way Direct Selection
cancels its drags: the art returns to its state at the press, an
Alt-drag leaves no copy, no undo step is added, and the release does
nothing.
Outside a drag Escape works as before.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:18:38 +07:00
dotCooCoo bbf01223cb Snap to Glyph: the x-height guide stays on the glyphs' x-height
Character Rotation turned a glyph's baseline shift and scaling with the glyph, and the Skew path effect slants a glyph's height away from the path's normal, so the x-height guide moved off the glyphs. Each positioned glyph now carries the transform that removes its Character Rotation (PositionedGlyph::unturned), and the guide goes through the glyph's x-height point placed without it, measured across the baseline. Snap to Glyph also takes Smart Guides being on in the tools, as View › Snap to Glyph already does in the engine.
2026-10-29 11:18:38 +07:00
dotCooCoo f87b8af62b Snap to Glyph: points land on glyph anchors and line up with x-height, character widths and angles
With View › Snap to Glyph and Smart Guides on, a point drawn near a single
line of live type now also lands on the anchor points of its glyphs and lines
up with its x-height (raised or lowered by Baseline Shift) and with the edges
of each character's advance width along the baseline (proximity guides). In
rotated type the baseline, x-height and proximity guides run at the type's
angle, and type of one glyph also has angular guides along the glyph's slanted
straight segments; a segment that Shift keeps to its angle slides onto them.
A paragraph shows glyph bounds only, so its baselines are no longer guides.

FontFace::x_height gives a face's x-height in font units.
2026-10-29 11:18:38 +07:00
bflatastic b97098367a Render: import the shape-mask helpers in renderer/nodes.rs and blobs.rs (#4299 made the imports explicit) 2026-10-29 11:18:37 +07:00
bflatastic 103ec3050f Envelopes: import Color and PreserveShape in live/expand.rs (#4299 made the imports explicit) 2026-10-29 11:16:30 +07:00
dotCooCoo acc5e53c91 Envelopes: a pixelated CMYK image with its own grid mask is written without /Interpolate
PDF export writes a CMYK image whose opacity mask is a gray image on its
own pixel grid with the mask as the image's soft mask. That path always
asked viewers to smooth the image, so a pixelated piece of an envelope
with Anti-Alias off was still written with /Interpolate when it had such
a mask. It now takes the pixelated flag as images without a mask do, and
caches the smoothed and pixelated versions apart.
2026-10-29 11:07:58 +07:00
dotCooCoo ab69d8f6d9 Envelopes: Anti-Alias and Preserve Shape Using change how distorted images draw
Envelope Options stored Anti-Alias and Preserve Shape Using, but nothing that
draws an image read them: a distorted image was always smoothed and always
cut into clip groups.

- With Anti-Alias off, each piece of a distorted image is marked pixelated
  (saved as `pixelated`). The renderer draws it with nearest-neighbor
  sampling and without mips, SVG export writes `image-rendering: pixelated`,
  and PDF export writes it without /Interpolate.
- With Preserve Shape Using Transparency, each piece is the image with a
  white opacity mask in the piece's shape instead of a clip group. The
  renderer paints an image whose mask is one plain white path straight into
  that path, as it does for an image in a clip group, when the path lies
  inside the image's frame.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:07:57 +07:00
dotCooCoo 4651e80973 Star tool: Ctrl/Cmd holds the inner radius the star shows when the key goes down
The reference app keeps the inner radius of a star constant when Ctrl
or Command is held after the drag starts. VectorCraft's Star tool took
the held radius from the first drag event that carried the key, using
that event's outer radius. The canvas sends a drag only once the
pointer moves, so a key pressed with the pointer still scaled the
inner radius with the next move before holding it.

Ctrl/Cmd now holds the inner radius the star showed at the last drag
position before the key went down, with Alt's straight shoulders when
Alt was held. A new press and Escape clear the held radius. Before,
after a drag canceled with Escape, the next star took the canceled
star's inner radius when Ctrl/Cmd went down before that star's first
drag event.

Docs: docs/target-app-parity.md.
2026-10-29 11:07:53 +07:00
dotCooCoo 25e8836af9 Tools: Escape cancels an Anchor Point, Mesh, Shape Builder or Measure drag
Escape did not cancel a drag with the Anchor Point, Mesh, Shape Builder
and Measure tools, or a Direct Selection drag of a mesh point, a corner
widget or the brackets of type on a path: the drag went on until the
mouse was released. Escape cancels every other Direct Selection drag
and a drag with the Free Transform, Rotate, Reflect, Scale, Shear and
Gradient tools.

Escape now cancels an Anchor Point drag, a Mesh tool drag of a mesh
point or handle, and a Direct Selection drag of a mesh point or handle,
a corner widget or the brackets of type on a path. The art goes back to
how the press found it, no undo step is added, and the rest of the drag
and the release send nothing. Escape during a Shape Builder drag ends
it, so the release merges and deletes nothing; the tool takes Escape
only during the drag. Escape during a Measure tool drag ends it and
brings back the measurement shown before the press. Escape after an
Anchor Point or Direct Selection press that has not moved yet drops the
press. Otherwise, outside a drag, Escape does what it did before.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 11:07:46 +07:00
Don Omondi 92904ebcab fix(doc): forget removed objects and their layers' guides (#3575)
drop_beyond_canvas removed objects but left what names them: saved
selections, Asset Export entries and variable bindings kept their ids,
and layer guides of a removed sublayer named a layer that was gone. It now
runs tidy_saved_selections, prune_assets and prune_variable_bindings when
it removes objects, as edits do, and removes the layer guides of the
layers it removed.
2026-10-29 11:07:40 +07:00
Don Omondi 8f5b6b81b0 fix(doc): look into containers for objects beyond the canvas (#3575)
A layer's bounds leave out its hidden objects and a clipping container's
are its clipping path's, so objects beyond the canvas inside them were
kept. Layers and groups are now looked into object by object.

A clipping path beyond the canvas goes with the objects it clips, so no
other object becomes the clip; a top-level clipping layer is emptied and
stops clipping. A removed artboard's ruler origin goes and its id is
retired, as deleting it does.
2026-10-29 11:07:40 +07:00
Don Omondi 5b18da25a8 fix(format): remove what a native file has beyond the canvas (#3575)
Edits refuse to leave anything beyond MAX_COORD (doc_sane checks the
artboards, the guides, the ruler origin and the objects an edit touches),
but the native loader didn't check them. A file with one object, artboard,
guide or ruler origin out there opened without a warning, and then every
move failed with "result would exceed the canvas", in-range objects
included; saving kept it that way.

Document::drop_beyond_canvas removes objects whose geometric bounds aren't
finite or reach past MAX_COORD (a layer or non-clipping group keeps its
children within it), artboards with their artboard guides, and guides out
there, and puts the ruler origin back at the origin. load_file runs it
after the depth limit and reports what it did with the other load notes; a
file whose artboards all lie beyond the canvas is refused.
2026-10-29 11:07:40 +07:00
Don Omondi d60b6bf883 fix(doc): count the values buffered before an object's type (#3587)
NodeKind's reader buffers the fields that come before `type` as
serde_json::Values. VectorCraft writes `type` first, but a crafted file
can put a long array there: a group whose children held 50M numbers before
its type (a 96 KB gzip) peaked at 1.6 GB, and the cost grows with the
array up to MAX_JSON, before any object in it is counted.

The buffer is read through a counting visitor: each JSON value counts
against MAX_BUFFERED_VALUES (32M, 4M on wasm32) as it is read, and values
replayed from the buffer aren't counted twice. load_file reads within it;
the same file is now refused at a 1.1 GB peak.
2026-10-29 11:07:37 +07:00
Don Omondi eeae8c9136 fix(format): read a native document within an object budget (#3587)
Opening a native document now reads at most MAX_READ_NODES objects (4M,
512K on wasm32) and refuses the file past that with a message naming the
budget. Paths already read within MAX_READ_ANCHORS and the web build's JSON
cap is 256 MB, but the number of objects had no bound: a gzip'd document of
empty groups costs about 270 bytes per object once read against some 50 in
its JSON, so 10M groups in a 24 MB file took 2.7 GB to open.

NodeWire's id is read through counted_id, so each object counts once as it
is read. load_file reads through a private load_file_within so a test can
exercise the budget without a 4M-object document.
2026-10-29 11:07:37 +07:00
dotCooCoo fea65ffb3d i18n: each catalog names leading with one term
In Turkish, Brazilian Portuguese, Indonesian, Bulgarian and Vietnamese
the Character panel's Leading field and the rows that name leading used
different words: Auto Leading in the Justification dialog, the Type
preferences' Size/Leading increment, and the Paragraph panel menu's
Top-to-Top and Bottom-to-Bottom Leading.
Turkish had four words, and its Leading field read "Lider", a leader.

Turkish now uses "Satır Aralığı", its Auto Leading word, in every
leading row, the Leading field included. In pt-br, id, bg and vi the
other rows now use the Leading field's word ("Entrelinhamento",
"Leading", "Междуредие", "Giãn dòng"). A new test checks that every
word of the Leading field appears in the four other rows of each
catalog.
2026-10-29 10:13:23 +07:00
el-j 813e30be69 i18n: fold Polish ł and additional Latin diacritics in name_sort_key 2026-10-29 10:13:23 +07:00
el-j ad3dac0031 Fix #4143: Window menu lists panel items alphabetically by localized name 2026-10-29 10:13:23 +07:00
dotCooCoo cf9087bb87 Keyboard Shortcuts: Go to Conflict is not offered for a row that leaves the list
When a recorded chord takes the shortcut from a command that needs a value, that command
leaves the list. The dialog now clears its Go to Conflict target as well as its recording
and selection.
2026-10-29 10:13:23 +07:00
dotCooCoo d5f77e18a8 Keyboard Shortcuts: a row that leaves the list stops recording
When Clear, Use Default, Reset to Defaults or another set takes the
shortcut from a command that needs a value, its row leaves the list. A
recording started on that row now stops and the row is no longer
selected, so the next key press does not give the hidden command a
shortcut or take one from another command.
2026-10-29 10:13:23 +07:00
dotCooCoo cd4511c548 Keyboard Shortcuts: an empty saved shortcut does not list a command that needs a value
A command that needs a value stayed listed when a saved or imported set held an empty shortcut
for it, showing no key. It is now listed only while its saved shortcut is not empty.
2026-10-29 10:13:23 +07:00
dotCooCoo 7b36442f41 Keyboard Shortcuts: Distribute Spacing stays listed, its axis is optional
object.distributeSpacing already spaces horizontally without an axis; its params doc now says so, so the editor keeps listing it.
2026-10-29 10:13:23 +07:00
dotCooCoo 610e4523e9 Keyboard Shortcuts: commands that need a value are not listed unless given a shortcut
Edit › Keyboard Shortcuts listed every engine command with a menu path,
whatever its params. A shortcut runs a command with no params, so a key
given to File › Document Setup › Units ran document.setUnits with {}
and failed with "unknown units".

The dialog and shortcuts.list now leave out a command with a menu path
and no default shortcut when no item of the menu bar runs it without
params, it opens no dialog when run without them, and its params doc
names a required field. This leaves out 38 commands, among them the
Graphic Styles, Swatches and character and paragraph style commands
that take a name. A command whose shortcut the user has changed is
still listed, where the key can be changed or cleared, and a saved key
still runs and conflicts as before. Every command with a default
shortcut stays listed.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 10:13:23 +07:00
bflatastic c591331d65 Object: import the isolation predicates in menucmds.rs and object/commands.rs (#4299 made the imports explicit) 2026-10-29 10:13:22 +07:00
dotCooCoo f715b9a1a5 Object: Lock, Hide, Unlock All and Show All are off in an object isolated inside pattern or opacity mask editing
Pattern editing mode and opacity mask editing allow these commands only
while their own layer is the isolated one.
2026-10-29 10:12:16 +07:00
dotCooCoo a6a850868c Object: Lock, Hide, Unlock All and Show All are off in isolation mode
The reference app disables its Lock, Hide and Show All menus in
isolation mode. In VectorCraft every item of Object › Lock and
Object › Hide, Unlock All and Show All worked there.

In isolation mode, object.lock, object.lock.above,
object.lock.otherLayers, object.hide, object.hide.above,
object.hide.otherLayers, object.unlockAll and object.showAll are now
off. Their menu items are greyed out, and a call returns an error
that ends "exit isolation mode first". The Contextual Task Bar's
Lock button is greyed out too, and the Properties panel's Quick
Actions leave out Lock. Pattern editing mode and opacity mask editing do not count as
isolation mode for these commands. The check is outside_isolation in
cmd.rs, with has_selection_outside_isolation beside it.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 10:12:16 +07:00
dotCooCoo 38bb903495 Distort tools: Escape cancels a Puppet Warp, perspective or Liquify drag
Escape did not cancel a drag with the Puppet Warp, Perspective Grid,
Perspective Selection and Liquify tools: the drag went on until the
mouse was released. Escape cancels a drag with the Direct Selection,
Gradient and Artboard tools.

Escape now cancels a Puppet Warp pin drag or turn, a Perspective Grid
handle or plane widget drag, a Perspective Selection move, resize or
plane widget drag, and a Liquify stroke. The art and the grid go back
to how they were when the drag began, the undo history does not
change, and the rest of the drag and the release send nothing. Puppet
Warp selects again the pins that were selected before the press.
During a Liquify Alt-drag that sizes the brush, Escape puts the brush
size back; the tool claims Escape only then. Escape during a
Perspective Selection press that has not moved past the drag threshold
ends the press, so moving on does not start a move. Otherwise, outside
a drag, Escape does what it did before.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 10:12:16 +07:00
dotCooCoo 5ed2fec4fd Text styles: the panel menu's Duplicate highlights the copy and Delete clears the highlight
Squashed from #4419 onto main.
2026-10-29 10:12:16 +07:00
dotCooCoo 02c9a3959d Arc tool: the quarter-ellipse sector holds at Slope 100
A closed arc at a lower slope encloses less than the quarter ellipse. The
doc comment on arc and the Draw arcs entry now name Slope 100.
2026-10-29 10:12:16 +07:00
dotCooCoo 758a50adfe Arc tool: a closed arc with Base Along Y draws the X-axis sector mirrored
A closed arc from Arc Segment Tool Options, an Arc tool drag or
shape.arc with lengthX and lengthY closed through the box's bottom-left
corner with Base Along X, which makes a convex arc a quarter-ellipse
sector. With Base Along Y it closed through the corner a convex arc
bulges toward, so it drew the thin sliver between the arc and that
corner instead.

Such a closed arc now closes through the corner across from the one a
convex arc bulges toward with either base, so the Y-axis arc, open or
closed, convex or concave, is the X-axis arc mirrored top to bottom.
Open arcs and arcs with Base Along X are unchanged. The two-point
shape.arc form, whose params list no baseAlong, now closes through
(x1, y2) whatever baseAlong says.

Docs: docs/target-app-parity.md.
2026-10-29 10:12:16 +07:00
dotCooCoo e28c08a8b8 Tabs: the position field's label is translated
The Tabs panel drew its X: and Y: label untranslated, so French showed
"X:" in place of its catalog's "X :" with a no-break space, and Chinese
showed an ASCII colon. The label now goes through the catalogs, and the
Polish and Vietnamese catalogs get their X: and Y: rows.
2026-10-29 10:12:16 +07:00
bflatastic 0dfae85814 Blob Brush: import Cap and Join in freehand.rs (#4299 made the imports explicit) 2026-10-29 10:12:15 +07:00
dotCooCoo 7f7067bcba Blob Brush: Tool Options set its Fidelity
Squashed from #4414 onto main.
2026-10-29 10:11:42 +07:00
dotCooCoo b6f916d211 Character and Paragraph Styles: a style dropped on New is copied, and one dropped on Delete is deleted
The Character Styles and Paragraph Styles panels took only clicks on
their rows. The reference app copies a style dragged onto Create New
Style and deletes a style dragged onto Delete.

A style row can now be dragged. Dropped on Create New Style, the style
is copied with charStyle.duplicate or paraStyle.duplicate, as the panel
menu's Duplicate does, and the panel highlights the copy. Dropped on
Delete, the style is deleted with charStyle.delete or paraStyle.delete,
as the Delete button deletes the highlighted style, and Delete accepts
a dragged style even when the highlighted one is Normal or none. Normal
is not deleted. Each button is outlined while a row it accepts is held
over it, and each drop is one undo step.

Docs: docs/target-app-parity.md.
2026-10-29 10:11:41 +07:00
bflatastic a76ee53709 Blob Brush: import Document and Node in freehand.rs (#4299 made the imports explicit) 2026-10-29 10:11:38 +07:00
dotCooCoo aefbe6793b Tests: a Blob Brush stroke merges across a matching path it does not touch
The merge test covered adjacent matches and a path of another color
between two matches. It now also covers a matching path the stroke
does not touch, between two it touches: both touched paths merge into
the upper one and the untouched path is left as it was.
2026-10-29 10:10:59 +07:00
dotCooCoo 3593dd4b0d Blob Brush: a stroke merges with the topmost matching path and its neighbors in its group or layer
A Blob Brush stroke used to merge with every closed path of its fill
color and no stroke that it touched, anywhere in the document, across
layers and groups and past objects of other kinds. It now merges with
the topmost such path it touches, and with the other touched matching
paths in the same group or layer that are next to that path in
stacking order, with no other kind of object between them. Merge only
with selection still limits the candidates to selected paths. The
merged path keeps the topmost path's id and place.
2026-10-29 10:10:59 +07:00
dotCooCoo 16d679fde7 Layers: Cmd/Ctrl+Alt-click places a new layer below the current one and a new sublayer at the bottom
The reference app places a new layer below the selected layer with
Ctrl+Alt-click (Command+Option-click) on the New Layer button, and a
new sublayer at the bottom of the layer list with the same click on
the New Sublayer button. In VectorCraft that click opened Layer
Options, and New Layer and New Sublayer always added the layer above
the current layer and the sublayer on top of the layer's contents.

layer.new takes below, which adds the layer directly below the current
layer at its level, and layer.newSublayer takes bottom, which adds the
sublayer under the parent's contents. In a clipping layer the new
sublayer goes above the clipping path. Each is one undo step, and
below is refused together with top. A Cmd/Ctrl+Alt-click on the
Layers panel's New Layer and New Sublayer buttons runs them. A plain
click, an Alt-click and a Cmd/Ctrl-click work as before.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-29 10:10:59 +07:00
bflatastic 12b7e0fc9c AI import: import ImageBlob and PatternDef in read/document.rs (#4299 made the imports explicit) 2026-10-29 10:10:59 +07:00
dotCooCoo 48602ade23 Fix #4036: a pattern defined only in a library's editing data keeps its swatch
A .ai swatch library without layers comes in as its PDF part with the
swatches of its editing data's palette. A pattern swatch was kept only
when the PDF part defined its pattern, and a pattern library's PDF part
usually defines none, so its pattern swatches were dropped.

ai::palette now gives pattern swatches for the patterns the editing
data defines too, and returns the definitions the PDF part lacks that a
palette swatch paints with, with the images their art shows. The
document gets them with new object ids.
2026-10-29 10:10:58 +07:00
dotCooCoo 754051c26a Fix #4036: a .ai swatch library without layers loads its palette, not the default swatches
A .ai file whose editing data has no layers, as swatch libraries are
saved, opens from its PDF part, and the PDF part has the default
swatches. layered_ai applied the editing data's palette only when it
built the document from the layers, so Other Library… listed the
default swatches for such a library, and File › Open gave the file the
default swatches as well. A .ai whose layers can't be used (an image
too large for the editing data's reader, or a read error) lost its
palette the same way, and so did an EPS that opens as its page.

The reader now reads the palette on its own (ai::palette, which shares
palette_swatches with the layer reader), and its swatches and color
groups replace the default swatches in those three cases. Spot and
pattern swatches of the PDF part's art that the palette does not have
follow the palette's swatches.

Fixes #4100. Part of #3349.
2026-10-29 10:10:58 +07:00