While the Type tool edits text, holding Space no longer lends the Hand
tool or, with Shift, the Rotate View tool: the canvas does not show the
hand or rotate cursor, and a drag with Space held does not pan or turn
the view. Cmd+Space still lends the Zoom tool. The default keyboard
shortcuts help page lists the Spacebar as the Hand tool when not in
text-edit mode.
Test: canvas::tests::space_types_a_space_while_editing_text_and_does_not_pan
Edit > Edit Colors > Convert to Indexed Color... (edit.colors.toIndexed)
stores the embedded images in the selection as indexed-color PNGs: a
palette of at most 256 RGB colors, each pixel the index of one. The
dialog previews live and OK is one undo step.
- The palette is chosen as for a PNG-8 export (selective, perceptual,
adaptive, web, gray, black and white), as evenly spaced levels of red,
green and blue (uniform), or from the document's swatches. Dithering
is diffusion, pattern, noise or none, with an amount.
- Pixels under half opacity become one transparent entry; the others
become opaque in their own color. Linked images, colorized images and
color glyph images keep their pixels.
- An image that is already an indexed PNG and would show the same pixels
is left as it is, and no undo step is recorded when no image changes.
- An image of more than 134,217,728 pixels (33,554,432 in the web app)
is refused, read from its header before decoding.
- With Select All, locked and hidden objects that hold an image that is
not an indexed PNG yet are counted in notChanged, without converting
them.
- The quantizer gains quantize_to, which maps pixels to a given palette.
Docs: docs/mcp.md, docs/target-app-parity.md.
The overlapping test now traces the same image eight times with several
levels found at a time and compares each result with the one traced a
level at a time. The doc comment of the stacked rounds says that the
components kept for the second pass take up to 256 MiB outside the half
gigabyte that sets how many levels are traced at once.
When the desktop portal's process exited while its save dialog was
open, no Response came. The stream that save_file read does not end
while the session bus stays up, so the save waited forever on the
calling thread and rfd's dialog never showed.
save_file now waits with the screen color sampler's Responses, which
moves from linux_screen_color/request.rs to portal_request.rs and is
built on Linux and the BSDs. Before the call it listens for the
Response and for the bus's NameOwnerChanged for the portal's name, so
the wait ends with an error when the connection that replied leaves
the bus, and the caller shows rfd's dialog, as for other portal errors.
When the bus reports the portal gone, the wait first reads the
Responses already queued, so an answer the portal sent before it left
is still the answer. Before, the sampler's wait could take the bus's
report while that answer was queued and end with an error.
The mock portal gains three modes that close its connection after the
reply: 300 ms later without a Response, at once without a Response,
and at once after the Response. The private bus helper of the
sampler's tests moves to portal_request.rs, and the save dialog's
tests use it, so a failing test also stops its bus.
file.newProfiles.edit and file.newProfiles.reset returned the automation
access check and file system errors with the profile file's whole path,
so a client confined to read roots that leave out the preferences folder
learned where the folder is. The errors now go through naming_in_error,
like other paths the client did not give: whole inside the read roots,
otherwise by file name. docs/mcp.md and the params text say so.
The graph compares the paints a command leaves on it with the paints it
stored before the command, not with the value before each mark's
capture. Apply Fill to Stroke on two marks whose strokes already had
their fills stored the back mark's stroke and then the front mark's
again, and recorded an undo step with nothing changed. The same
comparison applies to the opacity and blend mode a series keeps.
docs/mcp.md states that only the topmost visible row's paint is stored,
and the Graphs line of docs/target-app-parity.md states that a series
that draws Filled Lines bands keeps no stroke paint or weight.
The Scale and Reflect tools, object.scale, object.reflect and the
Transform panel flips act along a rotated bounding box instead of the
page axes, until Object > Transform > Reset Bounding Box, as Free
Transform does. The Scale and Reflect dialogs and the Properties panel
flips run the same commands and follow the box too.
- object.scale and object.reflect with a named axis take
basis?: "box"|"page" ("box" by default; "page" keeps the page axes;
any other value is an error before anything changes). The journal
records the basis, so recorded actions replay in the same frame; an
older recording without it acts along the box.
- A numeric Reflect axis and a Reflect axis drawn with the tool stay
lines on the page. While a Reflect drag has not drawn an axis away
from the reference point, the tool uses the box's vertical axis, and
Shift steps 45 degrees from the box.
- Mixed-angle selections and anchors selected with Direct Selection use
the page axes; an Alt-drag copy of such anchors scales the whole
objects along their box. Transform Again repeats the page matrix.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md.
A document or brush library file could hold two brushes of one name.
Renaming the first in Brush Options moved every stroke of that name and
left the second brush unreachable, deleting by name removed both, and
the Brushes panel could not select the second row.
Reading a stored brush library now keeps the first brush of a name as
stored and numbers each later one with the first "Name 2", "Name 3" and
so on that the file does not already have. Strokes name their brush,
and a name always found the first brush, so every stroke keeps drawing
the brush it drew before. The rule applies to opened documents and to
brush library files; brushes added to a document that already has the
name keep today's numbering.
Docs: docs/mcp.md, docs/target-app-parity.md.
An envelope around type of only spaces and a rectangle mapped from the
union of the rectangle and the type's laid-out box, so the empty type
moved and scaled how the rectangle was distorted. Type that draws
nothing now adds no box while other content in the envelope has one,
in groups and layers and as a clipping path too. When nothing else in
the envelope has a box, the type keeps its laid-out box, so an envelope
of only empty type keeps its frame, Make with Mesh and Reset with Mesh
still work, and Release still gives back the warp shape.
This applies to Make with Warp, Make with Mesh, Make with Top Object,
Reset with Mesh, Edit Contents and the stroke margins. Type that has
not been measured yet keeps its laid-out box as before.
The PDF/.ai import's boxes for envelopes with Distort Appearance
measure each object as the whole contents measure it, so the margins
the import tries match the contents' geometric box. The three import
tests that measure type without registering the glyph measure now
register it first, so another test registering it while they run no
longer changes their result.
vectorcraft_testkit::temp_dir returns a bare path, so every engine test
that wrote into one left the folder in the system temporary folder.
- testkit: TempDir, a test's own folder made through temp_dir (same
private creation and ownership checks) under a tag no other TempDir in
the process uses, removed with its contents when dropped, also when
the test panics.
- Engine unit and integration tests hold a TempDir for the whole test
instead of calling temp_dir: graph, fileio, compare, journal, library,
shortcut set, spelling, store, web export, cancel, containers, build,
color settings, command sweep, font folder, removed font, model-based
and the font search fuzz.
- The DCS and swatch library tests' own helpers and the PDF editing
tests' shared vc-pdfedit-<pid> folder use a TempDir per test too.
- The font and link file search, store, color settings and font folder
helpers no longer delete the private folder temp_dir checked and make
it again with default permissions.
With automation read roots in force, svgInteractivity.info and the files
reply of addFile and removeFile returned every linked JavaScript file as
stored, including paths outside the roots. They now name such a file by
its file name alone, as document.json does; a file inside the roots and
a URL are named as they are, and the document keeps every path.
removeFile also unlinks a file outside the roots by that file name. When
the name fits more than one linked file, it returns an error that names
no stored path and unlinks nothing. addFile still treats only the same
text as already linked.
In a frame a request drives, the JavaScript Files dialog lists such a
file by its file name while its selection keeps the file as linked, and
ui.inspect gives the selected file the same way. The person's frames
show full paths.
A file read for Place, for the place cursor or dropped on the canvas,
a bitmap read for Paste, and a paste waiting at its Swatch Conflict
dialog are no longer placed or pasted in a document that File › Revert
replaced while they waited. Revert keeps the document's tab and uid, so
these were placed in the reverted content with the selection, clipboard
and insertion context of the content it discarded.
- DocState::reverts counts the Reverts of a document; replace_document
increments it, and a batch that fails puts it back with the document.
- Reads, deferred swatch pastes and web canvas drops keep the count they
started with and are dropped, with a status message, when it changed.
- A paste given after a Revert starts a chain of its own instead of
following a paste given before it.
The widget's Constrain button was grayed out in Free Distort, and
neither it nor Shift changed a Free Distort corner drag. Constrain is
now available in Free Transform and Free Distort, and stays unavailable
in Perspective Distort.
With Constrain on, or Shift held, a corner dragged in Free Distort
moves only along the line through the opposite corner, in the box's
own proportions, and the other three corners stay in place. On a
turned box the line runs through the box's own corners. A box with no
size leaves the corner where it is. Cmd/Ctrl+Alt+Shift on a corner
still drags in Perspective Distort. The measurement label shows how
far the corner moved.
85 Hindi rows used an unrelated sense of their English word. Desired
read as देसी (local), and the range message dropped or garbled the
desired bound; rulers read as शासक (sovereigns), text characters as
चरित्र (personality), vector points and printer's marks as अंक
(digits), fills as the verb भरता, High Contrast as उच्च न्यायालय
(high court), Hide Thumbnails as हिंदुस्तान, Same as समीर and Even as
शाम (evening). They now use the catalog's terms: वांछित, रूलर,
कैरेक्टर, पॉइंट, मार्क्स, फ़िल्स and the plain words for the rest.
The Hindi glossary test rejects each of these senses in every row whose
English names the word, and the meaning test pins Desired and both
range messages, minimum <= desired <= maximum.
Object › Path's commands that need a path (Reverse Path Direction, Join,
Average, Simplify, Smooth, Add Anchor Points, Divide Objects Below, Split
Into Grid) report "select a path" when the selection holds no path, and
Select Path's stored selection reports it with nothing selected. Only the
Hindi and Swedish catalogs had a row for it, so the status bar showed the
reason in English in the other 21 languages that translate every message.
- Add the row to cs, de, el, es, fr, hu, it, ja, ka, ko, pl, pt-br, ru,
uk, vi, zh-hans, zh-hant, fi, tr, ca and nl, using each catalog's
wording from its "Divide Objects Below: select a path" row.
- Add a test that requires the exact row in every complete-message
catalog and checks the translated reason inside the disabled-command
error that path.reverse returns for a selection of type alone.
The new Trim at Crossings Tool removes stretches of paths between the
places where they are crossed by another path or by themselves. A click
removes the stretch under the pointer, a drag removes every stretch its
rectangle touches, and an Alt-drag removes every stretch its freehand
line crosses. It works on every path that can be edited, selected or
not, and each gesture is one undo step.
- `addon.pathtools.trim {at, tolerance?}`, `{rect}` or `{points}` is the
command behind the tool. A closed path that loses a stretch becomes
open, an open path that loses a middle stretch becomes two paths with
its appearance in its place in the stacking order, and a path that
nothing crosses is removed whole. The stretches that stay get no new
anchors.
- Hidden and locked paths, guides and clipping paths are not trimmed and
do not count as crossing. Only the paths the gesture reaches are
searched for crossings, and one work limit covers every path the
command trims.
- `crossings_on` and `crossings_on_within` in `vectorcraft-pathops` find
the crossings on one path and compare it only with the segments in
its box.
- The new tool shares the crossing search with Add Points at Crossings
and Split at Crossings, so those change too: two segments of a path
that meet at an anchor, directly or across segments with no length,
now count as crossing where they cross again away from that anchor,
and do not count as crossing at the anchor itself. With Self
Crossings, a curved segment that loops over itself counts as crossing
itself. Before, both crossings were missed.
- The tool is an add-on tool: Object › Plug-ins › Path Precision Tools
and the All Tools drawer list it, it has no shortcut, and neither
shipped toolbar shows it.
Docs: docs/mcp.md, docs/ui-parity.md, docs/target-app-parity.md.
A Transform effect whose result would pass the geometry budget (2 million
anchors or 8 million lines drawn) is left out and the art stays where it
was, but the raster effects listed before it still scaled and moved with
it: before a Transform effect that scales the art 1000 times, a Drop
Shadow's offset grew 1000 times and its blur went to the 1000 pt limit
around art that had not moved, and Expand kept those values.
The geometry pass now notes each Transform effect that moves the art once
and that it leaves out, in the Carry it already returns, and
Carry::hide_skipped hides those effects in the list the raster effects
are read from. The canvas (a path's own effects and its fills' and
strokes'), its bounds, bake (SVG and PDF export), shape art, Expand,
Expand Appearance and Outline Stroke use it, and a fill's Transform
effect inside a piece no longer scales the effects before it when the
budget leaves it out. The budget itself is unchanged.
A path drawn as its own art inside a group whose geometry effects run
after its members' own read its object effects whole. The reshaping pass
now notes a Transform effect that no path in the art took past the
budget, and the member's art hides it before its raster effects are read.
The bounds skip the extra geometry pass when no raster effect could
follow a Transform effect, and painted_reach runs it once for both
measurements.
A line in area type of a shape other than a rectangle was measured at five heights of its band, so an edge that pinched in between two of them (a notch tip at y = 47.5 in a 40..60 band) left the line running outside the frame. The frame's intervals over a band are now the intervals at its middle minus the x range of every edge within the band, which is exact for any number of vertices, holes and crossing contours.
Edges that contours meet along do not shorten a line: horizontal ones count by coverage, and collinear ones by overlap, and an edge that only touches the top or bottom of the band is left out. Rectangles keep their fast path.
Text wrap shapes were sampled at the same five heights, so a wrap object whose tip reached into a band between two of them let the line run across it. A line now keeps clear of every x at which a wrap shape, grown by its offset, lies anywhere in the band: the shape's intervals at the middle of the band plus every edge within it. With Invert Wrap, the band is cut into pieces a quarter of the offset tall (64 at most), and a line keeps to the x within the offset of the shape at the heights each piece's offset window holds, so it stays inside the grown shape over the whole band; an offset of 0 takes the shape's intervals over the whole band.
A frame less than 0.0002 pt tall no longer panics when a line is measured in it.
A param key names a file or a folder by a reviewed list of whole names,
read in the context of the command it is for (ParamsPlace): the
plug-in call check, the control server's reply hiding and recorded-step
filtering, and the sweeps' temp-folder redirection all use it.
- `url` (a web link) and `folders` (an export's grouping) no longer
count as files or folders, and keys that only end like one
(alignToPath, wholePaths, sortPaths, linksFolder and others) are no
longer path params in control replies or recorded steps.
- `dir`, `directory` and the folder preferences that prefs.set takes in
`values` (fontsFolder, pluginsFolder, recoveryFolder,
templatesFolder) are folders everywhere, so a relative path under one
outside the read root is taken out of a recorded step.
- A `command.batch` step, an `engine.execute` request and a recorded
step keep their own command's exceptions below the top level, so the
sweeps no longer replace a nested web link or export grouping.
- A new test sorts every catalog key that reads like a file or folder
key into the reviewed lists and checks every classifier against them.
A Fit to Artwork Bounds step of `command.batch` that gets no artboards from
the Artboard tool or the batch fits the first artboard, as the command does
by default, and its journal entry now records that `index`. Before, the
entry kept the step without it, so a replay with the Artboard tool choosing
two or more artboards fitted those instead.
A `command.batch` step with `"params": null` now reads as a step without
params. Before, it got neither the Artboard tool's selected artboards nor
the batch's `artboard`, so a Fit to Artwork Bounds step fitted the first
artboard and its journal entry recorded `null`.
When a Fit to Artwork Bounds step of `command.batch` takes the batch's
`artboard` as its `index`, the batch's journal entry now records that
`index`. Before, the entry kept the step without it, so a replay with the
Artboard tool choosing two or more artboards fitted those instead.
The params text, docs/mcp.md and docs/target-app-parity.md state that the
step uses the Artboard tool's selection only when two or more artboards
are selected, and the active artboard otherwise.
The Live Paint Selection tool always showed the plain arrow. Its cursor
now says what a click at the pointer selects, by the same rules as the
click (Alt, Select Fills, Select Strokes, locked, hidden and isolated
groups): the arrow with a filled square over a face, with a short line
between two anchor marks over an edge, and with an x where a click
selects no face or edge. From a press to the release the cursor keeps
the look it had at the press. Use Precise Cursors shows the crosshair.
- Cursor: LivePaintSelectFace, LivePaintSelectEdge and
LivePaintSelectOutside, with cursors.toml entries and drawn glyphs.
- Tests: live_paint_selection_cursor_shows_what_a_click_selects,
live_paint_selection_shows_the_arrow_with_a_face_edge_or_x_badge, and
the precise-cursor list.
Docs: docs/mcp.md, docs/target-app-parity.md.
An Overlapping layer covers the colors above it within its area, so an
area of another color that a shape enclosed was a hole in that shape
and in the shape of every layer around it, and its outline was fitted
once for each of them. In a code-drawn test, a white disc in a black
ring that four grays touch had six copies of its outline.
With Fills and without Create Strokes, each shape is now filled whole,
by a fill rule of VectorCraft's own. The area inside a filled hole is
traced on its own and drawn after the shapes around it, so its outline
is fitted once. An area of one color is drawn from the hole's outline.
A shape keeps a hole where the area holds a transparent pixel or a
color that Ignore Color or Keep Colors leaves out, and a hole smaller
than Noise is filled with the shape's color as before. Nested areas are
traced within a work budget of VectorCraft's own: four times the
image's pixels (counted as at least 65,536) times its colors, each area
costing its box times its colors. An area that costs more than what is
left keeps its holes and is not charged. Create Strokes and outlines
without Fills are unchanged.
Shapes have holes only in those cases, there are more paths, and the
stacking order changes. Expand by Color starts another group of a color
where an area is drawn above the shape around it. The trace's progress
counts finer steps across the rounds of enclosed areas, not one step
per color layer.
A layer's components without its own color are now left out after its
mask is traced, instead of in a labeling pass before it. Every route
finds its components through the same function.
file.close {index} on a tab other than the current one made the
document now at the closed index current and reset the tool, so the
document the user was working in went to the back. The current
document now stays current, found by its id as document.move does, and
its tool keeps its state. Closing the current tab still makes its
neighbor current.
Docs: docs/mcp.md, docs/target-app-parity.md.