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.
A constrained move keeps only the snap correction along its direction, and that correction exists only when an edge ends within a quarter cell of a gridline. When no edge on those axes is that close, the move does not snap.
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.
A constrained move keeps only the snap correction along its direction: a horizontal or vertical move lands the edge on its own axis, and a 45° move lands the edge on the axis whose nonzero correction is smaller. The per-axis rule now applies to unconstrained moves only.
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.
docs/mcp.md and docs/target-app-parity.md now state that a move in
perspective snaps an edge of the joint bounds only when it ends within
a quarter cell of a gridline (PerspectiveGrid::snap_offset), and that a
move perpendicular to the plane does not snap (perspective.move skips
snap_fix when perpendicular is true).
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.
Records why rust:S4790 (`crates/pdf/src/encrypt.rs`) stays, like wordcraft#6187 did for SHA-1 in ODT. MD5 is the key derivation of the PDF standard security handler revisions 2-4 (ISO 32000-1 §7.6.3.3), which PDF 1.3-1.6 encryption uses. It's needed to read those files and to write the compatibility the user picks. The default, 256-bit AES (revision 6), hashes with SHA-2.
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 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.
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.