mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 23:27:38 +03:00
81 lines
4.0 KiB
Markdown
81 lines
4.0 KiB
Markdown
|
|
# Custom Font Implementation Walkthrough
|
|
|
|
This document outlines the custom font implementation in the CrossPoint Reader codebase. The system allows users to load custom TrueType/OpenType fonts (converted to a binary format) from an SD card and select them via the settings UI.
|
|
|
|
## System Overview
|
|
|
|
The custom font system consists of four main components:
|
|
1. **Font Converter (`fontconvert.py`)**: A Python script that pre-processes standard fonts into a custom optimized binary format (`.epdfont`).
|
|
2. **Font Manager (`FontManager`)**: Scans the SD card for valid font files and manages loaded font families.
|
|
3. **Font Loader (`CustomEpdFont`)**: Handles the low-level reading of the binary format, including on-demand caching of glyph bitmaps to save RAM.
|
|
4. **UI & Integration**: A settings activity to select fonts and integration into the main rendering loop.
|
|
|
|
## 1. Font Conversion & Format
|
|
|
|
To optimize for the limited RAM of the ESP32 and the specific requirements of E-Ink displays, fonts are not loaded directly as TTF/OTF files. Instead, they are pre-processed.
|
|
|
|
* **Script**: `lib/EpdFont/scripts/fontconvert.py`
|
|
* **Input**: TTF/OTF files.
|
|
* **Output**: `.epdfont` binary file.
|
|
* **Format Details**:
|
|
* **Header**: Contains metadata (magic "EPDF", version, metrics, offsets).
|
|
* **Intervals**: Unicode ranges supported by the font.
|
|
* **Glyphs**: Metrics for each character (width, height, advance, offsets).
|
|
* **Bitmaps**: 1-bit or 2-bit (antialiased) pixel data for glyphs.
|
|
|
|
## 2. Storage & Discovery
|
|
|
|
Fonts are stored on the SD card in the `/fonts` directory.
|
|
|
|
* **Location**: `/fonts`
|
|
* **Naming Convention**: `Family-Style-Size.epdfont`
|
|
* Example: `Literata-Regular-14.epdfont`
|
|
* Example: `Literata-BoldItalic-14.epdfont`
|
|
* **Manager**: `src/managers/FontManager.cpp`
|
|
* **Scans** the `/fonts` directory on startup/demand.
|
|
* **Groups** files into `Family -> Size -> Styles (Regular, Bold, Italic, BoldItalic)`.
|
|
* Exposes available families to the UI.
|
|
|
|
## 3. Low-Level Implementation (RAM Optimization)
|
|
|
|
The core logic resides in `lib/EpdFont/CustomEpdFont.cpp`.
|
|
|
|
* **Inheritance**: `CustomEpdFont` inherits from `EpdFont`.
|
|
* **Metadata in RAM**: When a font is loaded, only the *header* and *glyph metrics* (width, height, etc.) are loaded into RAM.
|
|
* **Bitmaps on Disk**: Pixel data remains on the SD card.
|
|
* **LRU Cache**: A small Least Recently Used (LRU) cache (`MAX_CACHE_SIZE = 30`) holds frequently used glyph bitmaps in RAM.
|
|
* **Hit**: Returns cached bitmap.
|
|
* **Miss**: Reads the bitmap from the SD card at the specific offset, caches it, and returns it.
|
|
* **Benefit**: Allows using large fonts with extensive character sets (e.g., CJK) without exhausting the ESP32's heap.
|
|
|
|
## 4. User Interface & Selection
|
|
|
|
The user selects a font through a dedicated Settings activity.
|
|
|
|
* **File**: `src/activities/settings/CustomFontSelectionActivity.cpp`
|
|
* **Flow**:
|
|
1. Lists available font families retrieved from `FontManager`.
|
|
2. User selects a family.
|
|
3. Selection is saved to `SETTINGS.customFontFamilyName`.
|
|
|
|
## 5. Main Integration
|
|
|
|
The selected font is applied during the system startup or when settings change.
|
|
|
|
* **File**: `src/main.cpp`
|
|
* **Function**: `setupDisplayAndFonts()`
|
|
* **Logic**:
|
|
1. Checks if `SETTINGS.fontFamily` is set to `FONT_CUSTOM`.
|
|
2. Calls `FontManager::getInstance().getCustomFontFamily(...)` with the saved name and current font size.
|
|
3. If found, the font is dynamically inserted into the global `renderer` with a generated ID.
|
|
4. The renderer then uses this font for standard text rendering.
|
|
|
|
## Code Path Summary
|
|
|
|
1. **SD Card**: `SD:/fonts/MyFont-Regular-14.epdfont`
|
|
2. **Wait**: `FontManager::scanFonts()` finds the file.
|
|
3. **Select**: User picks "MyFont" in `CustomFontSelectionActivity`.
|
|
4. **Load**: `main.cpp` calls `renderer.insertFont(..., FontManager.getCustomFontFamily("MyFont", 14))`
|
|
5. **Render**: `CustomEpdFont::getGlyphBitmap()` fetches pixels from SD -> Cache -> Screen.
|