// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include "FontManager.hpp" #include "Common.hpp" #include "FontInfo.hpp" #include "RawFont.hpp" #include #include #include #include namespace { auto getFileSize(const std::string &path) -> std::uintmax_t { try { return std::filesystem::file_size(path); } catch (const std::filesystem::filesystem_error &e) { return 0; } } } // namespace namespace gui { FontManager::~FontManager() { clear(); } auto FontManager::init(const std::filesystem::path &baseDirectory) -> bool { loadFonts(baseDirectory); const auto fallbackFont = find(fallbackFontName); if (fallbackFont == nullptr) { return false; } for (auto &font : fonts) { font->setFallbackFont(fallbackFont); } initialized = true; return initialized; } auto FontManager::clear() -> void { for (auto font : fonts) { LOG_INFO("Deleting font '%s'", font->getName().c_str()); delete font; } fonts.clear(); } auto FontManager::getInstance() -> FontManager & { static FontManager instance; if (!instance.initialized) { LOG_WARN("Using uninitialized font manager will result in no fonts loaded"); } return instance; } auto FontManager::getFont() const -> RawFont * { return getFont(defaultFontName); } auto FontManager::getFont(std::uint32_t num) const -> RawFont * { if (fonts.empty()) { return nullptr; } if (num > fonts.size()) { return fonts[0]; } return fonts[num]; } auto FontManager::getFont(const std::string &fontType) const -> RawFont * { const auto fontPath = fontMap.find(fontType); if (fontPath != fontMap.end()) { auto rawFont = find(fontPath->second); if (rawFont != nullptr) { return rawFont; } } if (!fonts.empty()) { #if DEBUG_MISSING_ASSETS == 1 LOG_ERROR("=> font not found: %s, using default", fontType.c_str()); #endif return fonts[0]; } return nullptr; } auto FontManager::getFontByName(std::string_view name) const -> RawFont * { const auto font = find(name); // Default return first font if ((font == nullptr) && !fonts.empty()) { #if DEBUG_MISSING_ASSETS == 1 LOG_ERROR("=> font not found: %s, using default", std::string(name).c_str()); #endif return fonts[0]; } return font; } auto FontManager::getFontName(const std::string &fontType) const -> std::string { return getFont(fontType)->getName(); } auto FontManager::getDefaultFontFamilyName() const -> std::string { return defaultFontFamilyName; } auto FontManager::loadFont(const std::string &fontType, const std::filesystem::path &path) -> RawFont * { const auto fileSize = getFileSize(path); if (fileSize == 0) { return nullptr; } auto fontData = std::make_unique(fileSize); std::ifstream input(path, std::ios::in | std::ifstream::binary); if (!input.is_open()) { return nullptr; } if (!input.read(reinterpret_cast(fontData.get()), static_cast(fileSize))) { return nullptr; } const auto bytesRead = static_cast(input.gcount()); if (bytesRead != fileSize) { LOG_FATAL("Failed to read from file, expected %" PRIuMAX "B, got %" PRIuMAX "B", fileSize, bytesRead); return nullptr; } // Allocate memory for new font auto rawFont = new (std::nothrow) RawFont(); if (rawFont == nullptr) { return nullptr; } if (rawFont->load(fontData.get()) != gui::Status::GUI_SUCCESS) { delete rawFont; return nullptr; } // Set id and push it to vector rawFont->id = fonts.size(); fonts.push_back(rawFont); fontMap.emplace(fontType, rawFont->getName()); return rawFont; } auto FontManager::getFontsList() -> std::map { const auto fileSize = getFileSize(fontMapFile); if (fileSize == 0) { LOG_FATAL("File size is zero!"); return {}; } auto fontmapString = std::make_unique(fileSize + 1); std::memset(fontmapString.get(), 0, fileSize + 1); std::ifstream input(fontMapFile, std::ios::in); if (!input.is_open()) { LOG_FATAL("Failed to open file '%s'", fontMapFile.c_str()); return {}; } if (!input.read(fontmapString.get(), static_cast(fileSize))) { return {}; } const auto bytesRead = static_cast(input.gcount()); if (bytesRead != fileSize) { LOG_FATAL("Failed to read from file '%s', expected %" PRIuMAX "B, got %" PRIuMAX "B", fontMapFile.c_str(), fileSize, bytesRead); return {}; } json11::Json fontmapJson; std::string err; fontmapJson = json11::Json::parse(fontmapString.get(), err); if (!err.empty()) { LOG_FATAL("Failed to parse font map string!"); return {}; } auto fontmapObjects = fontmapJson.object_items(); const auto &infoJson = fontmapObjects["info"]; fallbackFontName = infoJson["fallback_font"].string_value(); defaultFontFamilyName = infoJson["default_font_family"].string_value(); defaultFontName = infoJson["default_font"].string_value(); const auto &styleJson = fontmapObjects["style"]; std::map fontFiles; for (const auto &entry : styleJson.object_items()) { const auto &fontName = entry.second.string_value(); if (!std::filesystem::is_regular_file(fontFolder / fontName)) { LOG_WARN("Could not find font '%s'", fontName.c_str()); } else { LOG_INFO("Add font to list: '%s' - '%s'", entry.first.c_str(), fontName.c_str()); fontFiles.emplace(entry.first, fontFolder / fontName); } } LOG_INFO("Total number of fonts: %zu", fontFiles.size()); return fontFiles; } auto FontManager::loadFonts(const std::filesystem::path &baseDirectory) -> void { fontFolder = baseDirectory / "fonts"; fontMapFile = fontFolder / "fontmap.json"; const auto &fontFiles = getFontsList(); for (const auto &font : fontFiles) { loadFont(font.first, font.second); } } auto FontManager::find(std::string_view name) const -> RawFont * { for (const auto &font : fonts) { if (name.compare(font->info.face) == 0) { return font; } } return nullptr; } } // namespace gui