/* Copyright 2026 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "gui_font.h" #include "gui.h" #include "settings.h" #include "oslib/resources.h" #include "IconsFontAwesome6.h" #include "imgui/misc/freetype/imgui_freetype.h" ImFont *boldFont; namespace FontFace { enum class Noto : ImU32 { Thin = 0x0, Light = 0x20000, DemiLight = 0x30000, Regular = 0x40000, Medium = 0x50000, Bold = 0x60000, Black = 0x70000 }; enum class PingFang : ImU32 { Ultralight = 15, Thin = 12, Light = 9, Regular = 0, Medium = 3, Semibold = 6 }; enum class GothicNeo : ImU32 { UltraLight = 12, Thin = 10, Light = 8, Regular = 0, Medium = 2, SemiBold = 4, ExtraBold = 14, Heavy = 16 }; enum class Gulim : ImU32 { Round = 0, RoundMono = 1, Sharp = 2, SharpMono = 3 }; enum class MSHei : ImU32 { Legacy = 0, UI = 1}; }; struct FontNo { ImU32 value = 0; constexpr FontNo() = default; constexpr FontNo(ImU32 v) : value(v) {} template ::value>> constexpr FontNo(E e) : value(static_cast(e)) {} }; struct FontEntry { const char* lang; // "ja", "ko", "zh", "zh_HK", "zh_TW", "zh_CN", "cjk" or nullptr for "generic" std::vector paths; #if defined(__linux__) && !defined(__ANDROID__) std::vector fcNames; #endif FontNo fontNo{}; float size = 1.0f; float offsetY = 0.0f; }; #if defined(__linux__) && !defined(__ANDROID__) #include static std::string getFontPath(const char* patternText) { static void* libfontconfig = dlopen("libfontconfig.so.1", RTLD_LAZY); if (!libfontconfig) { return ""; } typedef struct _FcConfig FcConfig; typedef struct _FcPattern FcPattern; typedef unsigned char FcChar8; typedef enum _FcResult { FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId, FcResultOutOfMemory } FcResult; static auto fcInitLoadConfigAndFonts = (FcConfig * (*)())dlsym(libfontconfig, "FcInitLoadConfigAndFonts"); static auto fcConfigDestroy = (void (*)(FcConfig*))dlsym(libfontconfig, "FcConfigDestroy"); static auto fcPatternDestroy = (void (*)(FcPattern*))dlsym(libfontconfig, "FcPatternDestroy"); static auto fcPatternGetString = (FcResult (*)(const FcPattern*, const char*, int, FcChar8**))dlsym(libfontconfig, "FcPatternGetString"); static auto fcNameParse = (FcPattern * (*)(const FcChar8*))dlsym(libfontconfig, "FcNameParse"); static auto fcConfigSubstitute = (int (*)(FcConfig*, FcPattern*, int))dlsym(libfontconfig, "FcConfigSubstitute"); static auto fcDefaultSubstitute = (void (*)(FcPattern*))dlsym(libfontconfig, "FcDefaultSubstitute"); static auto fcFontMatch = (FcPattern * (*)(FcConfig*, FcPattern*, FcResult*))dlsym(libfontconfig, "FcFontMatch"); if (!fcInitLoadConfigAndFonts || !fcConfigDestroy || !fcPatternDestroy || !fcPatternGetString || !fcNameParse || !fcConfigSubstitute || !fcDefaultSubstitute || !fcFontMatch) { return ""; } const int fcMatchPattern = 0; std::string path; FcConfig* config = fcInitLoadConfigAndFonts(); if (config) { FcPattern* pattern = fcNameParse(reinterpret_cast(patternText)); if (pattern) { fcConfigSubstitute(config, pattern, fcMatchPattern); fcDefaultSubstitute(pattern); FcResult result = FcResultNoMatch; FcPattern* match = fcFontMatch(config, pattern, &result); if (match) { if (result == FcResultMatch) { FcChar8* file = nullptr; if (fcPatternGetString(match, "file", 0, &file) == FcResultMatch && file) { path = reinterpret_cast(file); } } fcPatternDestroy(match); } fcPatternDestroy(pattern); } fcConfigDestroy(config); } return path; } #endif static void registerFont(std::vector& target, FontEntry entry) { #if defined(__linux__) && !defined(__ANDROID__) for (const std::string& name : entry.fcNames) { std::string path = getFontPath(name.c_str()); if (!path.empty()) entry.paths.push_back(path); } #endif std::string locale = i18n::getCurrentLocale(); auto bucketRank = [&](const char* l) -> int { if (!l) return 3; // generic last if (strcmp(l, "cjk") == 0) return 0; // cjk first if (locale.rfind(l, 0) == 0) return 1; // user locale return 2; // remaining langs }; const int rank = bucketRank(entry.lang); auto it = target.begin(); for (; it != target.end(); ++it) { if (bucketRank(it->lang) > rank) break; } target.insert(it, entry); } static void registerFont(std::initializer_list*> targets, const FontEntry& entry) { for (auto* target : targets) registerFont(*target, entry); } static void loadFonts(const std::vector& entries, const ImFontConfig& cfg) { std::unordered_set satisfiedLangs; satisfiedLangs.reserve(8); bool cjkLoaded = false; for (const FontEntry& e : entries) { const char* lang = e.lang ? e.lang : "generic"; // Skip specific CJK langs if a generic CJK font already loaded if (cjkLoaded && (!strcmp(lang, "ja") || !strcmp(lang, "ko") || !strncmp(lang, "zh", 2))) continue; // Break-per-langTag if (satisfiedLangs.count(lang)) continue; bool loaded = false; for (const std::string& path : e.paths) { ImFontConfig entryCfg = cfg; entryCfg.FontNo = e.fontNo.value; entryCfg.GlyphOffset.y = e.size * e.offsetY; static ImWchar faRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; entryCfg.GlyphExcludeRanges = faRanges; ImFont* font = ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), e.size, &entryCfg, nullptr); if (font) { loaded = true; break; } } if (!loaded) continue; // Mark this lang as satisfied satisfiedLangs.emplace(lang); // Traditional Chinese group: TW and HK satisfy each other if (strcmp(lang, "zh_HK") == 0 || strcmp(lang, "zh_TW") == 0) { satisfiedLangs.emplace("zh_HK"); satisfiedLangs.emplace("zh_TW"); } // Umbrella rule: zh satisfies all specific Chinese locales if (strcmp(lang, "zh") == 0) { satisfiedLangs.emplace("zh_HK"); satisfiedLangs.emplace("zh_TW"); satisfiedLangs.emplace("zh_CN"); } // Special rule: cjk satisfies all CJK languages if (strcmp(lang, "cjk") == 0) { cjkLoaded = true; satisfiedLangs.emplace("ja"); satisfiedLangs.emplace("ko"); satisfiedLangs.emplace("zh"); satisfiedLangs.emplace("zh_HK"); satisfiedLangs.emplace("zh_TW"); satisfiedLangs.emplace("zh_CN"); } } } void gui_loadFonts() { ImGuiIO& io = ImGui::GetIO(); io.Fonts->Clear(); io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()); // Regular font ImGuiStyle& style = ImGui::GetStyle(); const float fontSize = uiScaled(17.f); size_t dataSize; std::unique_ptr data = resource::load("fonts/Roboto-Medium.ttf", dataSize); verify(data != nullptr); ImFont *regularFont = io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, nullptr, nullptr); ImFontConfig fontConfig; fontConfig.MergeMode = true; fontConfig.DstFont = regularFont; // Bold font data = resource::load("fonts/Roboto-Bold.ttf", dataSize); verify(data != nullptr); boldFont = io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, nullptr, nullptr); ImFontConfig boldFontConfig; boldFontConfig.MergeMode = true; boldFontConfig.DstFont = boldFont; std::vector fonts; std::vector boldFonts; fontConfig.Flags |= ImFontFlags_NoLoadError; boldFontConfig.Flags |= ImFontFlags_NoLoadError; ImFontConfig emojiConfig = fontConfig; emojiConfig.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap; ImFontConfig emojiBoldConfig = boldFontConfig; emojiBoldConfig.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap; #ifdef _WIN32 // Windows std::string fontDir = std::string(nowide::getenv("SYSTEMROOT")) + "\\Fonts\\"; ImGui::GetIO().Fonts->AddFontFromFileTTF((fontDir + "seguiemj.ttf").c_str(), fontSize, &emojiConfig); ImGui::GetIO().Fonts->AddFontFromFileTTF((fontDir + "seguiemj.ttf").c_str(), fontSize, &emojiBoldConfig); /* registerFont(fonts, { .lang="ja", .paths={fontDir + "NotoSansJP-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="ja", .paths={fontDir + "NotoSansJP-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="ja", .paths={fontDir + "BIZ-UDGothicB.ttc"}, .size=fontSize * 0.85f }); registerFont(fonts, { .lang="ko", .paths={fontDir + "NotoSansKR-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="ko", .paths={fontDir + "NotoSansKR-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={fontDir + "malgunbd.ttf"}, .size=fontSize * 1.124891f }); registerFont({&fonts, &boldFonts}, { .lang="ko", .paths={fontDir + "Gulim.ttc"}, .fontNo=FontFace::Gulim::Sharp, .size=fontSize * 0.9f }); registerFont(fonts, { .lang="zh_HK", .paths={fontDir + "NotoSansHK-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_HK", .paths={fontDir + "NotoSansHK-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="zh_TW", .paths={fontDir + "NotoSansTC-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_TW", .paths={fontDir + "NotoSansTC-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="zh_CN", .paths={fontDir + "NotoSansSC-VF.ttf"}, .fontNo=FontFace::Noto::Medium, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_CN", .paths={fontDir + "NotoSansSC-VF.ttf"}, .fontNo=FontFace::Noto::Bold, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="zh_HK", .paths={fontDir + "msjhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); registerFont({&fonts, &boldFonts}, { .lang="zh_TW", .paths={fontDir + "msjhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); registerFont({&fonts, &boldFonts}, { .lang="zh_CN", .paths={fontDir + "msyhbd.ttc"}, .fontNo=FontFace::MSHei::UI, .size=fontSize * 1.13000f, .offsetY=0.015f }); // Windows 7 registerFont({&fonts, &boldFonts}, { .lang="zh_HK", .paths={fontDir + "msjhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); registerFont({&fonts, &boldFonts}, { .lang="zh_TW", .paths={fontDir + "msjhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); registerFont({&fonts, &boldFonts}, { .lang="zh_CN", .paths={fontDir + "msyhbd.ttf"}, .size=fontSize * 1.13000f, .offsetY=0.015f }); */ { FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "NotoSansJP-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; registerFont(fonts, entry); } { FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "NotoSansJP-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; registerFont(boldFonts, entry); } { FontEntry entry; entry.lang = "ja"; entry.paths = { fontDir + "BIZ-UDGothicB.ttc" }; entry.size = fontSize * 0.85f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "NotoSansKR-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; registerFont(fonts, entry); } { FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "NotoSansKR-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; registerFont(boldFonts, entry); } { FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "malgunbd.ttf" }; entry.size = fontSize * 1.124891f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "ko"; entry.paths = { fontDir + "Gulim.ttc" }; entry.fontNo = FontFace::Gulim::Sharp; entry.size = fontSize * 0.9f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "NotoSansHK-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; registerFont(fonts, entry); } { FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "NotoSansHK-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; registerFont(boldFonts, entry); } { FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "NotoSansTC-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; registerFont(fonts, entry); } { FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "NotoSansTC-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; registerFont(boldFonts, entry); } { FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "NotoSansSC-VF.ttf" }; entry.fontNo = FontFace::Noto::Medium; entry.size = fontSize * 1.05f; registerFont(fonts, entry); } { FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "NotoSansSC-VF.ttf" }; entry.fontNo = FontFace::Noto::Bold; entry.size = fontSize * 1.05f; registerFont(boldFonts, entry); } { FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "msjhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "msjhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "msyhbd.ttc" }; entry.fontNo = FontFace::MSHei::UI; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } // Windows 7 { FontEntry entry; entry.lang = "zh_HK"; entry.paths = { fontDir + "msjhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "zh_TW"; entry.paths = { fontDir + "msjhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } { FontEntry entry; entry.lang = "zh_CN"; entry.paths = { fontDir + "msyhbd.ttf" }; entry.size = fontSize * 1.13000f; entry.offsetY = 0.015f; registerFont({ &fonts, &boldFonts }, entry); } #elif defined(TARGET_OS_MAC) emojiConfig.GlyphOffset = { 0.0f, fontSize * (0.2f) }; emojiBoldConfig.GlyphOffset = emojiConfig.GlyphOffset; ImGui::GetIO().Fonts->AddFontFromFileTTF("/System/Library/Fonts/Apple Color Emoji.ttc", fontSize, &emojiConfig); ImGui::GetIO().Fonts->AddFontFromFileTTF("/System/Library/Fonts/Apple Color Emoji.ttc", fontSize, &emojiBoldConfig); registerFont(fonts, { .lang = "ja", .paths = { "/System/Library/Fonts/ヒラギノ角ゴシック W5.ttc" }, .size=fontSize * 0.85f } ); registerFont(boldFonts, { .lang = "ja", .paths = { "/System/Library/Fonts/ヒラギノ角ゴシック W7.ttc" }, .size=fontSize * 0.85f } ); registerFont({&fonts, &boldFonts}, { .lang="ko", .paths = { "/System/Library/Fonts/AppleSDGothicNeo.ttc" }, .fontNo=FontFace::GothicNeo::SemiBold, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="zh", .paths = { "/System/Library/Fonts/PingFang.ttc" }, .fontNo=FontFace::PingFang::Medium, .size=fontSize * 1.185f, .offsetY=-0.03f }); registerFont(boldFonts, { .lang="zh", .paths = { "/System/Library/Fonts/PingFang.ttc" }, .fontNo=FontFace::PingFang::Semibold, .size=fontSize * 1.185f, .offsetY=-0.03f }); #elif defined(__ANDROID__) ImGui::GetIO().Fonts->AddFontFromFileTTF("/system/fonts/NotoColorEmoji.ttf", fontSize, &emojiConfig); ImGui::GetIO().Fonts->AddFontFromFileTTF("/system/fonts/NotoColorEmoji.ttf", fontSize, &emojiBoldConfig); registerFont(fonts, { .lang="cjk", .paths={ "/system/fonts/NotoSansCJK-Medium.ttc", "/system/fonts/NotoSansCJK-Regular.ttc" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="cjk", .paths={ "/system/fonts/NotoSansCJK-Bold.ttc", "/system/fonts/NotoSansCJK-Medium.ttc", "/system/fonts/NotoSansCJK-Regular.ttc" }, .size=fontSize * 1.05f }); #elif defined(__linux__) std::string emojiPath = getFontPath("Noto Color Emoji"); if (!emojiPath.empty()) { ImGui::GetIO().Fonts->AddFontFromFileTTF(emojiPath.c_str(), fontSize, &emojiConfig); ImGui::GetIO().Fonts->AddFontFromFileTTF(emojiPath.c_str(), fontSize, &emojiBoldConfig); } registerFont(fonts, { .lang="cjk", .fcNames={ "Noto Sans CJK JP:medium", "Noto Sans CJK JP:regular" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="cjk", .fcNames={ "Noto Sans CJK JP:bold" }, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="cjk", .fcNames={ "Droid Sans Fallback" }, .size=fontSize * 1.2f }); registerFont(fonts, { .lang="ja", .fcNames={ "Source Han Sans JP:medium" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="ja", .fcNames={ "Source Han Sans JP:bold" }, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="ja", .fcNames={ "IPAPGothic" }, .size=fontSize * 0.9f }); registerFont({&fonts, &boldFonts}, { .lang="ja", .fcNames={ "VL Gothic" }, .size=fontSize * 1.15f }); registerFont({&fonts, &boldFonts}, { .lang="ja", .fcNames={ "TakaoPGothic" }, .size=fontSize }); registerFont(fonts, { .lang="ko", .fcNames={ "Source Han Sans KR:medium" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="ko", .fcNames={ "Source Han Sans KR:bold" }, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="ko", .fcNames={ "NanumGothic:bold" }, .size=fontSize * 0.8f }); registerFont(boldFonts, { .lang="ko", .fcNames={ "NanumGothic:extrabold" }, .size=fontSize * 0.8f }); registerFont({&fonts, &boldFonts}, { .lang="ko", .fcNames={ "NanumGothicCoding:bold" }, .size=fontSize * 0.8f }); registerFont({&fonts, &boldFonts}, { .lang="ko", .fcNames={ "UnDotum:bold" }, .size=fontSize, .offsetY=-0.1f }); registerFont({&fonts, &boldFonts}, { .lang="ko", .fcNames={ "Baekmuk Dotum" }, .size=fontSize * 1.18f, .offsetY=-0.02f }); registerFont(fonts, { .lang="zh_HK", .fcNames={ "Source Han Sans HK:medium" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_HK", .fcNames={ "Source Han Sans HK:bold" }, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="zh_TW", .fcNames={ "Source Han Sans TW:medium" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_TW", .fcNames={ "Source Han Sans TW:bold" }, .size=fontSize * 1.05f }); registerFont(fonts, { .lang="zh_CN", .fcNames={ "Source Han Sans SC:medium" }, .size=fontSize * 1.05f }); registerFont(boldFonts, { .lang="zh_CN", .fcNames={ "Source Han Sans SC:bold" }, .size=fontSize * 1.05f }); registerFont({&fonts, &boldFonts}, { .lang="zh", .fcNames={ "WenQuanYi Zen Hei" }, .size=fontSize, .offsetY=-0.05f }); // TODO BSD, iOS, ... #endif loadFonts(fonts, fontConfig); loadFonts(boldFonts, boldFontConfig); // Font Awesome symbols data = resource::load("fonts/" FONT_ICON_FILE_NAME_FAS, dataSize); verify(data != nullptr); ImFontConfig faFontConfig = fontConfig; faFontConfig.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(data.get(), (int)dataSize, fontSize, &faFontConfig); boldFontConfig.FontDataOwnedByAtlas = true; io.Fonts->AddFontFromMemoryTTF(data.release(), (int)dataSize, fontSize, &boldFontConfig); // AddFont() may sync the active ImGui font stack using the previous size. // Re-apply the rebuilt atlas size after all fonts have been registered. style.FontSizeBase = fontSize; }