Public Access
114 lines
3.1 KiB
C++
114 lines
3.1 KiB
C++
#include "display/font_store.h"
|
|||
|
|
|
||
|
|
#include <LittleFS.h>
|
||
|
|
#include <map>
|
||
|
|
|
||
|
|
#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<String, std::vector<uint8_t>> cache;
|
||
|
|
|
||
|
|
static String path(const char *name)
|
||
|
|
{
|
||
|
|
return String(DIR) + "/" + name + EXTENSION;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<FontInfo> listFonts()
|
||
|
|
{
|
||
|
|
std::vector<FontInfo> 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<uint8_t> 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;
|
||
|
|
}
|