Script-range boundaries alone don't bound group size — dense Unicode
blocks (CJK, user fonts) can produce groups of hundreds of KB. Add a
64 KB hard cap: when adding the next glyph would exceed it, close the
current group and start a new one with the same script ID.
64 KB is well above the largest current built-in group (~50 KB for
notosans_18 Cyrillic) and a safe transient malloc on the ESP32-C3.
The decompressor side already handles any number of groups per font.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The hot group buffer (formerly up to 50 KB BSS / heap) is removed.
During prewarm, each group is now decompressed into a transient malloc
that is freed immediately after its glyphs are extracted — only one
group buffer and the page buffer coexist at a time.
For getBitmap() cache misses (rare: only hit when a glyph wasn't
covered by prewarm), the group is also decompressed transiently and
freed after the single glyph is compacted into _hotGlyphBuf.
Peak heap during prewarm is now: page buffer + one group buffer.
Outside of prewarm, heap usage is only the page buffer itself.
Works correctly for large future fonts (Vietnamese, CJK) regardless
of group size, with no permanent allocation overhead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the 50 KB static BSS _hotGroupBuf array with a heap pointer
allocated once per font (ensureHotGroupBuf). The buffer is sized to the
largest group in the active font — 6 KB for small fonts, ~50 KB for 18pt
— so no memory is wasted when rendering body text at typical sizes.
The buffer persists across pages (freed only in freeHotGroup/deinit),
so there is exactly one malloc per font session rather than one per page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FontDecompressor: replace heap-allocated std::vector hot-group and glyph
scratch buffers with static BSS arrays, eliminating per-page malloc/free
and the heap fragmentation it causes during rendering
- FontDecompressor: add bounds checks on group/glyph buffer sizes; sort
prewarm group list by ascending index for sequential flash reads
- ParsedText: upgrade line-break cost function to Knuth-Plass cubic badness
(strongly penalises very loose lines, lenient on moderate looseness)
- ParsedText: exclude gaps before closing punctuation (. , ) » etc.) from
justification distribution so they stay at natural space width
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When downloading a book via OPDS, the last acquisition link is chosen when there are multiple. This allows choosing which format to download when there are many. Having only one format will automatically download that.
Fix potential use-after-free or double-free issue in the OPDS parser by
nulling it after free and guarding against using a nullptr parser in
`flush()`.