Skip to content

Commit 88c1d74

Browse files
Update docs
1 parent fc7592e commit 88c1d74

1 file changed

Lines changed: 27 additions & 34 deletions

File tree

docs/frameworks/font-manager.md

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
# FontManager
22

3-
FontManager is a singleton framework for loading, caching, and composing LVGL fonts — including built-in bitmap fonts, TrueType fonts, and emoji image fonts. It automatically selects the best-quality pre-rendered emoji asset for the requested size and uses LVGL's imgfont fallback mechanism to render emoji inline with text.
3+
FontManager is a singleton framework for loading, caching, and composing LVGL fonts — including built-in bitmap fonts, TrueType fonts, and emoji image fonts. It uses LVGL's imgfont fallback mechanism to render 20×20 emoji PNGs inline with text, with nearest-neighbour scaling to match any font size.
44

55
## Overview
66

77
FontManager centralizes all font concerns in a single class:
88

99
- **Unified API** - One call (`getFont`) to get any font, with or without emoji support
10-
- **Emoji Compositing** - Transparently layers an emoji imgfont on top of any base font via LVGL's `fallback` mechanism
11-
- **Size-tiered Assets** - Picks the closest pre-rendered emoji PNG directory so LVGL never has to scale further than necessary
10+
- **Emoji Compositing** - Transparently layers 20×20 emoji PNGs via LVGL's imgfont fallback, with nearest-neighbour scaling to match any font size
1211
- **Lazy Caching** - Fonts and scaled image descriptors are cached on first use; no redundant work on subsequent calls
1312
- **Android-Inspired** - Follows the same singleton/class-method pattern as other MicroPythonOS frameworks
1413

@@ -17,20 +16,20 @@ FontManager centralizes all font concerns in a single class:
1716
```python
1817
from mpos import FontManager
1918

20-
# Get a built-in Montserrat font at 16px, with emoji support (default)
19+
# Get a built-in Montserrat font at 16px (emoji disabled by default)
2120
font = FontManager.getFont(size=16, family="Montserrat")
2221

23-
# Get the same font without emoji (e.g. for glyph enumeration)
24-
base_font = FontManager.getFont(size=16, family="Montserrat", emoji=False)
22+
# Get the same font with emoji support
23+
emoji_font = FontManager.getFont(size=16, family="Montserrat", emoji=True)
2524

2625
# Load a TrueType font from a file
2726
ttf_font = FontManager.getFont(size=42, ttf="M:apps/com.myapp/assets/MyFont.ttf")
2827

29-
# List all available built-in fonts (each already has emoji composed in)
28+
# List all available built-in fonts (pass emojis=True for composed fonts)
3029
for info in FontManager.listFonts():
3130
print(info["name"], info["size"])
3231

33-
# Get all available emoji codepoints (union across all size tiers)
32+
# Get all available emoji codepoints
3433
for cp in FontManager.getEmojiCodepoints():
3534
print(hex(cp))
3635
```
@@ -41,10 +40,10 @@ FontManager is implemented as a singleton using class variables and class method
4140

4241
### Font composition
4342

44-
When `emoji=True` (the default), `getFont()` wraps the requested base font in an LVGL imgfont that renders emoji as scaled PNG images. The imgfont's `fallback` is set to the base font, so LVGL automatically falls through to the base font for any codepoint that is not an emoji.
43+
When `emoji=True`, `getFont()` wraps the requested base font in an LVGL imgfont that renders emoji as scaled PNG images. The imgfont's `fallback` is set to the base font, so LVGL automatically falls through to the base font for any codepoint that is not an emoji.
4544

4645
```
47-
getFont(size=16)
46+
getFont(size=16, emoji=True)
4847
└── base_font = font_montserrat_16 (builtin bitmap)
4948
└── emoji_font = lv.imgfont_create(...) (PNG-based imgfont)
5049
└── emoji_font.fallback = base_font
@@ -53,14 +52,13 @@ getFont(size=16)
5352

5453
### Emoji size tiers
5554

56-
Emoji PNGs are stored in size-specific directories under `builtin/res/emojis/`:
55+
Emoji PNGs are stored in `builtin/res/emojis/`:
5756

58-
| Directory | Max target height | Used for |
59-
|-----------|------------------|----------|
60-
| `20x20/` | ≤ 20 px | Montserrat 8–16 (line heights ≤ 20 px) |
61-
| `56x56/` | any | Larger fonts |
57+
| Directory | Used for |
58+
|-----------|----------|
59+
| `20x20/` | All fonts — emoji are rendered at 20×20 px and nearest-neighbour scaled up or down by LVGL as needed |
6260

63-
`_imgfont_path_cb` receives the rendering font's pixel height and picks the smallest tier whose `max_height` is ≥ the target. This minimises (or eliminates) the nearest-neighbour downscale performed by LVGL's software renderer.
61+
`_imgfont_path_cb` receives the rendering font's pixel height and returns the 20×20 emoji source. LVGL's software renderer performs nearest-neighbour scaling to fit the target font size.
6462

6563
### Caching layers
6664

@@ -75,7 +73,7 @@ Emoji PNGs are stored in size-specific directories under `builtin/res/emojis/`:
7573

7674
## API Reference
7775

78-
### `getFont(size=None, ttf=None, family=None, emoji=True)`
76+
### `getFont(size=None, ttf=None, family=None, emoji=False)`
7977

8078
Return a font object suitable for use with `set_style_text_font()`.
8179

@@ -84,7 +82,7 @@ Return a font object suitable for use with `set_style_text_font()`.
8482
- `size` (int, optional): Target pixel size. Snapped to the nearest available builtin size. Defaults to 12.
8583
- `ttf` (str, optional): Path to a `.ttf` file (e.g. `"M:apps/myapp/assets/MyFont.ttf"`). When provided, `family` is ignored.
8684
- `family` (str, optional): Font family name — `"Montserrat"` (default) or `"Unscii"`.
87-
- `emoji` (bool): If `True` (default), the returned font transparently renders emoji via a PNG imgfont fallback. Pass `False` to get the raw base font (useful for `get_glyph_dsc` enumeration).
85+
- `emoji` (bool, optional): If `True`, the returned font transparently renders emoji via a PNG imgfont fallback. Defaults to `False`. Pass `True` to enable inline emoji rendering (adds a small rendering cost per emoji glyph).
8886

8987
**Returns:** `lv.font_t` — the requested font, possibly wrapped with emoji support.
9088

@@ -94,17 +92,17 @@ Return a font object suitable for use with `set_style_text_font()`.
9492
from mpos import FontManager
9593
import lvgl as lv
9694

97-
font = FontManager.getFont(size=24, family="Montserrat")
95+
font = FontManager.getFont(size=24, family="Montserrat", emoji=True)
9896
label = lv.label(screen)
9997
label.set_style_text_font(font, lv.PART.MAIN)
10098
label.set_text("Hello ❤️ 😀")
10199
```
102100

103101
---
104102

105-
### `listFonts()`
103+
### `listFonts(emojis=False)`
106104

107-
Return a list of all available built-in fonts, each already composed with emoji support.
105+
Return a list of all available built-in fonts. By default returns raw base fonts; pass `emojis=True` to wrap each font with emoji composition.
108106

109107
**Returns:** list of dicts, each with keys:
110108

@@ -120,7 +118,7 @@ Return a list of all available built-in fonts, each already composed with emoji
120118
from mpos import FontManager
121119
import lvgl as lv
122120

123-
for info in FontManager.listFonts():
121+
for info in FontManager.listFonts(emojis=True):
124122
label = lv.label(screen)
125123
label.set_style_text_font(info["font"], lv.PART.MAIN)
126124
label.set_text(info["name"] + ": ABC 😀 ❤️")
@@ -130,7 +128,7 @@ for info in FontManager.listFonts():
130128

131129
### `getEmojiCodepoints()`
132130

133-
Return a sorted list of all emoji codepoints available across all size tiers.
131+
Return a sorted list of all available emoji codepoints.
134132

135133
**Returns:** list of int
136134

@@ -165,28 +163,24 @@ clean = FontManager.normalizeEmojiText("❤️") # removes U+FE0F after ❤
165163

166164
## Emoji Assets
167165

168-
Emoji PNGs are stored in `internal_filesystem/builtin/res/emojis/` and are included in the firmware image at `/builtin/res/emojis/` on the device filesystem. Each subdirectory is a size tier:
166+
Emoji PNGs are stored in `internal_filesystem/builtin/res/emojis/` and are included in the firmware image at `/builtin/res/emojis/` on the device filesystem:
169167

170168
```
171169
builtin/res/emojis/
172-
├── 20x20/ # Pre-rendered at 20×20 px (Lanczos-downscaled from 56×56)
173-
│ ├── 1F600.png
174-
│ ├── 263A.png
175-
│ └── ...
176-
└── 56x56/ # Cropped from original 72×72 OpenMoji PNGs
170+
└── 20x20/ # Pre-rendered at 20×20 px
177171
├── 1F600.png
178172
├── 263A.png
179173
└── ...
180174
```
181175

182-
Files are named by their Unicode codepoint in uppercase hex (e.g. `1F600.png` for 😀). FontManager scans each directory at runtime and builds a `{codepoint: path}` map.
176+
Files are named by their Unicode codepoint in uppercase hex (e.g. `1F600.png` for 😀). FontManager scans the directory at runtime and builds a `{codepoint: path}` map.
183177

184178
### Adding new emoji
185179

186-
1. Add a PNG named `<CODEPOINT_HEX>.png` to both `56x56/` and `20x20/` (generate the 20×20 version with ImageMagick):
180+
1. Add a PNG named `<CODEPOINT_HEX>.png` to `20x20/`:
187181

188182
```bash
189-
convert original.png -gravity center -crop 56x56+0+0 +repage -filter Lanczos -resize 20x20 20x20/CODEPOINT.png
183+
cp original.png 20x20/CODEPOINT.png
190184
```
191185

192186
2. The new emoji will be picked up automatically on next boot — no code changes needed.
@@ -198,8 +192,7 @@ internal_filesystem/
198192
├── lib/mpos/ui/
199193
│ └── font_manager.py # FontManager class
200194
└── builtin/res/emojis/
201-
├── 20x20/ # 20×20 px emoji PNGs
202-
└── 56x56/ # 56×56 px emoji PNGs
195+
└── 20x20/ # 20×20 px emoji PNGs
203196
```
204197

205198
## Related Frameworks

0 commit comments

Comments
 (0)