* hyphenation: linear heuristic, cache misses outside the lock
The syllable heuristic built each word suffix as a String to test it, which is
quadratic in the word's length; it now uses prefix sums of vowels and UTF-8
lengths. Mapping a word's break points to glyphs scanned the word for each
point; both are indexed once per word. A word not in the hyphenation cache is
worked out without holding the cache's global mutex.
* hyphenation: compare the word ending with ends_with
A cached story copied every wrap neighbour's whole signature and item list into
its entry; stories on one spread share its wrapping items, so entries grew with
all of theirs together. A neighbour's signature now goes in as its hash and its
items are kept once each.
Anchored-frame placement asked for the host's composition once per frame, and
again at every level a host was reached from. Each host is now asked for once
per placement pass, and the cache and the uncached path remember the hosts they
resolved for the whole traversal.
- Footnote references on a line are deduplicated with a set instead of
searching the references collected so far.
- Nested styles stream the paragraph up to their delimiter with one character
of lookahead instead of copying the rest of the paragraph for each style,
and a delimiter's characters are indexed once per style.
- Shaping resolves which nested/GREP style is on top with one sweep over the
overlay boundaries instead of scanning every overlay for each stretch.
- Column rules group crossing lines by paragraph through an index.
- Table fragments index each row's top once instead of searching the rows
for every cell.
The warning from #2190 looked only at the attributes passed. A composite
font made by style.compositeFont.set isn't an installed family, so
applying one warned "Missing font: Mixed" even when its entries were
installed. And a fontStyle-only call, or a family change that kept a
style the new family lacks, said nothing.
After a call that sets or clears fontFamily or fontStyle, type.char now
tallies the fonts the formatted text resolves to, with the code
font.list uses (composite entries per character), and warns about the
missing ones and substitute styles. At a caret it checks the font the
next typing gets. tally_story becomes tally_range over a byte range,
and describe_fonts is the part of used_fonts that type.char reuses.