## Summary
**What is the goal of this PR?**
### Problem
Inter-word gap widths were computed as two separately-snapped integers:
```cpp
gap = getSpaceWidth(fontId, style); // fp4::toPixel(spaceAdvance)
gap += getSpaceKernAdjust(fontId, leftCp, rightCp, style); // fp4::toPixel(kern1 + kern2)
```
Because `fp4::toPixel(a) + fp4::toPixel(b)` can differ from
`fp4::toPixel(a + b)` by +/-1 pixel when the fractional parts straddle a
rounding boundary, each inter-word space could be one pixel wider or
narrower than the correct value. This affected line-break width
decisions and word-position accumulation across the whole paragraph
layout pipeline.
### Fix
Replaces `getSpaceKernAdjust()` with `getSpaceAdvance(fontId, leftCp,
rightCp, style)`, which combines the space glyph advance and both
flanking kern values (`kern(leftCp, ' ')` + `kern(' ', rightCp)`) into a
single fixed-point sum before the snap:
```cpp
return fp4::toPixel(spaceAdvanceFP + kern(leftCp, ' ') + kern(' ', rightCp));
```
This is the same single-snap pattern already used by `getTextAdvanceX`
for word widths.
### Changes
- **`GfxRenderer`**: Replaces `getSpaceKernAdjust()` with
`getSpaceAdvance()`. `getSpaceWidth()` is retained for the
single-space-word case in `measureWordWidth` where no adjacent-word kern
context is available.
- **`ParsedText`**: All four call sites (`computeLineBreaks`,
`computeHyphenatedLineBreaks`, and both loops in `extractLine`) updated
to use `getSpaceAdvance()`. The now-redundant `spaceWidth`
pre-computation and parameter are removed from all three internal layout
functions.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**YES to analyze for
correctness**_
## Summary
Fixes a crash (load access fault) when opening EPUB chapters whose first
text block exceeds 750 words.
## Changes
* **Crash fix (`addLineToPage`)**: Added a null guard for `currentPage`.
If `makePages()` hasn't run yet (which can happen when the first block
triggers the "text block too long" split path), the page is now created
on demand.
* **Layout fix (`characterData`)**: The early-split path previously used
`viewportWidth`, ignoring CSS margins and padding. It now computes
`effectiveWidth` using `totalHorizontalInset()`, consistent with
`makePages()`.
## Additional Context
* Crash signature: `MCAUSE=0x5` (load access fault), `A0=0x0` (`Page*`
null), `MTVAL=0x4 / 0x8` (offsets into `Page::elements`).
* Confirmed in two user reports reported in #1328
* Tested on PR #1357 (not on `master`).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< PARTIALLY >**_
## Summary
* **What is the goal of this PR?** On a cold boot (or after a crash that
corrupts RTC RAM), logHead contains garbage. Then addToLogRingBuffer
does: ``strncpy(logMessages[logHead], message, MAX_ENTRY_LEN - 1); ``
With garbage logHead, this computes a completely invalid address. The %
MAX_LOG_LINES guard on line 16 only runs after the bad store, which is
too late. The fix is to clamp logHead before use.
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**NO**_ (did use claude
for the magic hash value)
## Summary
* **What is the goal of this PR?** Avoid repeated full central-directory
scans by using early stop when all requested targets are already
matched.
* **What changes are included?**
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** PARTIALLY **_
Identified by AI
## Summary
Extract shared reader utilities (`ReaderUtils.h`) to reduce duplication
across `EpubReaderActivity`, `TxtReaderActivity`, and (upcoming)
`MarkdownReaderActivity`.
Utilities extracted:
- `applyOrientation()` — orientation switch logic
- `detectPageTurn()` — page navigation input detection
- `renderAntiAliased()` — grayscale anti-aliasing pass
- `displayWithRefreshCycle()` — refresh mode cadence
- `GO_HOME_MS` — back button timing constant
## Impact
Flash: 32 bytes saved (6006441 → 6006409 bytes). Minimal immediate gain,
but meaningful once markdown reader and future reader types share these
functions.
Code quality: Eliminates ~100 lines of duplicated logic spread across
multiple files. All readers now follow the same patterns for
orientation, input handling, and rendering.
## Rationale
This refactor is preparation for markdown support, which requires
identical input and rendering logic. Instead of copy-pasting these
patterns a third time, all readers now share a single, tested
implementation. Future reader types can reuse `ReaderUtils` without
duplication.
---
## AI Usage
Did you use AI tools to help write this code? YES Claude extracted the
code, under my guidance. Tested on my device and seems to work fine.