An Xy right after a gradient mesh's /End X! is the mesh's only when the
mesh's own 6 () XW follows. When the layer's LB follows (directly or
after the layer's Xd and 7 () XW) it is the layer's, as before, and the
mesh keeps the state it had. Also corrects the revision-history row that
still said meshes with handle weights of 0 aren't read.
Highlight Alternate Glyphs compares each glyph with required shaping
alone, which turns liga and calt off. Older Arabic fonts (such as the
legacy Arial of the Microsoft core fonts) put the mandatory lam-alef
ligature in liga instead of rlig, so every lam-alef was highlighted.
A lam + alef cluster (any alef form, marks allowed) is now never an
alternate candidate, whatever feature the font uses.
Adds a test font built in code (lam-alef under liga or rlig) and
host-independent regressions. Also two equivalent one-line repairs
strict Clippy needs on main: a nonminimal_bool in the Path Precision
Tools inspect check and is_none() in a gradient panel test.
Object › Plug-ins › Artwork Cleanup and a hideable Text row in its panel
gain text tools, each one undo step:
- Break Text into Lines / Paragraphs / Words / Glyphs: point type pieces
placed by the glyph of their first character, keeping their styles.
- Combine Point Text: one-line point type merged into as few objects as
it can, every glyph staying where it was (tracking and spaces, checked
against the layout).
- Split Text at Cursor, Remove Line Breaks, Remove Paragraph Breaks,
Set Alignment, Keep Position, Reset Text Transform and Replace
Missing Fonts.
Type on a path from the editing data's text document came in at the
font's advances, while the file's own layout (and its page) closes the
glyphs up round the outside of a curve and opens them up round the
inside, even where the frame keeps no spacing. Round a circle the type
ran on past where the page ends it.
The spacing (Type on a Path Options > Spacing) that reproduces the
layout is now fitted from the layout's glyph positions, advances and
turns, using the text engine's own model, and set on the imported text
object. Corners and straight runs are left out of the fit.
The corner placement of placed files was tried only when the first
placement failed the comparison with the page. A small logo in the wrong
place differs too little to fail, so it stayed there. When the corner
placement draws closer to the page and still matches it, use it.
A .ai whose layers hold placed (linked) PDFs and no text object came in as
its PDF part, saying its layers had no text objects for its page's text.
The PDF part draws each placed file, type and all, where the layers show
its preview, so type that lies inside the placed files (at either of their
placements) is theirs and needs no text object. Type outside them still
does.
In the editing data of a .ai or EPS file, a gradient mesh's own state
(`Xy`) comes right after its `/End X!`, before its `Xd` and `XW`, as a
group's comes after its `U`. The reader took it as the next object's, so
a Multiply shadow mesh came in as Normal and painted opaque white over
the art under it, while the file's page shows the shadow.
An `Xy` read straight after a mesh ends now also applies to that mesh.
An `Xy` after the mesh's `Xd` and `XW` stays the next object's.
Some files write a handle weight of 0 on some gradient mesh corners. The
reader took only weights of 1, so the mesh and the whole file's layers
were unread. A weight of 0 draws as the same cubic edge against the files'
pages; other weights still leave the mesh unread.
bs.tsv: Bosnian (Latin script, Ijekavian) catalog covering every key of
VectorCraft's other catalogs, its wording shared with storytold/craft-i18n
locales/bs/ and the strings craft-i18n lacks translated new. Every bs-*
locale selects it; counts use one/few/other. Locale and plural tests,
localization docs and an attribution entry (AI-assisted, not yet
reviewed by a native speaker).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Tayw8nmxoLLmKv13jK93z
Effect › Color Effects › Duotone… (addon.color.duotone, also in Object ›
Plug-ins › Color Effects) prints the grey-equivalent tone of each colour
with one to four inks, each with its own tone curve. It is a colour
adjustment like the add-on's others, so it stacks with them and works as
a filter through addon.color.filter.
- One tone of a swatch the document has gives a tint of that swatch,
linked, so a spot tone separates onto its plate; several tones give
the CMYK mix of their tints (the inks overprint, so they multiply).
- Colour adjustments now map paints with their swatch links
(ColorMap::apply_link); the existing adjustments behave as before.
- Skip Objects with Spot Colors leaves spot-linked colours as they are.
- The dialog has a row per tone and the picked tone's curve (the Curves
graph, reused), its points and Reset to Linear / Inverse.
Print Ink Fixes (addon.color.printFixes, Object › Plug-ins › Color
Effects › Print Ink Fixes…) scales CMYK colours over a total ink limit
down to it, removes white overprint and sets black overprint (Overprint
Black's logic) in a CMYK document, as one undo step, and reports what
changed. RGB documents are refused.
The panel opens both under Inks.
Deferred: an opaque undercoat, tones for type only, extra gradient stops
for smoother tints, curve flip and all-100 % buttons.
When a process swatch listed before a global or spot swatch showed the
same color, a traced fill of that color took the process swatch and
stayed unlinked. Of the swatches that trace as one color, the first
linked one now gives the fill, and the first one otherwise.
With the Perspective Selection tool, Ctrl+Alt-drag (Command+Option-drag on
macOS) now copies objects on their perspective plane, as Alt/Option-drag
does. Before, Cmd/Ctrl lent the last selection tool even with Alt held, so
the drag made a flat copy. Cmd/Ctrl alone still lends the last selection
tool, and the cursor follows the same rule.
Follow-ups to the review of #4697 and #4698:
- With textAs: outlines, a .ai saved without PDF compatibility, read
from its editing data alone, has its type outlined too, as an empty
page or a text-only placeholder already did.
- Threaded type read from the editing data holds its story in its first
frame: it now flows through its frames before it is outlined, so each
frame's outlines show the text it holds, and the outlined frames
leave their threads.
- Each outline group keeps its type's opacity, blend mode, isolation,
opacity mask and effects (the same carry-over the flattener uses).
- A gradient-mesh corner (/N) holding anything but numbers is unread,
instead of being read with the numbers after it shifted into its
colour and opacity.
A page cropped to the art is recognised in a UserUnit page's units too,
when the art's box is the page in those units and the artboard isn't the
page in points. Such pages are taken only when every page is the art's
box, and only the first page's type is read: the later pages are copies
whose type was moved by the first page's offset and could fill the
layers' text objects twice.
prune_unseen ran before the retry that moves placed files to their box's
corner, and that retry cloned the pruned layers: text an image covered
only at its first placement was already gone, so the file came in without
it or as its PDF part. Clone the layers before pruning and prune each
placement on its own.
Character attribute 12 of the editing data's text document is the case
(1 Small Caps, 2 All Caps). The reader skipped it, so type set in All Caps
came in as typed, in lower or mixed case where the page draws capitals.
Map it onto CharStyle::all_caps and CharStyle::small_caps.
A successful command.batch now records its last Unite, Intersect or other
Pathfinder step as the operation Repeat Pathfinder runs, so playing an
action from the Actions panel updates it. A batch that fails and rolls back
leaves the previous operation in place. action.batch, which runs an action
over other files or data sets, does not set it.
Every successful Shape Mode or Pathfinder operation now sets the
operation that Repeat Pathfinder runs, including direct
object.pathfinder.* commands, not only the panel's buttons. Trap,
compound-shape edits and live effects leave it unchanged, and a failed
operation keeps the earlier one. The panel menu names the remembered
operation, and Repeat runs it on the current selection with the current
Pathfinder Options.
With too few objects selected, Cmd/Ctrl+4 leaves the art as it is and
shows the panel's selection message in the status bar. Adds the
"no Pathfinder operation applied yet" rows to the Catalan and Turkish
catalogs, and tests for direct and failed operations, Trap, compound
shapes, current options, a one-path selection and the default keys.
A straight Knife cut now starts only when Alt is held at the press.
Alt pressed later in the drag, even before the pointer moves, leaves
the cut freehand. Releasing Alt during a straight cut keeps it
straight.
With Shift+Alt, the cut keeps to 45° steps from the horizontal and
vertical whatever the Constrain Angle. The Pencil's straight line
still steps from the Constrain Angle.
Tests cover Alt after the press, Alt only at the release, a 90° cut,
a 30° Constrain Angle, and the cut boundaries in the engine.
Docs: docs/target-app-parity.md.
The Knife cut along the freehand drag whatever keys were held. The
reference app cuts in a straight line with Alt-drag, and it cuts at 45
or 90 degrees with Shift+Alt-drag.
With Alt held at the press, or pressed before the drag has moved, the
Knife's stroke is the press point and the pointer: the dashed line
shows it while dragging, and the release sends path.knife with those
two points. With Shift held too, the line keeps to 45° steps from the
Constrain Angle, as the Pencil's Shift line does. Shift is read on each
drag event and at the release. The cut stays straight when Alt is let
go, a line taken back to its press point sends no command, and Shift
alone leaves the cut freehand.
Docs: docs/target-app-parity.md.
paint.invert and paint.complement on the Fill proxy, the Edit Colors
commands, the Blend commands and recolor.apply now recolor the color
that colorizes a grayscale or 1-bit image, as one undo step with the
other targets. Before, they left it unchanged. Expand Appearance and
Object > Expand bake color adjustment effects into that color too.
- A colorized image's pixels are not recolored; they stay the gray
coverage of its color. A stroke-only pass reaches the image's stroke
items and leaves its colorize color unchanged.
- A command that would leave a process color on a grayscale image with
an alpha channel fails with the existing message and changes nothing.
Object > Expand undoes its earlier steps when that happens.
- A colorized image counts as a filled object in a blend.
Alt in a spiral drag now rounds the fitted segment count to whole winds
of four segments, counted from the segment count when Alt went down.
Ctrl/Cmd takes precedence when both keys are held, and switching back to
Alt starts from the current count with the held inner end kept. Arrow
keys, Escape, release and a new drag clear the held Alt count.
Tests cover both keys together, a switch mid-drag, Escape, the 2 to 1000
bounds and the committed path's anchor count. docs/mcp.md and
docs/target-app-parity.md describe the rule.
The reference app adds or subtracts winds from a spiral with Alt-drag
and changes its decay rate with Ctrl/Cmd-drag. VectorCraft's Spiral
tool did nothing with either key during a drag.
While a spiral is dragged out, Alt or Ctrl/Cmd keeps the spiral's
inner end at the distance from the center it had when the key went
down, and the outer end follows the pointer. With Alt the number of
segments changes to the nearest whole number that fits, within 2 to
1000, so dragging outward adds segments and dragging inward removes
them. With Ctrl/Cmd the decay changes to fit, within 5 to 99.99
percent, the decays a spiral is drawn with. When both keys are held,
Ctrl/Cmd changes the decay. Up/Down with a key still held move the
held radius to the new segment count. The new values stay when the
key is let go, and as the Spiral tool's segments and decay options
for the next spiral. Both keys also work with a 5 percent decay and
hundreds of segments.
Docs: docs/mcp.md, docs/target-app-parity.md.
Effect › Color Effects › Vector Halftone… (addon.color.halftone, also in
Object › Plug-ins › Color Effects) draws its object as the halftone dots
Object › Vector Halftone makes, live: mono (one dot colour, or each dot in
the art's colour under it) or CMYK screens, the five dot shapes,
frequency, angle, invert and clip to the art's outline.
- The screens, dots and caps move to vectorcraft_effects::addons::halftone,
shared by the command (its behaviour and tests unchanged) and the effect.
- The renderer samples the art (vectorcraft_render::addons::halftone) and
keeps the dots by the object's content and what it reads from the
document, so unchanged art isn't sampled again; past 100000 cells per
screen the object draws as it is.
- Expand Appearance turns the object into its dots, and the SVG, PDF, EPS
and AI exporters write them as paths.
- Its dialog reuses the command's fields, without Keep Original.
Deferred: stochastic and radial patterns, a dot-gain curve, per-screen
separation settings, type or symbols as dots.
Path Precision Tools gains the Parametric Shape Tool: ten kinds of shape
(rectangle, ellipse, polygon, star, ring, arrow, cross, heart, gear,
speech bubble) that stay editable as numbers.
- Geometry in vectorcraft_pathops::addons::pathtools::shapes: each kind's
outline, corner rounding limited to the middle of a side, a fingerprint
of the outline, recovery of centre, size and rotation after a move,
turn or scale, and recognition of plain rectangles, ellipses and
regular polygons.
- The tool (parametricShape, All Tools drawer and Object › Plug-ins ›
Path Precision Tools only, no shortcut): drag from the centre (Shift:
45° steps), arrow keys change the count and values while dragging,
handles edit a selected shape, double-click places one.
- Commands addon.pathtools.shape.create / .set / .convert, one undo step
each; params checked (counts 3..1000, sizes up to 16384 pt).
- A shape keeps its numbers in one object-data entry (vc-pathtools-shape)
that SVG export already leaves out; anchor edits make it a plain path.
- Parametric Shape panel, cursor stand-in, catalog rows in all 25
languages, docs and tests.
Art dragged off the canvas and colours dragged in from elsewhere (swatches, another library's)
used to be added at the end of the ungrouped items, wherever they were dropped. They now land
as items dragged within the list do: on another item of their kind, just before or after it by
the pointer's half and in its group; on a group, into it; away from the items, ungrouped at the
end as before.
library.add takes group and before/after, placed as library.moveItem places an item (the
placement is shared). A library change that fails partway now leaves the library as it was.
People reuse the same gradients across documents and rebuilt them by
hand. The Gradient panel now has a Presets menu: the presets of the
app's built-in gradient libraries, grouped by library (theme), then
the saved ones. Choosing one gives the gradient behind the active
proxy (or a solid paint) the preset's type, stops, method and dither,
keeping its placement, as one undo step, through paint.editGradient.
The panel menu saves the current gradient as a preset (named in a
dialog) and deletes saved ones; Hide/Show Presets toggles the row.
Saved presets are kept with the preferences, next to the other user
preset lists (width profiles, flattener, perspective). Stops linked to
a document swatch keep their colour, unlinked, since a preset is used
in other documents. Built-in presets can't be deleted; names are
unique.
Commands: gradient.presets.list, .apply, .save and .delete. The menu's
rows are drawn only while in view, as its headings are.
Tests: engine tests for list, apply (fill and stroke, placement kept,
one undo step), save (unique names, unlinked swatch stops) and delete;
panel tests for applying from the menu and saving, deleting and
hiding presets from the panel menu, including scrolling the menu to
the saved presets. Docs: gaps G13, target-app and UI parity, ROADMAP.
Patch and triangle mesh shadings of more than 4,096 patches, and strokes
painted with a mesh shading, came in as a flat grey. Illustrator writes
meshes of up to 14,355 patches, and writes a freeform gradient on a
stroke as a stroke painted with a type 7 shading.
- MAX_PATCHES is 16,384, as in the .ai editing-data reader.
- A stroke painted with a mesh shading is the meshes clipped to the area
the stroke paints (stroke_outline: width, caps, joins, miter limit and
dashes, built in the path's space and then transformed); draw_meshes
always clips a stroke's meshes.
Two follow-ups to the empty-page fallback:
- A page counts as empty only when nothing on it was skipped: its only
notes are the page's own (art off the artboards, art past the page). A
page whose only content couldn't be read, such as an image that can't
be decoded, keeps its notes and goes through the usual check against
the editing data, instead of being replaced by it unchecked.
- With textAs: outlines, the document read from the editing data alone
has its type outlined. The usual path takes outlines from the page,
but an empty page (or a text-only placeholder, #1387) has none to give.
A mesh in the editing data that the reader couldn't read made the whole
file come in as its page or PDF part, without its layers. Three cases
were rejected that files write routinely:
- A mesh starts grey, and `/CS` is written only where the colour space
changes, before the corner it applies to, even within a patch. Meshes
with no `/CS`, or one that changed between corners, were unread.
- Named colours (`[c m y k /DeviceCMYK (name) /Process]` or `/Spot`) and
`/NChannel` inks: each corner gives the tint, 1 being the full colour.
- Grids over 64 patches a side or 4,096 patches (files have 97 x 72).
Each corner now takes the colour space last set before it; named
colours paint at their tint and inks overprint at theirs; the limits
are 128 a side and 16,384 patches.
smithay-clipboard's load() waits for the selection owner's data with no limit, and
Paste and the "is the clipboard still ours" check read on the UI thread, so an owner
that never sends its data froze the window. Reads now run on a worker thread with a
second clipboard on the window's connection and are waited for 2 s at most. While a
read that timed out is unanswered, later reads return at once instead of queueing
behind it, and its late answer is dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where arboard can't open (GNOME has no data-control protocol, and the Flatpak on a
Wayland session has no X11), Copy and Paste use the compositor's core data-device
protocol on the window's own connection through smithay-clipboard, already in the
build through egui-winit: text only, a copy's text or its SVG markup. A bitmap-only
copy reports that this clipboard carries text only and leaves it as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In effect dialogs (like Color Halftone), length fields with defined bounds
(e.g. Max Radius 4..127 pt) were previously checked only after num_field
returned, leaving the field's temporary buffer holding the un-clamped string
(e.g. '0.5 px'). Subsequently entering invalid text caused the field to
fall back to that sub-minimum buffer.
- Added num_field_within to pass effect parameter bounds directly to unit_field.
- When committed, clamp the parsed value within bounds and update the field
buffer to the formatted clamped value so the UI reflects what is applied.
- When invalid text is committed, clear the invalid buffer and trigger a
one-frame border highlight so invalid input is rejected visibly.