Skip to content

Commit 8de42b1

Browse files
committed
fix(fonts): break circular unicode fallback\n\nPrevent AlternateUnicodeFont from reusing the same base family\nwhen unicode font config resolves to the same name (e.g., Arial),\nforcing fallback to continue into Unicode-capable candidates.\n\nThis targets Issue #144 macOS Cyrillic caption rendering path.
1 parent 15e669d commit 8de42b1

2 files changed

Lines changed: 17 additions & 8 deletions

File tree

  • GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/GUI
  • Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/GUI

Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/GUI/W3DGameFont.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
4747
#include <stdlib.h>
48+
#include <string.h>
4849

4950
// USER INCLUDES //////////////////////////////////////////////////////////////
5051
#include "Common/Debug.h"
@@ -57,14 +58,15 @@
5758
namespace
5859
{
5960
// GeneralsX @bugfix GitHubCopilot 20/05/2026 Resolve a usable Unicode fallback font on macOS/Linux when localized font names are unavailable.
60-
FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold)
61+
// GeneralsX @bugfix GitHubCopilot 29/05/2026 Prevent circular Unicode fallback when the localized unicode family equals the base font family.
62+
FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold, const char *base_name)
6163
{
6264
const char *preferred_name = nullptr;
6365
if (TheGlobalLanguageData && TheGlobalLanguageData->m_unicodeFontName.isNotEmpty()) {
6466
preferred_name = TheGlobalLanguageData->m_unicodeFontName.str();
6567
}
6668

67-
if (preferred_name != nullptr) {
69+
if (preferred_name != nullptr && (base_name == nullptr || strcmp(preferred_name, base_name) != 0)) {
6870
FontCharsClass *font = WW3DAssetManager::Get_Instance()->Get_FontChars(preferred_name, size, bold);
6971
if (font != nullptr) {
7072
return font;
@@ -135,7 +137,7 @@ Bool W3DFontLibrary::loadFontData( GameFont *font )
135137
font->height = fontChar->Get_Char_Height();
136138

137139
// load Unicode of same point size
138-
fontChar->AlternateUnicodeFont = LoadUnicodeFallbackFont(size, bold);
140+
fontChar->AlternateUnicodeFont = LoadUnicodeFallbackFont(size, bold, name);
139141

140142
return TRUE;
141143
}

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/GUI/W3DGameFont.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
4747
#include <stdlib.h>
4848
#include <stdio.h>
49+
#include <string.h>
4950

5051
// USER INCLUDES //////////////////////////////////////////////////////////////
5152
#include "Common/Debug.h"
@@ -59,7 +60,8 @@ namespace
5960
{
6061
// GeneralsX @bugfix GitHubCopilot 20/05/2026 Resolve a usable Unicode fallback font on macOS/Linux when localized font names are unavailable.
6162
// GeneralsX @tweak GitHubCopilot 27/05/2026 Add explicit stderr tracing for Unicode fallback font lookup decisions.
62-
FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold)
63+
// GeneralsX @bugfix GitHubCopilot 29/05/2026 Prevent circular Unicode fallback when the localized unicode family equals the base font family.
64+
FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold, const char *base_name)
6365
{
6466
const char *preferred_name = nullptr;
6567
char log_buffer[512];
@@ -69,13 +71,14 @@ FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold)
6971
}
7072

7173
sprintf(log_buffer,
72-
"[GX-ISSUE144] W3DFont fallback start size=%d bold=%d preferred=%s",
74+
"[GX-ISSUE144] W3DFont fallback start size=%d bold=%d preferred=%s base=%s",
7375
size,
7476
bold,
75-
preferred_name ? preferred_name : "<none>");
77+
preferred_name ? preferred_name : "<none>",
78+
base_name ? base_name : "<none>");
7679
fprintf(stderr, "%s\n", log_buffer);
7780

78-
if (preferred_name != nullptr) {
81+
if (preferred_name != nullptr && (base_name == nullptr || strcmp(preferred_name, base_name) != 0)) {
7982
FontCharsClass *font = WW3DAssetManager::Get_Instance()->Get_FontChars(preferred_name, size, bold);
8083
if (font != nullptr) {
8184
sprintf(log_buffer, "[GX-ISSUE144] W3DFont fallback hit preferred=%s", preferred_name);
@@ -86,6 +89,10 @@ FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold)
8689
sprintf(log_buffer, "[GX-ISSUE144] W3DFont fallback miss preferred=%s", preferred_name);
8790
fprintf(stderr, "%s\n", log_buffer);
8891
}
92+
else if (preferred_name != nullptr) {
93+
sprintf(log_buffer, "[GX-ISSUE144] W3DFont fallback skip preferred=%s reason=same-as-base", preferred_name);
94+
fprintf(stderr, "%s\n", log_buffer);
95+
}
8996

9097
static const char *kFallbackUnicodeFonts[] = {
9198
"Arial Unicode MS",
@@ -168,7 +175,7 @@ Bool W3DFontLibrary::loadFontData( GameFont *font )
168175
font->height = fontChar->Get_Char_Height();
169176

170177
// load Unicode of same point size
171-
fontChar->AlternateUnicodeFont = LoadUnicodeFallbackFont(size, bold);
178+
fontChar->AlternateUnicodeFont = LoadUnicodeFallbackFont(size, bold, name);
172179
sprintf(log_buffer,
173180
"[GX-ISSUE144] W3DFont alternate unicode %s for base=%s",
174181
fontChar->AlternateUnicodeFont ? "assigned" : "missing",

0 commit comments

Comments
 (0)