Skip to content

feat: Custom Font Implementation#455

Open
ruby-builds wants to merge 23 commits intocrosspoint-reader:masterfrom
ruby-builds:feature/custom-fonts
Open

feat: Custom Font Implementation#455
ruby-builds wants to merge 23 commits intocrosspoint-reader:masterfrom
ruby-builds:feature/custom-fonts

Conversation

@ruby-builds
Copy link

@ruby-builds ruby-builds commented Jan 20, 2026

Summary

This document details the modifications and enhancements made in this fork/branch compared to the upstream master branch (https://github.com/crosspoint-reader/crosspoint-reader).

1. Custom Font Support Framework

A complete system for loading and rendering custom fonts from the SD card was implemented.

New Core Components

  • lib/EpdFontLoader/EpdFontLoader.{cpp,h}: A new library responsible for discovering, validating, and ensuring custom fonts are loaded into the renderer. It includes safety fallbacks to the default "Bookerly" font if a custom font fails to load.
  • src/managers/FontManager.{cpp,h}: A singleton manager that scans the /fonts directory on the SD card, parses .epdfont headers, and creates EpdFontFamily instances for valid fonts.
    • Update: Now supports both V0 (Legacy Python script) and V1 (Web Converter) font formats.
    • Update: Implements flexible filename parsing (supports Family_Style_Size.epdfont, Family-Style-Size.epdfont, and single-file variants).
  • lib/EpdFont/CustomEpdFont.{cpp,h}: A new font implementation that reads glyph data directly from the binary .epdfont files on the SD card, implementing an LRU cache for bitmaps to optimize RAM usage.
    • Update: Handles dynamic glyph stride (13 bytes for V0, 16 bytes for V1) and header sizes (48 bytes for V0, 32 bytes for V1).
  • src/activities/settings/FontSelectionActivity.{cpp,h}: A new UI screen allowing the user to select from available custom fonts found on the SD card.
  • lib/EpdFont/EpdFontStyles.h: Added to define styles (Regular, Bold, Italic, BoldItalic) for better font family management.

Tooling Updates

  • lib/EpdFont/scripts/fontconvert.py: Rewritten to generate binary .epdfont files (V0 format) with correct offset calculations.
  • Web Converter Support: Added compatibility with fonts generated by epdfont.clev.app (V1 format).

2. EPUB Rendering & Parsing Improvements

The EPUB reader core was modified to improve stability, performance, and memory management.

  • lib/Epub/Epub/Section.cpp:
    • Removed SDLock usage which was causing compilation issues.
    • Cleaned up file I/O operations and caching logic.
  • lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp:
    • Removed SDLock dependency.
    • Integrated better progress reporting and memory monitoring logs.
  • lib/Epub/Epub.cpp: Enhanced error handling during book loading.
  • lib/Epub/Epub/Page.cpp: Optimized page serialization/deserialization.

3. Graphics Renderer Enhancements

  • lib/GfxRenderer/GfxRenderer.{cpp,h}:
    • Updated to support CustomEpdFont alongside built-in compiled headers.
    • Implemented font ID based lookup that seamlessly handles both built-in and dynamic custom fonts.

4. Application State & Settings

  • src/CrossPointSettings.{cpp,h}:
    • Added persistent storage for the selected customFontFamily.
    • Updated getReaderFontId() to resolve IDs dynamically via EpdFontLoader when a custom font is selected.
  • src/main.cpp:
    • Integrated EpdFontLoader::loadFontsFromSd into the startup sequence.

5. User Interface Updates

  • src/activities/settings/SettingsActivity.cpp: Added the "Reader Font Family" menu option to navigate to the new font selection screen.
  • src/activities/reader/EpubReaderActivity.cpp: Updated to use the dynamic font loading system and respect the user's custom font choice.

6. Documentation

Documentation files have been organized into the docs/ folder:

  • docs/CUSTOM_FONTS.md: Detailed developer documentation explaining the architecture of the custom font system and supported formats (V0/V1).
  • docs/FONT_CONVERSION.md: User guide for converting .ttf/.otf files using either the Web Converter or the Python script.
  • docs/CHANGES.md: A changelog tracking modifications from upstream.
  • USER_GUIDE.md: Updated with a new section on Custom Fonts.

Summary of Files Added/Modified

New Files:

  • docs/CHANGES.md
  • docs/CUSTOM_FONTS.md
  • docs/FONT_CONVERSION.md
  • lib/EpdFont/CustomEpdFont.{cpp,h}
  • lib/EpdFont/EpdFontStyles.h
  • lib/EpdFontLoader/EpdFontLoader.{cpp,h}
  • src/activities/settings/FontSelectionActivity.{cpp,h}
  • src/managers/FontManager.{cpp,h}

Modified Files:

  • Core Logic: src/main.cpp, src/CrossPointSettings.{cpp,h}, src/CrossPointState.cpp
  • UI: src/activities/settings/SettingsActivity.{cpp,h}, src/activities/reader/EpubReaderActivity.cpp, src/activities/reader/FileSelectionActivity.cpp
  • Rendering: lib/GfxRenderer/GfxRenderer.{cpp,h}, lib/EpdFont/EpdFont.{cpp,h}
  • EPUB Engine: lib/Epub/* (various files optimized and cleaned)
  • Tools: lib/EpdFont/scripts/fontconvert.py

Additional Context

Tested with multiple font families (LibreBaskerville, Aileron, Literata), cycling through sizes and line heights. Verified fallbacks work correctly when files are missing or malformed.


AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? < YES >

@kylejw1
Copy link

kylejw1 commented Jan 21, 2026

Something isn't quite right here.
If I load it with many sizes for a single font like so:
DejaVuSans_10.epdfont
DejaVuSans_12.epdfont
DejaVuSans_14.epdfont
DejaVuSans_16.epdfont
DejaVuSans_18.epdfont
DejaVuSans_6.epdfont
DejaVuSans_8.epdfont
DejaVuSans_Bold_10.epdfont
DejaVuSans_Bold_12.epdfont
DejaVuSans_Bold_14.epdfont
DejaVuSans_Bold_16.epdfont
DejaVuSans_Bold_18.epdfont
DejaVuSans_Bold_6.epdfont
DejaVuSans_Bold_8.epdfont
DejaVuSans_BoldItalic_10.epdfont
DejaVuSans_BoldItalic_12.epdfont
DejaVuSans_BoldItalic_14.epdfont
DejaVuSans_BoldItalic_16.epdfont
DejaVuSans_BoldItalic_18.epdfont
DejaVuSans_BoldItalic_6.epdfont
DejaVuSans_BoldItalic_8.epdfont
DejaVuSans_Italic_10.epdfont
DejaVuSans_Italic_12.epdfont
DejaVuSans_Italic_14.epdfont
DejaVuSans_Italic_16.epdfont
DejaVuSans_Italic_18.epdfont
DejaVuSans_Italic_6.epdfont
DejaVuSans_Italic_8.epdfont

Then I only see a single font in the selection menu. I'm expecting to see an entry for each size, or a field for selecting the numeric font size in the settings menu

@ruby-builds
Copy link
Author

ruby-builds commented Jan 21, 2026

@kylejw1 The fonts are grouped into a singular family in the viewer. You only need to generate size 12, 14, 16, and 18 which correspond to the existing font sizes of Small, Medium, Large, and X Large and are controlled by that existing setting. You also need to follow the naming convention for styles set in /docs/FONT_CONVERSION.md

@kylejw1
Copy link

kylejw1 commented Jan 21, 2026

I find it strange behaviour that I can put for example a size 10 font on the device as long as I don't put the size in the filename, but if I want multiple sizes I can only use 12/14/16/18 specifically

Aside, the naming convention suggests separating the name-style-size with hyphens but that isn't actually supported

@ruby-builds
Copy link
Author

Nothing stops you from uploading a font size, but the firmware originally supports only those 4 sizes. This PR is to get custom fonts working, I'll leave it up to the maintainers if they want to build on that and allow more than the 4 existing sizes of 12 (small), 14 (medium), 16 (large), and 18 (x large).

Not sure what you mean about the naming convention not being supported, that naming convention is exactly what the Font Manager seeks.

@kylejw1
Copy link

kylejw1 commented Jan 21, 2026

It works if that's the goal of the PR but the usage is a little counterintuitive.

Just thinking out loud, but since TrueType font names seem to follow a convention of "FontName-Style.ttf" but of course don't include the size as that is dynamic, it might make sense to do something like separate the size with a dot?

eg
DejaVuSans-Bold.12.ttf

That would make it a bit easier in code to determine a font size and break different sizes out in the custom font selection activity?

@ruby-builds
Copy link
Author

ruby-builds commented Jan 22, 2026

This implementation automatically senses for the supported font size so there is no need to select a specific file. You just select the family and then control the size with the existing setting for font size. If other sizes were to be supported they would just need the logic and enums added to the size setting.

Antigravity Agent and others added 10 commits January 22, 2026 17:48
Implementing faster .epub indexing + fixing minor issue with hiding /fonts in the file browser
Fixes improper <br/> behaviour
This fixes a bug where the loaded IDs do not exist in accessible memory if a user only changes font size in settings with a custom font defined; as a result, re-entering a book would rerender it with the correct size but the default font.
/fonts should be properly hidden now, instead of being the only folder shown in the file browser
@alpsfordays
Copy link

feature/custom-fonts should now be synced with the principal branch, including some fixes for odd behaviour caused by the somewhat large change in how settings are handled in Crosspoint

Fixed issue with the fonts list in the font selection menu; previously, only 8 fonts at a time could be displayed. TBD: Implementing scrolling if there are more fonts than screen real estate; the current function will continue rendering font names until it hits the bottom
@daveallie
Copy link
Member

Hey @ruby-builds, I've got a new font format I've been paying with in #589, there are still some rough edges and I need to finish implementing a few things, but I'd like to use it as the base for remote image files too. I am keen to branch of my PR to validate that some of the smarts around your approach here will work.

@ruby-builds
Copy link
Author

Looks great! I definitely think a singular file will be more convenient, looking forward to the progress!

@icannotttt
Copy link

Good idea! I used your Python script to generate the epdfont file, and it works properly. However, it takes up some memory, around 10 kB, I think?

Then I tried to use your new font version — the web-based generator — but there are limitations: the file must be smaller than 1 MB, and I got the following error prompt:
"Font validation failed

Font file is too complex and caused a memory error. Try a simpler font or a smaller file size (current: 4.04 MB)."

By the way, I'm using a Chinese TTF font. Perhaps the relatively complex glyphs are not supported yet?

I really hope you can debug this issue so that I can start using it as soon as possible. Thank you very much for your contributions to Crosspoint!

@alpsfordays
Copy link

@icannotttt Try using fontconvert.py with the appropriate Unicode intervals added; conversion and rendering works on my end with Unifont and an .epub with Traditional Chinese characters.

@Uri-Tauber
Copy link
Contributor

@ruby-builds do you plan to continue this PR?

@ruby-builds
Copy link
Author

ruby-builds commented Feb 21, 2026

@Uri-Tauber we have been attempting to resolve the conflicts yes, but many of the recent updates are making it extremely difficult. @daveallie also has #589 and it is unclear how that will progress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants