Type > Composite Fonts... has Custom: named character sets, each setting
the characters it lists in its own font, style, size, baseline and
scales ahead of their class. The later of two sets that list a character
sets it. Characters are added by Direct Entry or by Unicode code and
deleted one at a time, and the parts table scrolls when it grows.
- type.compositeFonts.set takes `custom`; part keys left out take the
Kanji part's values. Sets are saved with the document and in
.vccomposite files, and Export refuses a file over 4 MB.
- VectorCraft's own limits: 64 sets per font, 8,192 characters per set,
each character once per set, up to U+FFFF and no control characters.
- The font database caches each font's character lookup.
Docs: docs/mcp.md, docs/type-parity.md, docs/target-app-parity.md.
A subpath's anchors are now an `Anchors` list held in chunks of up to
256 anchors behind `Arc`s. Copying a path copies the chunk table and
shares the chunks, and editing one anchor copies only the chunk that
holds it, so the undo step of a one-anchor edit no longer keeps a copy
of every anchor of the path. Inserting or removing an anchor copies the
chunk it is in and rebuilds the table. Reads and edits follow the Vec
and slice methods the code used; files write and read each subpath's
anchors as one list, as before, within the same anchor read budget.
The undo memory estimate counts only the chunks the other version of
the path does not hold, and the renderer's kept paths compare only the
chunks an edit copied when they look for moved anchors.
Moving one anchor of an open path of 10,000 or 200,000 anchors
(path.moveAnchor, path.setHandle, path.moveAnchors, a nudge) keeps at
most 48 KiB in its undo and redo steps, where it kept 56 bytes per
anchor.
On a path or compound path, a Transform effect listed after the
object's own Rasterize effect now moves the image that Rasterize makes.
The image is made of the art before the Transform effect, at the
Rasterize effect's resolution and with its room around the art, and the
Transform effect scales, turns, reflects and moves it. Before, the
Transform effect moved the art and Rasterize made a new image of the
moved art on a grid square to the page.
- effects: rasterize_stage finds the Transform effects that move the
last Rasterize image; stage_map composes their maps about the
reference point of the art each one takes.
- render: the canvas draws the node without those Transform effects
through the map, and its drawn and painted bounds follow the map. A
map that scales the image to nothing draws nothing.
- engine: Expand Appearance places Object > Rasterize's image of the
art through the map, and PDF export makes those images before it
bakes the geometry effects.
A Transform effect that makes copies or moves at random, one followed
by another kind of effect, a fill's or stroke's own Rasterize effect,
and Rasterize on type, images, symbol instances and groups keep the
earlier behavior.
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.
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.
This reverts commit bada8e1710. #7638 merged with its gate failing: besides six unformatted
files, render and trace don't compile against craft-libs e2dc681 (u64::from(usize) in
render/src/paint.rs, a private craft-libs function, and a call with one argument too many in
trace/src/opaque.rs), so main's CI fails. The tree is that of 0adbdce6cb, whose CI passed.
While a document's composite fonts are lent, type naming a composite font
the document does not define is set as in a missing font, even when the
front document or the document a placed document sits in defines one of
that name. A document that defines no composite font gets an empty scope
too, so a document in the back, a tiled window, a thumbnail or an export
made while another document is in front no longer borrows the front
document's definitions. A temporary document drawn while a document draws
keeps that document's fonts, and type laid out with nothing lent still uses
the registered ones.
- FontDb::composite_font looks only in the latest loan of the database.
- lend_composite_fonts lends an empty set at the top level when the
database holds composite fonts; lend_document_composite_fonts also lends
one inside another loan, for placed documents, back-document measuring
and threaded reflow on open.
- A loan that leaves out a registered font gets its own generation, so
layouts made with that font are not reused under it.
A path's Drop Shadow, glow, blur, Feather or pixel filter listed before a
Transform effect that makes copies was drawn once under all the copies, at
the object's own distances. Each copy now carries those raster effects,
scaled by the copy's mean scale, with a Drop Shadow's offset turned,
reflected and scaled with the copy. Raster effects listed after the
Transform effect still paint once around all the copies. Copies that stop
at the effect budget keep their effects, and a Transform effect without
copies draws as before.
The canvas, the object's bounds, export, baking and Expand Appearance use
the same art. Type, images, symbol instances, groups, a fill's or stroke's
own copying Transform effect, and paths with Rasterize or an SVG filter
before the copying effect keep one set of raster effects.
The image crate's filtered resize filters each channel on its own, which
is correct only for premultiplied pixels, and its thumbnail averages each
channel on its own too. Placed images, linked-image previews, MCP
captures and previews, metafile bitmaps, PDF downsampling and library
thumbnails passed straight RGBA to them, so the color under clear pixels
(usually black) bled into the opaque pixels beside them and darkened
cut-out edges.
vectorcraft_doc::resample scales such images with each pixel's color
weighted by its alpha: filtered resampling premultiplies the pixels in
place and divides them back after, and averaging takes the area-weighted
mean of the pixels each new one covers. Opaque and alpha-free images are
scaled as before. PDF export also multiplies the inks of a CMYK image
with alpha by that alpha before resampling them, and divides them by the
resampled alpha after.
A gradient stop linked to a spot ink that also carries process plates
(a DeviceN stop opened from a PDF) printed only its spot tint: print
separations and Separations Preview left its Cyan, Magenta, Yellow and
Black plates blank. They now print the stop's plates beside the tint.
With spots printed as process, each process plate prints the stop's
plate layered with the spot tint's process equivalent, as a separated
image's spot plates layer. Plates beside no spot link, or beside
Registration, still print nothing of their own.
The color visitor also skipped a freeform gradient's points, so print
plates kept their display colors and Document Color Mode left them
unconverted. The points are now visited as unlinked colors.
Three cases still clipped differently from what the clipping path draws:
- A clip group with an appearance of its own (a Drop Shadow, a geometry
effect) was measured for culling by its plain clipping path, so a view
showing only the part of the clip that an effect moved drew nothing.
The renderer now grows that measure by the clip region in this case
too.
- The geometry effects on the fills and strokes of an unpainted clipping
path applied after the effects of a group around it. They now apply
first, as for other members. A clipping path that paints keeps them
live, so its fill and stroke still draw.
- Export baked a compound clipping path with an effect of its own from
its raw paths, dropping the effects on those paths. It now bakes the
clipping path as the canvas clips by it, and the pieces it bakes into
get ids of their own.
A raster that SVG import decodes and stores again (a TIFF, a BMP or any
other format that is not PNG, JPEG, GIF or WebP, or an image whose EXIF
or TIFF orientation turns or flips it) was written as a PNG with no pHYs
chunk, and a turned or planar CMYK TIFF was written without resolution
tags, so the image lost the resolution its file declares.
The converted PNG now carries the source's resolution in a pHYs chunk,
and the CMYK TIFF in its resolution tags, as Open and Place store them.
A quarter turn (orientations 5 to 8) swaps the horizontal and vertical
values. An image that declares no resolution is stored without one, and
an image stored unchanged keeps its own bytes.
The PNG, JPEG, TIFF, WebP and BMP resolution readers and the pHYs writer
move from the engine to vectorcraft-doc, so the SVG importer can use
them; the engine and renderer entry points forward to them unchanged.
With Window › Arrange › Tile or floating document windows, every document
other than the front one was drawn through the profiles the front document
is assigned, since the active color settings are process-wide.
- cms::with_active puts other settings in place of the active ones on one
thread, fast paths included, and puts them back after (also on a panic).
cms::for_document gives the context a document's assigned profiles make,
the same one each time for the same profiles and working settings.
- Each pane renders its document's art through colormgmt::doc_cms, on the
render worker and on the UI thread alike, and draws its paper, artboard
colors and transparency grid through the same settings.
- Loading or removing a profile redraws the documents assigned it, the
ones behind the front one included.
- The ink and separation tables keep the tables of up to four color
settings, so documents with different CMYK profiles side by side do not
rebuild them on each redraw.
Placed documents inside a document behind the front one still draw through
the front document's profiles.
A raster effect (Drop Shadow, Outer Glow, Gaussian Blur and the other
effects that paint pixels) on a symbol instance's topmost fill now draws
on each filled shape of the symbol's art, and one on its topmost stroke
draws on each stroked shape, after any geometry effects listed before
it. The same rules as for geometry effects apply: only a visible fill or
stroke with an opacity above 0 counts, a group's own fills and strokes
in the art take the effect, and an instance inside the art passes it on.
The canvas, Use Preview Bounds and the drawn bounds, SVG and PDF export
draw it, and Object > Expand Appearance is enabled for such an instance:
a shadow or glow becomes an image under the instance, and a blur,
feather or inner glow replaces it with the image, in one undo step.
Existing documents with a raster effect on a symbol instance's fill or
stroke draw differently: before, the effect drew nothing.
A clipping path with Roughen, Transform or another effect that reshapes
a path drew its own fill and stroke reshaped, but the canvas and raster
export still clipped the art by the plain path. SVG and PDF export bake
the effect into the path before writing the clip path, so the screen
and a saved SVG showed different art.
effects::clip_outline now applies the live geometry effects in the
clipping path before taking its region: the path's or compound path's
own effects, the effects of its fills and strokes (the region is then
the union of the paths they give, as an export bakes them), the effects
of a compound path's paths, of a group's visible members and of a
nested clip group's clipping path, shown or hidden, and a group's own
effects as an export evaluates them. Raster effects such as Gaussian
Blur leave the region as it is. The renderer, the engine commands that
measure a clip region (flatten, arrowheads, cutting) and the writers use
it; the writers receive the baked document, so their output for a path
with effects does not change.
The renderer measures a clip group by the region its clipping path's
effects give it, so a view or tile that shows only the moved part of
the clip still draws the clipped art, and it drops cached clip regions
that hold a plug-in effect when plug-ins change.
Two rules change in every export as well as on the canvas:
- When the clip group, or a group around it, has a geometry effect of
its own, the clipping path's effects apply first and its gradients
move with them. #7114 left the clipping path's effects live under the
group's effect, so they applied after it.
- Type used as a clipping path keeps its own effects when a group's
effect outlines it. #2073 and #2290 outlined it without them, to
match the plain clip the canvas drew.
Changing Preferences › Hyphenation › Exceptions bumped each open
document's revision, but the caches of laid-out type outside the text
crate were keyed on the font generation alone. The renderer kept the
glyph geometry, raster-effect pixels, live-effect and envelope art,
pattern tiles and placed symbol art of unchanged type objects, so
hyphenated area type kept its old line breaks on the canvas. The
hit-test art of blends and envelopes, the Fit to Artwork Bounds answer,
the layout caches of the Type and Touch Type tools and the out port's
overflow mark were keyed the same way.
vectorcraft_text::layout_inputs() returns a number that changes with
the fonts and with the hyphenation exceptions. Every one of those
caches now keys on it instead of FontDb::generation(), and envelope ink
uses it in place of the private envelope_ink_inputs(). The hit-test key
hashes it together with the plug-in revision.
Tests: a renderer that drew hyphenated area type, alone, with a drop
shadow, in a group with a Transform effect, in a symbol instance and in
an envelope, draws it as a new renderer does after the exceptions
change; the out port of selected area type shows the overflow that the
new exceptions cause. The render test helpers that run a step again
when the fonts change during it now also run it again when the
exceptions change.
A document's composite fonts were lent by writing them into the font
database's shared map, and a second lend of another definition of the same
name was skipped while the first was held. A document drawn in a tiled or
floating window while the front document drew, a document placed in one
that defines the name differently, and an export made while another
document is in front could draw their type in the other document's
definition. Registering the front document's fonts during a render could
also change the definition mid-render.
Lending now applies on the lending thread only: each guard pushes the
document's definitions onto a per-thread stack that lookups read before the
database's own fonts, latest loan first, and removes them when it drops,
also when the thread unwinds. The guard is neither Send nor Sync. Lending no
longer changes the database or its generation; while a thread lends
definitions other than the database's, FontDb::generation gives a number of
its own for them, so kept layouts and render caches made in one document's
definitions are not used for another's.
With repeated names numbered when a brush library is read, a stroke that
names a repeated brush draws the first brush of that name, and a later
brush of the name is read as "Name 2". Three tests built a library with a
repeated name and expected the later brush:
- Document Info lists a name two stored brushes share with the first
brush's type (art), not the last one's.
- The two render memory tests add their own `Dots` scatter brush beside
the default library's `Dots`. They now remove the default `Dots` first,
so the stroke draws their brush.
- The .ai writer test that leaves out a Pattern brush "X" gets the
Calligraphic brush after it back as "X 2", the name it had in the
document.
The two brush art tests in render's memory tests added a scatter brush
named Dots next to the default library's own Dots. Strokes now draw with
the first brush of a name, so the stroke drew the default brush and the
edits to the symbols and patterns its art shows no longer changed the
frame. The tests now remove the default Dots before adding their own.
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.
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.
An envelope or the Warp effect cuts an image into triangle pieces that
reach a little past their edges, so neighbors overlap and no seams show.
On an image with alpha, the overlap drew that alpha twice: a 50% image
came out about 75% opaque along every cell edge and diagonal.
- The pieces now form a knockout group, and each piece is marked as a
warp piece (`warp_piece`) whose knockout shape is its whole triangle,
so where pieces overlap only the top one draws.
- The canvas knocks each piece out by its triangle at full coverage.
- SVG and PDF draw the pieces of a partly transparent image (its pixels,
opacity, color mask or colorize paint) through one mask each, of the
later triangles that reach it; pixels on a triangle's antialiased edge
come out slightly lighter there. Pieces of an opaque image are drawn
one over another as before.
- SVG writes one copy of the image for pieces cut by opacity masks
(Preserve Shape Using Transparency) too.
- A cell count past the generated-object budget makes an empty group
instead of overflowing.
Docs: docs/target-app-parity.md.