fix: Cyrillic and other non-Latin text shows as question marks or garbage (#15419)

This commit is contained in:
Kris Austin
2026-08-29 12:48:05 -05:00
committed by GitHub
parent db29f570bd
commit a0e5beb606
3 changed files with 36 additions and 12 deletions

View File

@@ -2482,6 +2482,19 @@ static const ImWchar ranges_keyboard_shortcuts[] =
};
#endif // __APPLE__
// Names drawn through the atlas come from file names and CAD data, not from the UI language.
// GetGlyphRangesDefault() already gives every language the CJK ideographs, which is why a
// Chinese file name renders under an English UI; these are the alphabetic scripts it omits.
// Codepoints the font lacks are skipped at build time, so only existing glyphs cost anything.
static const ImWchar ranges_language_independent[] =
{
0x0100, 0x024F, // Latin Extended-A and Extended-B
0x0370, 0x03FF, // Greek and Coptic
0x0400, 0x04FF, // Cyrillic
0x1E00, 0x1EFF, // Latin Extended Additional (Vietnamese)
0,
};
std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, unsigned *outwidth, unsigned *outheight)
{
@@ -2792,6 +2805,7 @@ void ImGuiWrapper::init_font(bool compress)
ImFontAtlas::GlyphRangesBuilder builder;
builder.AddRanges(m_glyph_ranges);
builder.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesDefault());
builder.AddRanges(ranges_language_independent);
#ifdef __APPLE__
if (m_font_cjk)
// Apple keyboard shortcuts are only contained in the CJK fonts.
@@ -2813,12 +2827,17 @@ void ImGuiWrapper::init_font(bool compress)
// Orca: temp fix for Korean font
auto font_name_regular = "HarmonyOS_Sans_SC_Regular.ttf";
auto font_name_bold = "HarmonyOS_Sans_SC_Bold.ttf";
// The Korean and Thai fonts cover their own script and little else, so they need the
// default font merged in behind them to reach the full range.
bool needs_glyph_fallback = false;
if(m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesKorean()) {
font_name_regular = "NanumGothic-Regular.ttf";
font_name_bold = "NanumGothic-Bold.ttf";
needs_glyph_fallback = true;
} else if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
font_name_regular = "Sarabun-Medium.ttf";
font_name_bold = "Sarabun-SemiBold.ttf";
needs_glyph_fallback = true;
}
default_font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + font_name_regular).c_str(), m_font_size, &cfg, ranges.Data);
if (default_font == nullptr) {
@@ -2828,11 +2847,12 @@ void ImGuiWrapper::init_font(bool compress)
}
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
// A merged font only supplies glyphs the font ahead of it lacks, so this fills the gaps
// without restyling anything the script font already covers.
if (needs_glyph_fallback) {
ImFontConfig fallback_cfg = cfg;
fallback_cfg.MergeMode = true;
static constexpr ImWchar celsius_range[] = { 0x2103, 0x2103, 0 };
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Regular.ttf").c_str(), m_font_size, &fallback_cfg, celsius_range);
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Regular.ttf").c_str(), m_font_size, &fallback_cfg, ranges.Data);
}
bold_font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + font_name_bold).c_str(), m_font_size, &cfg, ranges.Data);
@@ -2841,11 +2861,10 @@ void ImGuiWrapper::init_font(bool compress)
if (bold_font == nullptr) { throw Slic3r::RuntimeError("ImGui: Could not load deafult font"); }
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
if (needs_glyph_fallback) {
ImFontConfig fallback_cfg = cfg;
fallback_cfg.MergeMode = true;
static constexpr ImWchar celsius_range[] = { 0x2103, 0x2103, 0 };
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Bold.ttf").c_str(), m_font_size, &fallback_cfg, celsius_range);
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Bold.ttf").c_str(), m_font_size, &fallback_cfg, ranges.Data);
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
@@ -2897,13 +2916,18 @@ void ImGuiWrapper::init_font(bool compress)
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max_tex_size));
constexpr int max_retries = 6;
for (int attempt = 0; attempt < max_retries && io.Fonts->TexHeight > gl_max_tex_size; ++attempt) {
io.Fonts->TexDesiredWidth = (io.Fonts->TexDesiredWidth > 0 ? io.Fonts->TexDesiredWidth : io.Fonts->TexWidth) * 2;
const int width = io.Fonts->TexDesiredWidth > 0 ? io.Fonts->TexDesiredWidth : io.Fonts->TexWidth;
// Both dimensions share the same limit, so widening past it would only trade an
// illegal height for an illegal width.
if (width * 2 > gl_max_tex_size)
break;
io.Fonts->TexDesiredWidth = width * 2;
io.Fonts->Build();
}
if (io.Fonts->TexHeight > gl_max_tex_size) {
// Shouldn't really happen
BOOST_LOG_TRIVIAL(error) << "Font atlas height " << io.Fonts->TexHeight
<< " still exceeds GL_MAX_TEXTURE_SIZE (" << gl_max_tex_size << ")"
// Needs both a very large glyph set and a small GL_MAX_TEXTURE_SIZE.
BOOST_LOG_TRIVIAL(error) << "Font atlas " << io.Fonts->TexWidth << "x" << io.Fonts->TexHeight
<< " does not fit GL_MAX_TEXTURE_SIZE (" << gl_max_tex_size << ")"
<< " after " << max_retries << " attempts; rendering may be incomplete";
}

View File

@@ -21635,7 +21635,7 @@ void Plater::show_object_info()
auto mesh_errors = p->sidebar->obj_list()->get_mesh_errors_info(&info_manifold, &non_manifold_edges);
if (non_manifold_edges > 0) {
info_manifold += into_u8("\n" + _L("Tips:") + "\n" + _L("Use \"Fix Model\" to repair the mesh."));
info_manifold += "\n" + _L("Tips:") + "\n" + _L("Use \"Fix Model\" to repair the mesh.");
}
info_manifold = "<Error>" + info_manifold + "</Error>";

View File

@@ -7530,7 +7530,7 @@ void Tab::delete_preset()
for (auto &preset2 : *m_presets)
if (preset2.inherits() == current_preset.name) {
++count;
presets += "\n - " + preset2.name;
presets += "\n - " + from_u8(preset2.name);
}
if (count > 0) {
msg = _L("Presets inherited by other presets cannot be deleted!");