The test for a Calligraphic path followed by samples of the pen varied
the diameter, roundness and angle with the pen all at once. It would
still pass if the reader checked only one of the three settings, left
out Tilt, or tested the pen modes and the variations separately.
- Four new cases vary one setting with the pen and fix the other two:
the diameter with the pressure, the diameter with the tilt, the
roundness with the bearing and the angle with the rotation. Each
keeps the drawn look with its note.
- A new brushed case pairs a Random setting that varies with a pen
setting that varies by 0. The path comes in brushed.
Follow-up to #7367.
The check that pen samples mark only the object before them used an
unbrushed filled rectangle as the following object. A style with no
brush ignores the samples, so the check passed even when the samples
marked every later object. The following object is now an unsampled
path with the same pen-varying Calligraphic brush, and the test asserts
that it comes in as a path stroked with that brush.
The panel collected the objects for its nested rows from the document
as it is between commands, where the art of the stored brushes is not
set, so rows whose owner is a brush found no object and drew a blank
thumbnail. When an id is not found otherwise, the panel now sets that
art on a copy of the document, as links.list does, and collects the
objects from it. A recheck of the files at the same revision keeps the
objects collected before.
The Links panel menu gets Copy Info, which runs the new UI command
links.copyInfo {ids?}: the file names of the selected rows (or of ids),
one per line in the order the panel shows them, go to the clipboard.
The item is off with no row selected. An id that is not an image or a
placed document, or a malformed ids, is an error and leaves the
clipboard as it was.
A computed name such as o[key], where key's toString returns a rope,
is flattened into a copy that only native code held. A getter or
setter that started a collection dropped that copy from the heap
estimate, so the live heap could pass the limit by one name per
nested access. Indexed reads, ++ and -- on an indexed property, and
Object.defineProperty now keep the converted name reachable while the
accessor runs.
The rename field in Export for Screens tracked the artboard by its index. If the
artboards changed order while a name was typed (a command from the control channel
or MCP), Enter renamed the artboard that had moved into that index. The field now
tracks the artboard by its id and by the document it was opened in. When that
artboard is deleted, or another document becomes active, while its name is typed,
the field closes, no artboard is renamed, and Escape closes the dialog again.
The Japanese row in docs/localization-parity.md said every message is
translated, and the Japanese catalog line in docs/target-app-parity.md
said 38 messages were missing, one of them waiting on #7214, which has
merged. On main the untranslated report lists 98 messages without a
Japanese row, 77 of which every complete-message language lacks. Both
lines now give the counted rows and the report's count, and name the
open pull requests that translate part of them.
A block's line took the tallest ascent and deepest descent of its
characters, or the height of the stack's em boxes, whichever was larger.
The top block line's characters reach above the stack by their ascent
past the em box, and the bottom line's below it by their descent, so with
a wide Line Gap the line was too short for them. The line's ascent and
descent now also take in each character's reach from its own block line's
baseline.
PDF, EPS, print and DXF export place each symbol instance whose art has
live effects as that art, baked. The bake followed only an object's
children, so an instance in the art of an enabled opacity mask stayed an
instance, and the mask was written with the symbol's stored art and
without its effects. A symbol whose art used such a symbol only inside a
mask was not placed either.
The bake now also looks into the art of enabled opacity masks: that art
is baked one level deeper and written back to the mask, and symbols that
use a placed symbol only inside a mask are placed too. Disabled masks
and the masks of objects the export leaves out stay as they are.
- Outline Stroke on a graph's strokes, including Object > Expand with
Stroke on, adjusts the bounds the graph keeps, the same way
Create Outlines on its labels does. Before, the stroke outlines
changed the bounds of the art without type, and the next redraw
stretched the plot by the width the outlines added.
- A graph that keeps `placedArt` and has no art other than type left
is no longer measured on the bounds of all its art. A label edit on
such a graph moved and stretched the plot at the next redraw; the
plot now stays where it is.
- A redraw no longer measures a move or scale on art that has no width
or no height now, such as a graph left with one axis line. Before,
the plot collapsed to no width.
- A clipping group in a graph that holds type is measured on its clip
when the clip does not depend on type, so moving art outside the clip
does not change the bounds. Blends, envelopes and repeats are
measured whole, as they are drawn.
- The doc crate's helper is named `graph_art_without_type` and sits
with the file's other free functions.
- docs/mcp.md lists Outline Stroke and states that moving an outlined
label, a swatch, an axis or a tick mark on its own can move or scale
the plot, and that a graph left with only labels, or with art that
has no width or height, does not follow a move.
- Tests: a scale of the graph made before outlining a label, a move of
a graph opened from `.ai` after its label is restyled and moved,
Outline Stroke after a move, a graph left with only its labels or
with one line, and clipping groups holding type.
Effect > Blur > Radial Blur... has a Blur Center box. A press or a drag
in the box sets the point the spin or zoom starts from, the arrow keys
move it 1% (10% with Shift), and Horizontal and Vertical fields take the
same position in percent. Screen readers get the box with both values.
- blur.radial takes centerX and centerY, from 0 to 1 across and down the
object's bounds (0.5 each by default), so the center moves and scales
with the object. An effect saved without them draws as before.
- effect.apply and effect.setParams refuse a value outside 0 to 1, or
one that is not a number; a saved one draws at the nearest edge.
- The raster's margin is measured from the center, and the center stays
in the object's own bounds when a Rasterize splits the stack.
- Params text, docs/mcp.md, the evidence line in
docs/target-app-parity.md and a Blur Center row in every catalog.
A WMF without a placeable box picks its scale from the map mode its
drawing records are played in. Lines and shapes drawn under R2_NOP or
R2_XORPEN, and PATBLT records the reader does not fill, no longer count,
with the mix mode saved and restored with SAVEDC and RESTOREDC.
An open stroke that reaches far outside the picture is left out before
it joins the strokes waiting to be joined, so the strokes drawn before
it are still added.
A fill in a glyph's clip marked the clip as filled before the fill was
added. When the fill painted nothing (a palette index past the palette,
or a radial gradient that draws no circle), the next glyph painted in
the same clip became the clip's only child, and closing the clip kept
that glyph on its own, so it drew outside the clip.
The clip is now marked filled only when the fill adds the clip's first
child, and a fill that adds nothing leaves the mark as it was.
Tsume narrows a cluster before Optical kerning measures its pairs, so
Optical measured the narrowed gap and gave part of the space back.
Optical now measures each pair as it would be without the Tsume, as it
already does for Insert Aki, and the Tsume stays on top. Tsume records
what it took on the glyph for this.
A clip group with a fill or stroke of its own and no geometry effect is
baked through bake_pieces, which baked the clipping path like any other
member instead of applying the geometry effects in it as the canvas clips
by it. A compound clipping path whose member has a Transform effect with
copies then baked into nested compound paths, and SVG, PDF and the other
exports dropped the moved member and its copies from the clip.
bake_pieces now applies with_clip_effects_applied to the clipping path
first, as the plain clip group route already does. Clipping type keeps
its own handling.
A plot of the art's bounds now leaves placed images, objects at 0%
opacity and unpainted paths out of the art of a dynamic symbol, as it
does for other symbols. An object that an instance's fill, stroke or
opacity makes the plot draw is measured for that instance only, and an
instance whose symbol holds nothing else is left out, so a document
with only such instances is refused.
With Show East Asian Options on, the Glyphs panel menu lists Traditional
Forms, Expert Forms, JIS 04, JIS 90, JIS 78 and JIS 83 Forms, Monospaced
Full-Width, Half-Width, Third-Width and Quarter-Width Forms, and Revert
To Default Forms. Each form draws the selected characters with the
font's OpenType feature for it (trad, expt, jp04, jp90, jp78, jp83,
fwid, hwid, twid or qwid). A form is checked when every selected
character has it and disabled when none of the faces that draw the
selected text, fallback faces included, has the feature.
The form is a new character attribute, glyphForm, one form at a time
(text.setFormat, text.setRangeStyle). It is saved only when set,
character styles carry it, and runs that differ only in their form
shape as one piece of text. SVG writes it in font-feature-settings;
PDF draws the glyphs from the layout.
A PDF/A-2b export at PDF 1.4 with Create PDF Layers and a page longer
than 14,400 pt is written as PDF 1.6, which has PDF layers. The export
decided which layers place their instances' symbol art before it raised
the version, so a hidden layer, written as a PDF layer that is off, kept
its instances' art without the art's live effects.
The version a long page needs is now settled before the document is
baked. The pages come from the artboards, which baking leaves as they
are.
Export Preferences… and Import Preferences… had a row to themselves,
and Reset Preferences started the next row, left of Previous, Next,
Cancel and OK. Reset Preferences now follows Import Preferences… on the
first row, which wraps when it is wider than the dialog, and the row
below holds only the category buttons, Cancel and OK.
Tests check that the three buttons share a row in English, and that in
every language the dialog keeps its 760 pt content width with each
button inside it and above the OK row. Hindi, Gujarati, Georgian,
Japanese, Korean and Chinese are measured with fallback fonts, since
the tests do not load the craft-fonts faces or installed fonts those
languages are drawn with.
Load Variable Library defines each variable with the kind of its trait
(textcontent, visibility, fileref, graphdata; any other or none is the
unknown kind) and reads visibility values from true or false, image
values from a file's absolute path or file: URL, and graph values from
a data element of series names and rows. A document variable declared
with the same name and kind keeps its bindings. Save Variable Library
writes every kind in the same forms and is enabled with any variable.
Image values that aren't absolute paths and graph data past what a
graph holds are refused, so every saved file loads back.
An option given to document.exportSelection skips the options dialog, but
without `path` the save panel still picks the file. docs/mcp.md said any
option writes without a dialog, and docs/control-protocol.md said params
export without the dialog. Both now say that only `path` writes without a
dialog, and a test pins the behavior.
- appearance.moveItem with from "contents" and aligned: true is refused when
the objects' stacks differ, as it is for rows.
- paint.invert and paint.complement with an item remember the paint of an
object they recolored, not that of design art they left out.
- effect.remove and effect.duplicate with indices and aligned: true are an
error, and change nothing, when the design art given first has no effect
at one of the indices.
oversized_tiff now also refuses a TIFF whose JPEG-compressed strip or
tile (Compression 7) declares a frame wider or taller than the strip or
tile, one with more than MAX_TIFF_CHUNKS strips or tiles, and one whose
JPEG headers take more than 16,777,216 steps to read. It reads BigTIFF
directories, follows the layout and the repeated tags the decoder
reads, and refuses offsets of types it does not read.
Keep upstream codec and Global Edit work identical to main so only the 38 Tech Circuit feature files remain in the PR diff. Resolve the G5 documentation conflict by preserving completed Global Edit behavior and the Tech Circuit estimate reduction. Fresh deep CodeRabbit review of the exact feature diff passed with zero findings; no full local rebuild repeated.
When Merge added a path that cut a painted edge, the piece holding the
old edge's middle took the old edge's stroke rows unchanged, so its
dashes started again and it kept both arrowheads. It now takes them
fitted to its part of the old edge, as Gap Options does.
The prepass writes a key in place of each <feImage> data URL before usvg
reads the SVG, and the filters kept as Effect > SVG Filters were read from
that edited text, so their XML held the key. The effect's check refused it
and the filter was dropped with a warning. The filter's XML now has each
keyed href put back as it was written.
The check before a PNG or JPEG is embedded as it is read the profile's
color space only from a profile of at least 128 bytes, so a gray or RGB
profile cut short after its color space reached the PDF writer. The
color space is now read from bytes 16..20 of any profile that holds
them, and valid_icc rejects the short profile.