feat: add SD card font support with on-device download and web management
Add a complete SD card font subsystem that enables users to install and use custom fonts beyond the three built-in families. This combines the back-end firmware support (#1327) with the font configuration, build pipeline, CI distribution, and user-facing management UI (#1392). Core font system: - Custom .cpfont binary format (v4) with multi-style support (regular, bold, italic, bold-italic) packed into a single file per size - On-demand glyph loading from SD card with two-pass prewarm rendering to bulk-read glyphs per page, achieving near-flash performance for Latin text (~697ms vs ~681ms) and viable CJK rendering (~32% slower) - Persistent advance cache for layout measurement without SD I/O - Overflow ring buffer for glyph cache misses during rendering - Memory-conscious design: only advance tables kept in RAM; glyph bitmaps, kern tables, and ligatures loaded on demand from SD Font management: - On-device WiFi download from GitHub Releases with manifest-based discovery, install/update detection, and progress UI - Web interface font upload, listing, and deletion via /fonts page - Manual SD card copy to /fonts/ or /.fonts/ directories - Font selection integrated into Settings > Reader > Font Family Build pipeline: - Declarative YAML config (sd-fonts.yaml) as single source of truth for the 17-family font library (serif, sans, mono, accessibility) - Python converter (fontconvert_sdcard.py) for TTF/OTF to .cpfont with FreeType rasterization, class-based kerning, and ligature extraction - Parallel build orchestrator with variable font instance extraction - CI workflow publishing versioned + stable releases to a dedicated crosspoint-fonts repository with auto-incrementing revision tags - Centralized version constants (cpfont_version.py) shared across build tooling and CI, with firmware headers as manual sync points Additional fixes: - CJK characters no longer get hyphens inserted at line breaks - Advance table eliminates 30+ second stalls during CJK section indexing for paragraphs with >512 unique codepoints Closes #930 Co-authored-by: Zach Nelson <zach@zdnelson.com> Co-authored-by: Justin <itsthisjustin@users.noreply.github.com> Co-authored-by: jpirnay <jens@pirnay.com> Co-authored-by: mcrosson <kemonine@kemonine.info>
This commit is contained in:
committed by
Zach Nelson
co-authored by
Zach Nelson
Justin
jpirnay
mcrosson
parent
29fd29f537
commit
7993b2bb97
@@ -4,6 +4,7 @@
|
||||
#include <HalDisplay.h>
|
||||
|
||||
class FontCacheManager;
|
||||
class SdCardFont;
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
@@ -42,6 +43,11 @@ class GfxRenderer {
|
||||
uint32_t frameBufferSize = HalDisplay::BUFFER_SIZE;
|
||||
std::vector<uint8_t*> bwBufferChunks;
|
||||
std::map<int, EpdFontFamily> fontMap;
|
||||
// Mutable because ensureSdCardFontReady() is const (called from layout code
|
||||
// that holds a const GfxRenderer&) but triggers SD card reads and heap
|
||||
// allocation inside the SdCardFont objects. Same pragmatic compromise as
|
||||
// fontCacheManager_ below.
|
||||
mutable std::map<int, SdCardFont*> sdCardFonts_;
|
||||
|
||||
// Mutable because drawText() is const but needs to delegate scan-mode
|
||||
// recording to the (non-const) FontCacheManager. Same pragmatic compromise
|
||||
@@ -69,9 +75,25 @@ class GfxRenderer {
|
||||
// Setup
|
||||
void begin(); // must be called right after display.begin()
|
||||
void insertFont(int fontId, EpdFontFamily font);
|
||||
// Clears both the flash-font map and any SD-font registration for fontId.
|
||||
// Coupled to avoid dangling SdCardFont* in sdCardFonts_ when callers free
|
||||
// the underlying SdCardFont and forget the SD-side unregister.
|
||||
void removeFont(int fontId) {
|
||||
fontMap.erase(fontId);
|
||||
sdCardFonts_.erase(fontId);
|
||||
}
|
||||
void setFontCacheManager(FontCacheManager* m) { fontCacheManager_ = m; }
|
||||
FontCacheManager* getFontCacheManager() const { return fontCacheManager_; }
|
||||
const std::map<int, EpdFontFamily>& getFontMap() const { return fontMap; }
|
||||
void registerSdCardFont(int fontId, SdCardFont* font) { sdCardFonts_[fontId] = font; }
|
||||
void unregisterSdCardFont(int fontId) { removeFont(fontId); }
|
||||
void clearSdCardFonts() { sdCardFonts_.clear(); }
|
||||
const std::map<int, SdCardFont*>& getSdCardFonts() const { return sdCardFonts_; }
|
||||
bool isSdCardFont(int fontId) const { return sdCardFonts_.count(fontId) > 0; }
|
||||
// Ensure SD card font glyph data is loaded for the given text. Called from layout code
|
||||
// (which holds a const GfxRenderer&) before measuring word widths. Safe to call on non-SD fonts (no-op).
|
||||
// styleMask: bitmask of styles to prepare (bit 0=regular, 1=bold, 2=italic, 3=bold-italic).
|
||||
void ensureSdCardFontReady(int fontId, const char* utf8Text, uint8_t styleMask = 0x0F) const;
|
||||
|
||||
// Orientation control (affects logical width/height and coordinate transforms)
|
||||
void setOrientation(const Orientation o) { orientation = o; }
|
||||
|
||||
Reference in New Issue
Block a user