From c807188b2b3f22b44434e5e6a32dd93446951635 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 24 Sep 2023 01:11:04 -0500 Subject: [PATCH] Refactoring fontconfig from Resource Manager to Font Manager --- .../source/scwx/qt/manager/font_manager.cpp | 202 +++++++++++++++++- .../source/scwx/qt/manager/font_manager.hpp | 9 +- .../scwx/qt/manager/placefile_manager.cpp | 3 +- .../scwx/qt/manager/resource_manager.cpp | 172 +-------------- .../scwx/qt/manager/resource_manager.hpp | 3 - 5 files changed, 206 insertions(+), 183 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 77dbf149..64b93d78 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -1,6 +1,13 @@ #include #include +#include + +#include +#include +#include +#include + namespace scwx { namespace qt @@ -11,33 +18,206 @@ namespace manager static const std::string logPrefix_ = "scwx::qt::manager::font_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); +static const std::string kFcTrueType_ {"TrueType"}; + +struct FontRecord +{ + std::string family_ {}; + std::string style_ {}; + std::string filename_ {}; +}; + class FontManager::Impl { public: - explicit Impl() {} - ~Impl() {} + explicit Impl() + { + InitializeFontCache(); + InitializeFontconfig(); + } + ~Impl() { FinalizeFontconfig(); } + + void FinalizeFontconfig(); + void InitializeFontCache(); + void InitializeFontconfig(); + + static FontRecord MatchFontFile(const std::string& family, + const std::vector& styles); + + std::string fontCachePath_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} FontManager::~FontManager() {}; -std::shared_ptr FontManager::Instance() +std::shared_ptr +FontManager::GetImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size) { - static std::weak_ptr fontManagerReference_ {}; - static std::mutex instanceMutex_ {}; + const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); + const std::string fontString = + fmt::format("{}-{}:{}", family, size.value(), styleString); - std::unique_lock lock(instanceMutex_); + logger_->debug("LoadFontResource: {}", fontString); - std::shared_ptr fontManager = fontManagerReference_.lock(); + FontRecord fontRecord = Impl::MatchFontFile(family, styles); - if (fontManager == nullptr) + // TODO: + Q_UNUSED(fontRecord); + + return nullptr; +} + +void FontManager::LoadApplicationFont(const std::string& filename) +{ + // If the font cache failed to create, don't attempt to cache any fonts + if (p->fontCachePath_.empty()) { - fontManager = std::make_shared(); - fontManagerReference_ = fontManager; + return; } - return fontManager; + // Make a copy of the font in the cache (if it doesn't exist) + QFile fontFile(QString::fromStdString(filename)); + QFileInfo fontFileInfo(fontFile); + + QFile cacheFile(QString::fromStdString(p->fontCachePath_) + + fontFileInfo.fileName()); + QFileInfo cacheFileInfo(cacheFile); + std::string cacheFilename = cacheFile.fileName().toStdString(); + + if (fontFile.exists()) + { + // If the file has not been cached, or the font file size has changed + if (!cacheFile.exists() || fontFileInfo.size() != cacheFileInfo.size()) + { + logger_->info("Caching font: {}", filename); + if (!fontFile.copy(cacheFile.fileName())) + { + logger_->error("Could not cache font: {}", filename); + return; + } + } + } + else + { + logger_->error("Font does not exist: {}", filename); + return; + } + + // Load the file into fontconfig + FcBool result = FcConfigAppFontAddFile( + nullptr, reinterpret_cast(cacheFilename.c_str())); + if (!result) + { + logger_->error("Could not load font into fontconfig database", filename); + } +} + +void FontManager::Impl::InitializeFontCache() +{ + std::string cachePath { + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + .toStdString() + + "/fonts"}; + + fontCachePath_ = cachePath + "/"; + + if (!std::filesystem::exists(cachePath)) + { + std::error_code error; + if (!std::filesystem::create_directories(cachePath, error)) + { + logger_->error("Unable to create font cache directory: \"{}\" ({})", + cachePath, + error.message()); + fontCachePath_.clear(); + } + } +} + +void FontManager::Impl::InitializeFontconfig() +{ + FcConfig* fcConfig = FcInitLoadConfigAndFonts(); + FcConfigSetCurrent(fcConfig); +} + +void FontManager::Impl::FinalizeFontconfig() +{ + FcFini(); +} + +FontRecord +FontManager::Impl::MatchFontFile(const std::string& family, + const std::vector& styles) +{ + const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); + const std::string fontString = fmt::format("{}:{}", family, styleString); + + // Build fontconfig pattern + FcPattern* pattern = FcPatternCreate(); + + FcPatternAddString( + pattern, FC_FAMILY, reinterpret_cast(family.c_str())); + FcPatternAddString(pattern, + FC_FONTFORMAT, + reinterpret_cast(kFcTrueType_.c_str())); + + if (!styles.empty()) + { + FcPatternAddString(pattern, + FC_STYLE, + reinterpret_cast(styleString.c_str())); + } + + // Perform font pattern match substitution + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + // Find matching font + FcResult result; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); + FontRecord record {}; + + if (match != nullptr) + { + FcChar8* fcFamily; + FcChar8* fcStyle; + FcChar8* fcFile; + + // Match was found, get properties + if (FcPatternGetString(match, FC_FAMILY, 0, &fcFamily) == FcResultMatch && + FcPatternGetString(match, FC_STYLE, 0, &fcStyle) == FcResultMatch && + FcPatternGetString(match, FC_FILE, 0, &fcFile) == FcResultMatch) + { + record.family_ = reinterpret_cast(fcFamily); + record.style_ = reinterpret_cast(fcStyle); + record.filename_ = reinterpret_cast(fcFile); + + logger_->debug("Found matching font: {}:{} ({})", + record.family_, + record.style_, + record.filename_); + } + } + + if (record.filename_.empty()) + { + logger_->warn("Could not find matching font: {}", fontString); + } + + // Cleanup + FcPatternDestroy(match); + FcPatternDestroy(pattern); + + return record; +} + +FontManager& FontManager::Instance() +{ + static FontManager instance_ {}; + return instance_; } } // namespace manager diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 4699a4b5..0a4a812e 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -19,7 +19,14 @@ public: explicit FontManager(); ~FontManager(); - static std::shared_ptr Instance(); + std::shared_ptr + GetImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size); + + void LoadApplicationFont(const std::string& filename); + + static FontManager& Instance(); private: class Impl; diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp index 9f10a714..d721e5cb 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -706,7 +707,7 @@ void PlacefileManager::Impl::LoadFontResources( styles.push_back("italic"); } - ResourceManager::LoadFontResource(font.second->face_, styles, size); + FontManager::Instance().GetImGuiFont(font.second->face_, styles, size); } } diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 10ad2557..a622ea48 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,14 +7,9 @@ #include #include -#include #include -#include -#include #include -#include -#include #include namespace scwx @@ -28,42 +24,26 @@ namespace ResourceManager static const std::string logPrefix_ = "scwx::qt::manager::resource_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); -static void InitializeFonts(); -static void InitializeFontCache(); -static void LoadFcApplicationFont(const std::string& fontFilename); static void LoadFonts(); static void LoadTextures(); -static const std::string kFcTrueType_ {"TrueType"}; - static const std::vector> fontNames_ { {types::Font::din1451alt, ":/res/fonts/din1451alt.ttf"}, {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; -static std::string fontCachePath_ {}; - static std::unordered_map fontIds_ {}; static std::unordered_map> fonts_ {}; -static FcFontSet* fcFontSet_ {nullptr}; -static FcObjectSet* fcObjectSet_ {nullptr}; - void Initialize() { config::CountyDatabase::Initialize(); - InitializeFonts(); - LoadFonts(); LoadTextures(); } -void Shutdown() -{ - // Finalize Fontconfig - FcFini(); -} +void Shutdown() {} int FontId(types::Font font) { @@ -85,72 +65,6 @@ std::shared_ptr Font(types::Font font) return nullptr; } -void LoadFontResource(const std::string& family, - const std::vector& styles, - units::font_size::points size) -{ - const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); - const std::string fontString = - fmt::format("{}-{}:{}", family, size.value(), styleString); - - logger_->debug("LoadFontResource: {}", fontString); - - // Build fontconfig pattern - FcPattern* pattern = FcPatternCreate(); - - FcPatternAddString( - pattern, FC_FAMILY, reinterpret_cast(family.c_str())); - FcPatternAddDouble(pattern, FC_SIZE, size.value()); - FcPatternAddString(pattern, - FC_FONTFORMAT, - reinterpret_cast(kFcTrueType_.c_str())); - - if (!styles.empty()) - { - FcPatternAddString(pattern, - FC_STYLE, - reinterpret_cast(styleString.c_str())); - } - - // Perform font pattern match substitution - FcConfigSubstitute(nullptr, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - // Find matching font - FcResult result; - FcPattern* match = FcFontMatch(nullptr, pattern, &result); - std::string fontFile {}; - - if (match != nullptr) - { - FcChar8* fcFamily; - FcChar8* fcStyle; - FcChar8* fcFile; - - // Match was found, get properties - if (FcPatternGetString(match, FC_FAMILY, 0, &fcFamily) == FcResultMatch && - FcPatternGetString(match, FC_STYLE, 0, &fcStyle) == FcResultMatch && - FcPatternGetString(match, FC_FILE, 0, &fcFile) == FcResultMatch) - { - fontFile = reinterpret_cast(fcFile); - - logger_->debug("Found matching font: {}:{} ({})", - reinterpret_cast(fcFamily), - reinterpret_cast(fcStyle), - fontFile); - } - } - - if (fontFile.empty()) - { - logger_->warn("Could not find matching font: {}", fontString); - } - - // Cleanup - FcPatternDestroy(match); - FcPatternDestroy(pattern); -} - std::shared_ptr LoadImageResource(const std::string& urlString) { @@ -187,47 +101,17 @@ LoadImageResources(const std::vector& urlStrings) return images; } -static void InitializeFonts() -{ - // Create a directory to store local fonts - InitializeFontCache(); - - // Initialize Fontconfig - FcConfig* fcConfig = FcInitLoadConfigAndFonts(); - FcConfigSetCurrent(fcConfig); -} - -static void InitializeFontCache() -{ - std::string cachePath { - QStandardPaths::writableLocation(QStandardPaths::CacheLocation) - .toStdString() + - "/fonts"}; - - fontCachePath_ = cachePath + "/"; - - if (!std::filesystem::exists(cachePath)) - { - std::error_code error; - if (!std::filesystem::create_directories(cachePath, error)) - { - logger_->error("Unable to create font cache directory: \"{}\" ({})", - cachePath, - error.message()); - fontCachePath_.clear(); - } - } -} - static void LoadFonts() { + auto& fontManager = FontManager::Instance(); + for (auto& fontName : fontNames_) { int fontId = QFontDatabase::addApplicationFont( QString::fromStdString(fontName.second)); fontIds_.emplace(fontName.first, fontId); - LoadFcApplicationFont(fontName.second); + fontManager.LoadApplicationFont(fontName.second); auto font = util::Font::Create(fontName.second); fonts_.emplace(fontName.first, font); @@ -237,52 +121,6 @@ static void LoadFonts() fontAtlas->AddFontDefault(); } -static void LoadFcApplicationFont(const std::string& fontFilename) -{ - // If the font cache failed to create, don't attempt to cache any fonts - if (fontCachePath_.empty()) - { - return; - } - - // Make a copy of the font in the cache (if it doesn't exist) - QFile fontFile(QString::fromStdString(fontFilename)); - QFileInfo fontFileInfo(fontFile); - - QFile cacheFile(QString::fromStdString(fontCachePath_) + - fontFileInfo.fileName()); - QFileInfo cacheFileInfo(cacheFile); - std::string cacheFilename = cacheFile.fileName().toStdString(); - - if (fontFile.exists()) - { - // If the file has not been cached, or the font file size has changed - if (!cacheFile.exists() || fontFileInfo.size() != cacheFileInfo.size()) - { - logger_->info("Caching font: {}", fontFilename); - if (!fontFile.copy(cacheFile.fileName())) - { - logger_->error("Could not cache font: {}", fontFilename); - return; - } - } - } - else - { - logger_->error("Font does not exist: {}", fontFilename); - return; - } - - // Load the file into fontconfig (FcConfigAppFontAddFile) - FcBool result = FcConfigAppFontAddFile( - nullptr, reinterpret_cast(cacheFilename.c_str())); - if (!result) - { - logger_->error("Could not load font into fontconfig database", - fontFilename); - } -} - static void LoadTextures() { util::TextureAtlas& textureAtlas = util::TextureAtlas::Instance(); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp index 45115fd7..7b8003c9 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp @@ -22,9 +22,6 @@ void Shutdown(); int FontId(types::Font font); std::shared_ptr Font(types::Font font); -void LoadFontResource(const std::string& family, - const std::vector& styles, - units::font_size::points size); std::shared_ptr LoadImageResource(const std::string& urlString); std::vector>