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>
212 lines
14 KiB
YAML
212 lines
14 KiB
YAML
# SD Card Font Families for CrossPoint Reader
|
|
#
|
|
# This file is the single source of truth for which fonts are generated,
|
|
# how they're sourced, and how they're described in the download manifest.
|
|
#
|
|
# Adding a new font family = adding a block here. No code changes needed.
|
|
#
|
|
# Fields:
|
|
# name: Output family name (used in filenames and on-device UI)
|
|
# description: Human-readable description (shown in download UI and manifest)
|
|
# intervals: Comma-separated Unicode interval presets for fontconvert_sdcard.py
|
|
# sizes: Point sizes to generate
|
|
# force_autohint: (optional) Force FreeType auto-hinter instead of native hinting
|
|
# styles: Map of style name -> font source
|
|
# path: relative to lib/EpdFont (for committed fonts)
|
|
# url: download URL (for fonts not in the repo)
|
|
#
|
|
# Variable fonts:
|
|
# Some fonts (Bitter, Inter, Alegreya) are distributed as variable fonts.
|
|
# freetype-py can't set variable font axis values, so the build script
|
|
# uses fonttools.instancer to extract static instances automatically.
|
|
#
|
|
# To use a variable font, add a 'variable' key to the style spec with
|
|
# axis values to pin:
|
|
#
|
|
# styles:
|
|
# regular: {url: "https://...Font[wght].ttf", variable: {wght: 400}}
|
|
# bold: {url: "https://...Font[wght].ttf", variable: {wght: 700}}
|
|
#
|
|
# Extracted static fonts are cached in instanced_fonts/ (gitignored).
|
|
# Requires fonttools: pip install -r requirements.txt
|
|
|
|
families:
|
|
# ── Serif ──────────────────────────────────────────────────────────────
|
|
|
|
- name: Literata
|
|
description: "Screen-optimized serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/googlefonts/literata/main/fonts/ttf/Literata-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/googlefonts/literata/main/fonts/ttf/Literata-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/googlefonts/literata/main/fonts/ttf/Literata-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/googlefonts/literata/main/fonts/ttf/Literata-BoldItalic.ttf"}
|
|
|
|
- name: SourceSerif4
|
|
description: "Adobe transitional serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/TTF/SourceSerif4-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/TTF/SourceSerif4-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/TTF/SourceSerif4-It.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/TTF/SourceSerif4-BoldIt.ttf"}
|
|
|
|
- name: NotoSerifExtended
|
|
description: "Serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/notofonts/NotoSerif/main/fonts/ttf/unhinted/instance_ttf/NotoSerif-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/notofonts/NotoSerif/main/fonts/ttf/unhinted/instance_ttf/NotoSerif-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/notofonts/NotoSerif-Italic/main/fonts/ttf/unhinted/instance_ttf/NotoSerif-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/notofonts/NotoSerif-Italic/main/fonts/ttf/unhinted/instance_ttf/NotoSerif-BoldItalic.ttf"}
|
|
|
|
- name: Merriweather
|
|
description: "Warm serif for long-form reading (Latin, Cyrillic)"
|
|
intervals: latin-ext,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/SorkinType/Merriweather/master/fonts/ttf/Merriweather-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/SorkinType/Merriweather/master/fonts/ttf/Merriweather-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/SorkinType/Merriweather/master/fonts/ttf/Merriweather-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/SorkinType/Merriweather/master/fonts/ttf/Merriweather-BoldItalic.ttf"}
|
|
|
|
- name: Lora
|
|
description: "Calligraphic serif for literary reading (Latin, Cyrillic)"
|
|
intervals: latin-ext,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/ttf/Lora-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/ttf/Lora-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/ttf/Lora-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/ttf/Lora-BoldItalic.ttf"}
|
|
|
|
- name: GentiumBookPlus
|
|
description: "Scholarly serif with wide Unicode coverage (Latin, Greek, Cyrillic, IPA)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/gentiumbookplus/GentiumBookPlus-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/gentiumbookplus/GentiumBookPlus-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/gentiumbookplus/GentiumBookPlus-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/gentiumbookplus/GentiumBookPlus-BoldItalic.ttf"}
|
|
|
|
- name: IBMPlexSerif
|
|
description: "Professional serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexserif/IBMPlexSerif-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexserif/IBMPlexSerif-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexserif/IBMPlexSerif-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexserif/IBMPlexSerif-BoldItalic.ttf"}
|
|
|
|
- name: Bitter
|
|
description: "Slab serif designed for e-ink (Latin, Cyrillic)"
|
|
intervals: latin-ext,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter%5Bwght%5D.ttf", variable: {wght: 400}}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter%5Bwght%5D.ttf", variable: {wght: 700}}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter-Italic%5Bwght%5D.ttf", variable: {wght: 400}}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter-Italic%5Bwght%5D.ttf", variable: {wght: 700}}
|
|
|
|
# ── Sans-serif ─────────────────────────────────────────────────────────
|
|
|
|
- name: NotoSansExtended
|
|
description: "Sans-serif (Latin, Greek, Cyrillic, Georgian, Armenian, Ethiopic)"
|
|
intervals: latin-ext,greek,cyrillic,georgian,armenian,ethiopic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {path: "builtinFonts/source/NotoSans/NotoSans-Regular.ttf"}
|
|
bold: {path: "builtinFonts/source/NotoSans/NotoSans-Bold.ttf"}
|
|
italic: {path: "builtinFonts/source/NotoSans/NotoSans-Italic.ttf"}
|
|
bolditalic: {path: "builtinFonts/source/NotoSans/NotoSans-BoldItalic.ttf"}
|
|
|
|
- name: Inter
|
|
description: "Modern sans-serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/inter/Inter%5Bopsz%2Cwght%5D.ttf", variable: {wght: 400, opsz: 14}}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/inter/Inter%5Bopsz%2Cwght%5D.ttf", variable: {wght: 700, opsz: 14}}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/inter/Inter-Italic%5Bopsz%2Cwght%5D.ttf", variable: {wght: 400, opsz: 14}}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/inter/Inter-Italic%5Bopsz%2Cwght%5D.ttf", variable: {wght: 700, opsz: 14}}
|
|
|
|
- name: SourceSans3
|
|
description: "Adobe humanist sans-serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/TTF/SourceSans3-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/TTF/SourceSans3-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/TTF/SourceSans3-It.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/TTF/SourceSans3-BoldIt.ttf"}
|
|
|
|
- name: IBMPlexSans
|
|
description: "IBM corporate sans-serif (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-sans/fonts/complete/ttf/IBMPlexSans-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-sans/fonts/complete/ttf/IBMPlexSans-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-sans/fonts/complete/ttf/IBMPlexSans-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-sans/fonts/complete/ttf/IBMPlexSans-BoldItalic.ttf"}
|
|
|
|
- name: Alegreya
|
|
description: "Calligraphic serif/display (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/alegreya/Alegreya%5Bwght%5D.ttf", variable: {wght: 400}}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/alegreya/Alegreya%5Bwght%5D.ttf", variable: {wght: 700}}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/alegreya/Alegreya-Italic%5Bwght%5D.ttf", variable: {wght: 400}}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/alegreya/Alegreya-Italic%5Bwght%5D.ttf", variable: {wght: 700}}
|
|
|
|
# ── Monospace ──────────────────────────────────────────────────────────
|
|
|
|
- name: IBMPlexMono
|
|
description: "Monospace for code and technical reading (Latin, Greek, Cyrillic)"
|
|
intervals: latin-ext,greek,cyrillic
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexmono/IBMPlexMono-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexmono/IBMPlexMono-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexmono/IBMPlexMono-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/ibmplexmono/IBMPlexMono-BoldItalic.ttf"}
|
|
|
|
- name: SourceCodePro
|
|
description: "Adobe monospace with excellent hinting (Latin)"
|
|
intervals: latin-ext
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/TTF/SourceCodePro-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/TTF/SourceCodePro-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/TTF/SourceCodePro-It.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/TTF/SourceCodePro-BoldIt.ttf"}
|
|
|
|
# ── Accessibility ──────────────────────────────────────────────────────
|
|
|
|
- name: AtkinsonHyperlegibleNext
|
|
description: "Accessibility font for low vision (Latin)"
|
|
intervals: latin-ext
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/googlefonts/atkinson-hyperlegible-next/main/fonts/ttf/AtkinsonHyperlegibleNext-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/googlefonts/atkinson-hyperlegible-next/main/fonts/ttf/AtkinsonHyperlegibleNext-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/googlefonts/atkinson-hyperlegible-next/main/fonts/ttf/AtkinsonHyperlegibleNext-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/googlefonts/atkinson-hyperlegible-next/main/fonts/ttf/AtkinsonHyperlegibleNext-BoldItalic.ttf"}
|
|
|
|
- name: LexicaUltralegible
|
|
description: "Accessibility font for low vision / dyslexia (Latin)"
|
|
intervals: latin-ext
|
|
sizes: [12, 14, 16, 18]
|
|
styles:
|
|
regular: {url: "https://raw.githubusercontent.com/jacobxperez/lexica-ultralegible/main/fonts/ttf/LexicaUltralegible-Regular.ttf"}
|
|
bold: {url: "https://raw.githubusercontent.com/jacobxperez/lexica-ultralegible/main/fonts/ttf/LexicaUltralegible-Bold.ttf"}
|
|
italic: {url: "https://raw.githubusercontent.com/jacobxperez/lexica-ultralegible/main/fonts/ttf/LexicaUltralegible-Italic.ttf"}
|
|
bolditalic: {url: "https://raw.githubusercontent.com/jacobxperez/lexica-ultralegible/main/fonts/ttf/LexicaUltralegible-BoldItalic.ttf"}
|
|
|