Unit::parse took one level of recursion per + - * / sign, so a text such
as "1+1+...+1" with 200,000 terms overflowed the stack and aborted the
process. Every number field, parse_number and every command parameter read
as a length went through it.
Text with more than 128 + - * / signs now reads as nothing; text with 128
or fewer reads as before. docs/mcp.md states the limit for length strings.
The test a_very_long_expression_reads_as_nothing aborted with a stack
overflow before the change.
Type on an open path can now start before the path's start: its start
is stored below 0, the span runs from there, and the characters whose
middle falls before the path's start are not drawn, as SVG 2 lays out
text on a path (https://www.w3.org/TR/SVG2/text.html#TextLayoutPath).
The text keeps every character, and the start bracket stays at the
path's start. Past the path's end, text that does not fit is overflow,
as for other type on a path. Type that starts on the path draws every
glyph as before.
- SVG import keeps a <textPath> start before an open path's start
(text-anchor end or middle, or a negative startOffset) instead of
starting the text at the path's start, which drew every character.
- SVG export writes it as a negative startOffset.
- PDF export writes only the characters drawn on the path.
- The legacy .ai and .ai editing data writers write the type from the
path's start with the type attributes warning.
- type.pathOptions takes a start below 0 for type on an open path; on a
closed path it is the path's start, as before. A flip starts the type
at its start bracket. Dragging the center bracket slides it from where
it starts, and no further before the path.
- A click at the path's start places the caret by the first drawn
character, and the caret in the hidden characters stands at the
path's start. The highlight of substituted fonts and glyphs leaves
them out.
- Release Type on a Path keeps the first drawn glyph where it was
drawn, with the hidden characters before it.
- The Touch Type tool shows no box for a hidden character, and
text.touchType returns an error for one. A character with a drawn
glyph is changed by that glyph.
Docs: docs/mcp.md, docs/target-app-parity.md.
Reverse Path Direction reversed each member path of a compound path and
turned the members' own stroke records, but left the compound path's
strokes as they were. A compound path's strokes run along its members'
subpaths in order, so its width profile and pen pressure, tilt, bearing
and rotation then ran the old way and the stroke drew differently.
reverse_stroke_records now also takes a compound path, reading its
members' subpaths in order, and path.reverse turns each compound path's
strokes with the combined reversal of its members before they reverse.
As for a path, the strokes turn only when every subpath turns alike.
- 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.
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.
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.
The doc comments of read_samples, with_room and extra_samples_tiff_rgba
name the SampleFormat check, and those of tiff_source and tiff_inks name
the 64-sample limit, the SampleFormat check and the decoder row check.
read_samples gives the decoder's limit as 256 MiB.
the_planar_copy_is_allocated_at_its_length copies 33 samples and checks
that the copy has room for at least 33 and fewer than 64, the room a
copy grown as it was filled ends with, rather than an exact capacity.
A CMYK TIFF with two or more samples past its inks and its ExtraSamples
values, or with no ExtraSamples tag, which the decoder gives no color
type, now opens opaque when none of those values names an alpha. It
failed to open before; one that names an alpha already read with it.
The 64-sample limit is now checked against the file's SamplesPerPixel
before anything is decoded. It was checked against the samples the
decoder gives, which is five for any CMYK file whose first ExtraSamples
value is alpha, so a small file with 65,535 samples a pixel was read and
the decoder took a row of every sample (1 GiB for a 16-bit file 8,192
pixels wide) before reading its data.
- The row the decoder reads each strip or tile into, when the file has
more samples a pixel than it gives, counts in the allowance.
- Files whose samples aren't unsigned integers are refused from their
tags; no reader takes them.
- The same checks run before CMYK inks and plates are decoded.
- The copy that puts a planar file's samples pixel by pixel is allocated
once, at its length, so the reader holds no more than it counts.
A closed key that winds the other way is reversed keeping its start anchor.
Its dash offset was computed as if the reversed path ran back from position
0 instead of from the end of the subpath, so the dashes shifted by the
subpath length modulo the pattern period. The offset now uses the full
length in that case, and the dash test covers a closed key 40 long with a
7.5 period.
The WebP chunk walk added the chunk length to the offset without a
checked add, so a length near u32::MAX overflowed on 32-bit targets
(wasm32) with overflow checks on. It now reads the chunk through the
same checked slice helper as the PNG, JPEG and TIFF readers.
A CMYK or grayscale TIFF with extra samples is now read only when its
decoded samples, the copy that puts a planar file's samples pixel by
pixel and the RGBA pixels together fit the decoding allowance; the
check counted the decoded samples alone, so a small compressed file
could make the reader hold about twice the allowance plus the RGBA
image.
- The decoder may decode no more than the counted samples, and reads no
strip or tile of more compressed bytes than the file holds.
- A pixel of more than 64 samples is not read (VectorCraft's own limit).
Measuring an instance of a symbol with 9-slice guides looked its map up
through Document::symbol_slice_map, which scanned the symbol list and
measured the art box again in new quick boxes, and NineSliceMap::place
and most_placed made new quick boxes of their own. Each of those boxes
built the symbol name index over the whole list on its first lookup, so
every 9-slice instance measured read every symbol once or more. Picking
an object inside a 9-slice instance's art with Direct Selection did the
same for each object of the art that holds a symbol instance.
- SymbolBoxes cuts the guides through the art box it has measured
(SymbolBoxes::slice_map), and places and counts the art with itself as
the boxes, in the mode quick boxes use. Art with no 9-slice map is
moved by the instance's transform without looking the map up again.
- Document::symbol_slice_map, place_symbol_art,
place_symbol_art_unclipped and edited_nine_slice use one set of quick
boxes for each call.
- symbol_part_at and symbol_part_outline keep one set of quick boxes
for the instance's art.
The new tests count the symbols read. Measuring 200 instances of a
symbol with guides and 200 of a symbol with guides holding it, among
2,002 symbols, read 1,205,204 symbols before; now each set of boxes
reads the list once. Picking in a 9-slice instance whose art holds 20
instances read 84,042 symbols among 2,001; now it reads the list at
most twice.
When two gradients with different stop counts are blended, the second stop at the other
gradient's step took the last stop within the step tolerance, so two close but distinct stops
became a hard step. It now takes the last stop of a step of its own there, and otherwise paints
what the gradient paints at that place.
The doc comments of with_room and of the large-file test, and the
progress-doc line, gave the tiff decoder's default decoding buffer
(268,435,456 bytes) as 256 MB.
A hue gradient that reflects unrolls through its curve, whose stops have no swatch links, so a
gradient along a closed key started elsewhere lost its links. The links now come from the
gradient's own stops unrolled as a Classic gradient's, in order, so stops at one place and the
stops where two runs meet keep their own links.
A document saved with a CMYK TIFF whose alpha follows another extra
sample keeps it as a CMYK image with its ink amounts, so EPS and PDF
export write the same inks as before; Open and Place still read such a
file with its alpha.
- A CMYK file with two or more samples past its inks and its
ExtraSamples values, which the decoder gives no color type, reads the
alpha that one of those values names, and its Orientation tag is read.
- A TIFF with extra samples whose samples take more than the decoder's
256 MB buffer reads, up to the decoding allowance.
- A file read as black-is-zero gray is read in place instead of copied.
Direct Selection on a dynamic symbol's instance below a layer whose
Preview is off tested and outlined the art in Preview, where the canvas
draws it in outline with the plain clipping paths. When a clipping
path's effects left no region, a selected object in its clip group kept
an outline cut to the plain clipping path.
- `hit::symbol_part_at` and `hit::symbol_part_outline_in_view` count the
layers around the instance, the isolated container and the layers
around it apart, as clicks outside symbols do.
`symbol_part_outline_in_view` takes the click options in place of the
outline flag.
- The outline of an object is empty when the effects of a clipping path
around it leave no region.
Direct Selection on a dynamic symbol's instance picked an object in the
region its clipping path's effects give it, but cut the object's outline
to the plain clipping path, so the outline came out empty or cut short.
In a layer of the art whose Preview is off, it tested the effect-shaped
region and the fills, where the canvas draws the plain clipping path and
outlines.
- `hit::symbol_part_outline` cuts to the bounds of the effect-shaped
region. The new `hit::symbol_part_outline_in_view` takes the view's
outline state, and the Direct Selection overlay passes it.
- `hit::symbol_part_at` tests art in a layer whose Preview is off in
outline.
- A graph opened from a .ai file now saves `placedArt` too. Moving or
restyling one of its labels before the first redraw no longer moves or
stretches the plot.
- Type > Create Outlines on a graph's labels now adjusts the saved bounds,
so the outlines do not read as a move at the next redraw. A move of
the graph made before the outlining is still followed.
- A `placedArt` with no width or height falls back to the bounds of all
of the art, as a graph saved without it does.
- `art_bounds` moves to the doc crate so the .ai reader can use it.
- docs/mcp.md describes `placedArt`, `placed` and when a redraw follows
a move.
- Tests: a legend across the top, a legend after a right value axis and
legend labels of several lines keep the plot in place through label
size changes; moving a category label on its own; outlining labels
before and after a move; a flat `placedArt`; a label moved in an
opened .ai graph.
Only a pie with Legends in Wedges picks its legend labels' fill, white on
dark wedges. Paint visits and swatch mappings now leave its default black
legend fill out only for such a pie; other graphs draw the kept fill, so
it is visited and mapped like the other label fills. A color mode change
still leaves the default black legend fill as the default, so turning on
Legends in Wedges later keeps the white labels on dark wedges.
The next redraw of a graph compared the bounds of all its generated art,
labels included, with the bounds stored at the last generation, and took
any difference for a move or scale of the graph. Label edits that change
the size of the type (a new size, an indent, a legend size) changed those
bounds without a move, so the plot shifted and stretched when the graph
was drawn again.
The spec now also stores the bounds of the art without type
(`placedArt`), and moves and scales are measured on those. Graphs saved
without them are measured on the whole bounds as before. Moving one
label with Direct Selection no longer moves or stretches the plot at the
next redraw.
A CMYK or grayscale TIFF reads its alpha from the first extra sample whose
ExtraSamples value is 1 or 2, wherever it comes among the extra samples, at
8 and 16 bits, chunky or planar. TIFF 6.0 gives each extra sample its own
ExtraSamples value, in order, and asks readers to skip extra samples they do
not use.
- A CMYK file with alpha after an unspecified extra sample was kept as
opaque inks; it is now shown as RGB with its alpha.
- A grayscale file with alpha after an unspecified extra sample opened
opaque.
- A six-sample CMYK file whose ExtraSamples tag lists only an unspecified
value no longer reads that sample as transparency.
- A white-is-zero grayscale file with an alpha channel opens; it failed to
read before.
When a closed key path now starts where its stroke gradient steps between
two stops of one color linked to different swatches, the gradient's new
first stop takes the swatch link of the stop after the step instead of
the one before it.