Skip to content

Dynamic font loading - #674

Merged
KenVanHoeylandt merged 7 commits into
mainfrom
develop
Oct 4, 2026
Merged

KenVanHoeylandt merged 7 commits into
mainfrom
develop

Conversation

@KenVanHoeylandt

@KenVanHoeylandt KenVanHoeylandt commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Fonts

Dynamically load text and icon fonts when the device boots.

Font bitdepth depends on whether the device is an ESP32 one and whether it has external RAM available.
When no external RAM is available on ESP32, only small and medium font variants are generated; large font variants refer to the medium size ones.

Implemented Appearance settings app for changing font and font size.

Other changes

  • Added keyboard shortcuts to delete or rename files in directory views.
  • Improved app-grid tile sizing to provide more room for labels.
  • Reduced the default LED strip brightness.

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

The font pipeline adds cmap-based codepoint extraction, Adwaita font generation and caching, configurable font sizes, and appearance settings for regular and monospace fonts. LVGL, the boot screen, and the terminal use registered or loaded BinFont fonts. The graphics module’s fixed bitmap fonts and renderer are removed. The file browser gains keyboard actions for deleting and renaming entries. The LED-strip brightness default and Apps package-list stack depth also change.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 502a1

Font changes can appear to apply but differ after a restart, particularly if settings change while fonts are generated or an existing cache is unusable. These bounded cases warrant fixes or owner acceptance before merge.

Security Architecture Review

Security architecture risk: 🟠 High · up to 502a1

Selected font files now reach a native parser that is not designed for untrusted input and affect shared rendering and startup. The apply flow can also save settings different from those it prepared. Confirmed exposure is device-local; remote control has not been established.

Retained concerns

  • High · security · inferred: The new appearance flow passes selected font bytes into a native parser explicitly unsuitable for untrusted fonts. Character counting invokes initialization before cmap bounds checks, so a malicious imported font can cause unsafe memory reads or disrupt the device before ordinary error handling or fallback. The parser limitation is inherited, but this PR adds the configurable selection and boot-consumption exposure. Remote delivery and exploitation are not established.
  • Medium · reliability · observed: Preparation, persistence, and activation do not consume one immutable configuration. Font-size and default-font controls remain active while the worker prepares snapshot A; completion saves the later pending settings B but loads A. This breaks state ownership and rollback expectations: a reset can be recorded without taking effect, and reboot can activate settings that were not prepared by that apply operation.
  • Medium · reliability · inferred: An existing cache file counts as successful preparation without being opened. Completion then persists settings before runtime loading validates the cache. An interrupted or damaged cache can therefore pass preparation, fail during activation, and require regeneration or system fallback after the configuration is already committed. Because runtime loading returns no activation result, the apply flow cannot roll back this mismatch.
Security review details

Security Blast Radius

  • inferred — The confirmed scope is one device's native font-processing context, persistent font cache, and shared LVGL rendering. Persisted configuration is consumed during boot, so effects need not remain confined to the appearance window. Cross-device, tenant, credential, or remote-service exposure is not established.

Security Findings and Attack Paths

  • inferred — An actor who supplies a malicious font that a user selects can reach native parsing during character counting, before Apply. File-derived offsets are read without complete bounds validation. This supports an unsafe-read and device-disruption concern, not a demonstrated disclosure channel, code execution, or remote attack.

Trust Boundaries and Controls

  • observed — Custom source selection relies on filesystem metadata, not authenticated contents. Cache identity hashes path, size, and modification time; existing cache bytes are opened by the native loader. These are cache-selection mechanisms, not authenticity controls. Effective cache permissions and other writers remain unknown.

Resilience and Maintainability Implications

  • observed — Unreadable caches trigger regeneration, failed cache writes are deleted, and returned custom-font load failures retry system fonts. These recovery paths help contain ordinary corruption and resource failures but run too late to protect against unchecked reads inside TTF initialization.

Hardening Proposals

  • proposed — Establish an explicit font-input trust policy: use a parser suitable for untrusted files, isolate conversion where feasible, or restrict conversion to trusted assets. Cmap checks after initialization are not a substitute for validating every native parser access.
  • proposed — Make apply consume one immutable settings snapshot throughout preparation, persistence, and activation. Validate existing caches during preparation and retain a last-known-good configuration until activation is confirmed.
  • proposed — Validate persisted font sizes at the configuration boundary, define cache-writer ownership, and use interruption-safe cache publication. Treat metadata-derived cache keys as invalidation aids rather than proof of trusted contents.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 144 functions across 44 files. (2 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fonts are loaded dynamically instead of relying on the previous fixed font setup.
Full details: Docstring Coverage

Explanation

Docstring coverage is 13.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 144 functions across 44 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5f338c8f-5c14-407d-b388-6613f3b41820
📥 Commits

Reviewing files that changed from the base of the PR and between fe2dcd2 and 89345a2.

⛔ Files ignored due to path filters (16)
  • Data/system/fonts/AdwaitaMono.ttf is excluded by !**/*.ttf
  • Data/system/fonts/AdwaitaSans.ttf is excluded by !**/*.ttf
  • Documentation/pics/screenshot-AppList.png is excluded by !**/*.png
  • Documentation/pics/screenshot-Launcher.png is excluded by !**/*.png
  • Documentation/pics/screenshot-Settings.png is excluded by !**/*.png
  • Tactility/Fonts/AdwaitaMono-Regular.ttf is excluded by !**/*.ttf
  • Tactility/Fonts/AdwaitaSans-Regular.ttf is excluded by !**/*.ttf
  • partitions-16mb-no-sd-dev.csv is excluded by !**/*.csv
  • partitions-16mb-no-sd.csv is excluded by !**/*.csv
  • partitions-16mb-with-sd.csv is excluded by !**/*.csv
  • partitions-32mb-no-sd-dev.csv is excluded by !**/*.csv
  • partitions-32mb-no-sd.csv is excluded by !**/*.csv
  • partitions-4mb-with-sd.csv is excluded by !**/*.csv
  • partitions-8mb-no-sd-dev.csv is excluded by !**/*.csv
  • partitions-8mb-no-sd.csv is excluded by !**/*.csv
  • partitions-8mb-with-sd.csv is excluded by !**/*.csv
📒 Files selected for processing (55)
  • Modules/binfont-module/include/binfont/generator.h
  • Modules/binfont-module/source/generator.cpp
  • Modules/binfont-module/tests/CMakeLists.txt
  • Modules/binfont-module/tests/source/binfont_test.cpp
  • Modules/graphics-module/Kconfig
  • Modules/graphics-module/include/font/font.h
  • Modules/graphics-module/include/font/fonts.h
  • Modules/graphics-module/include/font/ibmplexmono12.h
  • Modules/graphics-module/include/font/ibmplexmono14.h
  • Modules/graphics-module/include/font/ibmplexmono16.h
  • Modules/graphics-module/include/font/ibmplexmono18.h
  • Modules/graphics-module/include/font/ibmplexmono24.h
  • Modules/graphics-module/include/font/ibmplexmono28.h
  • Modules/graphics-module/include/font/render.h
  • Modules/graphics-module/scripts/.gitignore
  • Modules/graphics-module/scripts/generate-all.py
  • Modules/graphics-module/scripts/generate.py
  • Modules/graphics-module/source/ibmplexmono12.c
  • Modules/graphics-module/source/ibmplexmono14.c
  • Modules/graphics-module/source/ibmplexmono16.c
  • Modules/graphics-module/source/ibmplexmono18.c
  • Modules/graphics-module/source/ibmplexmono24.c
  • Modules/graphics-module/source/ibmplexmono28.c
  • Modules/graphics-module/source/module.c
  • Modules/graphics-module/source/render.cpp
  • Modules/lvgl-module/CMakeLists.txt
  • Modules/lvgl-module/README.md
  • Modules/lvgl-module/include/lvgl/fonts.h
  • Modules/lvgl-module/source/fonts.c
  • THIRD-PARTY-NOTICES.md
  • Tactility/CMakeLists.txt
  • Tactility/Fonts/generate.py
  • Tactility/Include/Tactility/settings/LedStripSettings.h
  • Tactility/Kconfig
  • Tactility/Private/Tactility/app/boot/BootScreen.h
  • Tactility/Private/Tactility/app/terminal/TerminalRenderer.h
  • Tactility/Private/Tactility/lvgl/FontCache.h
  • Tactility/Private/Tactility/lvgl/FontSizes.h
  • Tactility/Private/Tactility/lvgl/FontVersions.h
  • Tactility/Private/Tactility/lvgl/TextFonts.h
  • Tactility/Source/app/apppackagelist/AppPackageList.cpp
  • Tactility/Source/app/boot/BootInit.cpp
  • Tactility/Source/app/boot/BootScreen.cpp
  • Tactility/Source/app/terminal/Terminal.cpp
  • Tactility/Source/app/terminal/TerminalRenderer.cpp
  • Tactility/Source/lvgl/FontCache.cpp
  • Tactility/Source/lvgl/FontSizes.cpp
  • Tactility/Source/lvgl/IconFonts.cpp
  • Tactility/Source/lvgl/Lvgl.cpp
  • Tactility/Source/lvgl/TextFonts.cpp
  • Tactility/Source/service/displayidle/MatrixRainScreensaver.cpp
  • Tactility/Tests/CMakeLists.txt
  • Tactility/Tests/Source/FontCacheTest.cpp
  • device.py
  • lv_conf.h
💤 Files with no reviewable changes (22)
  • Modules/graphics-module/scripts/.gitignore
  • Modules/graphics-module/include/font/fonts.h
  • Modules/graphics-module/include/font/ibmplexmono24.h
  • Modules/graphics-module/include/font/ibmplexmono16.h
  • Modules/graphics-module/include/font/ibmplexmono14.h
  • Modules/graphics-module/include/font/ibmplexmono12.h
  • Modules/graphics-module/include/font/font.h
  • Modules/graphics-module/include/font/render.h
  • Modules/graphics-module/include/font/ibmplexmono18.h
  • Modules/graphics-module/scripts/generate.py
  • Modules/graphics-module/source/render.cpp
  • Modules/graphics-module/source/ibmplexmono18.c
  • Modules/graphics-module/source/ibmplexmono16.c
  • Modules/graphics-module/source/ibmplexmono24.c
  • Modules/graphics-module/source/ibmplexmono14.c
  • Modules/graphics-module/source/ibmplexmono28.c
  • Modules/graphics-module/source/ibmplexmono12.c
  • Modules/graphics-module/include/font/ibmplexmono28.h
  • Modules/graphics-module/scripts/generate-all.py
  • Modules/graphics-module/Kconfig
  • Modules/graphics-module/source/module.c
  • Modules/lvgl-module/CMakeLists.txt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Comment thread Modules/binfont-module/source/generator.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 381eff62-5681-4be7-95a7-89c7a8a733ea
📥 Commits

Reviewing files that changed from the base of the PR and between 89345a2 and c3ddb93.

📒 Files selected for processing (24)
  • Modules/binfont-module/include/binfont/generator.h
  • Modules/binfont-module/private/stb_truetype.h
  • Modules/binfont-module/source/generator.cpp
  • Modules/binfont-module/source/module.c
  • Modules/binfont-module/tests/source/binfont_test.cpp
  • Tactility/Include/Tactility/settings/AppearanceSettings.h
  • Tactility/Private/Tactility/app/files/View.h
  • Tactility/Private/Tactility/lvgl/FontCache.h
  • Tactility/Private/Tactility/lvgl/FontSizes.h
  • Tactility/Private/Tactility/lvgl/Fonts.h
  • Tactility/Private/Tactility/lvgl/IconFonts.h
  • Tactility/Source/InitApps.cpp
  • Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
  • Tactility/Source/app/boot/BootInit.cpp
  • Tactility/Source/app/boot/BootScreen.cpp
  • Tactility/Source/app/files/View.cpp
  • Tactility/Source/app/terminal/Terminal.cpp
  • Tactility/Source/lvgl/FontCache.cpp
  • Tactility/Source/lvgl/FontSizes.cpp
  • Tactility/Source/lvgl/Fonts.cpp
  • Tactility/Source/lvgl/IconFonts.cpp
  • Tactility/Source/settings/AppearanceSettings.cpp
  • Tactility/Tests/CMakeLists.txt
  • Tactility/Tests/Source/FontsTest.cpp
💤 Files with no reviewable changes (2)
  • Tactility/Source/lvgl/IconFonts.cpp
  • Tactility/Private/Tactility/lvgl/IconFonts.h

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment thread Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
Comment thread Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
Comment thread Tactility/Source/settings/AppearanceSettings.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Validate cached fonts before approving the appearance change. · FontCache.cpp:144-160

Tactility/Source/lvgl/FontCache.cpp:144-160
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate cached fonts before approving the appearance change.

When binfont_open_file rejects an existing custom-font cache and regeneration from the selected TTF also fails, ensureCachedFont still returns true. The appearance app then saves the custom path before loadFonts falls back to the system font. Validate the cache here so a failed regeneration prevents the save.

Suggested fix
     if (!cache_path.empty() && file::isFile(cache_path)) {
-        return true;
+        BinFont* cached_font = nullptr;
+        if (binfont_open_file(cache_path.c_str(), &cached_font) == ERROR_NONE) {
+            binfont_close(cached_font);
+            return true;
+        }
     }

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 44b000bd-49d5-4933-a7d1-a130cf07160a
📥 Commits

Reviewing files that changed from the base of the PR and between c3ddb93 and 502a196.

⛔ Files ignored due to path filters (1)
  • Data/system/fonts/MaterialSymbolsRounded.ttf is excluded by !**/*.ttf
📒 Files selected for processing (10)
  • Data/system/fonts/MaterialSymbolsRounded.codepoints
  • Modules/lvgl-module/generate-icons.py
  • Modules/lvgl-module/include/lvgl/icons/names.h
  • Modules/lvgl-module/include/lvgl/icons/shared.h
  • Modules/lvgl-module/source/icon_names.c
  • Tactility/Kconfig
  • Tactility/Source/app/AppGrid.cpp
  • Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
  • Tactility/Source/app/settings/Settings.cpp
  • Tactility/Source/lvgl/Fonts.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment thread Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
@KenVanHoeylandt
KenVanHoeylandt merged commit 04ada9d into main Oct 4, 2026
67 checks passed
@KenVanHoeylandt
KenVanHoeylandt deleted the develop branch October 4, 2026 22:00
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.

1 participant