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>
51 lines
1.6 KiB
C++
51 lines
1.6 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class GfxRenderer;
|
|
class SdCardFont;
|
|
struct SdCardFontFamilyInfo;
|
|
|
|
class SdCardFontManager {
|
|
public:
|
|
SdCardFontManager() = default;
|
|
~SdCardFontManager();
|
|
SdCardFontManager(const SdCardFontManager&) = delete;
|
|
SdCardFontManager& operator=(const SdCardFontManager&) = delete;
|
|
|
|
// Load the font file matching fontSizeEnum (SMALL=0 .. EXTRA_LARGE=3) by
|
|
// ordinal position in the family's sorted size list. Only one .cpfont file
|
|
// is loaded; other sizes remain on disk. This keeps resident interval +
|
|
// kern/ligature tables to one size's worth of memory.
|
|
// Returns true on success.
|
|
bool loadFamily(const SdCardFontFamilyInfo& family, GfxRenderer& renderer, uint8_t fontSizeEnum);
|
|
|
|
// Unload everything, unregister from renderer.
|
|
void unloadAll(GfxRenderer& renderer);
|
|
|
|
// Look up the font ID for the loaded family. Returns 0 if nothing loaded
|
|
// or familyName doesn't match.
|
|
int getFontId(const std::string& familyName) const;
|
|
|
|
// Get name of currently loaded family (empty if none).
|
|
const std::string& currentFamilyName() const { return loadedFamilyName_; };
|
|
|
|
// Point size that was actually loaded (closest match to targetPtSize).
|
|
// 0 if nothing loaded.
|
|
uint8_t currentPointSize() const { return loadedPointSize_; };
|
|
|
|
private:
|
|
struct LoadedFont {
|
|
SdCardFont* font; // heap-allocated, owned
|
|
int fontId;
|
|
uint8_t size;
|
|
};
|
|
static int computeFontId(uint32_t contentHash, const char* familyName, uint8_t pointSize);
|
|
|
|
std::string loadedFamilyName_;
|
|
uint8_t loadedPointSize_ = 0;
|
|
std::vector<LoadedFont> loaded_;
|
|
};
|