A gradient placed with the Gradient tool stayed where it was when the
object's Transform or Free Distort effect moved, turned or scaled the
outline, so the shape slid across its own gradient.
The Transform effect now maps placed gradients by its own transform,
and each of its copies takes them as many times as it takes the
transform. Under Free Distort, each path's placed gradients move and
scale from the path's bounds before the effect to its bounds after it.
This change does not alter gradients fitted to the object.
The canvas, Expand Appearance and export, Pathfinder and compound
shapes, groups, a fill's or stroke's own effects, and Object › Expand
of a gradient fill apply the same rule. In a group, the own fills and
strokes of the group, of the groups inside it and of a symbol's art
follow it too, and images and symbol instances that a group's
Transform effect moves take the placed gradients of their own fills
and strokes along. A group whose path keeps its shape because the
effect would pass the geometry budget keeps its own gradients in
place.
This changes how existing documents draw: a gradient placed on a path,
or on an image's or symbol instance's own fill, inside a group with a
Transform effect now moves with it. Before, only the gradients of type
moved (#2214).
How gradients follow each effect is VectorCraft's own rule. The
Distort & Transform help page describes what the effects do to the
object:
https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/distort-and-transform-effects-in-illustrator.html
Saved corners from an .ai file stay on the same anchors when the
envelope is reflected, as they already did when it was moved or
rotated. A reflected outline runs counter-clockwise; the corners were
then mapped as if the outline had been drawn the other way round, no
longer fit it, and were found again from the outline.
On a tie between two corner anchors, or two nearest outline points,
the one met first along the outline in the order it was drawn wins,
for outlines drawn counter-clockwise too.
The docs state the corner rule in full: a handle 0.01 pt long or
shorter makes an anchor a corner whichever way it turns; otherwise its
tangent unit vectors must differ by 0.01 or more in x or y and turn
out of the outline. Content spreads evenly along a segment without
handles.
An anchor with a handle shorter than 0.01 pt counts as a corner of a top
object whichever way it turns; only an anchor whose tangents differ must
turn out of the outline. docs/mcp.md and the progress line in
docs/target-app-parity.md now say so.
A top-object envelope splits its outline into four sides at four corners
and maps the content onto the Coons patch of those sides. The sides
shared out the content by length, and the corners were the anchors or
curve points nearest the bounding box corners.
Each piece of a side between anchors and corners now takes a share of the
content's width or height in proportion to the length of its control
polygon, spread along the piece by its curve parameter. A straight
segment spreads it evenly by length. A rounded rectangle stays close to
the identity, and the content follows the curves of a side whose segments
differ in size.
Each corner is now the nearest anchor to its bounding box corner among
the sharp anchors that point out of the outline and lie within 47.5% of
the box's width and height from that corner. A handle shorter than
0.01 pt, or tangent unit vectors that differ by 0.01 or more in x or y,
makes an anchor sharp, and the first one along the outline wins a tie.
Without such an anchor, the corner is the outline's nearest point to the
box corner, moved onto the end anchor of its segment within 0.5% of the
segment's parameter. Envelopes opened from .ai files keep the corners the
file saves.
These are VectorCraft's own rules. Existing top-object envelopes redraw by
them. Gradient meshes made from a path keep their sides measured by length.
- crates/doc/src/live/mesh.rs: Coons::from_top_object and the corner
search, used for top-object envelopes.
- EnvelopeKind::TopObject gains an optional `corners` field (anchor
indices); the .ai importer fills it from the mesh's corners.
- Params text of object.envelope.makeWithTopObject, docs/mcp.md
(Envelopes) and a progress line in docs/target-app-parity.md.
File › Open and File › Place read HEIC and HEIF still images (.heic,
.heif) through heic-rs 0.1.4, the pure-Rust decoder photocraft-heif
uses, and store the pixels as PNG. Single pictures and grids of tiles,
8 to 10 bits, with or without an alpha image, are read with the
container's rotation, mirror and crop applied. A grid without a color
box of its own takes its tiles'. When neither the image nor its tiles
have an nclx box, the HEVC stream's video signal type gives the
samples' matrix and range. An RGB ICC profile, or Display P3 with the
sRGB curve named by an nclx box or, without a color box, by the
stream, is converted to sRGB. The EXIF resolution tags size the image;
without them it is 72 ppi. When the container's rotations turn the
image an odd number of quarter turns, the resolution's axes swap with
the pixels'.
Image sequences, images that aren't HEVC-coded, grids of cropped tiles
and images past the pixel allowance are refused with a message before
the pixels are decoded. The allowance counts each coded picture at the
size its sequence parameter sets give it.
The decoder is in the engine's `heif` feature, which is on by default
and listed by the desktop, CLI and web apps. A build without it still
recognizes the files and refuses them with a message.
Docked Tools stays at the window's left edge again, outside any panels
docked on the left. Window › Toolbars › Keep Tools Beside the Canvas
(`window.toolsBesideCanvas {on?}`) docks it between those panels and the
canvas instead.
- The setting is its own field in the UI state and in workspaces, and a
missing field reads as off, so saved state and workspaces keep Tools at
the window's edge. Built-in workspaces have it off.
- Off, app.rs draws docked Tools before the docking layout, the canvas
reserves no width for it, and only the full-height window-edge strip
docks a floating Tools panel. On, the canvas panel draws it, the canvas
keeps room for it, and both the window edge and the canvas edge dock it
beside the canvas.
- Turning the setting off forgets the canvas-edge strip, so a strip laid
out while it was on does not dock Tools. A floating Tools panel stays
floating when the setting changes.
- `window.panel.dock {panel: "tools"}` docks Tools where the setting says.
- The menu label has a row in every catalog.
Docs: docs/mcp.md, docs/control-protocol.md, the ROADMAP progress log and
docs/target-app-parity.md. The headline-numbers row and the paragraphs
under the status lines are gone, and scores are unchanged.
With Snap to Grid on, a move of objects on one perspective plane at
different depths applied the snap correction only to the objects at the
first object's depth, and every other object moved by the pointer's
offset read at its own depth. Those objects left the grid and lost their
spacing.
Every object on the first object's plane now moves by that object's
plane offset plus the correction, so the edge of the joint bounds nearer
a gridline lands on it and the objects keep their spacing. A selection
at one depth snaps as before, and objects on other planes keep their own
offset.
Typed list detection gave every bulleted trigger the filled dot. Now
`*` and a space start a list with the filled dot (•), `+` with the
hollow circle (◦), `#` with the filled square (▪) and `>` with the dash
(–), each one of the existing Bullets presets. `1.`, `1)`, `A.` and `A)`
start the same numbered lists as before.
Only the list a typed trigger starts changes. Lists already in a
document keep their bullets, pasted triggers start no list, and Undo
and Redo of a typed list work as before.
Docs: docs/mcp.md (typed list paragraph), docs/target-app-parity.md.
A recorded save in the template format writes a copy and leaves the
document's file as it was, so it no longer clears the warning or the
refusals. A recorded Save or Save As with its own path gives a never-saved,
converted or restored document its file before Save and Close, so the
batch runs and warns that every data set saves over that one file.
With a destination folder and Override Save on, a recorded Save or Save As
step writes each data set to its own file in that folder and makes that
file the document's own, so Save and Close then saves each data set to its
own file. The batch no longer warns that every data set saves over one
file in that case, and no longer refuses a document that was never saved,
converted or restored, since the recorded step gives it a file first.
A recorded PDF, EPS, DXF, Save for Web or Save for Office Documents step
that gives a `path` writes the same file for every data set, because the
batch does not point these steps into the destination folder. The one-file
rule now counts them, so the dialog and `action.batch` warn.
Save and Close over the data sets of a document converted from an older
version or restored by Data Recovery is refused with "save the document
with Save As before Save and Close" before a data set is selected. Saving
such a document does not write its file, so the batch reported success
without saving anything. The message has a row in each of the 26 catalogs.
Batch… with a Data Sets source offers None, Save and Close and Folder again.
Save and Close saves the open document over its own file after each data
set. When every data set saves over the same file, the dialog shows a
warning and `action.batch` returns `warning`: with Save and Close, or with
a recorded step that writes a file and that the batch does not point into
the destination folder. A Save step without a path counts when it writes
the document's own file; Save As, Save a Copy, Save as Template and Export
steps count when they give a path; Export for Screens counts when its
folder, given or from its preset, names a folder. Save and Close over the
data sets of a document that was never saved is refused with "the
document has never been saved" before a data set is selected.
Data sets share one open document, so Save and Close saved every data set
over the same file and only the last one survived. action.batch now
returns an error for source datasets with destination saveAndClose before
it selects a data set or runs a step, and the Batch dialog offers None and
Folder for a Data Sets source, moving a Save and Close choice to Folder.
wasmi pauses a run before an instruction the store's fuel can't pay for
and charges nothing for it. A memory.fill or memory.copy is charged for
its whole length, so a pause can leave up to that cost in the store.
Plugin::call replaced the store's fuel with the next slice and compared
the instruction's cost with the rest of the budget alone, so a run whose
budget covered its work could end with Limit("instruction budget").
Each pause now tops the store up to the slice size, keeping what it
held, and ends the run only when the store's fuel and the rest of the
budget together can't pay for the next instruction.
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.
A plug-in whose manifest gives "api": 2 and lists the new `progress`
permission may import three host functions from module `env`:
vc_progress(done, total), vc_cancelled() -> i32 and
vc_log(level, ptr, len) -> i32. Installing checks every import and
refuses, by name, one the host doesn't offer, one with another type and
one the manifest doesn't allow.
- Progress is clamped and passed on once per tenth of a percent (past
2,000 reports, once per 1,000 changes). A heavy filter's progress
window and the export's progress window show it as a percentage bar,
and their Cancel makes vc_cancelled return 1. A live effect's progress
is not shown.
- A run cancelled before it returns ends as cancelled, and one that
returns after its time budget ran out ends at that budget, so what a
plug-in returns after it sees vc_cancelled changes nothing. This also
applies to a plug-in of API level 1 that finishes after a Cancel or
past its time budget.
- Log text must lie in the module's memory and be UTF-8. Lines go to the
app's log as "<id>: <text>" with hidden characters escaped, at most
1,024 characters a line, 200 lines and 32 KiB a run, and the host reads
at most 1 MiB of log text a run.
- plugin.list and plugin.info give the manifest's `api`; the install
prompt names the new permission in plain words.
Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
The Links panel's Relink... button and menu item open a Relink dialog
(ui.relinkDialog) with a Replace menu. Selected Instances relinks the
selected images and placed documents, as Relink did before. All
Instances also relinks every other object in the layers linked to the
same file. Missing Links from This Folder relinks the selection to the
chosen file, then every other missing link to the file of its name in
that file's folder, and the status bar names the files not there. Each
Link Separately asks for a file for each selected link in turn, one OK
each, and relinks them all at the last one: the dialog has one row per
linked file, and each selected embedded image gets a row of its own.
Every choice is one undo step.
links.relink takes scope: selected (default), all, missingInFolder
(with path) or each (with paths: [{ids, path}] in place of ids, path
and folder). A scope or paths that doesn't fit is an error and relinks
nothing. With copy, every file is read and checked before any is
copied, copies made before a failed copy are removed, and
missingInFolder looks for missing images only.
Docs: params text, docs/mcp.md, docs/control-protocol.md,
docs/target-app-parity.md. Catalog rows for the new labels and messages
in every catalog.
Add Used Colors, New Color Group from artwork and Select All Unused
looked up each linked paint's swatch by searching every swatch. They now
build one map of swatch names first, with the built-in Registration
swatch, and the first swatch of each name as before.
New tests and test cases: New Color Group from artwork with repeated,
near-equal and -0.0 colors, a Registration link and names that need
numbering, and New Swatch and Edit Color Group with near-equal and
-0.0 colors; each checks the names, their order and one undo step.
New Swatch with several colors, New Color Group and Edit Color Group's
extra colors searched every swatch and group for each candidate name
of each new swatch. They now build the set of taken names once
(FreeSwatchNames, with take_new for New Swatch's default names and
insert for the group name and the group's own swatches) and keep it up
to date as they add swatches. The names they give are unchanged.