/
SilovAndrey
/
easel_gui
Обзор
Документация
Войти
/
SilovAndrey
/
easel_gui
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/theme.cpp
207 строк
7 KB
Andrey HomePC
первая рабочая версия
21 июл 2026, 11:50
21 июл 2026, 11:50
38b8d4a
Код
Авторство
О чём код?
module; #include <fontconfig/fontconfig.h> #include <thorvg.h> #include <sstream> #include <algorithm> #include <memory> #include <filesystem> module Easel.GUI; import Easel.GUI.Log; namespace easel::gui { namespace { inline void trim(std::string& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return ch != ' ' && ch != '"'; })); s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return ch != ' ' && ch != '"'; }).base(), s.end()); } std::string extract_font_identifier(const std::string& file_path) { std::string result = file_path; auto pos = result.find_last_of('/'); if (pos != std::string::npos) result = result.substr(pos + 1); pos = result.find_last_of('.'); if (pos != std::string::npos) result = result.substr(0, pos); return result; } // ============================================================================ // Умные указатели для Fontconfig // ============================================================================ template <typename T, void(&delete_)(T*)> struct deleter { void operator()(T* p) const { delete_(p); } }; using fc_pattern_ptr = std::unique_ptr<FcPattern, deleter<FcPattern, FcPatternDestroy>>; using fc_font_set_ptr = std::unique_ptr<FcFontSet, deleter<FcFontSet, FcFontSetDestroy>>; using fc_object_set_ptr = std::unique_ptr<FcObjectSet, deleter<FcObjectSet, FcObjectSetDestroy>>; // ============================================================================ // Поиск шрифта через Fontconfig // ============================================================================ // Вспомогательная функция для проверки, подходит ли формат для ThorVG bool is_supported_by_thorvg(const std::string& format) { // ThorVG нативно работает с векторными контурами TrueType и OpenType return (format == "TrueType" || format == "CFF" || format == "OpenType"); } std::string find_font_path(const FontDescr& descr) { std::string families_str(descr.m_families); if (families_str.empty()) return ""; auto pattern = fc_pattern_ptr(FcPatternCreate()); if (!pattern) return ""; std::istringstream families_stream(families_str); std::string family; while (std::getline(families_stream, family, ',')) { trim(family); if (!family.empty()) { FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(family.c_str())); } } FcPatternAddInteger(pattern.get(), FC_WEIGHT, static_cast<int>(descr.m_weight)); FcPatternAddInteger(pattern.get(), FC_SLANT, static_cast<int>(descr.m_slant)); FcPatternAddInteger(pattern.get(), FC_WIDTH, static_cast<int>(descr.m_stretch)); // Подготовка паттерна (nullptr использует глобальный конфиг процесса) FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern); FcDefaultSubstitute(pattern.get()); FcResult result; // Получаем список отсортированных шрифтов по убыванию релевантности FcFontSet* font_set = FcFontSort(nullptr, pattern.get(), FcTrue, nullptr, &result); std::string final_file_path = ""; if (font_set) { // Перебираем кандидатов от лучшего к худшему for (int i = 0; i < font_set->nfont; ++i) { FcPattern* font = font_set->fonts[i]; // 1. Проверяем формат шрифта (например: "TrueType", "Type 1", "PCF") FcChar8* format_str = nullptr; if (FcPatternGetString(font, FC_FONTFORMAT, 0, &format_str) == FcResultMatch) { std::string current_format(reinterpret_cast<char*>(format_str)); // Если формат не поддерживается движком ThorVG — пропускаем этот шрифт if (!is_supported_by_thorvg(current_format)) { continue; } } // 2. Если формат подошел, извлекаем путь к файлу FcChar8* file_path = nullptr; if (FcPatternGetString(font, FC_FILE, 0, &file_path) == FcResultMatch) { final_file_path = reinterpret_cast<char*>(file_path); break; // Мы нашли лучший поддерживаемый шрифт, выходим из цикла } } // Освобождаем набор шрифтов, возвращенный FcFontSort FcFontSetDestroy(font_set); } // Резервный сценарий (Fallback): если вообще ничего векторного не нашли, // возвращаем пустую строку, чтобы GUI отрендерил хотя бы заглушку или залогировал ошибку. if (final_file_path.empty()) { log_error("Font::find_font_path: No supported vector fonts found for families: %s", families_str.c_str()); } return final_file_path; } class FontCache { public: static FontCache& instance() { static FontCache cache; return cache; } ~FontCache(){ FcFini(); } void add_path(std::filesystem::path const& dir) { // Добавляем путь в Fontconfig FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(dir.string().c_str())); } // Возвращает string_view в кеш (валиден до следующего find_or_load) std::string_view find_or_load(FontDescr const& descr) { std::string key = make_key(descr); auto it = m_cache.find(key); if (it != m_cache.end()) { return it->second; // уже в кеше } // Поиск через Fontconfig std::string path = find_font_path(descr); if (!path.empty()) { log_debug("find font ->> %s", path.c_str()); std::string identifier = extract_font_identifier(path); tvg::Text::load(path.c_str()); // Вставляем в кеш и возвращаем string_view auto [inserted, _] = m_cache.emplace(std::move(key), std::move(identifier)); return inserted->second; } // Пустая строка-заглушка в кеше auto [inserted, _] = m_cache.emplace(std::move(key), ""); return inserted->second; } private: FontCache() { if (!FcInit()) { log_error("FontCache: FcInit() failed"); } } std::string make_key(FontDescr const& descr) { std::ostringstream ss; ss << descr.m_families << '|' << static_cast<int>(descr.m_weight) << '|' << static_cast<int>(descr.m_slant) << '|' << static_cast<int>(descr.m_stretch); return ss.str(); } std::unordered_map<std::string, std::string> m_cache; }; } // anonymous namespace // ============================================================================ // Реализация Font // ============================================================================ void add_font_path(std::filesystem::path p) { FontCache::instance().add_path(p); } void Theme::setFont(FontDescr const& descr) { font = FontCache::instance().find_or_load(descr); size_font = descr.m_size; } }