Merge current main and retain both the Pencil snapping documentation and Tech Circuit library evidence. Only docs/mcp.md required manual conflict resolution. Focused checks passed: no conflict markers or whitespace errors, all 28 catalogs retain four unique nonempty labels, and brush implementation/tests remain unchanged. No full local Cargo CI rerun.
An action or journal entry recorded before Scale and Reflect followed the
turned bounding box has no basis value. Replayed, such a step now acts along
the page's axes, as it did when it was recorded; steps recorded now carry
basis "box" and replay along the box. The built-in Reflect Horizontal action
names basis "box", so it keeps reflecting across the box's own axis.
The symbol test that reflects a turned instance names basis "page", which
is the reflection it was written for.
Object › Distribute follows Object › Align and lists Horizontal
Distribute Left, Center and Right, Vertical Distribute Top, Center and
Bottom, Horizontal Distribute Space and Vertical Distribute Space. Each
item runs the button's `align.distribute*` command, so it uses Align To,
the key object and the Distribute Spacing value, and with the Artboard
tool chosen it spaces the selected artboards.
Unit::parse took one level of recursion per + - * / sign, so a text such
as "1+1+...+1" with 200,000 terms overflowed the stack and aborted the
process. Every number field, parse_number and every command parameter read
as a length went through it.
Text with more than 128 + - * / signs now reads as nothing; text with 128
or fewer reads as before. docs/mcp.md states the limit for length strings.
The test a_very_long_expression_reads_as_nothing aborted with a stack
overflow before the change.
A PSD or PSB file can name one of its saved paths (image resources 2000
to 2997) as its clipping path in resource 2999. psdread::clipping_path
reads that path in the image's pixel space, under the even-odd rule the
file format specification gives, with a subpath around the whole image
first when the initial fill record says the fill starts with all
pixels.
File > Place of such a file, and File > Open of its merged image, put
the image in a clip group of that path with no paint. file.place answers
the image's id as `image`, as for a DCS file. Replacing an image alone
in a clip group with such a file keeps the image's scale and rotation.
links.update and links.relink leave the clip group as it is. Files that
name no clipping path come in as before.
The Rough Pastels dialog has a Load Texture… button, as Glass and
Texturizer have. The image's gray levels become the paper surface,
stored with the effect as `textureImage` and listed as Loaded Texture
in the Texture menu; Scaling, Relief, Light Direction and Invert act on
it as on the built-in surfaces. The dialog's Texture and Light
Direction controls are now menus instead of text fields.
effect.apply and effect.setParams read `textureImage` from a file or
take the stored string and refuse anything else, and a plug-in filter
cannot have the host read a file for it.
In the Effect Gallery's Settings, a file dialog that answers later
(the macOS sheet, the Linux dialog) now gives the texture to the values
that asked: the filter being browsed, or the stack row that asked,
found by a number each row now carries. An answer for a removed row, a
filter no longer browsed or a gallery that closed is dropped.
Params text, docs/mcp.md, docs/control-protocol.md, docs/gaps.md and
the evidence line are updated.
Type on an open path can now start before the path's start: its start
is stored below 0, the span runs from there, and the characters whose
middle falls before the path's start are not drawn, as SVG 2 lays out
text on a path (https://www.w3.org/TR/SVG2/text.html#TextLayoutPath).
The text keeps every character, and the start bracket stays at the
path's start. Past the path's end, text that does not fit is overflow,
as for other type on a path. Type that starts on the path draws every
glyph as before.
- SVG import keeps a <textPath> start before an open path's start
(text-anchor end or middle, or a negative startOffset) instead of
starting the text at the path's start, which drew every character.
- SVG export writes it as a negative startOffset.
- PDF export writes only the characters drawn on the path.
- The legacy .ai and .ai editing data writers write the type from the
path's start with the type attributes warning.
- type.pathOptions takes a start below 0 for type on an open path; on a
closed path it is the path's start, as before. A flip starts the type
at its start bracket. Dragging the center bracket slides it from where
it starts, and no further before the path.
- A click at the path's start places the caret by the first drawn
character, and the caret in the hidden characters stands at the
path's start. The highlight of substituted fonts and glyphs leaves
them out.
- Release Type on a Path keeps the first drawn glyph where it was
drawn, with the hidden characters before it.
- The Touch Type tool shows no box for a hidden character, and
text.touchType returns an error for one. A character with a drawn
glyph is changed by that glyph.
Docs: docs/mcp.md, docs/target-app-parity.md.
With Show East Asian Options on, the OpenType panel menu has H or V
Style and Roman Italics. H or V Style is the character feature tag
`hkna`: kana take the forms the font draws for horizontal type, and in
vertical type the shaper applies `vkna` in its place. Roman Italics is
`ital`, the font's italic half-width alphanumerics. Both are on or off
only (`hkna=2` is refused), set through `features` and
`featureChanges`, cleared by Reset OpenType Features, and are switches
in Character Style Options > OpenType Features.
Docs: docs/mcp.md, docs/type-parity.md, docs/target-app-parity.md.
The view items of these panels' menus kept their choice in egui panel
state, so no command could set the view. The views now live in the UI
state for the session, and ui.panelView {panel, view?} sets or reads
them; the menu items and the Swatches panel's list and thumbnail buttons
run it. An unknown panel or view is an error and changes nothing.
Docs: docs/mcp.md, docs/target-app-parity.md.
Merge carried each old edge's paint only to the new edge nearest the old
edge's middle, so when an added path cut a repainted edge, the other
piece went back to its source's stroke. Every new edge whose middle lies
on an old edge now takes that edge's paint, with its rows fitted to its
part of the edge, as Gap Options does.
Reverse Path Direction reversed each member path of a compound path and
turned the members' own stroke records, but left the compound path's
strokes as they were. A compound path's strokes run along its members'
subpaths in order, so its width profile and pen pressure, tilt, bearing
and rotation then ran the old way and the stroke drew differently.
reverse_stroke_records now also takes a compound path, reading its
members' subpaths in order, and path.reverse turns each compound path's
strokes with the combined reversal of its members before they reverse.
As for a path, the strokes turn only when every subpath turns alike.