Skip to content

How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean) using hello_imgui.load_font #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
inkydragon opened this issue Mar 29, 2024 · 11 comments
Labels
faq A frequent issue, remaining opened to facilitate discoverability

Comments

@inkydragon
Copy link

env:

  • Python imgui-bundle 1.3.0

# ImVec2 GlyphExtraSpacing; /* original C++ signature */
glyph_extra_spacing: (
ImVec2 # 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
)
# ImVec2 GlyphOffset; /* original C++ signature */
glyph_offset: ImVec2 # 0, 0 // Offset all glyphs from this font input.
# float GlyphMinAdvanceX; /* original C++ signature */
glyph_min_advance_x: float # 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font
# float GlyphMaxAdvanceX; /* original C++ signature */
glyph_max_advance_x: float # FLT_MAX // Maximum AdvanceX for glyphs

struct ImFontConfig
{
	// ...

    ImVec2          GlyphExtraSpacing;      // 0, 0     // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
    ImVec2          GlyphOffset;            // 0, 0     // Offset all glyphs from this font input.
    const ImWchar*  GlyphRanges;            // NULL     // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).
    float           GlyphMinAdvanceX;       // 0        // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font
    float           GlyphMaxAdvanceX;       // FLT_MAX  // Maximum AdvanceX for glyphs

	// ...
};

https://github.com/ocornut/imgui/blame/d3c3514a59bb31406c954c2b525f330e9d167845/imgui.h#L2889

Context

I want to load Chinese fonts and specify glyph_range.

test code
void LoadDefaultFont_WithFontAwesomeIcons()
{
    std::string fontFilename = "fonts/SarasaUiSC-Regular.ttf";
    if (HelloImGui::AssetExists(fontFilename))
    {
        HelloImGui::FontLoadingParams fontLoadingParams;
        fontLoadingParams.mergeFontAwesome = true;
        // NOTE
        fontLoadingParams.fontConfig.GlyphRanges =
            ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
        ImFont* font = LoadFont(fontFilename, 15.f, fontLoadingParams);
        (void) font;
    }
    else
    {
        ImGui::GetIO().Fonts->AddFontDefault();
    }
}
def load_default_font_with_fa_icon():
    font_filename = "fonts/SarasaUiSC-Regular.ttf"
    if hello_imgui.asset_exists(font_filename):
        font_loading_params = hello_imgui.FontLoadingParams()
        font_loading_params.merge_font_awesome = True
        sc_cn_range = imgui.get_io().fonts.get_glyph_ranges_chinese_simplified_common()
        # font_loading_params.font_config.??? = sc_cn_range
        hello_imgui.load_font(font_filename, 15.0, font_loading_params)
        logger.info(f"Loaded font: {font_filename}")
    else:
        imgui.get_io().fonts.add_font_default()
        logger.info(f"Loaded default font.")
Workaround
def load_default_font_with_fa_icon():
    font_filename = "fonts/SarasaUiSC-Regular.ttf"
    if hello_imgui.asset_exists(font_filename):
        sc_cn_range = imgui.get_io().fonts.get_glyph_ranges_chinese_simplified_common()
        imgui.get_io().fonts.add_font_from_file_ttf(
            hello_imgui.asset_file_full_path(font_filename),
            24.0,
            glyph_ranges_as_int_list=sc_cn_range)

        # font_loading_params = hello_imgui.FontLoadingParams()
        # font_loading_params.merge_font_awesome = True
        # # font_loading_params.font_config.??? = sc_cn_range
        # hello_imgui.load_font(font_filename, 15.0, font_loading_params)
        logger.info(f"Loaded font: {font_filename}")
    else:
        imgui.get_io().fonts.add_font_default()
        logger.info(f"Loaded default font.")
@pthom
Copy link
Owner

pthom commented Mar 29, 2024

I don't understand your question (there is none in your message, in fact). Does your work around work?

@pthom pthom changed the title Missing fields ImFontConfig.GlyphRanges in python binding How to load chinese font with hello_imgui.load_font and set the glyph range (Python) Jul 6, 2024
@pthom
Copy link
Owner

pthom commented Jul 6, 2024

Hello,

I come back to this question after a few months, sorry for the delay.

I understand that you needed a way to translate the glyph ranges coming from Dear ImGui, into ranges used by hello_imgui.load_font.

This commit adds support for this.

Example usage:

"""Demonstrates how to load a font with Chinese characters and display them in the GUI,
using the common glyph ranges defined in by ImGui.
"""
from imgui_bundle import imgui, hello_imgui, imgui_ctx
from imgui_bundle.demos_python import demo_utils


demo_utils.set_hello_imgui_demo_assets_folder()


font_cn: imgui.ImFont = None


def load_font():
    global font_cn
    if not hello_imgui.asset_exists("fonts/NotoSerifSC-VariableFont_wght.ttf"):
        return

    # Note: this font is not provided with the ImGui bundle (too large).
    # You will need to provide it yourself, or use another font.
    font_filename = "fonts/NotoSerifSC-VariableFont_wght.ttf"

    # The range of Chinese characters is defined by ImGui as a single list of characters (List[ImWchar]), with a terminating 0.
    # (each range is a pair of successive characters in this list, with the second character being the last one in the range)
    cn_glyph_ranges_imgui = imgui.get_io().fonts.get_glyph_ranges_chinese_simplified_common()
    # We need to convert this list into a list of pairs (List[ImWcharPair]), where each pair is a range of characters.
    cn_glyph_ranges_pair = hello_imgui.translate_common_glyph_ranges(cn_glyph_ranges_imgui)

    font_loading_params = hello_imgui.FontLoadingParams()
    font_loading_params.glyph_ranges = cn_glyph_ranges_pair
    font_cn = hello_imgui.load_font(font_filename, 40.0, font_loading_params)


def gui():
    if font_cn is not None:
        with imgui_ctx.push_font(font_cn):
            imgui.text("Hello world")
            imgui.text("你好,世界")
    else:
        imgui.text("Font file not found")
        imgui.text_wrapped("""
        This font is not provided with the ImGui bundle (too large).
        You will need to provide it yourself, or use another font.
        """)


runner_params = hello_imgui.RunnerParams()
runner_params.callbacks.load_additional_fonts = load_font
runner_params.callbacks.show_gui = gui
hello_imgui.run(runner_params)
image

@pthom pthom changed the title How to load chinese font with hello_imgui.load_font and set the glyph range (Python) How to load chinese font with hello_imgui.load_font and set the glyph range from get_glyph_ranges_chinese_simplified_common() Jul 6, 2024
@pthom pthom changed the title How to load chinese font with hello_imgui.load_font and set the glyph range from get_glyph_ranges_chinese_simplified_common() How to load fonts with specific glyph ranges (e.g., Chinese, Japanese, Korean) using hello_imgui.load_font Jul 6, 2024
@pthom pthom added the faq A frequent issue, remaining opened to facilitate discoverability label Jul 6, 2024
@BiliBiliFox
Copy link

不,你没解决问题.
image

@pthom
Copy link
Owner

pthom commented Jan 6, 2025

不,你没解决问题. image

image

@zzzy-zh86
Copy link

zzzy-zh86 commented May 14, 2025

不,你没解决问题.image

image

hello_imgui.asset_exists("fonts/NotoSerifSC-Black.otf") False
The font file is confirmed to exist but returns False.

@pthom
Copy link
Owner

pthom commented May 15, 2025

Did you call hello_imgui.set_assets_folder

@zzzy-zh86
Copy link

Did you call hello_imgui.set_assets_folder
I am using exactly the example from the project without any modifications, but it indeed returns False, indicating that the font file has not been loaded correctly.

@pthom
Copy link
Owner

pthom commented May 15, 2025

From the code:
Note: this font is not provided with the ImGui bundle (too large). You will need to provide it yourself, or use another font.

Did you add it ?

@zzzy-zh86
Copy link

From the code: Note: this font is not provided with the ImGui bundle (too large). You will need to provide it yourself, or use another font.

Did you add it ?

I have tried many different suffixes for Chinese font files, and I have also experimented with various file sizes, but there was no response.

@LiyueAnd
Copy link

LiyueAnd commented May 27, 2025

@pthom
Hello author, according to the example in the source code, I loaded the Chinese font, but it cannot be displayed correctly no matter what. I have confirmed that the font has been placed in the path searched by the program, because if the font is not loaded, the pointer will be empty and no text will be rendered, but the result will be garbled content. If you have time, please help me check where the problem is. Looking forward to your reply. Thank you.
`#include "immapp/immapp.h"
#include "hello_imgui/hello_imgui.h"
#include "hello_imgui/icons_font_awesome_6.h"
#include "immapp/snippets.h"
#include "demo_utils/api_demos.h"
#include "imgui_md_wrapper/imgui_md_wrapper.h"

#include
#include
#include <windows.h>
#include

ImFont* font_cn = nullptr;
void demo() {
if (font_cn != nullptr)
{
ImGui::PushFont(font_cn);
ImGui::Text("test");
ImGui::Text("测试");
ImGui::Text(ICON_FA_FACE_SMILE"测试");
ImGui::PopFont();
}
}

using VoidFunction = std::function<void(void)>;

void ToAssetsFolder()
{
auto getExeParentPath = -> std::filesystem::path {
char path[MAX_PATH] = { 0 };
DWORD len = GetModuleFileNameA(NULL, path, MAX_PATH);
if (len == 0 || len == MAX_PATH) return {}; // 获取失败或路径太长
return std::filesystem::path(path).parent_path();
};
//
std::filesystem::path exeFolder = getExeParentPath();
if (exeFolder.empty()) {
std::cerr << "获取 exe 路径失败,无法切换目录" << std::endl;
std::exit(EXIT_FAILURE);
}
//
std::error_code ec;
std::filesystem::current_path(exeFolder, ec);
if (ec) {
std::cerr << "切换工作目录失败: " << ec.message() << std::endl;
std::exit(EXIT_FAILURE);
}
//
std::filesystem::path assetFolder = exeFolder / "Assets";
//
if (!std::filesystem::exists(assetFolder) || !std::filesystem::is_directory(assetFolder)) {
std::cerr << "Assets 目录不存在: " << assetFolder << std::endl;
std::exit(EXIT_FAILURE);
}
//
HelloImGui::SetAssetsFolder("Assets");
}

void LoadFont()
{
HelloImGui::GetRunnerParams()->callbacks.defaultIconFont = HelloImGui::DefaultIconFont::FontAwesome6;
const char* font_filename = "fonts/msyh.ttc";
if (!HelloImGui::AssetExists(font_filename)) {
std::cerr << "字体加载失败:" << font_filename << std::endl;
std::exit(EXIT_FAILURE);
}
const ImWchar* cn_glyph_ranges_imgui = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
auto cn_glyph_ranges_pair = HelloImGui::TranslateCommonGlyphRanges(cn_glyph_ranges_imgui);

HelloImGui::FontLoadingParams font_loading_params;
font_loading_params.mergeFontAwesome = true;
font_loading_params.glyphRanges = cn_glyph_ranges_pair;
font_cn =HelloImGui::LoadFont(font_filename, 18.0f, font_loading_params);

}

void RunMyWindow(VoidFunction demoFunction)
{
if (ImGui::GetFrameCount() < 2) //
return;
demoFunction();
}

int main(int, char**)
{
ToAssetsFolder();//

HelloImGui::RunnerParams runnerParams;
//
runnerParams.appWindowParams.windowTitle = "TestView";
runnerParams.appWindowParams.windowGeometry.size = { 1400, 950 };
//
runnerParams.imGuiWindowParams.showMenuBar = true;
runnerParams.imGuiWindowParams.showStatusBar = true;

//
runnerParams.imGuiWindowParams.defaultImGuiWindowType = HelloImGui::DefaultImGuiWindowType::ProvideFullScreenDockSpace;
//
runnerParams.imGuiWindowParams.enableViewports = true;

//
std::vector<HelloImGui::DockableWindow> dockableWindows; 

struct DemoDetails
{
    std::string Label;
    VoidFunction DemoFunction;
};

auto addDemoDockableWindow = [&dockableWindows](const DemoDetails& demoDetails)
    {
        HelloImGui::DockableWindow window;
        window.label = demoDetails.Label;
        window.dockSpaceName = "MainDockSpace";
        window.GuiFunction = [&demoDetails]()
            {
                RunMyWindow(demoDetails.DemoFunction);
            };
		//
        dockableWindows.push_back(window);
    };

#define Window_Details(label, function_name) DemoDetails{ label, function_name }

std::vector<DemoDetails> MyWindows{
    Window_Details("Demo", demo),
};

for (const auto& window : MyWindows)
    addDemoDockableWindow(window);

runnerParams.dockingParams.dockableWindows = dockableWindows;

// 
auto showGui = [&runnerParams]
    {
        static int nbFrames = 0;
        if (nbFrames == 1)
        {
            //
            runnerParams.dockingParams.focusDockableWindow("Demo");
        }
        nbFrames += 1;
    };

runnerParams.callbacks.ShowGui = showGui;
runnerParams.callbacks.LoadAdditionalFonts = LoadFont;


auto addons = ImmApp::AddOnsParams();
addons.withMarkdown = true;
addons.withNodeEditor = true;
addons.withImplot = true;
addons.withImplot3d = true;
addons.withTexInspect = true;
ImmApp::Run(runnerParams, addons);

return 0;

}
`

Image

Image

Image

@LiyueAnd
Copy link

@pthom Hello author, according to the example in the source code, I loaded the Chinese font, but it cannot be displayed correctly no matter what. I have confirmed that the font has been placed in the path searched by the program, because if the font is not loaded, the pointer will be empty and no text will be rendered, but the result will be garbled content. If you have time, please help me check where the problem is. Looking forward to your reply. Thank you. `#include "immapp/immapp.h" #include "hello_imgui/hello_imgui.h" #include "hello_imgui/icons_font_awesome_6.h" #include "immapp/snippets.h" #include "demo_utils/api_demos.h" #include "imgui_md_wrapper/imgui_md_wrapper.h"

#include #include #include <windows.h> #include

ImFont* font_cn = nullptr; void demo() { if (font_cn != nullptr) { ImGui::PushFont(font_cn); ImGui::Text("test"); ImGui::Text("测试"); ImGui::Text(ICON_FA_FACE_SMILE"测试"); ImGui::PopFont(); } }

using VoidFunction = std::function<void(void)>;

void ToAssetsFolder() { auto getExeParentPath = -> std::filesystem::path { char path[MAX_PATH] = { 0 }; DWORD len = GetModuleFileNameA(NULL, path, MAX_PATH); if (len == 0 || len == MAX_PATH) return {}; // 获取失败或路径太长 return std::filesystem::path(path).parent_path(); }; // std::filesystem::path exeFolder = getExeParentPath(); if (exeFolder.empty()) { std::cerr << "获取 exe 路径失败,无法切换目录" << std::endl; std::exit(EXIT_FAILURE); } // std::error_code ec; std::filesystem::current_path(exeFolder, ec); if (ec) { std::cerr << "切换工作目录失败: " << ec.message() << std::endl; std::exit(EXIT_FAILURE); } // std::filesystem::path assetFolder = exeFolder / "Assets"; // if (!std::filesystem::exists(assetFolder) || !std::filesystem::is_directory(assetFolder)) { std::cerr << "Assets 目录不存在: " << assetFolder << std::endl; std::exit(EXIT_FAILURE); } // HelloImGui::SetAssetsFolder("Assets"); }

void LoadFont() { HelloImGui::GetRunnerParams()->callbacks.defaultIconFont = HelloImGui::DefaultIconFont::FontAwesome6; const char* font_filename = "fonts/msyh.ttc"; if (!HelloImGui::AssetExists(font_filename)) { std::cerr << "字体加载失败:" << font_filename << std::endl; std::exit(EXIT_FAILURE); } const ImWchar* cn_glyph_ranges_imgui = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon(); auto cn_glyph_ranges_pair = HelloImGui::TranslateCommonGlyphRanges(cn_glyph_ranges_imgui);

HelloImGui::FontLoadingParams font_loading_params;
font_loading_params.mergeFontAwesome = true;
font_loading_params.glyphRanges = cn_glyph_ranges_pair;
font_cn =HelloImGui::LoadFont(font_filename, 18.0f, font_loading_params);

}

void RunMyWindow(VoidFunction demoFunction) { if (ImGui::GetFrameCount() < 2) // return; demoFunction(); }

int main(int, char**) { ToAssetsFolder();//

HelloImGui::RunnerParams runnerParams;
//
runnerParams.appWindowParams.windowTitle = "TestView";
runnerParams.appWindowParams.windowGeometry.size = { 1400, 950 };
//
runnerParams.imGuiWindowParams.showMenuBar = true;
runnerParams.imGuiWindowParams.showStatusBar = true;

//
runnerParams.imGuiWindowParams.defaultImGuiWindowType = HelloImGui::DefaultImGuiWindowType::ProvideFullScreenDockSpace;
//
runnerParams.imGuiWindowParams.enableViewports = true;

//
std::vector<HelloImGui::DockableWindow> dockableWindows; 

struct DemoDetails
{
    std::string Label;
    VoidFunction DemoFunction;
};

auto addDemoDockableWindow = [&dockableWindows](const DemoDetails& demoDetails)
    {
        HelloImGui::DockableWindow window;
        window.label = demoDetails.Label;
        window.dockSpaceName = "MainDockSpace";
        window.GuiFunction = [&demoDetails]()
            {
                RunMyWindow(demoDetails.DemoFunction);
            };
		//
        dockableWindows.push_back(window);
    };

#define Window_Details(label, function_name) DemoDetails{ label, function_name }

std::vector<DemoDetails> MyWindows{
    Window_Details("Demo", demo),
};

for (const auto& window : MyWindows)
    addDemoDockableWindow(window);

runnerParams.dockingParams.dockableWindows = dockableWindows;

// 
auto showGui = [&runnerParams]
    {
        static int nbFrames = 0;
        if (nbFrames == 1)
        {
            //
            runnerParams.dockingParams.focusDockableWindow("Demo");
        }
        nbFrames += 1;
    };

runnerParams.callbacks.ShowGui = showGui;
runnerParams.callbacks.LoadAdditionalFonts = LoadFont;


auto addons = ImmApp::AddOnsParams();
addons.withMarkdown = true;
addons.withNodeEditor = true;
addons.withImplot = true;
addons.withImplot3d = true;
addons.withTexInspect = true;
ImmApp::Run(runnerParams, addons);

return 0;

} `

Image

Image

Image

I apologize for my mistake here. When I added "# pragma executioner_character_st (" utf-8 ")" at the beginning of the code, everything in Chinese was displayed normally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
faq A frequent issue, remaining opened to facilitate discoverability
Projects
None yet
Development

No branches or pull requests

5 participants