Offset Path took each selected path in turn, so offsetting many paths, or a
group of many paths, used one CPU. It now reads the selected paths first,
works out their offsets on as many threads as the computer has logical CPUs
(one thread on the web and for a single path), and then makes the new
objects one after another in the selection's order on the editing thread.
The new objects, their ids, their stacking, the selection, the messages and
the undo step are the same as before. When several paths fail, the error is
the one for the first of them in the selection's order, and the paths after
a failure that have not started yet are skipped.
A right-to-left line draws the text before a tab after it, but its width
was measured in the order of its text. A right-aligned line such as
"אב גד<tab>הו זח" with a stop at 150 pt in a 300 pt frame ended 5.59 pt
past the frame's right edge, and right, center and decimal stops aligned
the text before the tab instead of the text drawn after it.
- The line's width is measured in drawing order, each tab up to its stop
from where the glyphs drawn before it end. Right, Auto and centered
lines are aligned by that width. Stops are still measured from the
left edge.
- Right, center and decimal stops align the text drawn after the tab.
- Area type breaks a line with a tab where it fits as drawn, and wraps a
line that fits only in the order of its text. Once the line holds a
tab, it reads on while the least it can be drawn at fits, then ends at
the latest break that fits as drawn: up to 256 breaks are measured
from the latest back, and earlier breaks without a hyphen are
bisected. Hyphenation points are measured as drawn the same way, so
the line hyphenates at a point that fits.
- Only lines of horizontal paragraphs with right-to-left text keep a
list of their break candidates.
- A tab that ends a wrapped right-to-left line is drawn before the line
at its own width instead of pushing the line to the right.
Docs: docs/target-app-parity.md.
Type on a path whose first paragraph is aligned Justify All places its line
from the start bracket, the same as Left, so a threaded story is now measured
with the paragraphs it needs rather than the whole rest. A 1,500-paragraph
story through 40 paths aligned Justify All was laid out over 2,135,202 bytes
while measuring and is now laid out over 20,583. The frames take the same
text as before.
Center, Right, Auto right to left, hanging punctuation, Dynamic Text and
horizontal right-to-left text are still measured with the whole rest: a
centered or right-aligned line starts where the width of all its glyphs puts
it, and negative tracking can make an advance negative.
Tests compare every frame's share with the whole-rest share for Justify All
with spacing, on closed paths, with negative tracking and with punctuation
ending paragraphs.
An SVG shown as an image that could not be read (text that isn't XML,
an SVG without a size) still took one of the 256 nested SVG images an
SVG may show, so an image after 256 broken ones was left out. Only
images that decode now count toward that limit; a failed image gives
its place back.
Every nested SVG image read, decoded or not, counts toward new limits
of 1,024 reads and 512 MB of text per import, so a broken image shown
many times through <use> is still read a limited number of times. Past
them, nested SVG images are left out with a warning.
A nested SVG image with text that usvg then refuses no longer warns
that its text isn't imported.
Under a light that is not white, an opaque Inflate fill or stroke in a
solid color was drawn as one composite of all its facets, so the
object's solids were drawn whole, one after another: the stroke's faces
covered every face of the fill, nearer or not.
When an object's solids share one order (all opaque with Normal
blending, or one member's under its opacity mask), each facet of such a
solid is now drawn on its own, with the composite's paint, channel
light ramps and seam width, and takes its place in that order as the
faces of a gradient already do. A lone solid, and the solids of an
object with a translucent or blended fill or stroke, keep the one
composite.
A face drawn on its own is about 16 nodes, against about 6 per facet
in the composite of a whole surface. These faces go in one order while
they number at most 30,720 together, counted again after the cuts.
Past that, each such solid is drawn as one composite.
Docs: docs/three-d.md, docs/target-app-parity.md.
With a path selected whole by the Direct Selection tool, the bar's
"Smooth" button converted every anchor point to smooth at once. It was
the anchor convert, labeled like the path Smooth that the overview lists
beside Simplify as adjusting the path's curvature, and the bar had no
button for Object › Path › Smooth….
- A path selected whole with Direct Selection now offers Smooth after
Simplify. It runs object.path.smooth the way the menu item does, so
it opens the Smooth slider and changes nothing until it is confirmed.
- The two anchor convert buttons, with a whole path or with anchors
selected, show their icons alone, named by the Control bar's tool
tips "Convert Selected Anchor Points to Corner" and "to Smooth".
The keyboard shortcut part of #6784 is fixed on main by #6810.
Docs: docs/mcp.md, docs/target-app-parity.md, the ui.smoothAuto params.
Fixes#6784
A knockout group was written as a chain of soft masks, one inside the
next for each element, so a group of about 200 elements made a PDF that
VectorCraft's own PDF import refused (form nesting past 50) and an SVG
that the web build refused as too deep.
- PDF writes a knockout group as one transparency group with /K true,
with /I true when the group isolates, and PDF import reads it back as
a knockout group. An element with several paints is drawn as one
group inside it, so its own fills and strokes do not knock each other
out.
- Where an element knocks out less than the area it paints (its opacity
or mask defines its knockout shape, or it paints translucently), each
element is drawn through one soft mask of the overlapping elements
above it. Past 65,536 objects in a group's masks, or 2,048 elements,
the group is a PDF knockout group, with a warning.
- SVG draws each element through one mask of the overlapping elements
above it, built from one shared copy of each element's shape, so the
elements stay siblings. Past 65,536 shapes in a group's masks, or
2,048 elements, the group is written as a plain group, with a warning.
Docs: docs/mcp.md, docs/target-app-parity.md.
With Invert Wrap, each piece of a line's band intersected the line's
spans so far with the spans the wrap shape holds over that piece by
comparing every pair of them. A wrap shape of many parts (a comb or a
zigzag) gives a line many spans, and with an offset the band is cut
into up to 128 pieces, so a dense shape made layout quadratic in the
number of parts for every line.
intersect_intervals now walks the two sorted lists once, stepping past
whichever interval ends first, and gives the same spans in the same
order. Tests compare it with the pairwise intersection on every list of
non-overlapping intervals with ends among seven points, check that two
interleaved lists of 3,000 and 2,000 intervals take at most 5,000
steps, and lay out a line inside a 500-tooth Invert Wrap comb with an
offset.
Spot Color Objects listed every spot color linked from a gradient's stops
once any stop was above opacity 0. A spot stop at opacity 0 between other
clear stops, at the end of the ramp after a clear stop, or meeting an
opaque stop only at one offset was listed though it prints no ink, and so
was an opaque stop out of order whose pairs have no width.
A stop now counts when it bounds a stretch of some width that the
gradient sampler mixes and either stop of that stretch is above opacity
0, or when it is the first or last stop and above opacity 0, since those
color the offsets before and after the others. Stops in any order are
judged by the stretches the sampler uses. A freeform gradient keeps the
old rule, since it draws from its points. Type runs follow the same rule.
The document.info params text, docs/mcp.md and the progress line
describe it.
The pieces of a Warp effect, an envelope or their expanded art each
held a copy of the whole image in the .ai editing data and in legacy
.ai files: 200 copies for a Warp's 10 x 10 mesh.
- An embedded image cut to a clipping path (in the editing data, also
an image under an opacity mask that clips) is written whole the
first time; later copies of the same pixels are written as the
rectangle the path or mask shows, 2 pixels more on each side, with
the matrix moved to keep their place. A copy that the path shows
none of is written as one pixel.
- An opacity mask's window covers everything the mask's art paints,
its effects and symbol art included.
- An image's editable original and an image in an opacity mask's art
are written whole. A live Warp's drawn look is not cut to the
clipping groups around the object, so a clipped image with a live
Warp reads back with its Warp live.
- When art that held an image's whole copy is left out (an opacity
mask's art, a live envelope or a definition past the object limit),
the next copy is written whole again. A whole copy written on hidden
lines after an earlier one leaves the earlier one as the copy that
counts.
- The writers read an image's pixels once for the pieces that follow
one another.
Docs: docs/target-app-parity.md.
Live Paint Bucket Options gets a Cursor Swatch Preview checkbox (on at
first, saved with the other options). With the Bucket selected, Left and
Right set the active fill, or the stroke when the Stroke proxy is in
front, to the color swatch before or after it in the Swatches panel,
wrapping around at the ends, with the preview on or off. The selected art
keeps its paint. The preview shows the active paint above the pointer
between the swatches before and after it; a color that is no swatch shows
alone until Right picks the first swatch or Left the last, and a paint
that is not a solid color shows no chip. The chips hide during a fill
drag and while Alt samples with the eyedropper. The fill and the stroke
each remember the swatch picked for them, so swatches of the same color
are each reached in turn.
The Bucket and the Shape Builder now take the arrows for swatches only
without Shift, Alt or Cmd/Ctrl, judged by the modifiers held when the key
went down, so modified arrows keep their shortcuts
(Tool::claims_key_with, Session::tool_claims_key_with).
Docs: docs/mcp.md, docs/target-app-parity.md, tool.options params text.
In a PDF without a standard, the warning for a page longer than 14,400 pt
written with a UserUnit above 1 now ends with "turn on Write pages longer
than 200 in at full size in points". The warning already said that apps
ignoring UserUnit show such a page smaller; it now names the Save PDF
option that writes the page at its size in points instead. PDF/A files,
which keep the UserUnit with the option on, and .ai saves, whose options
do not have it, keep the warning as it was.
The longer message has a row in every complete-message catalog. The
params text of document.exportPdf, docs/mcp.md and the evidence line in
docs/target-app-parity.md describe it.
object.distort warped the anchors of paths, groups, compound paths and
clip groups but left their bounding-box angle as it was, so a rotated
path kept its old box after a warp and an upright one stayed square to
the page however far the warp tilted it. Text, images and symbol
instances already took the angle of the warp's affine approximation.
- Each path, group, compound path and clip group now takes the angle
the warp turns its x axis to at its own center before the warp, the
members of a warped group and the art of a linked opacity mask each
at their own. An unlinked mask's art keeps its angle. Mirroring keeps
the nearer angle and a collapsing warp keeps the old one. Reset
Bounding Box squares the boxes again.
- Each subtree's bounds are measured once while it is warped.
- Corners that equal the corners of the box being warped change
nothing and add no undo step.
Docs: object.distort params text, docs/mcp.md, docs/target-app-parity.md.
When a Smooth tool drag refits the stretch at the first or last anchor
of an open path, the curve there now leaves or arrives along the
smoothed path within the tool's reach instead of along the first or
last segment, so a jagged end no longer keeps its jag. The end point
stays where it is. An end where the smoothed path turns back by 90
degrees or more keeps its segment's direction, and a stretch that ends
inside the path keeps the direction of the part outside the drag.
- pathops: SimplifyOptions gains free_start and free_end, off by
default; closed subpaths and simplify_part ignore them, so Simplify
and Object > Path > Smooth give the same output as before.
- engine: path.smoothRegion sets them for the ends of an open path
that a run's refit reaches.
Docs: docs/mcp.md, docs/target-app-parity.md.
When a drag reached the first anchor of a closed path, the smoothed path
started at the first anchor past the drag, and a drag over the whole
path could start it at a corner the refit kept. Dashes and type on the
path then started somewhere else.
The smoothed path now starts at its point nearest where the first anchor
was: at an anchor there, or at a new anchor split into the segment
there, which leaves the outline as it is. Of points equally near, the
one earliest along the path before smoothing, from its first anchor,
is used. A drag that does not reach the first anchor leaves it first,
as before.
Part of #2224.
An SVG or SVGZ writes each linked JavaScript file of SVG Interactivity
into a <script xlink:href> element as stored, so a caller with
automation read roots could read a path outside them from the exported
text. With read roots in force, an SVG or SVGZ of a document that links
such a file is now refused with "a linked JavaScript file is outside the
read roots", an error that names no file or folder. No file or folder is
written, and the document keeps the link.
- The check uses the rule document.json already applies to
svg_scripts: a relative path is taken from the document's folder, a
file: URL is a file, and another URL is not checked.
- It covers document.serialize, document.export, document.exportSelection,
document.exportForScreens and assets.export (before the folder is made
or any row is written), the SVG and SVGZ saves (also a save without a
path of a document saved as SVG), file.exportAs and the SVG Options
dialog's Show Code.
- Callers without roots, and the person at the keyboard, export the
links as stored.
Docs: docs/mcp.md, docs/target-app-parity.md.
The Smooth tool showed its own cursor with nothing selected, though a
drag then changes nothing. It now shows Not Allowed until a path it can
act on is selected (an editable path, not a guide, with three or more
anchors on one of its subpaths); a selected path anywhere on the canvas
gives the Smooth cursor wherever the pointer is.
- The Pencil with Alt key toggles to Smooth Tool on and the Blob Brush
show Not Allowed the same way when Alt gives them the Smooth tool. The
Paintbrush keeps its closing cursor, since its release with Alt closes
the stroke.
- A Smooth release, from the Smooth tool or from the Pencil, Paintbrush
or Blob Brush with Alt, sends path.smoothRegion only while such a path
is selected. With only type or other objects selected it sends no
command.
- Use Precise Cursors leaves Not Allowed as it is.
- The cursor catalog and docs/target-app-parity.md describe the rule.
The panel showed the values Document Info makes in English in every
language. It now shows the Units, the Raster Effects resolution, each
font's file, substitution and embedding permission, each image's size
and linked file, each Repeat object's kind and counts (in the
language's plural forms) and gradients named by their kind in the
interface language. Font, face, file and swatch names stay as they
are, a gradient swatch named like a kind included.
document.info keeps its English rows and adds the values the panel
builds them from: document.unitId, fontDetails, imageObjects and a
kinds list on each gradientObjects entry. Save… and Package's report
stay in English.
The outline of the area a Blob Brush or Eraser drag covers was outlined
again for the whole stroke on every frame, so a long stroke slowed the
canvas as it grew.
- The drag is outlined in pieces of up to 32 segments (one segment each
with Size set to Pressure), and a frame outlines again only the pieces
it added segments to. A grid of squares lists the segments near each
square, and each piece's outline drops the parts inside the rest of
the area, so the pieces show as one edge.
- One frame takes at most 256 new points and a fixed amount of work;
Tool::overlays_pending makes the canvas draw again until the rest is
done with the pointer still. Points not outlined yet show as a thin
line, and the tip shows at the pointer.
- The settled outline is kept in joined paths of 64 pieces, and a frame
joins again only the paths that changed. The grid's size is capped.
- The Blob Brush's length label and the Size-by-pressure check keep
running values instead of rescanning the stroke each frame. With Size
set to Pressure the outline shows only the edge of the whole area.
The release sends the same path.blob and path.eraseRegion parameters.