Scripts write to a new Script Console with $.write(...values) and
$.writeln(...values), each value as String(value) gives it. vc.log
entries, a run's uncaught error as name:line:column: message, and a
start, end and report line for each run go there too. The report line
gives the run's time, host calls and slowest commands.
- The console keeps at most 2,048 lines and 1 MiB of text, dropping the
oldest and counting what it dropped. A line is cut at 4,096 bytes and
a script's name at 256 bytes, with a count of cut lines. Line feeds,
carriage returns and CRLF break lines; other control characters show
as Unicode Control Pictures.
- script.run's result and scriptReport gain runTimeMs, timingAvailable,
hostCallCounts, slowestCommands and the run's own console. A failed
run's report has them too.
- script.console and script.console.clear read and empty the console;
ui.copyScriptConsole {first?, last?} copies lines as the panel shows
them.
- The panel lays out only the rows in view, selects with click and
Shift-click, and has Clear, Copy Selected and Copy All. Output goes on
while it is closed, and Clear works during a run.
Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
When the first object of a perspective move was not yet in perspective
and a later one was attached deeper on the same plane, Snap to Grid and
Shift read the shared offset at the later object's depth and left the
first object out of the joint bounds. The first object then moved by
that offset instead of the one under the pointer at its own depth.
The move now reads Snap to Grid and Shift with the objects that are not
in perspective attached to the active plane, as the move attaches them,
so the result matches attaching them first and then moving.
Part of #6435.
Reset Preferences in the Preferences dialog now opens a question over
the dialog (`resetPreferences`). Cancel or Escape goes back to
Preferences as it was, with its edits, category and search kept. OK goes
back with every preference at its default value; OK in Preferences
applies them and Cancel there discards them, as before. `prefs.reset`
still resets at once.
Docs: docs/mcp.md, docs/target-app-parity.md.
text.setStyle now reads `size` and `tracking` with `number_given` and
`length_given` from pathops.rs instead of its own copies.
Both helpers read a string only when all of it is a number, or for
`length_given` a length with a unit (`"5 mm"`). Before, `"20abc"` read
as 20, so `text.setStyle` applied a 20 pt size and Offset Path a 10 pt
offset for `"10abc"`. Such a string is now refused with "`size` must be
a number, not \"20abc\"" and the document is left as it was.
visit_node_colors visits the fill and stroke paints a dynamic symbol
instance keeps for objects of its art (symbol_overrides). Before, print
separations, Separations Preview and document color conversion left
those colors as they were, so an instance's own fill printed and
previewed unseparated.
Two PDF export passages in docs/target-app-parity.md said a gradient
that mixes a spot color with RGB or Lab stops is always written without
the spot ink, or as an image when Perceptual. A CMYK document, or an
export that converts colors to a CMYK destination profile, writes those
stops in CMYK first, so the gradient is a DeviceN shading that keeps the
ink; an RGB destination profile keeps them in RGB. The passages now say
so, as docs/mcp.md and the paint.editGradient params text already do.
A new test checks the Classic case in an RGB document, a CMYK document,
with a CMYK destination and with an RGB destination.
Batch with Override Export compares output names without case, so
`a.svg` and `sub/A.svg` get the Export for Screens folders `a` and
`A-2`. A source whose name without its extension is empty (` .svg`)
gets the folder `file`, and a later `file.svg` gets `file-2`. Two tests
cover these cases.
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
Shaping split a cluster wherever two characters' styles differed, so
setting Standard Vertical Roman Alignment or a tate-chu-yoko offset on the
f of "fi" in horizontal type broke the ligature. Horizontal type does not
read those settings, so they no longer split a cluster there. Vertical
type still shapes such characters apart.
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 check that refuses a symbol instance before its art is placed
counted only the symbol's own objects. Placing art through 9-slice
guides can make up to 28 objects of each one, and an instance's
dynamic-symbol overrides can turn a path that would be cut at the
guides into clip groups in every cell it reaches. Such an instance
passed the check, and its art was placed in full before the work
limit refused it.
The check now counts the symbol's own objects first, then the most
objects placing makes (NineSliceMap::most_placed) of the art with the
instance's overrides applied, and refuses the instance with the same
message before anything is placed.
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.