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.
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 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.
Linked images and placed documents in the art of the stored brushes (a
scatter or art brush's art, a pattern brush's tiles) are listed in the
Links panel after the other stored art, one row per linked or placed
object in each brush tile, with the brush as owner
(`{kind: brush, name, index, tile?}`). Check Links, the missing and
modified files reported on open, the link watch, Find in Folder, Relink
(including Relink All Instances), Update, Embed, Placement Options, Link
Info and Go To act on them, and their tooltip names the brush.
- vectorcraft-doc: `Document::mounted_art` holds art kept outside the
document's trees while a command looks it up by id; the stored-art
lookups, link visitors and `art_roots` include it, and
`number_mounted_art` gives its objects ids of their own.
- vectorcraft-brush: `stored_art::StoredArt` mounts the art of the
brushes that hold images or placed documents and writes back only the
entries whose art changed. Entries that do not read as a brush are left
as stored. `brush::store` keeps ids of their own in that art, so a row
keeps its id between commands.
- vectorcraft-engine: the links commands, opening a document, saving a
placed document's source, output of linked files and placed documents,
color masks after Update or Relink, and Package relinking go through
the mounted art.
Brush art rows follow the general Links rule as VectorCraft's own
application of it.
A fill or stroke row that the Appearance commands gave a graph label, its
object opacity, and the characters the Eyedropper gave it lasted only
until the graph was drawn again: the graph stored nothing for a label's
rows, and the Eyedropper's character attributes were not stored.
- The graph keeps, for value axis numbers, category labels and legend
labels, the paint, weight, opacity and blend mode of a label's topmost
visible fill and stroke row, their order and place relative to the
Characters row, and the label's object opacity and blend mode
(labelStyles valuesRows, categoriesRows, legendRows). Every label of
the kind gets those rows when the graph is drawn again, with a placed
gradient fitted to each label.
- Every appearance.* command stores them, and so do graphicStyle.apply
and redefine and the paint, stroke, gradient and transparency edits
aimed at a row, even when the label already showed them.
transparency.set without a row and object.setProps store only the
object opacity and blend mode. With several labels of a kind, the last
one edited wins.
- appearance.copyFrom onto all of a label's text stores the character
attributes it applied and, from type, the paragraph attributes; a
transparency pick stores the object opacity and blend mode, and
append stores the rows.
- paint.setFill and paint.setStroke store a character fill or stroke
only when the characters take the paint (the whole label, or all of
its text highlighted with the Type tool); aimed at a row, they store
the row.
- Document Color Mode and swatch edits reach the kept rows.
Docs: docs/mcp.md, the commands' params text, docs/target-app-parity.md.
Type with an inline graphic was resolved only in the layers, so type in
the art of another symbol, a graph design, a pattern tile or an opacity
mask kept the size its graphic had when it was made. The symbol holding
that type, its instances and the inline graphics that show it were
measured from the stale layout.
- Inline graphics are resolved in every tree that holds type. Symbols
are resolved in dependency order, so a symbol's type is laid out
before the type that shows that symbol inline. Edits, opening,
reverting and Refresh Font List use the same pass.
- The pass visits each object once: it resolves the children first and
copies a node only when something below it changed. It measures each
symbol's art once per pass.
- An inline graphic of a symbol whose art shows that symbol inline,
directly or through other symbols, is an empty slot. The symbols on
such a cycle are found once and cached until the symbols change.
- A changed inline graphic also clears the text's cached baselines.
The Character panel menu has Warichu and Warichu Settings… with Show East
Asian Options. Warichu characters are set at a percentage of their size
and stacked on 1 to 6 lines inside their line: one above the other in
horizontal type and side by side in vertical type. Warichu Settings sets
Lines, Line Gap, Scale and Alignment.
- text.setFormat and text.setRangeStyle take warichu, warichuLines,
warichuGap, warichuScale and warichuAlign; ui.warichuSettings opens
the dialog.
- A stretch of warichu characters is one block that stays whole on one
line. The canvas and every export draw the stacked lines; SVG, PDF and
the .ai writers write such type as outlines, as they do ruby. Type on a
path sets the characters at their own size.
- The attributes are saved natively, carried by character styles and
translated in every complete catalog.
Docs: docs/mcp.md, docs/type-parity.md.
Copy now remembers the whole chain of layers around each object, not only
the name of the nearest one. With Paste Remembers Layers on:
- In the same document, art goes back into the sublayer it came from while
that sublayer exists, even after it was moved, so two sublayers of the
same name in different layers stay apart. A deleted sublayer is made
again inside the layer that held it.
- In another document, the sublayer and the layers around it are matched
by name from the top level down; of repeated names, the topmost chain
that matches the most names wins, a shown and unlocked one first. A
missing sublayer is made inside its layer, and a missing layer on top
with the sublayer inside it, in the paste's undo step.
- When the matched layer, or a layer around it, is hidden or locked, the
paste fails with "the target layer is locked or hidden" and nothing is
pasted, instead of putting art in a hidden layer or making a second
layer of the same name.
A 9-slice guide can now be placed outside the symbol's art, by dragging
it past the art's edge in symbol editing mode, by symbol.options
nineSliceGuides, or by a Symbol Options field below 0% or above 100%.
A scaled instance then scales by the cells its art falls in.
- Each guide is a fraction from its edge in -16..16, and the two guides
of an axis keep their order (left + right < 1, top + bottom < 1).
- A guide past an edge cuts the art at that edge, so that side's outer
cell holds no art. With both guides of an axis past opposite edges,
the art scales as a whole along it. With both past the same edge, the
art keeps its size in the outer cell at the other edge.
- Symbol Options shows the guides with three decimals, and OK keeps a
field that was not changed at its exact value.
- Native files keep the guides exactly. SVG and .ai write such an
instance as its placed art, without guides.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
Linked images and placed documents kept only in symbols, graph designs,
pattern swatches or opacity masks now have ids of their own. links.list
lists them after the layers' own, one row per stored object, with an
owner naming what holds them; links.check, the missing, modified and
updated files reported on open, the link watch and links.findFiles
report them by id; relink (All Instances included), update, embed,
placementOptions, info and image.info act on them. links.goTo on one
clears the selection and returns onCanvas: false with its owner, and the
Links panel says the link has no place on the canvas.
- Document::visit_stored, stored_node and stored_node_mut find objects
in every stored art. fix_next_id counts their ids and gives an object
there whose id is 0, past the id range or already used a new one,
updating Objects on Path references to renamed children.
- Copies of masked objects give the mask art new ids, and symbols and
patterns that placed art brings from another document take ids of the
document they join.
- The Links panel picks such rows in the panel itself, drops the pick
when the canvas selection changes, and draws their thumbnails from a
lookup made once per list refresh. Select by Link Status leaves them
out, as it cannot select them.
The Color Theme Picker button beside the Assign and Edit tabs turns on
picking from the canvas. A click takes the color palette of the object a
Selection tool click takes there, a drag takes the palette of the art
inside its marquee, and Shift adds more picks. The rows that are not
excluded take the palette as their new colors and the art previews them;
OK keeps the recolor as one undo step.
- recolor.pickTheme {picks, colors?} returns the palette of the art
picked: visible fills and strokes, gradient stops, text, mesh points,
pattern tiles, symbol art, colorize colors and image pixels. Clip
groups, clipping opacity masks and isolation mode limit it to what
shows, and it changes nothing.
- Each object read weighs the same; similar colors merge (k-means in Lab)
into at most `colors`, most used first. Image reads are bounded (65,536
pixels a pick, 1,048,576 pixels and 64 images in all), and past 4,096
distinct colors, close colors merge before clustering.
- The dialog's themePicker and themePicks fields drive it from
ui.dialog.set. A new Colors count keeps the picks.
File › Place with Link reads a DCS 1.0 or 2.0 file (an EPS file whose
header names one EPS program per printing plate) as one linked image of
its plates. Each spot plate adds a spot swatch in the CMYK values the
file gives, and the clipping path the file's composite is drawn through
clips the image. document.open, file.place without link or from bytes,
and links.embed refuse a DCS file and change nothing.
- The screen, composite print and exports show the plates mixed into
CMYK. Separations Preview and print separations show each plate on
its own ink; a spot plate prints as its CMYK values when spots print
as process.
- links.list answers the format DCS, and with dcsTransparency marks the
DCS images that are transparent or overlap transparent objects (the
Links panel menu's Show DCS Transparency Interactions).
- swatch.unused leaves out a spot swatch while a DCS image has a plate
of its ink.
- links.update and links.relink read the plates and the clipping path
again, and the link shows as modified when one of its plate files
changes or is gone.
- The plates go on the first plate's pixel grid, grown to hold every
plate where it prints; a plate of another size or position is
resampled onto it.
- A file may name 64 plates, a plate file may be 1 GB, and the plates
together may hold 512 MB of ink amounts. A file naming more plates is
an error.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md.
Object › Plug-ins › Image Tools gains Color Mask… (`addon.image.colorMask`),
Remove Color Mask and Apply Color Mask. Color Mask hides colors of the
selected images without changing their pixels. It matches colors as Remove
Background does: the edge color, the corner color or picked colors, with
Contiguous, Match, Tolerance and Softness. The mask is kept with the image
object and saved with the document: its settings, and a PNG of the image's
pixels with the hidden colors made clear, at the source's depth with its
color profile and resolution. It is a VectorCraft addition.
- The canvas (clip groups and white-shape opacity masks included),
printing and separations, PDF (soft masks of CMYK images included), SVG,
EPS, the raster formats, the legacy .ai and .ai editing data, EMF/WMF,
DXF and the flattener draw the masked pixels. A linked image stays
linked, and an output that would refer to its file writes the masked
pixels instead.
- Copy and paste, placing a document and placed documents carry the masked
pixels with the image, under the names they are given there.
- The dialog opens on the selected image's mask settings, with a Preview.
Without settings, the command returns them and changes nothing.
- Setting, removing and applying a mask are each one undo step, and no
step is added when nothing changes. Apply makes the masked pixels the
image's own and embeds a linked image. A mask that hides nothing is only
taken off, and the image keeps its pixels and its link.
- When an image's own pixels are replaced (Update Link, Relink, Crop
Image, a recolor, a linked file read again on open), its mask is made
again for the new pixels, in every art tree and in opacity-mask art.
Crop Image cuts the image's own pixels. When the new pixels can't be
masked, the mask keeps its settings and hides nothing.
- Objects in `ids` that are not images, CMYK images, linked images that
show their preview, images past the decode allowance, masks that would
hide every pixel, and grayscale images colorized with a process color
are refused. The Eyedropper, Object Mosaic, Create Gradient Mesh, Crop
Image's trim and Image Trace read the image's own pixels.
- An image is selected by a click anywhere in its frame, as before.
Remove Background now also refuses a grayscale image colorized with a
process color. Its result has an alpha channel, and a process color is
refused on a grayscale image with transparency.
Docs: docs/mcp.md (Image Tools add-on › Color Mask),
docs/target-app-parity.md.
The Stroke panel menu's Edit Arrowheads… (stroke.arrowheads.edit) opens
Arrowheads.vectorcraft next to the preferences, creating it with one
example head when it is missing. Each symbol in the file is one head,
listed by name after the built-in heads in both arrowhead menus: it
points right, its registration point is the tip, and its height is drawn
as wide as the built-in heads. The file is read at startup and again
each time VectorCraft saves it; stroke.arrowheads.reload and
stroke.arrowheads.list read and list it.
Arrowhead gets a Custom variant, appended after the built-in names, that
carries the head's name and outline, so a document that uses one draws,
saves, reopens and exports it without the arrowheads file. stroke.set
takes {"custom": name} or the {"Custom": {name, path}} form that
document.inspect reports.
Part of #5421.
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:
- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
Select All, the art bounds, clicks and marquees, and a layer's
Pathfinder and Transform effects leave them out. The Layers panel
lists them as guide rows of their layer, the highest guide.list
index first.
- Deleting a layer deletes its guides, and merging layers keeps their
guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
they were made.
New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.
File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
A PDF, an EPS or .ai without ruler units, a raster image, a metafile, an
Affinity file or an importer plug-in's document opened (and reverted) in
points whatever the General unit was. They now open in the General unit,
as the reference app opens files that record no unit. Native, SVG and DXF
files and a .ai or EPS with %AI5_RulerUnits keep their own units. Only the
displayed units change, not the art.
The Shape Builder, Live Paint and run_around tests in planar/tests.rs
measured elapsed time against a 5 s limit, and the large edit tests
(large_edits.rs, with the Lock and Hide All Artwork Above and perspective
Alt-drag tests that share `soon`) against 20 s. On a loaded machine they
failed without any change in the code.
- pathops: a test-only counter adds up the items `RectIndex::at` reads
and the segments `nearest_u` searches. The four planar tests assert a
bound on that count.
- doc: `tree_walks` counts, per thread, the walks of a document's tree
that lookups by id make. `soon` fails when a command walks the tree
more than 64 times, and keeps a 300 s wait for a hang. The tests that
named a 20 s limit are renamed.
Looking objects up one at a time in Delete or Offset Path, or scanning
the whole index or ring in the planar code, makes these tests fail.
The editing copy of a .ai or .ait file that VectorCraft saves or exports
now has `%_<n> /Int (CropAreaActive) ,` after the ArtboardArray in its
recorded part, and marks that artboard as the selected one. The app
records its view's active artboard before each command
(DocState::active_artboard; an index past the last artboard is the last,
as in the Artboards panel); without a view, the artboard the document
opened on (Document::opened_artboard) is written, else the first. A .ai
file read from its editing copy opens on that artboard. Before, the copy
named no active artboard and always marked the first as selected.
Docs: docs/mcp.md, docs/file-format-parity.md, the editingData option text.
VectorCraft now reads `CropAreaActive`, an `/Int` in the dictionary of
the editing data that holds the `ArtboardArray`, as the index of the
active artboard in that array. The reader ignored it before, and every
file opened on its first artboard.
The opened document starts with that artboard active, so the view the
document opens at and the Artboards panel use it. The index counts
every entry of the array, so an artboard that is left out (one with no
area) does not shift it. When the entry is missing, is not an `/Int`,
or names an artboard that does not open, the first artboard is active,
as before. A file that comes in as its PDF part, with its pages placed
where its artboards are, opens on the page of the active artboard, also
when the reading stops before the artboards. The same reader serves the
editing data of EPS and PDF files.
The active artboard is kept on the opened document only; native files
do not save it.