In vertical type on a path, every glyph was turned a quarter turn with
its top toward the path's end. An upright glyph's characters then read
backwards along the path, so a ligature's font caret other than its
middle did not line up with the glyph as drawn.
An upright glyph whose top faces the path's end now turns half a turn
about the centre of its cell, so its top faces the path's start. It
keeps the same stretch of the path. Glyphs lying on their side, and
upright glyphs that the Skew, 3D Ribbon and Stair Step effects already
turn this way on a path running to the left, are unchanged. The ligature
stops follow the top of the cell, so the caret, a click and the
selection meet the drawn caret line.
An envelope mapped type from its laid-out box: ascent to descent for
point type, the frame for area type. The distorted glyphs then filled
less of the envelope than other art does, with room above the capitals
and below the baseline.
The envelope now maps type from the bounds of its glyph outlines,
underlines and strikethroughs when the type is on its own, in groups
and layers, or used as a clipping path. Inline graphics count by their
boxes. Rotated type and type in perspective map from their outlines as
they are drawn. Make with Mesh,
Make with Top Object and Reset with Mesh without Maintain Shape use
those bounds. Type that draws nothing keeps its laid-out box, and so
does type inside any other object, such as a blend, a repeat or a
symbol instance. Shrink Text to Fit type is measured as it is laid out
on the page.
vectorcraft-text's envelope_ink measures the type, and
register_envelope_ink registers it with the document crate. The engine
session, Renderer::new, live evaluation in vectorcraft-effects and the
.ai reader register it. The document crate keeps the measured boxes per
thread, by object and by a hash of the content, and measures again when
the type, the fonts, the hyphenation exceptions or the transform's
scale or rotation change.
This is VectorCraft's own rule. Envelopes of type made in VectorCraft
and in saved files map from the glyph outline bounds instead of the
type's laid-out box, so they redraw differently.
An installed face added for characters the interface fonts lack (Microsoft
YaHei for Simplified Chinese on Windows, for example) now gets a FontTweak
y_offset_factor computed from its ascent, descent and line gap against the
first face of the Proportional family, so its glyphs share that face's
baseline instead of centering the difference in row height. The bundled
faces and Latin text do not move.
This follows designcraft#394 (baseline_tweak from line metrics) and
effectcraft#1395. vectorcraft-text gains line_metrics, and a test face with
a CJK system face's vertical proportions checks the baseline at 13, 24 and
40 pt in the interface families.
Opening a document laid out every type object for its bounds and
baselines and threw the layouts away, and the first frame laid the same
objects out again. The open now keeps them, up to 128 MiB, and a frame
takes each one once.
A frame takes a layout only for a text object equal to the one laid out,
and only while the fonts, the system fallback setting and the hyphenation
exceptions are unchanged. When the last frame being drawn ends, the rest
are dropped if a frame took one, and all of them are dropped after eight
frames that take none. A layout with bitmap or color glyphs and a text
object with a gradient are not kept.
Paint-only harakat runs stay inside one shaping cluster so letters keep
their joining forms and marks their attachment. When a character-level
shaping pass matches the monotone glyphs, a glyph may take a later
in-cluster character's run only if GDEF classifies it as a mark (class 3)
and that character is a harakat; otherwise ownership stays at the cluster
start. Conservative: a mark may miss its colour; a base letter must never
take a mark's. Fonts without GDEF and AAT faces fall back the same way.
Insert Kashidas tatweels take the base letter's paint, not a trailing
mark's. Private harakat module; type-parity, target-app-parity and roadmap
M7 evidence updated (scores unchanged). Tests cover paint_only / ranges /
own_chars (wide mark, equal advance, multi-component, single-glyph
ligature, review counterexample), colour, hide, repeated glyph ids,
kashida, forced mark-last emit_kashida, and installed fonts.
- Auto leading stays auto: each line takes the paragraph's share (key 7)
of the largest type on it. The leading was the first two lines'
distance for every line, so 10 pt lines under a 15 pt heading came in
18 pt apart.
- The paragraph's hyphenation options (keys 10 to 15) are read.
- Area type of one frame whose last lines the file sets, but which don't
fit here (the fallback font, or a descent past the frame's bottom),
gets a frame taller from its bottom (vectorcraft_text::fit_source_lines),
with a warning.
A user's .ai whose type differed from its page by 6.6%, so that it opened
from its PDF part with each line its own text object, now opens from its
layers.
text.glyphAlternates reported the default glyph as `chosen` for any
character that overflows its area. A character whose style substitutes
another glyph (`salt`, or `vert` in vertical type) was reported with a
glyph it is not drawn with. The query now shapes such a character alone,
with the rest of its grapheme in their own styles and in the text's
writing direction, by the same helper text.setGlyphAlternate uses.
The helper also shapes the character in the direction its paragraph
sets it in, from its bidi level, so a parenthesis set right to left is
reported with its mirrored glyph in both commands.
On a path, vertical type turns an upright glyph about its pen, so its cell
runs from 0.12 em before the pen to 0.88 em after it without the font's
vertical metrics. The ligature caret, a click and the selection measured
the cell from the pen instead. They now take the cell where the glyph's
transform draws it, with its placement, baseline shift, Horizontal Scale
and Skew.
The OpenType panel gets a Position menu after Figure, with Default
Position, Superscript, Subscript, Numerator and Denominator. `numr` and
`dnom` join the extra features (the existing bits keep their indices),
so they round-trip through the stored tags and reach the shaper.
text.setStyle and text.setRangeStyle take `featureChanges`, which turns
features on or off on top of each character's own features in one
command and keeps every tag the list does not name. The Figure menu,
the Position menu and the checkboxes send it, so on a selection whose
characters have different features each character keeps its other
features. Reset OpenType Features still sends `features`.
Catalog rows for Position:, Default Position, Numerator and
Denominator; one line each in docs/type-parity.md and
docs/target-app-parity.md.
Area type made from the editing data's text document came in with one
row, one column, the 18 pt gutter and the Ascent first baseline,
whatever its frame kept. Each area frame now keeps its rows and columns,
the gutter between rows and the gutter between columns, and its first
baseline (Fixed, Cap Height, Leading or x Height) with its minimum,
scaled with the art's units. Each frame of a thread keeps its own, and a
key that is missing or can't be read leaves that option as it was. A
first baseline the frame keeps is no longer replaced by the fixed one
that matches the file's first line.
AreaOptions gets row_gutter (rowGutter, left out while unset), which
layout spaces rows by; text.areaOptions takes and reports rowGutter, a
gutter given alone sets both, and the Area Type Options dialog shows
Row Gutter.
Baseline shift now moves a character in the line's frame, outside
Character Rotation, so a positive shift moves a character up from its
line at any rotation, and the character turns about its shifted pen
position. The Touch Type tool splits a drag into its part along the line
(kerning) and its part up from the line (baseline shift) at every
rotation; before, within about 6 degrees of a quarter turn it dropped
the cross-line part.
Characters that already combine a rotation with a baseline shift are
drawn in a new position. Their stored values do not change.
- text: glyph transform order, PositionedGlyph::pivot (the turning
point), tab leaders in the same order
- tools: Touch Type splits drags against the line's up axis and measures
rotate-handle turns about the shifted pen position
- engine: text.touchType description; dy is up from the line
- docs: mcp.md Touch Type paragraph, target-app-parity.md progress line
Character Rotation turned a glyph's baseline shift and scaling with the glyph, and the Skew path effect slants a glyph's height away from the path's normal, so the x-height guide moved off the glyphs. Each positioned glyph now carries the transform that removes its Character Rotation (PositionedGlyph::unturned), and the guide goes through the glyph's x-height point placed without it, measured across the baseline. Snap to Glyph also takes Smart Guides being on in the tools, as View › Snap to Glyph already does in the engine.
On a curved path each glyph was placed rigidly (turned to the tangent at
its middle), so its baseline ends stood off the curve and neighbours
turned by different angles: in scripts whose letters join, every join
opened into a notch. Measured with Arial on circles: at 29 pt on a
radius of 110 pt, 0.64 pt at the baseline and 1.24 pt at the height of
the joining stroke (a quarter of the size); 14.7 pt at 72 pt on a radius
of 60. Placing glyphs on the chord alone closes the baseline but leaves
about h (a_i + a_{i+1}) / 2R above it (0.71 pt here), so joined glyphs
are bent instead.
- Shaping marks each glyph of a segment whose script joins (Arabic,
Syriac, N'Ko, Mongolian, Adlam and the other cursive scripts), by the
segment's script, so a word bends as a whole.
- With the Rainbow effect such a glyph's outline is mapped point by
point: pen space (x, y) -> P(s + x step/advance) + (y + rise) N there,
each segment cut into pieces short enough for 0.01 pt (capped). A
glyph the path doesn't turn under stays exactly as it was; Skew, 3D
Ribbon, Stair Step and Gravity, vertical type, inline art and colour
glyphs are unchanged. `PositionedGlyph::bend` records it; `xf` stays
the straight glyph for the caret, hit testing and tools.
- PDF export draws bent glyphs as outlines and writes them again as
invisible text where they stand, so they can be selected, searched
and copied. SVG export outlines them (right-to-left type already was),
never as a <textPath>, and labels the group with its text
(role="img", aria-label) for screen readers and search.
Tests (crates/text/src/tests_path_joins.rs; installed Arabic font, skip
without one): the gap at the baseline and at join height is <= 0.01 pt
at 12, 29 and 72 pt on radii 60, 110 and 300, on both sides of the
path, with finite outlines; Latin on a curve and joined type on a
straight path are byte-for-byte what they were; the chord-only
prediction is checked.
vectorcraft-cli perf (load average 43 before, 34 after: not trustworthy;
the synthetic document has no type on a path, so bending doesn't run):
render fit 113.8 -> 132.7 ms, pan 115.5 -> 140.0 ms, 400% 32.9 -> 43.3
ms, hit test 1.36 -> 1.59 ms, save 81.5 -> 88.2 ms, open 306 -> 401 ms,
SVG 426 -> 439 ms, Pathfinder 93.7 -> 85.5 ms, handle drag 5.5 -> 6.7
ms, segment drag 5.1 -> 6.7 ms.
CodeRabbit findings #7, #72, #92, #94, #95, #99, #100, #140, #158, #174:
- Link search hashes at most the size it looked at (a growing file no longer matches).
- Resolving links reads, decodes and previews each linked file once, shared by every key.
- Placed documents read inside one another resolve links at most 8 deep, never one already
being read.
- A data set's image path and the placed-document rereader follow stored paths through
`document_path` (no other computer's share).
- The Links panel looks at a linked file within the roots of the automation caller that
opened the document.
- A fonts folder set by a confined caller is scanned without following links out of it.
- Recovery refuses an area folder that is a link or another account's.
- Atomic saves keep the replaced file's group and Linux access ACL, or make the replacement
owner-only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VectorCraft had no lists. A bullet could only be typed as a character,
and the tab after it did not stop at the left indent. The reference app
turns paragraphs into a bulleted list from the Paragraph panel, the
context menu and Type › Bullets and Numbering › Apply Bullets.
text.setFormat takes list ("none" or "bullet") and bullet ("none",
"dot", "dash", "circle" or "square"). The layout draws the bullet before
the first line of each list paragraph that has text, in the font, size
and color of its first character, and starts the text at the next tab
stop, with the left indent counted as a stop. Wrapped lines start at
the left indent, and type on a path draws the bullet too. In a
horizontal right-to-left list paragraph the indents are mirrored: the
bullet hangs at the right, and the wrapped lines start at the right
where the first line's text starts after the bullet. The bullet is not
a character of the text. A paragraph whose left and first-line indents
are both 0 gets 18 pt and -18 pt when it joins a list. Return continues
the list, paragraph styles carry it, Reset Panel turns it off, and the
native file saves it only when it is set.
The Paragraph panel has a Bullets button with a list of the five
presets, and Type › Bullets and Numbering › Apply Bullets and the
context menu run type.applyBullets. Live SVG text writes the bullet's
character, and SVG writes type on a path with bullets as outlines. DXF
writes the bullet as a TEXT entity of its own, and legacy .ai export
warns that bulleted lists are left out. The caret, selections, Touch
Type and Show Hidden Characters skip the bullet.
Live SVG text of horizontally scaled type now writes each tab in a
tspan of its own. Before, the characters before a tab shared its tspan,
and its textLength made a viewer stretch them across the tab's gap.
Docs: docs/mcp.md, docs/target-app-parity.md.
Shrink Text to Fit scales area type down at layout time, and the canvas,
PDF export and the exports that write type as outlines draw it at that
size. SVG export wrote the live text at its stored font size, letter
spacing and baseline shift, so a viewer drew it larger than the canvas
does. Legacy .ai saves wrote the stored size, leading and baseline shift
into the text objects, DXF export took the TEXT heights from the stored
size, and the CSS Properties panel gave the stored font size, letter
spacing and line height.
The text crate's scaled, which the layout scales shrunk text with, is
now public. SVG export, legacy .ai saves and the CSS Properties panel
take the type's styles from it at the layout's fit scale, and DXF
export multiplies the TEXT heights by that scale. Positions and
textLength in SVG still come from the layout.
Docs: docs/target-app-parity.md.
The reference app justifies Arabic text with kashidas, set per paragraph
with the Paragraph panel's Insert Kashidas list (None, Short, Medium,
Long, Stylistic). VectorCraft had no kashidas, so a justified Arabic
line was spaced out at its word spaces like a Latin one.
The Paragraph panel, with the Indic options shown, now has Insert
Kashidas. On a justified horizontal area type line that is shorter
than its measure, each Arabic word with joined letters gets one kashida
between its last pair of them, drawn as tatweels (U+0640) in the font
of the letter after it. The kashidas share the line's room equally,
each at most a quarter of an em (Short), half an em (Medium) or an em
(Long); a Stylistic kashida is a row of the font's own tatweels, up to
an em. The word spaces take the room the kashidas leave, so the line
keeps its width, and line breaks do not change. The letter after a
kashida advances over it, so the caret before that letter, a selection
of it and a click on it take the kashida in. None, the default, leaves
lines as they were.
In horizontal type that is not on a path, the caret in a letter drawn
with several glyphs (a letter with a vowel mark) is now placed by the
whole letter, as hit testing already measured it, instead of by its
first glyph.
text.setFormat takes kashida. The setting is saved with the paragraph
only when it is not None, paragraph styles carry it, and Reset Panel
sets it back to None.
Docs: docs/mcp.md, docs/target-app-parity.md.
The Paragraph panel menu's Justification… was a disabled stub, and justified
lines stretched word spaces between fixed limits (80% to 150%).
Each paragraph now has Justification options: Word Spacing, Letter Spacing
and Glyph Scaling (Minimum, Desired, Maximum, in percent), Auto Leading and
Single Word Justification. Desired applies to every line; a justified line
fills its measure with word spacing first, then letter spacing, then glyph
scaling, each within its range, and past them its word spaces take the
rest. The Every-line Composer scores lines by that flexibility. Auto
Leading sets the leading of characters whose leading is Auto. A justified
line holding one word is spread between its letters (Full Justify) or
aligned left, centre or right.
The defaults are the documented ones (80/100/133, 0/0/0, 100/100/100, 120,
Full Justify), saved only when they differ. Because the default maximum
word spacing is 133%, justified paragraphs break differently than before;
the Every-line Composer's second pass is now unbounded for justified text
(it used to give up past four times the stretch and fall back to the
Single-line Composer, which the narrower default range would make common),
and lines that can't stretch at all are still refused.
text.setFormat takes wordSpacing, letterSpacing, glyphScaling ([min,
desired, max] or {min?, desired?, max?}), autoLeading and
singleWordJustification; Minimum ≤ Desired ≤ Maximum must hold or nothing
changes. Paragraph styles carry the options (New and Redefine capture
them), and Reset Panel resets them. Justification… opens a dialog
(ui.justification) with Preview; OK is disabled while a row is out of
order. Live SVG text places characters the options move, and a glyph that
Glyph Scaling scales is stretched to its width; PDF uses the glyphs'
transforms. Vertical type isn't glyph scaled; type on a path doesn't use
the options.
Docs: docs/mcp.md, docs/type-parity.md, docs/gaps.md.
Closes#2755.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In type that holds right-to-left characters, Left and Right move the
caret through the visual caret stops (`caret_horizontal`). A cluster
drawn with no advance, such as a zero width non-joiner that the font
shapes as a glyph of its own, puts two stops at one x. A step kept the
stop with the lower byte, and the next step passed over the other one,
so the arrow keys never reached it. In Persian میخواهم laid out on
macOS, the caret could not be put between the non-joiner and خ with
the arrow keys.
Stops at one x are now visited one at a time in byte order. Going
right, the caret takes them in increasing order on left-to-right
glyphs and in decreasing order on right-to-left glyphs; going left,
it takes them in the reverse order. The end of a soft-wrapped line,
which the caret shows on the next line, is not one of those stops.
Where no two stops share an x, a step goes where it went before.
VectorCraft listed a variable font's named instances as styles, but
type could not be set between or past them. The reference app's
Character panel has a Variable Font control that adjusts a variable
font's axes, such as Weight, Width and Slant.
Type now keeps Variable Font settings by axis tag
(CharStyle::font_axes, saved as fontAxes only when set). text.setFormat
and text.setRangeStyle take fontAxes: each value is clamped to its
axis's range, a tag the font has no axis for is left out, and null
goes back to the style's own setting. Another font or style clears
them, including one from a character style that does not set them. A
character style captured from type sets them, with {} for none.
text.fontList {family, style} lists a font's axes.
Layout, outlines, the substituted-glyph highlight and PDF export use
the font instanced at the settings, and neighboring runs at different
settings are shaped apart, as runs in different fonts are. SVG export
writes them as font-variation-settings, the wght setting as the weight
and the wdth setting as font-stretch, and embeds the font instanced
there; SVG import reads font-variation-settings back. Legacy .ai export
warns that the settings are left out.
The Character panel shows a Variable Font button for a variable font.
It opens a slider and a field for each axis the font does not hide,
labeled with the font's name for the axis, and screen readers get each
slider under that name. A drag is one undo step. Reset Panel leaves
the settings, as it leaves the font and style.
Missing Glyph Protection now keeps the settings and the font version
on the characters that keep their font; before, it kept only the
family and style. A character style applied or redefined with another
font now clears the old font's version, as text.setStyle does, unless
the style sets one. SVG export with embedded fonts warns when faces of
one weight, style and width share characters, which browsers then draw
in one of the faces.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md.
A syllable drawn with several glyphs that share one cluster, such as a
pre-base i matra and its consonant (कि), had a caret stop at the edge
of each glyph. A click just right of the matra, in the left half of
the syllable, put the caret after the syllable. byte_in_line now takes
one extent for each cluster, and type on a path compares the click
with the middle of the cluster, from the start of its first glyph to
the end of its last.
New tests with an installed OpenType Devanagari font check that क्ष
forms one conjunct glyph, that र्क draws the reph after its base, that
कि draws the matra before its consonant, that Ka with a nukta matches
the precomposed क़, and that each syllable is one cluster with the
arrow keys stopping at its ends. They print SKIPPED when no such font
is installed.
Docs: docs/localization-parity.md (Hindi row), docs/target-app-parity.md.
The nine OFL faces in `assets/fonts` are compiled into every build of
vectorcraft-text: 3.4 MB of the binary, about 1.6 MB of a gzipped wasm.
An embedder that loads every font at run time with `FontDb::add_font`
(a web app serving its own font catalogue, say) ships them twice and
can't turn them off.
- vectorcraft-text: `bundled-fonts`, a default feature, holds the
`include_bytes!` list. Off, a database starts without them (craft-fonts
and system fonts are unaffected) and text has no glyphs until a font
is added, as when the bundled fonts fail to parse.
- vectorcraft-engine: a `bundled-fonts` default feature forwards to it,
so the apps and any embedder using the engine keep the fonts unless
they build it with `default-features = false`.
- The workspace declares vectorcraft-text without default features, so
no library crate turns the fonts back on. The tests of cad, metafile
and tools, which lay type out, ask for the feature in their
dev-dependencies; the other crates' tests get it through testkit.
`cargo test --workspace` builds with the fonts, as before. A new
integration test, `without_bundled_fonts`, covers the configuration
without them (`cargo test -p vectorcraft-text --no-default-features
--test without_bundled_fonts`): the database starts without them, text
in no font lays out with no glyphs, and a font added later is used and
is the fallback for families the database lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For Auto Size area type in columns, every text edit, each keystroke of
the Type tool included, laid the text out in one column and then laid
the columns out at that height and at each step of a bisection for the
least height that holds the text. In the release-build timing test
auto_height_in_columns_typing_timing (stories of 5,000 to 50,000
characters at 9 pt in a frame 500 pt wide), a keystroke laid the
columns out 18 to 22 times, and typing at the start of the story of
50,000 characters in 2 columns took 1,670 ms a keystroke.
The bisection now runs on the lines of the one-column layout.
columns_hold computes where those lines would sit in the columns, with
each column's first line at its first baseline below the column's top
(LineInfo::first_baseline, new in vectorcraft-text). The columns are
then laid out at the upper height the bisection ends on and, when they
hold the text there, at the lower one, unless that is the 1 pt it starts
from. When they hold the text at the upper height and not at the lower
one, Auto Size takes the upper height. When taller columns always hold
the text, the old search ends between the same two heights. When the
text wraps around other objects or does not fit the one-column layout,
or when that check fails, the columns are laid out at every step as
before. The same keystroke now takes 292 ms.
Docs: docs/target-app-parity.md.
In a dev build, opening a native file whose area type has 2^60 rows
or 2^60 columns failed with an internal error ("capacity overflow"),
and opening one with 2^32 rows and 2^32 columns failed with "attempt
to multiply with overflow". Text layout reserved room for rows times
columns frame cells. `text.areaOptions` keeps both counts within 1 to
100, but reading a document kept the counts the file gave.
Reading a document now clamps each count to 1 to 100
(`AreaOptions::MAX_ROWS_COLUMNS`): a larger count reads as 100, and 0
reads as 1, as layout already treated it. Text layout clamps both
counts the same way. Area type with 1 to 100 rows and columns lays out
as before.
The cells of a frame now share one copy of the frame's flattened
polygons and one copy of the flattened text wrap shapes. Each cell held
its own copy: in a dev build, a test process that laid out text in 100
rows and 100 columns of a circle frame of radius 1,000,000 pt peaked at
1.33 GB of memory, and now peaks at 100 MB.
A character style's OpenType features took nine tags. OtFeatures now
also keeps ss01-ss20, salt, titl, pnum, lnum, sups and subs (off by
default, one bit each in `extra`): they are accepted by the type
commands, stored with the style and passed to the shaper, so a font
that has them uses them.
The Affinity import test used ss01 as its example of a feature kept but
not rendered; it now uses cv01 for that (character variants still
aren't rendered) and keeps ss01 as one that is.
Fixes#1607
The font scan read /usr/share/fonts, /usr/local/share/fonts, ~/.fonts and the
XDG data folders. Packages add folders outside those to fontconfig's
configuration (TeX Gyre under /usr/share/texmf/fonts), so fc-list and other
apps showed fonts VectorCraft didn't have. The scan now also reads the <dir>
folders of /etc/fonts/fonts.conf and the files it includes (conf.d, the user's
own), as the fontconfig documentation describes them. The files are read as
they are: no library is linked and no program is run.
When a document opens with fonts that aren't available, fonts used
only in its symbols included, VectorCraft lists them in a Missing
Fonts dialog after the missing linked file questions, one document at
a time, with Find Fonts and Close. Find Fonts opens Type › Find Font
on the first missing font of the layers' type, to replace it. After
Find Fonts or Close the dialog doesn't open again for that open; when
another dialog replaces it, it opens again once that dialog closes.
Search Fonts Folder (the Fonts folder File › Package writes next to
the document) and Find in Folder… search a folder and its subfolders
for the fonts' files, and Add Fonts copies the files the user chooses
into a Fonts folder next to the preferences. The desktop app and
vectorcraft-cli read that folder with the system's font folders. Type
› Find Font has a Find in Folder… button too when fonts the document
uses are missing. The web app shows no dialog; a status message says
how many fonts aren't available.
- text.missingFonts lists each missing family and style once,
whatever version the document's type names, and leaves out CSS
generic families such as sans-serif. text.findFontFiles searches a
folder on up to 8 threads and returns at once; text.findFontFiles {}
reports the search, and {stop: true} stops it. A search reads only
what the font scan reads of font files (their table directories and
name, fvar and OS/2 tables) and, on macOS, of suitcase fonts, and
lists a file for a font only when the file makes the font resolve
exactly. It does not enter app packages, the Program Files and
ProgramData folders, the system's folders at the root of a volume,
the Shared and Public folders in Users, a home folder's Library,
AppData, Applications, snap and dot folders, a Library folder's app
data folders or the folders the environment names for apps, and it
skips other hidden folders and does not follow links below the
picked folder. It ends after 60 s by default, after 10,000,000
entries listed, with more than 1,000,000 folders waiting, after
10,000 font files read or 1,000 kept, or once every font has a file;
Stop and Close end it too.
- text.addFontFiles copies only files the last search found. Of
those, it copies only regular .ttf, .otf, .ttc, .otc, .woff and
.woff2 files and suitcase fonts with their resource fork, at most
256 MB a file and 1 GB in all. It never replaces a file: a file with
the same contents is kept, and when another file has that name, the
copy gets a number (Name 2.otf). The fonts are then scanned again,
and type set in them redraws.
- ui.missingFontsDialog and ui.findFontsInFolder open the dialog for
agents; docs/control-protocol.md lists its fields.
- File › Package gives a font file whose name has no font extension
the extension that matches its data (.ttf, .otf or .ttc).
- vectorcraft-text adds set_app_font_dir, which system_font_dirs
reads, WantedFonts, which reads a font file's names as the font scan
does, is_font_file and is_suitcase. #762 split its face reader, which
now takes read budgets: file_face_names_within tries the suitcase
reader first, and reader_face_names_within applies the budgets to
each font. vectorcraft-format adds write_new_with.
Docs: docs/mcp.md, docs/control-protocol.md, ROADMAP.