A spot swatch defined in RGB keeps its spot flag and its own plate when
the document changes to CMYK, and its definition converts like any other
swatch: a fill that uses it and a 40% tint stroke take the converted
color. The Lab spot test's comment now says it covers a Lab definition.
Follows #5442.
- text.setGlyphAlternate keeps the default glyph as a choice when a
feature of the character's style (salt, a stylistic set) draws another
glyph for it, so the default glyph is drawn. A character that is not
laid out (overflowing text) is shaped alone in its style to decide.
gid: null reports the glyph the style draws.
- The Glyphs panel's alternates grid shows the default glyph first when
the font does not list it, so a chosen alternate can be put back.
- A test for a chosen character that starts a longer grapheme (f with a
combining acute), which is not split from its mark.
- The alternates tests in the engine hold the shared font lock while
they add fonts to the global font database.
The png reader's stream buffer grows by doubling its reservation, and
while it grows the old reservation and the new one are both allocated.
The room estimate counted the buffer twice, for the doubled
reservation, but not the moment both are held, so a wide PNG could be
approved for a row-at-a-time decode with less room than reading it
takes. The estimate now counts the stream buffer three times.
The bar decided to show the inline fields against a fixed 440-point
width, smaller than the room the fields take, so just above that width
the H field ran past the bar's right margin and was clipped. The bar
now adds up the room from the values that draw the fields: the label
widths in the current language, the four 80-point fields, the W/H link
and the item spacing ahead of each. With less room it shows the
Transform link alone, as before.
The comment in add_item said a new fill or stroke is a copy of the top
one. The code takes only the top item's paint, and for a stroke its
weight. The comment now says so, as the params text does.
VectorcraftApp::new starts the installed fonts' scan in the background,
and Type > Font lists only the loaded fonts until it ends (#5483). When
the scan ended between two reads of the menus, the second read had the
installed families too, and every_in_window_item_is_in_the_mac_menu_bar_once
and documents_and_selections_change_state_not_structure failed. The
tests' app() helper now waits for the scan after making the app.
Space pressed after a tool's press has begun (Pencil, a brush, a shape, a
selection drag) switched the cursor to the hand but panned nothing: the pan
starts only with Space down at the press. It is ignored now, so the cursor keeps
the tool's own.
Test: space_during_a_pencil_drag_keeps_the_pencil_cursor (fails without the fix:
Grab instead of the pencil's crosshair).
The last column was egui's remainder column, which has no drag handle, so
it could only be resized from the edge before it. It is now an ordinary
column with its own handle.
A table keeps its column widths, so switching to a longer name left the
Name column at its old width and clipped the text. The table now goes
back to the opening fit whenever that fit changes: a longer widest cell
or a resized window. Dragged widths are dropped at that point.
fit_widths could overflow the room when one column hit the minimum,
because the other columns did not take up its share. Columns now give
way in proportion to their width above the minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The type.size.* commands, type.resetTracking and type.resetHorizontalScale
do not take `id`, `start` or `end`, so they refuse those keys as unknown
parameters. The list in docs/mcp.md now leaves them out.
None now clears only the rows the search shows, as Select All adds only
those, so artboards hidden by the search stay chosen. The count reads
"Chosen: {n} of {total}". The dialog's new strings have rows in all 26
catalogs, written from their meaning with each catalog's own terms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A double-click makes the row's artboard active and fits it in the
window (view.goToArtboard, as the Artboards panel does), keeping the
dialog open and the artboards chosen before it. Parity docs record the
Selection Tools add-on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Object › Plug-ins › Selection Tools › Select by Artboards… lists each
artboard's number, name and size, filters by name, chooses rows with
click, Shift-click and Cmd/Ctrl-click, and runs addon.select.byArtboards
on OK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
addon.select.byArtboards {artboards, relation} in a new Selection Tools
add-on. Overlapping is All on Active Artboard's test, now one shared
helper (select::on_artboard); Fully inside is within one artboard, edges
included; Fully outside is on none. Objects are those Select All takes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nonzero tile pointer below the end of the tile pointer table made the reader decode header or table bytes as pixels. The table end is now computed with checked arithmetic, and such a pointer is refused as damaged image data.
GTK and the desktop portal match a filter's *.ext patterns case-sensitively,
so on Linux and the BSDs files such as image.DIB, photo.PNG or art.Svg did not
show under any Open or Place filter, "All readable files" and "All placeable
files" included. On those systems each open and place filter now also lists
every extension with each letter as a [xX] pair, the form the portal's
FileChooser documentation recommends. Save dialogs and the other platforms
keep the plain extensions.
PathData::nearest, which the Direct Selection cursor runs to find the
segment under the pointer, projected the pointer onto every segment of
the path.
It now projects the pointer first onto a segment at the anchor nearest
to it, then skips each segment whose control-point box lies farther
away than that distance or the nearest point found so far. Each box is
measured once and the search allocates no memory. The result is the
same, including which of equal distances wins. A segment is never
skipped when the pointer or one of its control points is past 1e100 or
not finite.
Over a segment of an open path of 200,000 anchors, the cursor takes
51 ms instead of 59 ms in a release build (anchor_bench). The progress
line in docs/target-app-parity.md gives the timing.
Tests compare nearest with every segment projected over 400 random
paths, each probed at 30 points on a coarse grid (ties included), and
on open, closed, compound, one-anchor, empty, huge and non-finite
paths.
OK in Tate-chu-yoko Settings read an Up/Down or Left/Right that is not a
length as 0 and applied it. It now returns "Up/Down must be a number" or
"Left/Right must be a number" and keeps the dialog open, and a blank
field leaves its value alone.
The editing copy of the art that a PDF-compatible .ai carries now holds:
- Transparency: `mode opacity isolate knockout shape Xy` (the blending
mode's number, Knockout Group 0 off, 1 on, 2 neutral), written when it
changes: in a path's paint after its colors, before a guide, and after
a group's end followed by `0 0 Xd` and `6 () XW`. A group without
transparency of its own ends `9 () XW`, and a group's name comes
before its state block. A layer's transparency follows its art: `Xy`,
`0 0 Xd`, `7 () XW`, `LB`.
- Clipping groups as `q`, the art, the clipping path or compound path last
with `h` (or `H`) and `W` before its painting operator, `Q`, as section
5.6.2 of the version 7 specification shows them
(http://mercator.elte.hu/~saman/hu/okt/AI7FileFormat.pdf). A clipping
layer whose clipping object is a path or compound path sets the last
`Lb` flag and writes its clipping path last.
- Opacity masks as an `AI9_Mask` entry in the object's dictionary: the
mask's art between `X=` and `X+`, and Clipping, Inverted, Disabled and
Linked.
Before, clipping groups were written as their art cut to the clipping
path, and transparency and opacity masks were left out. Still left out,
with a warning: the opacity and blending mode of single fills and
strokes, opacity masks on layers (disabled ones too), and opacity masks
nested more than 8 deep. A clipping group or layer whose clipping object
isn't a path or compound path is written as a group of the art cut to
that object, in a layer that doesn't clip, and so are the clipping
groups inside that art. A clipping path's paint that a plain fill and
line can't draw is written as paths inside the clipping, fills under the
art and strokes over it, each side in a group of its own when it has
several, with the clipping path's transparency, lock and hidden state;
the clipping path's opacity mask stays on the clipping path, with a
warning. A clipping group without members is an empty group, and an
empty clipping layer is an empty layer.
Each of these is written in the form VectorCraft's .ai reader reads, and
the tests read the written data back through that reader.
Reading .ai editing data: an `Xy` after a layer's last group or compound
path, followed by the layer's `7 () XW`, is the layer's (a following
`6 () XW` keeps it the group's); groups, compound paths and layers keep
Opacity & Mask Define Knockout Shape.
The `.ai` option `editingData` and docs/file-format-parity.md describe
what the copy now holds.
A `color` parameter shows a color button that opens the color picker in plug-in
dialogs and the Appearance panel's effect editor; its default and values may be
a color object or a hex string, and the plug-in gets a color object. A filter's
returned objects may set `guide` and `locked`; objects it locked or made guides
are left out of the selection after the run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a_file_placed_while_idle_follows_its_changes asserts that an idle
document without links asks for no frame. Its app started the real render
worker, and once a frame took over 8 ms the canvas sent the next render to
it. The worker asks for a repaint when it finishes, at a time the test
does not control, so under heavy CPU load the asserted frame could report
a repaint delay of 0 instead of none.
The test now sets canvas.worker_started, as the cursor idle test does, so
every frame renders on the test thread. It also dates the rewritten file
2 s after the original with File::set_times, as the opening tests do,
instead of sleeping 30 ms before the write. Shipped code is unchanged.
Select > Object > All on Same Layers took the layer of every selected
object, including one that select.set had kept while it is hidden or
locked, on a template layer, on a locked artboard, or outside the
isolated group. That object's layer then decided which art was
selected. The command now takes the layers only of the selected
objects that the Select commands could select. When the selection
holds no such object, it clears the selection.
At a caret the panel sends text.setRangeStyle with no range, which
styles all of the text, but the check that skips a choice that changes
nothing read only the insertion style. When the characters at the caret
already had the chosen feature and others did not, the choice did
nothing. The check now covers every character of the text at a caret,
so the choice applies, and a choice that changes no character still
adds no undo step.
With System Layout on or Fractional Widths off, each advance is rounded
to whole points, but a right-to-left character with manual kerning
still moved by the unrounded kerning value. The difference between the
two changed the space after the character before it in reading order.
The character now moves by the change the kerning makes to its rounded
advance, so only the space before the next character changes.