A process color can't colorize a grayscale image that has an alpha channel,
and paint.setFill refuses that combination. Remove Background gave such an
image an alpha channel and kept its process colorize color. It now refuses
the run, changing nothing, when an image it is given is gray and colorized
with a process color. It checks this before any image is decoded. Another
image in the document that already has that combination does not affect a
run on other images. Black, white and spot colors still colorize the result.
The rule check_colorized applies to each image is now one function,
paint::gray_colorized_with_process, which both checks call.
Docs: docs/mcp.md (Remove Background), docs/target-app-parity.md.
text.create clamped a size above 1296 pt to 1296 without an error, so a
request for 2835 pt type returned 1296 pt type. text.createInPath,
text.setStyle and text.setRangeStyle clamped the same way. Each now
refuses a size outside 0.1 to 1296 pt with "`size` must be from 0.1 to
1296" and changes nothing. Both ends of the range are still taken.
Follows #6643.
- Weed Box and Lines counts the outline points a nested clip group hides when
it tests pieces for being inside a region, and counts the work of every clip
group in the selection together, so a selection cannot run far past the
limit.
- Stencil Bridges refuses art when its measuring budget runs out only while an
island is still unjoined, and a refusal for size or detail says so.
A Cutting Tools add-on command (Object › Plug-ins › Cutting Tools › Weed Box
and Lines…, `addon.cutting.weed`) for vinyl cutting: around the selected art
it adds a weed box, a rectangle at a margin outside the art's bounds, and
weeding lines across the box, horizontal, vertical or both, by count or by
spacing. Each line runs from one side of the box to the other and is left out
over the art and within a gap of its outlines, so it stops at the art and the
waste lifts off in pieces. The lines and the box are straight paths stroked
in a chosen color swatch ([Registration] by default) with no fill, in one
group named Weeding on top of the current layer, selected, as one undo step.
While a clip group, or a group inside one, is edited in isolation, the
group goes on top of the container that holds the outermost clip group
around it.
The art is measured by the outlines a cutter cuts: closed paths by their fill
rule (the hole of an even-odd compound path gets lines too), open paths as
lines, type by its glyph outlines, images by their frames, placed files by
the art they show (by their boxes when the file can't be read), symbol
instances by their art, and blends and other live objects by the art they
draw. A clip group, and an object selected inside one, is measured by what
shows inside the region its clipping paths clip to, and a clipping path
selected on its own as its clip group. Art in a hidden group or layer is
left out. The command refuses an empty selection, art with no outlines, bad
params, and art or lines too complex to measure, with a message.
The dialog has Preview; a Spacing above 0 is used instead of the Count.
Docs: docs/mcp.md (Cutting Tools add-on) and docs/target-app-parity.md.
Object › Plug-ins › Cutting Tools › Stencil Bridges… (`addon.cutting.bridges`)
cuts strips of a set width out of the selected art so that a stencil cut
from it holds together: every island (the inside of an O, A or 8, a hole in
a shape) stays joined to the sheet, directly or through another island.
Bridges take the shortest crossings that fit first, then spread around each
island up to the count asked for, at least two widths apart. They run
vertically, horizontally or along the shortest crossing among 12
directions 15° apart.
- The art is measured by the area its outlines enclose, whatever their
paint, with hidden objects left out; type by its glyph outlines, without
converting the type. The result is one compound path with the strips cut
out, painted as the topmost selected shape with outlines, in the place of
the topmost selected object with outlines. Selected objects with no
outlines in the art stay where they are.
- Keep Original (on by default) moves the selected objects that make up the
art to a new hidden layer, Stencil Original; off replaces them.
- Art with no islands, or none a bridge of the width fits, is left as it is
with a message and no undo step. Islands the cut result still encloses
are counted in `unbridged`, with a message. Otherwise the run is one undo
step.
- Curves are followed within a twentieth of the width (0.002 to 0.25 pt).
Art that reaches more than 10000000 pt from the origin or has more than
200000 anchors is refused before it is combined. Art is also refused when
its combined area has more than 4000 outlines, when its outlines need
more than 200000 straight segments, or when placing the outlines or
joining every island takes more work than a fixed budget allows; when
the budget runs out while extra bridges are added, the bridges found so
far are kept.
Tests in vectorcraft-pathops, vectorcraft-engine and vectorcraft-ui-egui,
command_sweep cases, catalog rows in all 27 languages, docs/mcp.md, the
evidence list in docs/target-app-parity.md and the ROADMAP progress log.
In distance mode, each pixel took a square root for every background color.
The nearest color is now found by squared distance, and only that distance is
converted to a percentage. The levels do not change.
A new built-in add-on, Image Tools (Object › Plug-ins › Image Tools),
adds Remove Background… (`addon.image.removeBackground`). It makes the
background of the selected images transparent by lowering the alpha of
their own pixels, in one undo step, with no service or model. The
background is the most common color along the image's edges, the
average of the image's corners, or colors the user picks. Contiguous
(on by default) clears it only where it reaches in from an edge; off,
it is cleared wherever it is. Tolerance and Softness (0 to 100, 10 by
default) set how near a color must be to be cleared and the band over
which pixels fade out. Match measures that nearness as the RGB
distance or as the largest difference on one channel. It is a
VectorCraft addition.
- The image stays an image: an embedded PNG with alpha, the source's
ICC profile and resolution, and 16 bits or gray when the source is.
A linked image is embedded and keeps its Placement Options.
- When the edges hold at least as many clear pixels as pixels of their
most common color, edge mode clears nothing, so a second run leaves
the subject alone. Corner mode leaves clear corners out of the
average and clears nothing when all four are clear.
- With Match set to Each Channel, a tolerance of t / 255 × 100 clears
the colors within t 8-bit steps on every channel.
- A CMYK image, a linked image that shows its preview, an image that
would need more than the decode allowance (checked from its header)
and a result with no pixel left are refused, and the document stays
as it was. When no pixel changes, no undo step is added. The result
reports `cleared`, the pixels made fully clear.
- Picked colors are hex colors, with or without `#`, or black, white,
red, green or blue.
- The dialog opens with Preview off; with Preview on, the canvas shows
the result, and Cancel puts the image back. While the dialog
previews, the session keeps the images' decoded pixels (at most
128 MiB, 32 MiB in the web app) so each preview step does not decode
them again, and releases them when the dialog closes with OK or
Cancel. A run that is not a preview step keeps nothing.
Docs: docs/mcp.md (Image Tools add-on), docs/target-app-parity.md.
Document Setup gains alignNewObjectsToPixelGrid. When it is on, every
path or compound path drawn in the document (shape tools, Pen,
Curvature, Pencil, Paintbrush, Blob Brush, grids, flares and add-on
shapes) gets the pixel-aligned property and snaps in the same undo
step. Text, paste, duplicate, Place and import are not affected.
- The Transform panel menu has an Align New Objects to Pixel Grid
item for the active document.
- New Document's Advanced Options has the checkbox; the Mobile and Web
presets turn it on, other presets and older saved presets leave it
off, and file.new, Save Preset and Recent keep it.
- The setting is saved with the document only when it is on.
https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/pixel-preview-mode-for-web-graphics.html
Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md.
Closing a path by dragging on its first anchor made that anchor smooth
with symmetric handles, so the first segment bent along with the closing
one. The drag now sets the first anchor's incoming handle to the mirror
of the pointer. Its outgoing handle stays where it is on a corner, so
the first segment keeps its shape, and on a smooth anchor it turns to
stay in line at its own length. Alt makes the anchor a corner and leaves
the outgoing handle where it was when Alt went down. A click on the
first anchor closes the path with the incoming handle retracted and the
anchor a corner, so the closing segment comes in straight.
The Smart Pen's closing drag follows the same rule.
path.close's `in` now places the handle as path.setHandle does, and a
new `out` param places the outgoing handle and makes the anchor a
corner.
Opening a .psd or .psb used to give one merged image. document.open now
reads the layer records and builds the document from them. The PSD
import options page
(https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html)
says the option that converts layers to objects keeps as much of the
layer structure as it can, merges the layers of a clipping mask into one
layer and discards hidden layers that require merging, and that Import
Hidden Layers brings hidden layers in. document.open does the same, with
hiddenLayers: true for the hidden layers; layers: false keeps the merged
image.
VectorCraft's own rules:
- Each layer becomes a layer holding its pixels as an image, with its
name, visibility, opacity and blend mode. A group becomes a layer of
sublayers; a Pass Through group is not isolated.
- Clipped layers show where the layer below has pixels. That layer's
fill opacity fades only its own pixels. Layers clipped to an
adjustment layer, or to a fill that stores no pixels and isn't drawn,
show over the whole canvas.
- A layer mask becomes the layer's opacity mask, its default color and
invert flag applied; a disabled mask is kept, switched off.
- Fill opacity multiplies the layer's opacity.
- Layers and groups in Dissolve, Linear Burn, Linear Dodge, Vivid Light,
Linear Light, Pin Light, Hard Mix, Subtract, Divide, Darker Color and
Lighter Color are merged with the layers below, whether or not they
hold transparent pixels; Color Dodge, Color Burn and Difference keep
their mode. A merged Dissolve layer's pixels are opaque or
transparent, in a pattern of each layer's own.
- A solid color fill layer that stores no pixels and has no vector mask
comes in as its color (RGB, CMYK, Grayscale, Lab or HSB).
- A layer name stored only as a Pascal string, without its Unicode
name, is read as Mac Roman.
- Adjustment layers, fill layers that aren't drawn, layer effects that
are switched on, knockouts and vector masks are not applied; the
warnings count them.
- A merge whose canvases need more memory than allowed leaves the layer
alone, in the Normal mode or unclipped, with a warning.
This changes what opening a layered PSD does. Batch Trace Folder passes
layers: false, so a PSD is still traced as its merged image. A file
whose layers can't be read, or whose layers and merges need more memory
than allowed, opens as the merged image with a warning.
Lengths, counts, nesting, decoded pixels and the canvases merges make
are bounded. Zipped 32-bit data with prediction is now read, in layers
and in the merged image. import_fuzz gains a case that mutates the PSD
and PSB samples and opens them with their layers. The PSD reader's new
error messages have rows in every catalog.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md,
README.
Release Type on a Path copied the stored runs into point type with the
Dynamic Text flag still set, so the text dropped back to its stored size
and the file saved the flag on point type. The point type now gets the
drawn sizes and the flag is turned off.
The legacy .ai writer writes Dynamic Text type at its drawn sizes; it
now also warns that the setting itself is not written, as the editing
data writer does, whether the type is written as text or as outlines.
docs/target-app-parity.md lists reshaping or scaling the path among
VectorCraft's own rules, and a task bar comment marks leaving the item
out for threaded type as VectorCraft's own rule.
The 27 commands that act on `ids` or else the selection (Apply as
Filter, Print Ink Fixes, the Arrange, Cleanup and Path Tools plug-in
commands, Smooth, Text Wrap, variable.bind and variable.unbind among them)
refused `ids: null` with "ids must be an array of non-negative integer
object ids". They now read null as `ids` left out, as the other commands
that take `ids` do. Remove Breaks also reads null as left out when it
decides whether to act on the characters the Type tool selected.
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:
- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
Select All, the art bounds, clicks and marquees, and a layer's
Pathfinder and Transform effects leave them out. The Layers panel
lists them as guide rows of their layer, the highest guide.list
index first.
- Deleting a layer deletes its guides, and merging layers keeps their
guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
they were made.
New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.
File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
The params docs now state the direction (and the zero) of each angle or
rotation a command takes; behaviour is unchanged. Shape creation, text.create,
the grids, add-on shapes, saved views, Liquify, Puppet Warp pins, Mirror and
Radial Repeat and Vector Halftone turn clockwise; text character rotation,
gradients, the eraser tip and print screen angles turn counter-clockwise.
view.saved.new's rotation note changes its schema, so the API level goes to 3.
A test walks every command's params doc and fails when a param named like an
angle or rotation doesn't say clockwise or counter-clockwise.
A centred or right-aligned line of tracked type was measured with the
tracking after its last character, so it sat half that tracking
(centred) or the whole tracking (right-aligned) left of where the page
of users' .ai files draws it. That page aligns a line as wide as its
characters without that tracking.
Shaping now records the tracking each glyph's advance holds
(SGlyph::tracking), and a line that isn't justified leaves its last
character's out of the width it is aligned by. The last character is
the last one the line draws before a paragraph's end, a forced line
break or the spaces a wrap leaves; spaces or a tab typed before a
paragraph's end or a forced line break keep the letter's tracking, as
the page does. Forced line breaks and wraps aren't in the files: they
follow the rule so that turning a wrap into a paragraph or a forced line
break keeps the line in place. Hyphenated lines, lines whose last
character hangs, vertical and right-to-left lines and type on a path
keep it. The tracking stays in the glyph's advance, so the caret,
selections, line breaking and Fit Headline are unchanged.
LineInfo::trailing gives the amount. Right-aligned area type sets the
text after its last tab the same way.
What aligns by line widths follows. Convert To Area Type makes its
frame end where the lines are aligned, with room for the tracking, so
tracked type converted either way stays in place. Affinity import lines
up differently aligned paragraphs of artistic text by the aligned
widths. SVG import moves a centred or right-aligned letter-spaced line
back by its share of the last letter spacing, so it stays where a reader
draws it and SVG export, which anchors lines at the end of their
advances, reads back unchanged.
Centred and right-aligned tracked type lays out differently than before
in every document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In horizontal type, tracking and manual kerning are now thousandths of
the em as the Horizontal Scale draws it: at 50%, a tracking of 100 adds
0.05 em after each character. They were added at the full em while the
glyph advances took the scale, so narrow tracked type drifted from the
page of users' .ai files letter by letter. That page writes such type's
character spacing as the tracking itself in a text space as wide as the
scale, which PDF scales by it (ISO 32000-1 9.4.4). Vertical type keeps
the full em: its advances down the column don't all take the scale.
vectorcraft_text::spacing_scale gives the factor. Shaping, inline
graphics and the Optical pairs use it, and so do the conversions from
points back to thousandths of an em: the automatic kerning the Character
panel reads and Touch Type's limits (glyph_em), Combine Point Text, and
PDF import, whose tracking is now the file's Tc. Scale Text Attributes
now keeps tracked type where a horizontal stretch drew it.
SVG export writes letter-spacing in thousandths of the font size it
writes (the vertical scale), so uniformly scaled type keeps its spacing
in a viewer.
Type with both a Horizontal Scale other than 100% and tracking or manual
kerning lays out differently than before, in every document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
object.path.offsetPath, object.path.simplify, object.path.splitIntoGrid,
object.path.cleanUp, object.pathfinder.trap and addon.contour.set read
their params with helpers that fell back to the default for a value of
the wrong type: "offset": true offset by 10 pt, "joins": 3 gave mitered
joins, "addGuides": "yes" added no guides. Each such value is now an
error naming the param, and the document and the undo history stay as
they were. An omitted or null param keeps its default, and a distance
still takes a string with a unit ("5 mm"). An unknown `joins` name is
refused as well.
Measure Type by Glyph Bounds (`typeGlyphBounds`, off by default) is in
Preferences > Type > Glyph Bounds and the Transform panel menu. With it
on, the bounding box, the Transform, Control bar and Properties X, Y, W
and H, `object.setBounds`, the transform tools' reference point and the
Arrange commands measure point type selected on its own by the ink of
its glyphs instead of its em box. A typed W or H scales the live type
until its ink has that size.
- The engine caches each type object's ink in text space with its
layout bounds; the cache is not saved.
- Use Preview Bounds adds the characters' strokes, and type with
geometry effects is measured as drawn. Skewed type is measured by its
outlines.
- Type that draws nothing, area type, type on a path and type inside a
selected group are measured as before. Align and Distribute keep
following Align to Glyph Bounds.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md,
object.setBounds params text.
- Extend vectorcraft-geom::shapes with clean-room open/pie/chord/arc ellipse and rectangle builders, per-corner radii, polygon/star corner rounding and deterministic vertex randomization, logarithmic/Archimedean spiral divergence and inner radius, and perspective/isometric 3D box face builders.
- Isolate 3D Box and customizable shape parameters as a VectorCraft add-on under crates/*/src/**/addons/:
- crates/tools/src/addons/box3d.rs (Box3dTool and CustomShape extras stored under Node::extras["vc-shape-tools"])
- crates/engine/src/cmd/addons/shapes.rs (addon.shapes.rectangle, addon.shapes.ellipse, addon.shapes.polygon, addon.shapes.star, addon.shapes.spiral, addon.shapes.box3d, and addon.shapes.configure)
- crates/ui-egui/src/addons/shapes.rs (Object > Plug-ins > 3D Box > 3D Box Tool and context-sensitive Control bar options)
- Keep crates/engine/src/cmd/create.rs, crates/tools/src/shape.rs, and crates/tools/src/draw2/family.rs byte-for-byte identical to main.
- Add translations for "3D Box" and "3D Box Tool" across all 26 .tsv catalogs in crates/ui-egui/src/i18n/.
As asked in review: the reference app has no such commands, so they ship
in the Color Effects add-on. paint.fillToStroke / paint.strokeToFill are
now addon.color.fillToStroke / addon.color.strokeToFill in
cmd/addons/color.rs, listed in Object > Plug-ins > Color Effects instead
of the Color panel menu, with catalog rows in all 26 catalogs.
Two fixes from CodeRabbit's review:
- A graph's series marks and labels give the graph the copied paint in
the same undo step (capture_series_paints / capture_label_styles, as
paint.setFill does), so drawing the graph again keeps it. Targets go
through graph::paint_targets, so a series group stands for its marks.
- A copy that would leave the effective paint as it is skips the object,
so None copied onto a missing fill or stroke adds no item and records
no undo step.
After Select All in a frame of threaded text, Remove Line Breaks and
Remove Paragraph Breaks changed only the edited frame, and Convert to
Text over MCP converted only the selected frame. Both now act on the
text of every frame of the story as one undo step.
- addon.cleanup.removeBreaks reads the frames of the story as one text,
so a break at the end of a frame before a word in the next one becomes
a space. The same applies when every frame of a story is selected as
objects.
- text.convertListToText without ids, id or a range converts every
paragraph of the story. Locked and hidden frames of the selected story
are not converted, even when ids names them. When the call converts
the whole story, the story stays selected; before, the call reset the
Type tool's selection to the edited frame.
- A selection inside one frame, and unthreaded type, behave as before.
Docs: docs/mcp.md, docs/target-app-parity.md.
Doc comments for the Select add-on commands, the Select by Link Status
dialog body, the Links panel menu and the test helpers (review: docstring
coverage).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Selection Tools add-on: addon.select.byLinkStatus selects the image
objects and placed documents whose link is linked, embedded, missing or
modified (one status or several), as links.list reports it. Optional ids
limit the search, add adds to the selection, and zoom runs the existing
Zoom to Selection in the desktop app. Objects that are hidden, locked, on
a template layer or outside the isolated group are left out and returned
with the reason. Only the selection changes.
Select Linked / Embedded / Missing / Modified are in Object > Plug-ins >
Selection Tools and the Links panel menu. Each is off when no image has
that status, and Shift adds to the selection. Select by Link Status...
lists the images with name, status and format, with a name search, a
status filter, checkboxes with All / None and Zoom to Selection. The
status bar says what was selected and how many were left out, with
plural rows in every catalog.
Tests: engine (statuses, ids, add, zoom, skip reasons, bad params),
command sweep, and UI (message, list filtering, menu items, Links panel
menu, headless dialog frame). Docs: mcp, target-app-parity, ui-parity,
roadmap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #5694 asked for two changes.
Add-on, not a core tool, as Enclosing Lasso is:
- The command is addon.contour.set, in an add-on module of its own
(crates/engine/src/cmd/addons/contour.rs) with its tests beside it.
- The tool is listed under Object > Plug-ins > Contour and under its own
"Contour" heading in All Tools. It has no Basic or Advanced toolbar
slot and no default shortcut. The Contour bar is unchanged.
- The add-on drawer test lists Contour with the other add-on tools.
- Docs (mcp.md, the parity documents, ROADMAP) say add-on and use the
new command name; mcp.md gains a Contour add-on section.
Translations: rows for "Contour", "Contour Tool", "Sharp", "Bake" and
"Turn the Contour into plain paths" in all 26 catalogs ("Round" and
"Square" already had them). Drafted from each catalog's own vocabulary
(for Bake, the verb it uses for Expand Appearance; a word other than the
catalog's Stroke or Path word for the tool's name where they collide);
they are not native-speaker reviewed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Type Tools add-on command (Object › Plug-ins › Type Tools › Release Type on a
Path…, `addon.type.releasePath`): each selected type on a path becomes point
type with the same runs, character and paragraph attributes, on one straight
line whose baseline starts where its first character sat, and the plain path
it was set on, with its exact shape and transform, in its layer and stacking
place, with no fill or stroke. `mode` keeps both, the path only or the text
only; `keepOriginal` leaves the type on a path and adds the released objects
above it. Other objects are left alone, nothing to release makes no undo step,
the run is one undo step and the released objects are selected. The command
dialog previews it as Type on a Path Options does (#5360): Cancel leaves no
history, OK is one step. Tests, command_sweep cases, catalog rows in all 26
languages, docs/mcp.md and the parity docs (beyond Illustrator: scores
unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
addon.pathtools.cornerSet takes `every` ("all", "odd" or "even"; default
"all"), which keeps, of the anchors already chosen (named, direct-selected
or all), only the odd- or even-numbered ones, counted along each path from
its first anchor as number 1. Anchors that take no corner still count, so
the pattern follows the path's anchor order on open and closed paths; when
nothing is left the command says so. Unknown values are refused.
Object › Plug-ins › Path Precision Tools › Set Corners… (also a button in
the Anchor Inspector's Corners section) opens a dialog with Radius, Corner
kind and Anchors (All / Odd-Numbered / Even-Numbered) that previews on the
canvas and commits as one undo step.