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.
swatch.edit stored a replacement gradient without its placement, so a
radial gradient with an aspect, Alt-dropped on a gradient swatch or
given as `paint`, lost that aspect, while swatch.new keeps it and
applies the swatch with it. A gradient given an aspect without `start`
and `end` lost the aspect in both commands. A replacement pattern kept
the tile transform of the swatch it came from, while swatch.new stores
a pattern untransformed, and `paint: {pattern: name}`, which an Alt-drop
of a pattern fill sends, was refused.
swatch.edit and swatch.new now store a gradient or pattern through one
helper: a gradient keeps its aspect, with or without a vector, and holds
no swatch link, and a pattern is untransformed. swatch.edit takes a
pattern by name, as swatch.new does.
brush.freehand drew the plain path when `brush` named no brush in the
document or was not a string, and drew a pencil stroke for any `style`
other than "brush". It now refuses an unknown name with "no brush named
`<name>`", as brush.apply and brush.setCurrent do, a `brush` that is not
a string, and a `style` other than "pencil" or "brush". The document does
not change and no undo step is recorded. A `brush` left out or null still
draws the plain path.
The Paintbrush skips a tool brush or current brush that has been deleted
and paints with the other, or draws the plain path when neither is in the
document, so a stroke after a brush is deleted is not refused.
With points of a gradient mesh picked (Mesh tool, Direct Selection or the
Lasso), a solid fill color from the Color panel recolors just those points,
but the Fill proxy, the Color panel, the Color Wheel and the Control bar's
fill chip kept showing the mesh object's own fill, so the panel's sliders
jumped back after each drag.
- The new `Session::fill_mesh_points` resolves the targets as
`paint.setFill` does: the picked points of every selected mesh, unless the
Appearance panel's active row is a fill or an object of a symbol instance
is selected. When the first selected object is a mesh among them, the Fill
proxy (`paint.proxies`) shows its first picked point's color, and "?"
compares the picked points' colors with each other and with the other
selected objects' fills.
- `Session::mesh_points_picked` lists the points `transparency.set` fades: it
is empty while the Appearance panel's active row takes the opacity, and
`transparency.info` and the Transparency panel now follow it there too.
- The cached "?" state and the Transparency panel's values are recomputed when
other points are picked, which gives the document no new revision.
- The Control bar's opacity shows the first picked point's opacity, which
`transparency.set` changes then.
- The `paint.setFill` and `transparency.set` params texts name the picked
points instead of the point last clicked, and the `paint.proxies` text,
docs/mcp.md and the progress doc describe what the panels show.
When a document opens or is reverted with dimensions whose art is not in
its color mode, the dimensions are drawn again before the document's
composite fonts are registered. A label whose font names a composite font
that another open document defines differently was measured in that other
definition, so it was centered off its anchor. The redraw now measures the
labels in the document's own composite fonts, in front and in the back.
A blend between two gradients whose matching stops are tints of one spot
swatch kept the link and interpolated the tint, but every step's stop lost
the process inks (C, M, Y, K) printed with the spot ink, so PDF export wrote
the steps as the spot ink alone.
- Matching stops linked to one swatch interpolate each process ink with the
tint; a stop without process inks counts as none of each, and the ends
keep their own.
- Gradients with different stop counts keep the inks at every resampled
stop, interpolated with the midpoint-adjusted fraction between two stops
of one swatch.
- A solid blended with a gradient brings no process inks of its own: its
stand-in gradient no longer copies the other gradient's.
Stops of different swatches, or linked and unlinked stops, still blend into
unlinked process colors without plates.
A gradient's Separation or DeviceN space, and EPS's %%DocumentCustomColors,
named the spot ink of every linked stop. A stop out of order whose
neighboring stretches have no width, a stop at a tint of 0 without process
plates, and in PDF a stop whose stretches are fully transparent print no
ink, but their spot inks were still declared, so separations got an empty
plate.
- vectorcraft-color: Gradient::drawn_stops (the rule Document Info uses,
moved here) and Gradient::reached_stops (every stop taken as opaque).
- PDF: Separation and DeviceN shadings, the Perceptual spot shadings and
the spot-image warning name only inks of drawn stops at a tint above 0
or with process plates; other stops are written with no ink, so their
process inks and RGB or Lab colors no longer shape the space either.
- EPS: the same with reached stops, since EPS writes stops opaque; a
transparent spot stop keeps its ink.
Output is unchanged when every stop colors part of the gradient and every
spot stop has a tint above 0 or process plates.
The canvas clips a clip group by its clipping path with the path's live
geometry effects applied, but clicks, hovers and marquees still tested
the plain clipping path. A click where a Transform effect had moved the
clip selected nothing, and a click on the clipping path's own place
selected art that was not drawn there.
- The engine registers the effect-shaped region with the doc crate
(`hit::set_clip_region`), as it does for image paint and live art. It
gives a region only when the clipping path holds a visible geometry
effect; otherwise clicks keep the plain shapes, and type keeps
counting by its frame.
- Clicks, `objects_at`, the hidden art of a selected object, the Unlock
submenu's list and marquees inside a clip group or a clipping layer
use that region. Outline view and layers with Preview off draw no
effects and keep the plain shapes; the Selection, Direct Selection
and Enclosing Lasso marquees pass the view's outline state
(`hit::marquee_in_view`). Each container around a selected object
tests its clipping path in the outline state a click has there.
- A marquee touches the clipping path itself by its own geometry, as a
click on its outline finds it, and also on the edge of the region, so
a marquee around a clipping path that an effect moved into large art
still selects it. `hit::marquee_in` keeps the plain shapes for the
Color Theme Picker, which clips by them.
- The region is kept per thread until the clipping path or the live
inputs change. A clip group's reach takes in the region's box, so
culling does not skip art the region moved past the plain clipping
path. Up to 4096 boxes are kept; when they are full, a new box
replaces only as many others as it needs room for.
Docs: docs/target-app-parity.md.
Create Outlines copied each character run's fills and strokes onto every
glyph, ruby glyph and underline or strikethrough bar as they were, so a
gradient with no vector of its own refitted to each piece's own box,
while the canvas fits it across the whole layout. A gradient with a
vector kept its text-space coordinates in the document, and the
characters' stroke kept its text-space weight on scaled type.
Each piece is now painted by paint_type_piece, the rule outline_text
already used: unplaced gradients are fixed to the layout's bounds, then
the type's transform places the piece, its gradients, stroke weights and
pattern tiles. Object > Expand outlines type the same way, and
object.expand.info measures gradient fills on the same paths.
Text on a path of one closed subpath that is anchored to start before
the path's start point (text-anchor end or middle, right-to-left start,
or a negative startOffset) started at the start point, and text offset
past the path's end started at its end. It now wraps around the path,
as SVG 2's startOffset attribute describes for a single closed subpath
(https://www.w3.org/TR/SVG2/text.html#TextPathElementStartOffsetAttribute):
text-anchor end at startOffset 0 ends at the start point. Open paths
and paths of several subpaths still start the text at the path's start
or end.
A CMYK TIFF written again, a CMYK JPEG that the Compression settings
resample or compress again, and a CMYK image written with its mask as
its soft mask lost the CMYK profile the image embeds. Such an image is
now written in an ICCBased space holding that profile, byte for byte,
when output.profiles is all or taggedSource, and in PDF/A-2b, PDF/X-3
and PDF/X-4 unless profiles is destination. Pixels the Output settings
convert are never tagged with it.
A CMYK JPEG embedded as it is now follows the same rule instead of
always carrying its own ICC_PROFILE segments: with profiles none it is
DeviceCMYK, and PDF/X-1a export no longer refuses a CMYK JPEG that
carries a profile.
A profile tags an image only when it is at most 16 MiB and its declared
size, acsp signature, device class, CMYK color space and tag table are
well formed. Tagged CMYK documents blend transparency in the document's
CMYK profile space, not in an image's own; without that space they
blend in DeviceCMYK, or keep their RGB space in PDF/A.
An SVG file that an <image> links to was parsed with usvg's
from_data_nested, which drops every href to a file, so a PNG or JPEG
that the SVG file linked to disappeared without a placeholder or a
warning.
The files a linked SVG file's own <image> elements link to are now read
the way the imported SVG's links are, relative paths from that file's
folder: a raster stays linked, an SVG file imports as vector art, and
one that can't be read (missing, or refused by the reader) gets the
placeholder stretched to its box, its link and a warning. The same href
in SVG files of two folders names two files. A link that leads back to
an SVG file showing it gets the placeholder and a warning. Reading the
links of linked SVG files stops at the element depth cap and at 1,024
reads and 512 MB of text per import, the same limits decoded nested SVG
images have, counted separately from them. An SVG data URL still reads
no file links, having no folder of its own.
SVG 2 tells a viewer to ignore external references in an SVG shown by
<image>; reading them on import is VectorCraft's own rule.
An instance whose baked art passed the export's placed symbol art budget,
or drew too deep, gave its count back, so any number of instances could
each bake their art in full and be dropped without the work counting.
- Art baked and then dropped is now counted with the placed art against
the budget, both before more art is baked and when baked art is checked.
- While an instance's art bakes, the art's objects before baking are held
in the budget, so instances nested in it are placed only within what is
left.
Documents whose placed art fits the budget export as before.
Checking an Extrude & Bevel (both kinds) or Inflate effect built every
shape's profile about the profiles' bounds, then built the fill and
stroke solids again about the bounds of the regions they paint. A
filled shape's profile is its fill's solid, so that solid was built
twice. When a stroke reaches past the shape, the first build also used
bounds the effect does not draw it about.
The check now builds the profile on its own only where no drawn solid
is built from it: every shape for Rotate, and for Extrude & Bevel and
Inflate a shape with no fill (strokes only, or no paint). A filled
shape's solid is built once, about the bounds the effect draws it
about. The shadow check is unchanged.
Tests count the solids the check builds: an object with a fill and a
stroke, a group of two such members, an effect on the fill and the
shadow on; a shape with no fill still has its profile built.
A recovery copy written in the background set "Saved recovery data for
<title>" (or its failure line) when it finished, over any message shown
since it started, such as Restore's count with its notes or the
"Saving…" line of a copy started after it.
The status bar now counts the messages VectorcraftApp::status sets. A
copy shows its result only while its own "Saving recovery data for …"
line is still the latest message, or right away when it was written
without the worker. A failure is still logged, and a failed folder is
still recorded for automation replies, when its line is not shown.
Place, Open and relink read a CMYK or grayscale TIFF with an alpha
channel through extra_samples_tiff_rgba, which cut every 16-bit sample
to its high byte before converting it. A faint ink or a low alpha was
lost: cyan 129 at full alpha showed as white, and an alpha of 129
became fully transparent.
The samples are now normalized at the depth the file stores them, and
only the resulting color and alpha are rounded to 8 bits (a 16-bit
alpha to the nearest 8-bit value). Associated alpha is divided out at
16 bits. A pixel whose associated alpha is zero now has no color,
instead of keeping its stored color. TIFF 6.0's Associated Alpha
Handling section states this arithmetic and the zero-alpha rule for
RGB data; applying them to CMYK and gray samples is VectorCraft's own
rule.
With Invert Wrap and a positive offset, a line's band was cut into at
most 64 pieces, and anchor cuts were dropped when a shape had more than
64 of them. Beside a notch the offset fills, a line could stop at the
notch instead of at the grown edge: 20 pt short beside a 0.5625 pt notch
with an offset of 1, and short beside sloped notch sides and tall bands.
The grown shape is now measured exactly: the shape near the band is cut
into trapezoids, each grown by the offset into a convex polygon, and the
line keeps the x at which those polygons hold every height of its band.
The work is capped; a shape that needs more steps than the cap, for
example one whose edges cross one another tens of thousands of times,
keeps the 64-piece measure, which keeps the line inside the grown shape.
A band of no height exactly the offset past a shape's tip now holds a
line too.
With Smart Guides on, a selection the Selection tool grabbed by one of
its anchors only landed that anchor on other anchors and centers, or
lined its bounds up. The anchor now also lands on the other objects'
paths ("path"), and where a path crosses another path or a ruler guide
("intersect"), in the order Direct Selection uses for a dragged anchor.
When the anchor lands on a path, the bounds do not also line up with
other objects, and a ruler guide nearby does not pull it off the path.
A selection grabbed anywhere else keeps lining its bounds up.
- Targets::for_move_by_anchor gathers the other art's path segments as
well, and Targets::snap_onto_path lands a point on them or on their
crossings with each other and with the ruler guides.
- MoveSnap::by_anchor uses those targets; the Selection tool picks it
when the press grabbed an anchor.
Reported by pivot-al.
Docs: docs/target-app-parity.md.
While the Type tool edits text, a zoom marquee started with Cmd+Space
read Space as up once Cmd was let go. Space pressed again moved the
marquee only in the frame of the key press, and later frames sized it.
The marquee now reads the Space key itself, so it moves and sizes as it
does outside text editing.
A space typed or committed by an input method during a Zoom tool drag
(the marquee or an animated zoom) is no longer typed into the text.
With no zoom drag, Space types a space as before.
The outline a Blob Brush or Eraser drag shows was copied, flattened and
tessellated again every frame, settled parts included, so the per-frame
cost of a long stroke grew with its whole length.
- The settled outline blocks are kept as shared paths and returned as a
new Overlay::SharedPath; a block gets a new path only when it is
joined again.
- The canvas keeps the mesh each shared path was drawn with, by the
path's allocation and for the view, clip and stroke it was built for.
A frame with the same path and view draws the kept mesh; a pan, zoom
or rotation builds it once more. Lines outside the canvas get no
triangles. A frame keeps only the meshes it draws.
egui still copies each kept mesh into the frame's mesh on every repaint.
The release sends the same path.blob and path.eraseRegion command as
before. The round Eraser tip's line is unchanged.
type.ruby, text.setStyle, text.setText, text.fitHeadline, text.reshapeArea,
text.reshapePath and text.setPathHandle recorded an undo step even when the
call left the type as it was: the same ruby reading again, a removal where
there is no ruby, attributes the characters already have, the same text, a
second run of Fit Headline, or a reshape by 0. Each now runs on a copy and
records a step only when one of its type objects (or, for text.setStyle, a
graph's kept label styles) changed; otherwise the undo and redo steps and
the saved state stay as they were.
Area Type Options already skips unchanged options, and Scale Text
Attributes returns an error for type with no scale to bake, so neither
changes. Type Orientation, Optical Margin Alignment, System Layout and
Fractional Widths are fixed in #7505.
The Confining file access bullet said the app's own folders are not
confined. It now names the commands that check the roots for a file
there: the arrowheads edit, reload and list commands, the New Document
profile commands and file.new, and the Scripts folder listing and run,
each linked to its section.
An inset ring limited a corner's miter by moving the corner four times
the bevel width along its bisector. At a corner sharper than about 29°
that the bevel opens (a convex corner with the bevel outside, a notch
with it inside), the bevel still ran out to a spike up to four times
the width and no longer lined up with the art. With the bevel outside,
the wall behind it spiked the same way.
Such a corner is now given twice in the outline before the solid is
built, and each copy moves along its own edge's normal, so the bevel
(and, with the bevel outside, the wall) cuts the corner square, as a
bevel join cuts a stroke. Corners within the limit keep their miters,
and corners where the inset edges meet keep the four-times limit.
A cap's outline skips the straight segment of no length between the
two copies where they do not move apart.
Docs: docs/three-d.md, docs/target-app-parity.md.
Once a right-to-left line holds a tab and a glyph with a negative
advance, the least it can be drawn at no longer grows with its tab
stops, so a line that overflowed in the order of its text read on to
the end of the paragraph before settling its break. Setting every line
of a long paragraph that way took time quadratic in its length.
Such a line now reads at most 256 legal breaks past where it first
overflows in the order of its text, and ends at the latest break read
that fits as drawn, the same limit the breaker uses for breaks it
measures one by one. Lines without negative advances, and lines that
fit, keep their breaks.
File › Place of a text file with the Unicode character set read UTF-16
only after a byte-order mark. Without one, UTF-16 text was read as UTF-8
(or the 8-bit set): characters outside U+0001 to U+00FF came out as
other characters, and each CR LF placed as two paragraph breaks.
Place now reads a file without a byte-order mark as UTF-16 by the rule
Import Data Sets uses, through the shared byte-order helper: an even
length, at least half of the two-byte units holding a NUL byte, the
byte order that reads a line break (for one line, the order its NUL
bytes give), and valid UTF-16 with no NUL character in that order. Any
other file is read as before, as UTF-8 or else the platform's 8-bit set.
Docs: docs/mcp.md, file.place params text, docs/target-app-parity.md.
Finding where the Rotate All, Space, Move All and Select/Move widgets
go and testing whether the pointer is over one flattened the group's
path two to three times per widget. Starting a Space, Move All or
Select/Move drag flattened it five times, and one pointer move over the
group could flatten it eleven times. Each lookup now flattens the path
once and shares that measurement across the widgets:
- vectorcraft-doc makes `Measured` public with `stop` and `stops`, and
adds `attachable`, which returns the path with its measurement.
- `Widgets::of` gives all four widgets for the canvas, `widget_at` gives
the widget under the pointer, and `WidgetDrag::hit` gives the drag a
press starts, each from one flattening. The Selection and Objects on
Path tools and the canvas use them.
- A Space, Move All or Select/Move drag keeps its measurement and
measures again only when the group's path changes.
Widget places, hits, cursors and drag results are unchanged. The new
`spine-count` feature of vectorcraft-doc exposes the flattening counter
to vectorcraft-tools' tests; it is enabled only as a dev-dependency and
leaves Cargo.lock unchanged.