Reset Preferences put Points in Units > General, and OK sent it to
prefs.set, which also set the open document's units as an undo step.
OK after Reset now resets the General preference and leaves the
document's units as they are. Choosing a General unit after Reset, in
the dialog or with ui.dialog.set, still sets the document's units.
prefs.set takes documentUnits (default true); false makes unitsGeneral
set only the preference. Docs: params text, docs/mcp.md.
Part of #6435
object.repeat.radial, object.repeat.grid, object.repeat.mirror and
object.repeat.options, and object.pattern.make, object.pattern.edit,
object.pattern.saveCopy, pattern.options and pattern.delete, read their
params with helpers that fell back to the default for a value of the
wrong type: "instances": "8" made 8 instances whatever the text said,
"rows": "2" made 3 rows, "edit": "false" entered pattern editing mode,
and a `name` that was not a string edited the selected object's pattern.
Each such value is now an error naming the param, and the document, the
selection and the undo history stay as they were. An omitted or null
param keeps its default.
- pattern.options refuses an `overlap` that is not an object, and an
`overlap.h` other than left or right or an `overlap.v` other than top
or bottom.
- object.repeat.options refuses a `center` that is not [x, y].
- object.repeat.radial refuses a negative `radius`; it used the
automatic radius before.
- object.repeat.grid reads `rows` and `cols` as object.repeat.options
does: a fraction is cut to a whole number, within 1 to 500.
- object.repeat.options applies `brickOffset` given without `gridType`
to a brick grid, and its params text lists the key.
Docs: docs/mcp.md.
After a Scissors cut (`path.split`) the whole path stayed selected: both
pieces of an open path, or the opened path of a closed one. A Direct
Selection drag from the cut then moved every anchor, so an ellipse cut
at an anchor could not be pulled open by dragging its end.
- `path.split` selects the first anchor of the piece after the cut and
nothing else, as `path.cutAtAnchors` does: anchor 0 of an opened
closed subpath, of the new path when an open path becomes two, or of
the subpath added to a path of several subpaths.
- A Direct Selection press on anchors of a selected path that lie on top
of each other takes the selected one. It took the first in path order,
which in a path of several subpaths was the unselected end.
Docs: docs/mcp.md, docs/target-app-parity.md, the `path.split` params
text.
`text.setStyle` read `font`, `style` and `justify` with `str_param`,
`size` with `len_param` and `tracking` with `num_param`, so a value of
the wrong type was skipped: `{"size": 30, "style": ["Bold"]}` set the
size and ignored the style, and `{"font": "Inter", "size": [12]}` set
the font and ignored the size. `features` dropped an entry that was not
a string and applied the rest. `text.setRangeStyle` skipped a `font` or
`style` that was not a string, `type.ruby` skipped such a `text`,
`position` or `align`, and `text.setPathHandle` moved the out handle
for any `which` other than "in".
Each of these now returns an error that names the param, before the
command edits the document. Omitting a param or giving null keeps its
default. `size` and `leading` still take a length with a unit
("12 pt", "5 mm"), and `tracking` still takes a number written as a
string ("20").
- `text.setStyle`: `font`, `style`, `justify` (`text_param`), `size`
and `tracking` (checked reads in `typecmd.rs`), `features`.
- `text.setRangeStyle`: `font`, `style`, `features`.
- `type.ruby`: `text`, `position`, `align`.
- `text.setPathHandle`: `which` must be "in" or "out".
Docs: docs/mcp.md.
answer_dialog sent ui.dialog.set and then ui.dialog.confirm or
ui.dialog.cancel as two requests. A dialog another control connection
opened between them was confirmed or canceled without the fields.
ui.dialog.set takes `action: confirm | cancel`, checked before any field
is set, and confirms or cancels the dialog the fields were set in within
the same request, replying {dialog, result}. answer_dialog sends set and
action together; an app whose reply has no `dialog` key did not answer
it, and the dialog is then confirmed or canceled with its own request.
A cap inset by a bevel fitted each run of sampled edges along one curve
as a whole, while the wall next to it fitted each sampled edge by itself.
The two outlines lay up to 0.25 pt apart on an 800 pt circle, which left
a gap or an overlap between the cap and the wall.
An inset outline is now fitted one sampled edge at a time in the cap as
well, so both faces draw the same curves. Caps that are not inset keep
one cubic for each curve of the art.
Test: solid::tests::a_beveled_cap_and_its_walls_share_their_curves.
Docs: docs/three-d.md, docs/target-app-parity.md.
A 3D effect on a group built its solids from the members' paths and
paint and left out each member's opacity mask, and the mask of a group
inside the 3D group. Clipping groups, blends and symbol instances
resolved for the effect lost their masks the same way.
A member, or a group inside the 3D group, with an opacity mask now
gets a scope like one with its own opacity or blending mode, and the
mask goes on the group that holds that member's surfaces. Revolve
applies it with the member's opacity and blending mode; Extrude &
Bevel, Extrude & Bevel (Classic), Inflate and Rotate draw each solid
on its own when a mask is present and put the masked members' solids
in a group with the mask. A resolved clipping group, blend or symbol
instance keeps its mask. A disabled mask is ignored.
The progress entry said caps and the walls' cap edges are cubics without
its limits. It now lists the cases three-d.md describes: an edge of a
twisted wall that is cut into pieces, a piece of curve that does not fit
after 8 halvings, and the curves past 1,048,576 for one solid.
transparency.makeOpacityMask read `clip` and `invert` with bool_or,
transparency.setOpacityMask read `clip`, `invert`, `disabled` and
`linked` with as_bool, and the New Opacity Masks and page toggles read
`value` with bool_or, so a value of the wrong type was dropped:
"clip": "false" made a clipping mask, "invert": "yes" next to another
flag was ignored, and "value": "on" toggled the page knockout group.
They are now read with switch_or, and such a value is an error that
names the param, before anything changes. Omitting a param or giving
null keeps its default.
Extrude & Bevel (both kinds) and Rotate wrote every face as straight
segments between sample points, so Expand Appearance and the exports
gave a corner at every sample. Sampling now records the curve each
polygon edge follows, and a facet carries an outline of cubics rebuilt
from it: exact without perspective, within 0.001 pt with it, and within
the sampling tolerance for bevel insets. Caps, Rotate's face and the
wall and bevel edges that meet a cap keep curves; interior bevel edges,
edges a twist cuts and edges past 1,048,576 curves stay straight.
Inflate's faces stay polygons.
Docs: docs/three-d.md, docs/target-app-parity.md.
A plug-in that went on calling after a failed `vc_call` still had each
later call made on the document, although the run fails and is undone
at its end. After the run's first failed call, `vc_call` now returns
`-11` (CALL_NO_CONTEXT) and the host makes no further call; the run
still fails with the first call's reason.
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.
Make Compound Path leaves each member's graphic style id in place. A
member copied or cut on its own took the compound's appearance but kept
that old id, so the clipboard carried an unrelated style and the pasted
path was linked to a style that did not match its look.
The copied member or piece now takes the compound's link while it has
that style's look, and no link otherwise (no style on the compound, or
a gradient pinned to the compound's bounds). It also takes the
compound's knockout setting with the rest of its transparency.
object.expand read `object`, `fill` and `stroke` with bool_or, `steps`
with f64_or and `gradient` with str_param, so a value of the wrong type
fell back to the default: "fill": "yes" expanded the fill, "steps": "16"
made 255 strips and "gradient": 3 made strips. They are now read with
switch_or, number_or and text_param, and such a value is an error that
names the param, before anything changes. Omitting a param or giving
null keeps its default.
A call whose arguments hold a file or folder key at any depth, such as an
effect's textureImage.path, is refused before the command runs; the walk
keeps its own stack. Each call also runs with automation file access
confined to no roots, so every checked read or write a command makes is
refused, whichever param carried the path.
Create Gradient Mesh, a Mesh tool click and Object > Expand of a gradient
to a mesh gave each border edge one cubic fitted between two mesh points
and picked the corners from the bounding box, so the border cut off
corners and curves: 49 pt off a five-point star, 54 pt off a notch.
- The border follows the outline of the largest subpath by length; border
points sit on it and an edge with no anchor inside is the exact piece
of its segment.
- Where no single cubic follows a run within 0.1 pt (a thousandth of the
size on a small outline), a mesh line goes through an anchor, the
sharpest corner first; at most 256 lines each way. Rectangles and
ellipses keep the rows and columns asked for.
- Smooth runs keep their end tangents; border handles are no longer made
collinear.
- To Center and To Edge highlight placement accounts for the extra lines
(for_path_shaded, also used for gradient fills).
- Non-finite points give no mesh; a degenerate outline keeps the old
bounding-box mesh.
Holes of a compound path are still left out, as before.
Docs: docs/target-app-parity.md.
- PDF 1.3 and 1.4 files, PDF/X-1a, PDF/X-3, PDF/X-4 and PDF/A-2b at
PDF 1.4 among them, write a page longer than 14,400 pt in points
again, now with a warning that apps that keep to the limit cut it to
200 in. Only a page that failed with
"PDF writer error: Limit(TooLargeFloat)" gets the new error: one
whose size or art needs a real number past 32,767, the most PDF 1.4
holds. Whole numbers are written as integers, so a page of whole
points past 32,767 is written as before.
- A page whose side is past 32,767 pt and not a whole number of points
is refused before the thumbnails are drawn and the transparency is
flattened.
- A PDF 1.5 file written as PDF 1.6 for a long page keeps PDF 1.5's
encryption (128-bit RC4), which the Save PDF dialog shows.
- A long page whose short side would be under 3 units at its UserUnit
(ISO 32000-1, Annex C.2), such as 50 m x 4 mm, gets a smaller
UserUnit that keeps the short side at 3 units, with a warning.
- The warnings give a page's size rounded up, as the error does.
A page of 14,400 pt or less is written as before. The new warning has
rows in every complete catalog. Docs: docs/mcp.md,
docs/target-app-parity.md and the document.exportPdf params.
`Session::history_budget` (default `default_history_budget()`) caps the
memory the undo steps of all open documents keep together. After each
command, tool edit, interaction commit, direct edit, framed script run
and closed undo group, `Session::trim_histories` drops the oldest steps
of any document first, undo steps before redo steps. Each document keeps
its last eight undo steps and next eight redo steps, and a held history
keeps all of its steps. While a script runs or an undo group is open,
no steps are dropped, so a canceled group leaves the other documents'
steps in place. Closing a document frees its share. The steps are not
counted again while neither the budget nor any history changed. Each
document's own `byte_budget` stays as its ceiling.
edit.colorSettings set only the process-wide colour settings, so the RGB and
CMYK working spaces, intent and black point compensation went back to the
defaults at every start, although the profiles color.loadProfile loads are
kept (#2168, #2174). The engine now writes them to a settings file when they
change (Session::attach_color_settings), and the desktop app and
vectorcraft-cli keep that file as `Color Settings.json` next to the
preferences, attached right after the profile store so a saved working space
may be a loaded profile. Saved settings that can't be used (a profile that is
gone, a damaged or oversized file) leave the defaults with a log record, and
the next change writes over them. Nothing is read or written with
VECTORCRAFT_NO_PREFS.
A compound member copied or cut on its own now keeps the compound's
gradient fitted where it was: an unplaced gradient is pinned to the
compound's bounds, measured once per compound.
Clear that deletes every anchor or segment of every member of a compound
path now removes the compound too, as Cut does, instead of leaving an
empty compound in the layer.
The edit.copy and edit.clear descriptions, docs/mcp.md and the progress
line now say that selecting the compound copies it whole, that isolation
mode takes each member as itself, and how a compound of one and Paste in
Front with the copied member still selected behave.
vc_call returning -11 (from the start function, or in a run that can't make
calls) now fails the run like every other code, so a plug-in that ignores
it can't report success for a call that never ran. Calls are allowed once
the module has loaded, so a start-function call gets -11 instead of
trapping.
The message for a group member that can't become a solid now names groups
and clipping groups too. docs/three-d.md states that an image left out of a
clipping group also needs no visible fills or strokes of its own.
A compound-path member selected on its own, whole with Direct Selection
or with Group Selection, is now copied as an ordinary path instead of
promoting the selection to its compound. Cut removes the same objects
Copy takes, so the other members stay a compound path, a compound of
one included. The compound is still taken whole when every member is
selected whole, or when the compound itself is selected.
A copied member or piece takes the compound's paint, opacity, blending
mode, opacity mask, pixel alignment and fill rule.
Tests that pinned the earlier rule from #786 now check the new one.
A command's result describes the document, so a plug-in without doc.read
gets -6 for every call, whether or not the id names a command. A run that
calls commands commits an edit still in progress first and prepares its
input again when that changed the document or the selection.
- A new test walks every code point from U+0000 to U+10FFFF, surrogates
left out, and compares the start and part checks with Unicode Standard
Annex #31's general-category definition of ID_Start and ID_Continue,
written out in the test. The test also checks the set sizes against the
totals in Unicode 16.0's DerivedCoreProperties (141,269 and 144,541).
- The Other_ID_Continue test covers every code point of the property,
U+136A to U+1370 and the zero width non-joiner and joiner included.
- The zero width non-joiner and joiner are part of the Other_ID_Continue
list in chars.rs instead of a separate case.
is_id_start now accepts the Other_ID_Start characters (U+1885, U+1886,
U+2118, U+212E, U+309B, U+309C), and is_id_part the Other_ID_Continue
characters (U+00B7, U+0387, U+1369 to U+1371, U+19DA, U+30FB, U+FF65),
as Unicode's ID_Start and ID_Continue properties and the ECMAScript
Names and Keywords clause define them. Letters, marks, digits and
connectors that are Pattern_Syntax are refused, which replaces the
U+2E2F special case. Escaped identifier characters go through the same
check. A supplementary letter right after a number is now the
"identifier starts immediately after numeric literal" error.
Letter categories still come from Unicode 16.0, so characters assigned
in later versions are not identifier characters yet.
Docs: docs/mcp.md, docs/target-app-parity.md.