-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathSdCardFontSystem.cpp
More file actions
178 lines (156 loc) · 7.08 KB
/
Copy pathSdCardFontSystem.cpp
File metadata and controls
178 lines (156 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include "SdCardFontSystem.h"
#include <GfxRenderer.h>
#include <Logging.h>
#include <iterator>
#include "CrossPointSettings.h"
#include "ReaderFontSizes.h"
#include "fontIds.h"
namespace {
// Point the reader font size at a size the given family actually ships, and
// persist the change so the settings UI and the loaded font never disagree.
// Guarded by the value-change check: a no-op snap must not write SPIFFS.
void snapFontPointSizeTo(const uint8_t availablePointSize) {
if (availablePointSize == 0 || availablePointSize == SETTINGS.fontPointSize) return;
LOG_DBG("SDFS", "Font size %u unavailable, snapping to %u", SETTINGS.fontPointSize, availablePointSize);
SETTINGS.fontPointSize = availablePointSize;
SETTINGS.saveToFile();
}
// Built-in UI fonts and their physical point sizes (at 150 DPI, matching the
// SD-font converter). Each is paired with a same-size SD fallback so CJK UI
// text matches the surrounding Latin. See SdCardFontSystem::setupUiFallbacks.
struct UiFontSize {
int fontId;
uint8_t pointSize;
};
constexpr UiFontSize kUiFontSizes[] = {
{SMALL_FONT_ID, 8},
{UI_10_FONT_ID, 10},
{UI_12_FONT_ID, 12},
};
} // namespace
void SdCardFontSystem::begin(GfxRenderer& renderer) {
registry_.discover();
// Register this system as the SD font ID resolver in settings.
// Uses a static trampoline since CrossPointSettings stores a plain function pointer.
SETTINGS.sdFontIdResolver = [](void* ctx, const char* familyName, uint8_t pointSize) -> int {
return static_cast<SdCardFontSystem*>(ctx)->resolveFontId(familyName, pointSize);
};
SETTINGS.sdFontResolverCtx = this;
// If user has a saved SD font selection, load it
if (SETTINGS.sdFontFamilyName[0] != '\0') {
const auto* family = registry_.findFamily(SETTINGS.sdFontFamilyName);
if (family) {
if (manager_.loadFamily(*family, renderer, SETTINGS.fontPointSize)) {
snapFontPointSizeTo(manager_.currentPointSize());
setupUiFallbacks(renderer);
LOG_DBG("SDFS", "Loaded SD card font family: %s", SETTINGS.sdFontFamilyName);
} else {
LOG_ERR("SDFS", "Failed to load SD font family: %s (clearing)", SETTINGS.sdFontFamilyName);
SETTINGS.clearSdFontFamily();
}
} else {
LOG_DBG("SDFS", "SD font family not found on card: %s (clearing)", SETTINGS.sdFontFamilyName);
SETTINGS.clearSdFontFamily();
}
}
LOG_DBG("SDFS", "SD font system ready (%d families discovered)", registry_.getFamilyCount());
}
void SdCardFontSystem::ensureLoaded(GfxRenderer& renderer) {
// If the web server (or another task) installed/deleted fonts, re-discover.
// Track whether we just re-discovered so we can force a reload below even
// when the wanted family/size still maps to the same point size — the file
// contents on disk may have changed (e.g. user re-uploaded a new build).
const bool registryWasDirty = registryDirty_.exchange(false, std::memory_order_acquire);
if (registryWasDirty) {
LOG_DBG("SDFS", "Registry dirty — re-discovering fonts");
registry_.discover();
}
const char* wantedFamily = SETTINGS.sdFontFamilyName;
const std::string& currentFamily = manager_.currentFamilyName();
if (wantedFamily[0] == '\0') {
if (!currentFamily.empty()) {
manager_.unloadAll(renderer);
}
// Back on a built-in family, which exists only at BUILTIN_READER_POINT_SIZES:
// a size inherited from an SD family has to come back into that set.
snapFontPointSizeTo(snapToNearestPointSize(BUILTIN_READER_POINT_SIZES, std::size(BUILTIN_READER_POINT_SIZES),
SETTINGS.fontPointSize));
return;
}
// Reload if family changed OR if the user-selected size maps to a
// different file than what's currently loaded OR if the registry was
// just rediscovered (file may have been replaced on disk).
bool familyMatches = (currentFamily == wantedFamily);
if (familyMatches) {
const auto* family = registry_.findFamily(wantedFamily);
if (!family) {
LOG_DBG("SDFS", "SD font family disappeared: %s (clearing)", wantedFamily);
manager_.unloadAll(renderer);
SETTINGS.clearSdFontFamily();
return;
}
const auto* selected = family->findNearestSize(SETTINGS.fontPointSize);
const uint8_t wantedPt = selected ? selected->pointSize : 0;
// Snap before the early return: the wanted size can already be loaded while
// the setting still names a size this family does not ship.
snapFontPointSizeTo(wantedPt);
if (!registryWasDirty && wantedPt == manager_.currentPointSize()) return;
LOG_DBG("SDFS", "Reloading %s: size %u -> %u%s", wantedFamily, manager_.currentPointSize(), wantedPt,
registryWasDirty ? " [registry dirty]" : "");
}
if (!currentFamily.empty()) {
manager_.unloadAll(renderer);
}
const auto* family = registry_.findFamily(wantedFamily);
if (family) {
if (manager_.loadFamily(*family, renderer, SETTINGS.fontPointSize)) {
snapFontPointSizeTo(manager_.currentPointSize());
setupUiFallbacks(renderer);
LOG_DBG("SDFS", "Loaded SD font family: %s", wantedFamily);
} else {
LOG_ERR("SDFS", "Failed to load SD font family: %s (clearing)", wantedFamily);
SETTINGS.clearSdFontFamily();
}
} else {
LOG_DBG("SDFS", "SD font family not found: %s (clearing)", wantedFamily);
SETTINGS.clearSdFontFamily();
}
}
void SdCardFontSystem::setupUiFallbacks(GfxRenderer& renderer) {
const std::string& familyName = manager_.currentFamilyName();
if (familyName.empty()) return; // no SD family loaded — nothing to fall back to
const auto* family = registry_.findFamily(familyName);
if (!family) return;
// Probe the already-loaded reader-size font before paying for the UI sizes:
// resolveTextFontId only redirects on CJK codepoints, so a Latin-only family
// can never act as a fallback and its UI sizes would be dead weight in RAM.
const auto readerIt = renderer.getFontMap().find(manager_.getFontId(familyName));
if (readerIt == renderer.getFontMap().end()) return;
// One representative codepoint per script: Han, Hiragana, Katakana, Hangul.
static constexpr uint32_t kCjkProbes[] = {0x4E00, 0x3042, 0x30A2, 0xAC00};
bool hasCjk = false;
for (const uint32_t cp : kCjkProbes) {
if (readerIt->second.hasCodepoint(cp)) {
hasCjk = true;
break;
}
}
if (!hasCjk) {
LOG_DBG("SDFS", "%s has no CJK coverage - skipping UI fallback sizes", familyName.c_str());
return;
}
for (const auto& ui : kUiFontSizes) {
const int sdFontId = manager_.loadFamilyExtraSize(*family, renderer, ui.pointSize);
if (sdFontId != 0) {
renderer.setFallbackFont(ui.fontId, sdFontId);
} else {
LOG_DBG("SDFS", "No %u pt SD glyphs for UI fallback in %s", ui.pointSize, familyName.c_str());
}
}
}
int SdCardFontSystem::resolveFontId(const char* familyName, uint8_t /*pointSize*/) const {
// The manager holds exactly one reader-size font, already selected for
// SETTINGS.fontPointSize, so the size argument is implicit — always return
// that font's ID. ensureLoaded() must have run for the current settings first.
return manager_.getFontId(familyName);
}