mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 15:17:37 +03:00
4.0 KiB
4.0 KiB
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:
- Font Converter (
fontconvert.py): A Python script that pre-processes standard fonts into a custom optimized binary format (.epdfont). - Font Manager (
FontManager): Scans the SD card for valid font files and manages loaded font families. - Font Loader (
CustomEpdFont): Handles the low-level reading of the binary format, including on-demand caching of glyph bitmaps to save RAM. - 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:
.epdfontbinary 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
- Example:
- Manager:
src/managers/FontManager.cpp- Scans the
/fontsdirectory on startup/demand. - Groups files into
Family -> Size -> Styles (Regular, Bold, Italic, BoldItalic). - Exposes available families to the UI.
- Scans the
3. Low-Level Implementation (RAM Optimization)
The core logic resides in lib/EpdFont/CustomEpdFont.cpp.
- Inheritance:
CustomEpdFontinherits fromEpdFont. - 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:
- Lists available font families retrieved from
FontManager. - User selects a family.
- Selection is saved to
SETTINGS.customFontFamilyName.
- Lists available font families retrieved from
5. Main Integration
The selected font is applied during the system startup or when settings change.
- File:
src/main.cpp - Function:
setupDisplayAndFonts() - Logic:
- Checks if
SETTINGS.fontFamilyis set toFONT_CUSTOM. - Calls
FontManager::getInstance().getCustomFontFamily(...)with the saved name and current font size. - If found, the font is dynamically inserted into the global
rendererwith a generated ID. - The renderer then uses this font for standard text rendering.
- Checks if
Code Path Summary
- SD Card:
SD:/fonts/MyFont-Regular-14.epdfont - Wait:
FontManager::scanFonts()finds the file. - Select: User picks "MyFont" in
CustomFontSelectionActivity. - Load:
main.cppcallsrenderer.insertFont(..., FontManager.getCustomFontFamily("MyFont", 14)) - Render:
CustomEpdFont::getGlyphBitmap()fetches pixels from SD -> Cache -> Screen.