* 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.
A relative link names another file once the document is saved elsewhere,
so a status remembered for the old file must not answer for the new one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fontFamily the app doesn't have was applied with a null result, so a
script that asked for Courier Prime exported the PDF in the substitute
and exited 0. The font is still applied, since documents can carry
missing fonts and Find Font and preflight list them, but type.char now
returns {"warnings": ["Missing font: Courier Prime (using Source Sans 3)"]},
or "Missing font style: ..." for a style the family doesn't have, in
preflight's words. Other calls still return null.
The drop cap is set at the indent and the list number beside it, so the
number's character is the later one on the line and sets the indent.
Pin that with a test, and say so where the character is looked up.
The character was looked up among the line's body glyphs, so one inside
the drop cap was missed, and one in a list number's ^i matched the first
body glyph (after the label's tab) instead of itself. Find it among the
placed glyphs, which include the drop cap's and the label's.
The lines after an Indent to Here start at the character, which already sits past the drop cap, but the lines beside the drop cap still added its width on top. They no longer do. Adds tests for the drop cap, for Indent to Here after a tab (the usual hanging number) and for a right-to-left paragraph, and documents how the indent behaves beside a text wrap.
links.list decided a placed graphic's status by reading the whole file and comparing it with the asset's bytes whenever the sizes matched, and the Links panel polls it twice a second, so a document of press-size images read every one of them in full on every poll. Image links now have the size-and-time fast path linked text files already had: a file of the remembered size and time isn't read, a new time is read once and remembered (unless the file changed in the last two seconds), a different size is modified without a read. The answer is keyed by the asset's bytes' identity, so a relink, update or undo can never be served a stale one; status() stays as the exact check for callers that want it.
Verified locally on a288a87b: fmt, clippy, engine link tests including the new unchanged_linked_images_are_not_read_again.
The composer treated Indent to Here as an invisible zero-width character and nothing else, so inserting it changed nothing. A line holding one now sets the start-side indent of the paragraph's following lines to the character's position, and those lines are broken again with the narrower measure. The indent holds into the next columns and frames; a later Indent to Here in the paragraph replaces it.