object.expand.info reported a gradient (and a fill) to expand for any
type whose characters have a gradient fill. Expand outlines type first,
and each outline then holds the run's gradient on its own box, so a
gradient that repeats or reflects in more than 256 runs across every
outline stays a gradient: the dialog enabled options that changed
nothing.
- The query now measures the paths type.createOutlines makes of the
glyphs, ruby glyphs and underline and strikethrough bars, each with
its run's paint, and reports a gradient when one of them expands. It
lays the text out only when a run has a gradient fill, stops at the
first outline that expands, and does not change the document. Type in
symbol art, blends and repeats goes through the same check.
- docs/mcp.md says when gradient is false; docs/target-app-parity.md
gets an evidence line.
Without a plug-in store, plugin.install read the process grant once
for its later check and again for its approval. A grant another session
recorded between the two reads approved the module; when that session
revoked it before the install, the check saw the grant it first read
and the install recorded the revoked grant again. The approval now comes
from the first read.
plugin.install published the plug-in before it wrote the module and its
grant to the Plug-ins folder. A plugin.remove in another session in
between removed the plug-in, its file and its grant, and the install then
wrote both back, so the plug-in loaded with its grant at the next launch.
A session attaching the folder in between loaded the old version, which
kept running while the folder held the new one. plugin.remove, loads of
the stored plug-ins and loads of a plug-ins folder now wait until an
install has written the folder, and an install waits for them.
Docs: docs/plugins.md, docs/target-app-parity.md.
Object > Path > Remove Anchor Points removed each selected anchor with
its own Vec::remove, shifting the rest of the subpath every time, so
removing k anchors from an n-anchor subpath took O(k*n) moves.
- pathops: remove_anchors removes a subpath's anchors the last first,
reading each anchor's current neighbors, handles, length and closed
state as removing them one at a time does, and compacts the anchor
list once. remove_anchor and remove_anchors share one refit, so the
result is bit for bit the same.
- engine: the command groups the selected anchors by subpath, works out
the next selected anchor with each subpath reading only its own
selected anchors, and takes emptied paths out of the selection
together (Selection::remove_all).
- Tests compare remove_anchors bit for bit with remove_anchor over 600
generated subpaths and focused cases, count the anchors moved (2001
anchors, every other one removed: at most one move per anchor left,
500,500 one at a time), and compare the next-anchor selection and
the command on many paths and subpaths with removing one at a time.
$.fileName gave the file being run, so a function defined in a file
that #include inserted, in a file $.evalFile ran, or in an earlier run
of a named engine reported the calling file instead of its own.
- vectorcraft_js: HostItem::SiteGetter gives a getter the CallSite
(source tag, line, column) of the code that reads it.
- $.fileName maps that place through the run's source maps, or the
maps a named engine kept, to the file the code is in. It is empty in
code from a script given as source and in kept code whose map was
dropped. Code that eval or Function compiled still gets the file
being run.
Docs: docs/mcp.md, docs/target-app-parity.md.
- With art, the bar is 10 pt tall and the Edit tab leaves out the gaps
above the wheel and above the sliders. The dialog is as tall on the
Edit tab as on the Assign tab, and all of it but the color chips fits
a 1024 by 576 screen.
- recolor.weigh counts a use for the first row that is not excluded
when an excluded row lists the same color first.
plugin.install wrote the module and its grant to the Plug-ins folder,
attached the plug-in's settings and cache and turned a disabled plug-in
on before the registry's last approval check. When another session
revoked the grant, or recorded one for other bytes, after the command
recorded its own grant and before that check, the install was refused
but left the new module and grant in the folder for the next launch.
A plug-in another session installed under the same id between the
command's checks and the install took no part in the decision, so the
install replaced it without `replace: true`.
- registry::install_checked records the grant (or forgets it) and
installs the plug-in as one step, after a caller check that sees the
plug-in installed under the id and its grant; a refusal changes
nothing.
- plugin.install commits through it before any store write, refusing
with the existing messages when another plug-in now needs `replace`
or the grant its approval came from changed. The store writes, the
put-back on a failed write and the disabled-list update now run after
the commit; their logic is unchanged.
- The grant call-site audit also counts registry::install_checked.
Docs: docs/plugins.md, docs/target-app-parity.md.
- recolor.weigh drops another row's entry for a color that moved, so
recolor.apply recolors it with the row it moved to and the shares
describe the recolor. A color that the first color of another row
with a new color also lists stays where it is.
- Linked swatch colors are grouped through a map, so a row with many
unlinked colors no longer compares each one with every group.
- The Prominent Colors bar reads the shares again when a row gains or
loses a new color, since that changes which row takes a shared color.
Part of #2224.
A Smooth tool drag only refitted the anchors in its reach within
Fidelity, so a wobble wider than Fidelity stayed, and a second drag over
the same place changed nothing. Repeated refits also drifted away from
the path, and a drag over a whole closed path left a corner at its first
anchor.
- path.smoothRegion now smooths the stretch in reach along its length
with a Gaussian whose spread is a third of the reach, then refits it
within Fidelity. The run's outer end points and handles stay fixed.
- Each refit stays within twice the distance the smoothing moved the
path (at least a tenth of Fidelity), so repeated drags do not drift
(SimplifyOptions::smooth_moved_only).
- A whole closed path is smoothed across its start as a loop, so a
bump at the first anchor is smoothed like any other and no corner is
left. Object > Path > Smooth smooths a closed path across its start
the same way.
A group's or layer's geometry effects now run after its members' own:
the geometry effects of each visible path and compound path in it and
of their fills and strokes, and a nested group's geometry and
Pathfinder effects, are applied first, at any depth, and the group's
effects reshape the result. A symbol instance's geometry effects run
after those of its symbol's members in the same way. Before, the group
reshaped the members' outlines and their own effects ran afterwards:
a 100 pt square with its own 10 pt Offset Path in a group with a 50%
Transform drew 70 pt wide, and it now draws 60 pt wide. Existing
documents with such groups draw differently.
The canvas, export (including groups that keep their type), Expand
Appearance and the art that Pathfinder operations and compound shapes
take from such a group use the same order. The reply of
effect.expandAppearance names such a group and no longer names the
members whose effects the group's art already holds.
The renderer now drops the art it keeps for a group when a plug-in is
installed or removed and a member of the group has a plug-in effect.
Docs: docs/target-app-parity.md. Not yet: the own geometry effects of
images, symbol instances, paths with a linked opacity mask or a 3D
effect, and nested groups with a 3D effect in a group still run after
the group's.
With Real-time Drawing off, a command that ends a drag first (Undo, Redo,
Clear History, a plug-in command and the others) applies the drag's
deferred preview, but dropped the warning that preview gave, which
releasing the drag shows in the status bar.
- The applied preview's warning is now kept while a top-level command or
the tool's actions run. A command's result carries it as
`previewWarning`, and as `warning` when the command has no warning of
its own, so the status bar shows it.
- Typing kept as its own step when a tool's drag begins, or when Cmd
lends the Selection tool, shows the warning in the status bar.
- The Cmd-lend path no longer ignores an error from keeping the typing.
Docs: docs/mcp.md (undo/redo row), docs/target-app-parity.md.
A top-object envelope gives each piece of a side a share of the content
in proportion to its control polygon's length. When anchors split a
curve without changing its shape, the pieces' polygons don't divide the
side where the curve's own parameter does, so the envelope bent its
contents differently although its outline was the same: Add Anchor
Points on a top object moved the art, and a top object whose .ai mesh
saves each side's curve split into thirds drew its type up to about
half a point off its drawn art, which follows the whole curve's
parameter.
Coons::from_top_object now checks whether a side's pieces, from corner
to corner, are all pieces of one cubic. The pieces' spans of that
curve's parameter come from the handle lengths either side of each
split, the curve from the outer ends and handles, and every piece must
lie within 0.02 pt of the curve's piece over its span (each control
point). Such a side runs along that curve, by its parameter. Any other
side keeps the polygon shares, including a side only partly made of one
curve and a smooth side drawn in many pieces that isn't one curve as a
whole. Corners found from the outline can still move when anchors are
added, as before. Gradient meshes made from a path are unchanged.
Existing top-object envelopes with such sides redraw.
a_split_curve_moves_the_top_object_share_to_its_new_anchor, which
pinned the earlier behaviour, becomes a_split_curve_keeps_the_top_object_patch.
Tests: a_split_curve_keeps_the_top_object_patch,
a_top_object_joins_a_split_curve_saved_with_rounded_coordinates,
a_top_object_keeps_smoothly_joined_curves_apart,
a_top_object_side_partly_of_one_curve_keeps_the_polygon_shares and
a_top_object_side_of_many_arcs_stays_on_them (vectorcraft-doc),
envelope_top_object_keeps_its_look_when_anchors_are_added
(vectorcraft-engine) and
a_top_object_whose_top_is_one_curve_split_in_thirds_opens_live
(vectorcraft-eps).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A right-click on a character style in the Libraries panel now opens Apply
Character Style, Add to Character Styles, Move to Group, Rename… and
Delete. Apply is enabled only with type selected. Opening the menu applies
nothing, and the paragraph style menu is unchanged.
- library.addToCharacterStyles adds the style to the document's character
styles without applying it, so no type needs to be selected. A document
style with the same name and attributes is reused with no undo step, and
a name taken by other attributes gets a number.
- library.renameItem renames a library item in place and keeps its group.
The name is trimmed and has 1 to 256 characters. Another item of the same
kind with that name is an error, the item's own name writes nothing, and
a rename is not a document undo step.
- library.use and library.addToCharacterStyles compare a library style
with the document's in the form the document stores it in. A whole
number size from a library file no longer adds a numbered copy.
- A character style color linked to a swatch that is not a global or spot
swatch of the document keeps the color it shows without the link.
Before, adding or applying such a style failed with "no swatch".
Help page: https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/add-and-use-text-and-character-styles-in-libraries.html
path.smoothRegion replaced the anchors of each path it smoothed but left
the selection alone, so anchor and segment refs selected with Direct
Selection still named the old indices: after a smooth that removed
anchors, a ref could point past the end of the path or at another
anchor. The edit now drops those refs for each path it changes, so the
path stays selected whole. Selected paths the drag does not change keep
their anchors, and undo restores the earlier selection.
With art selected, the Edit tab shows a Prominent Colors bar under the
wheel: a part for each row in its new color, as wide as the row's share
of the art, the largest first. Dragging the edge between two parts moves
width from one to the other; when the drag ends, the rows' current
colors move toward the new widths and the art previews the result. OK
keeps the recolor as one undo step.
- recolor.weigh {map, weights?, ids?} returns each row's share of the
uses of the art's colors and, with weights, the rows with their
current colors moved: one color at a time between rows that have a
new color, while a move brings the shares nearer the weights, the
color nearest in Lab to the row it goes to first. A row keeps its
first color, the tints of one global swatch move together, colors
that stay keep their order, and the command changes nothing. A use
counts for the row recolor.apply recolors it with, and the work is
bounded at 50,000,000 color-by-row comparisons.
- The New Color Group menu beside the link button has Save All Colors
(the new colors in row order) and Save Prominent Colors (the bar's
colors in its order); each saves a color group as its own undo step.
- The dialog's weights field drives the bar from ui.dialog.set. A new
Colors count drops it.
object.mesh.movePoint and object.envelope.setMeshPoint compare a handle
end with point plus handle, the form the caller sends, so a rounding
difference in (p + v) - p no longer turns an unchanged warp into a mesh
envelope with an undo step.
Adds a test with a half-transparent square that its own clip group,
its clipping layer, or two nested clipping paths together hide, so the
square no longer marks the DCS image under it.
A plain group holding just the image becomes its clip group again only
when the image takes its DCS file's clipping path. When Placement Options
clip the image, it goes into a new clip group as before, so a group the
user made around the image alone stays a plain group.
The check that decides whether a linked EPS file is DCS now gets the
file's size. When the bytes read hold the whole file, it reads them as a
full read does. A binary EPS header whose PostScript or TIFF preview ends
past the end of the file names no plates, so such a file keeps its
preview with lowResProxyEps on instead of going missing.
A selected warp envelope now shows the points of its surface on a 4x4
grid (8x8 or 16x16 where 4x4 strays more than 0.1% of the warp's
diagonal from it). object.mesh.movePoint, movePoints, addLine,
deletePoint and object.envelope.setMeshPoint accept a warp: the first
edit turns it into a mesh envelope on that grid and applies the edit as
one undo step, so Undo or Escape gives the warp back with its style and
bend. An edit that moves no point leaves the warp and adds no undo
step. A warp that no such grid follows (some version 0 maps) shows no
points and its edits fail without changing anything.
movePoints and setPointColor on a gradient mesh record no undo step
when they change no point.
When a linked file lost its clipping path, the clip group holding the
image stayed as a plain group of the image. When the file had a clipping
path again, the image went into a new clip group inside that plain group,
so each round nested one more group. The plain group holding just the
image now becomes its clip group again, in the layers and in symbol,
pattern, graph design and mask art.
With the lowResProxyEps preference on, opening a document kept the saved
preview of a linked DCS file, so the link showed as EPS without its
plates. Opening now reads the first 1 MiB of each such EPS file and reads
a DCS file in full. The whole file is read only when a binary EPS header
puts the PostScript past those bytes or the header comments run past
them. Other EPS files keep their preview.
After a rotation the Selection tool already shows and drags a bounding
box turned with the objects, but the Free Transform tool still used the
page-aligned box. Its handles sat on the wrong corners, a side handle
scaled along the page axes, and Perspective Distort and Free Distort
started from a box that did not match the art.
- The Free Transform tool keeps the selection's turned box for the
drag. Handles, the rotate zone and the move zone are read in its
frame; a handle scales along the selection's own sides from the
opposite handle (or the center with Alt) and snaps as the Selection
tool's does; Cmd/Ctrl on a side handle shears along that side; the
outline, handles and cursors are drawn on the turned box.
- Perspective Distort and Free Distort move the corners of the turned
box. `object.distort` takes `angle?` (degrees): `from`, or the
selection's bounds without it, is then a box in the frame turned by
that angle. Upright boxes send the same params as before.
- The Scale and Reflect tools, their dialogs and commands, and the
Transform panel's flips keep the page's axes.
Docs: docs/mcp.md, docs/target-app-parity.md.
Copy now remembers the whole chain of layers around each object, not only
the name of the nearest one. With Paste Remembers Layers on:
- In the same document, art goes back into the sublayer it came from while
that sublayer exists, even after it was moved, so two sublayers of the
same name in different layers stay apart. A deleted sublayer is made
again inside the layer that held it.
- In another document, the sublayer and the layers around it are matched
by name from the top level down; of repeated names, the topmost chain
that matches the most names wins, a shown and unlocked one first. A
missing sublayer is made inside its layer, and a missing layer on top
with the sublayer inside it, in the paste's undo step.
- When the matched layer, or a layer around it, is hidden or locked, the
paste fails with "the target layer is locked or hidden" and nothing is
pasted, instead of putting art in a hidden layer or making a second
layer of the same name.
Show DCS Transparency Interactions compared full object bounds, so a
transparent object over the part of a DCS image that its clipping path
hides marked the image. Bounds are now cut to the clipping paths of the
clip groups and clipping layers that hold each object.
The series fill and stroke and the label fill and stroke a graph keeps
for its next redraw are now in the paint traversal, so Delete Pattern
and canceling a new pattern make them no paint and a Pattern Options
rename renames them. Before, a later graph.setData drew the paint of a
missing pattern again. Global swatch edits reach the same paints.
The progress-doc line now says that only canceling a new pattern clears
its paints; canceling the edit of an existing pattern puts the
definition, and after a rename the old name, back.
A 9-slice guide can now be placed outside the symbol's art, by dragging
it past the art's edge in symbol editing mode, by symbol.options
nineSliceGuides, or by a Symbol Options field below 0% or above 100%.
A scaled instance then scales by the cells its art falls in.
- Each guide is a fraction from its edge in -16..16, and the two guides
of an axis keep their order (left + right < 1, top + bottom < 1).
- A guide past an edge cuts the art at that edge, so that side's outer
cell holds no art. With both guides of an axis past opposite edges,
the art scales as a whole along it. With both past the same edge, the
art keeps its size in the outer cell at the other edge.
- Symbol Options shows the guides with three decimals, and OK keeps a
field that was not changed at its exact value.
- Native files keep the guides exactly. SVG and .ai write such an
instance as its placed art, without guides.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
effect.apply, appearance.addEffect and effect.setParams checked an effect
parameter's type only when the effect has a dialog default for the key. Keys
that only the params text names were stored as given: an Outline Stroke with
`width: "10 pt"` drew at the stroke's own weight, and a Gaussian Blur with
`linear: 1` blurred in linear light.
- The earlier 3D `perspective`, `lightAzimuth` and `lightElevation`,
Scribble's `variation` and Outline Stroke's `width` must be numbers, and
Gaussian Blur's `linear` must be true or false. A test walks every effect
so a new key without a default needs a listed type or a check of its own.
- The `style` of Crop Marks must be `roman` or `japanese`, and `null` is
refused. Any other value drew as Roman.
- An installed plug-in effect's parameters are checked against its manifest
when the command runs, as plugin.run already checks the effect records a
filter returns.
- effect.setParams with `warpStyle` checks the parameters against the new
Warp style's effect, as it already did for a 3D effect's new
`threeDType`. It checked them against the old style.
- effect.setParams refuses a `visible` that is not true or false, such as
`1` or `"false"`, when called and when a step plays, and nothing changes.
Before, the value was ignored and the rest of the call applied. `null`
still leaves the visibility as it is.
A step of command.batch is refused the same way. A replayed journal or
action still plays a wrongly typed effect parameter, as it plays a key the
effect no longer takes.
Docs: docs/mcp.md, docs/target-app-parity.md.