#include "display/font_store.h" #include #include #include "display/bitmap_store.h" #include "logic/bitmap_format.h" #include "logic/builtin_fonts.h" static const char *DIR = "/fonts"; static const char *EXTENSION = ".fnt"; static uint32_t revision = 0; // Loaded fonts by name; cleared when a font is saved or deleted static std::map> cache; static String path(const char *name) { return String(DIR) + "/" + name + EXTENSION; } std::vector listFonts() { std::vector list; for (int i = 0; i < BUILTIN_FONT_COUNT; i++) list.push_back({BUILTIN_FONTS[i].name, true, (uint8_t)fontHeight(builtinFont(BUILTIN_FONTS[i]))}); for (const String &name : storedNames(DIR, EXTENSION)) if (!findBuiltinFont(name.c_str())) list.push_back({name, false, (uint8_t)fontHeight(findFont(name.c_str()))}); return list; } uint32_t fontListRevision() { return revision; } bool fontExists(const char *name) { if (findBuiltinFont(name)) return true; return bitmapStorageAvailable() && validBitmapName(name) && LittleFS.exists(path(name)); } Font findFont(const char *name) { const BuiltinFont *builtin = findBuiltinFont(name); if (builtin) return builtinFont(*builtin); Font font; if (!bitmapStorageAvailable() || !validBitmapName(name)) return font; auto cached = cache.find(name); if (cached == cache.end()) { if (cache.size() >= 16) cache.clear(); std::vector data; File file; if (LittleFS.exists(path(name))) // open() logs an error for a missing file file = LittleFS.open(path(name), "r"); if (file && file.size() <= FONT_MAX_SIZE) { data.resize(file.size()); if (file.read(data.data(), data.size()) != data.size() || !validFont(data.data(), data.size())) data.clear(); } if (file) file.close(); cached = cache.emplace(name, std::move(data)).first; // also remembers a missing font } if (!cached->second.empty()) { font.data = cached->second.data(); font.size = cached->second.size(); } return font; } const char *saveFont(const char *name, const uint8_t *data, size_t size) { if (!validBitmapName(name)) return "Invalid name: use 1-24 letters, digits, spaces, - or _"; if (findBuiltinFont(name)) return "A built-in font has this name"; if (!validFont(data, size)) return "Invalid font data"; if (!bitmapStorageAvailable()) return "No storage (install the new flash layout over USB, see README)"; if (!LittleFS.exists(path(name)) && storedNames(DIR, EXTENSION).size() >= (size_t)MAX_FONTS) return "Too many fonts, delete some first"; File file = LittleFS.open(path(name), "w"); bool ok = file && file.write(data, size) == size; if (file) file.close(); cache.clear(); revision++; return ok ? nullptr : "Could not write the file (storage full?)"; } const char *deleteFont(const char *name) { if (findBuiltinFont(name)) return "Built-in fonts can't be deleted"; if (!bitmapStorageAvailable() || !validBitmapName(name) || !LittleFS.exists(path(name))) return "Font not found"; if (!LittleFS.remove(path(name))) return "Could not delete the file"; cache.clear(); revision++; return nullptr; }