Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,32 @@ FontCharsClass *LoadUnicodeFallbackFont(Int size, Bool bold, const char *base_na

return nullptr;
}

// GeneralsX @feature felipebraz 26/09/2026 Load Font Awesome Brands as dedicated brand icon fallback font
FontCharsClass *LoadBrandFallbackFont(Int size, Bool bold, const char *base_name)
{
static const char *kBrandFonts[] = {
"fa-brands-400",
"Font Awesome 6 Brands",
nullptr
};

for (int i = 0; kBrandFonts[i]; ++i) {
if (base_name != nullptr && strcmp(kBrandFonts[i], base_name) == 0)
continue;

FontCharsClass *font = WW3DAssetManager::Get_Instance()->Get_FontChars(kBrandFonts[i], size, bold);
if (font != nullptr) {
// GeneralsX @bugfix felipebraz 27/09/2026 Ensure font has the required OS brand glyphs (Apple, Windows, Linux)
if (font->Has_Glyph(0xF179) && font->Has_Glyph(0xF17A) && font->Has_Glyph(0xF17C)) {
return font;
}
font->Release_Ref();
}
}

return nullptr;
}
}

// DEFINES ////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -163,6 +189,8 @@ Bool W3DFontLibrary::loadFontData( GameFont *font )
"Arial Unicode MS",
"Arial Unicode",
"DejaVu Sans",
"fa-brands-400",
"Font Awesome 6 Brands",
nullptr
};
for (int i = 0; kFullCoverageFonts[i]; i++) {
Expand All @@ -174,6 +202,12 @@ Bool W3DFontLibrary::loadFontData( GameFont *font )
if (!skipFallback) {
fontChar->AlternateUnicodeFont = LoadUnicodeFallbackFont(size, bold, name);
}

// GeneralsX @feature felipebraz 26/09/2026 Provide brand glyph fallback for UI fonts
bool isBrandFont = (name != nullptr && (strcmp(name, "fa-brands-400") == 0 || strcmp(name, "Font Awesome 6 Brands") == 0));
if (!isBrandFont) {
fontChar->AlternateBrandFont = LoadBrandFallbackFont(size, bold, name);
}
}

return TRUE;
Expand All @@ -189,6 +223,8 @@ void W3DFontLibrary::releaseFontData( GameFont *font )
// the W3D asset manager which is all taken for of us
if (font && font->fontData)
{
if(((FontCharsClass *)(font->fontData))->AlternateBrandFont)
((FontCharsClass *)(font->fontData))->AlternateBrandFont->Release_Ref();
if(((FontCharsClass *)(font->fontData))->AlternateUnicodeFont)
((FontCharsClass *)(font->fontData))->AlternateUnicodeFont->Release_Ref();
((FontCharsClass *)(font->fontData))->Release_Ref();
Expand Down
116 changes: 108 additions & 8 deletions Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,7 @@ FontCharsClass::FontCharsClass () :
IsBold (false)
{
AlternateUnicodeFont = nullptr;
AlternateBrandFont = nullptr;
::memset( ASCIICharArray, 0, sizeof (ASCIICharArray) );
}

Expand Down Expand Up @@ -1225,6 +1226,11 @@ FontCharsClass::Get_Char_Data (WCHAR ch)
{
retval = ASCIICharArray[normalized_char];
}
// GeneralsX @feature felipebraz 26/09/2026 Route Private Use Area (Font Awesome Brand icons) to brand font
else if ( normalized_char >= 0xE000 && normalized_char <= 0xF8FF && AlternateBrandFont && this != AlternateBrandFont )
{
return AlternateBrandFont->Get_Char_Data( glyph );
}
else if ( AlternateUnicodeFont && this != AlternateUnicodeFont )
{
return AlternateUnicodeFont->Get_Char_Data( glyph );
Expand Down Expand Up @@ -1260,6 +1266,16 @@ FontCharsClass::Get_Char_Data (WCHAR ch)
int
FontCharsClass::Get_Char_Width (WCHAR ch)
{
const uint16 normalized_char = static_cast<uint16>(ch);
const WCHAR glyph = static_cast<WCHAR>(normalized_char);

if ( normalized_char >= 0xE000 && normalized_char <= 0xF8FF && AlternateBrandFont && this != AlternateBrandFont ) {
return AlternateBrandFont->Get_Char_Width( glyph );
}
if ( normalized_char >= 256 && AlternateUnicodeFont && this != AlternateUnicodeFont ) {
return AlternateUnicodeFont->Get_Char_Width( glyph );
}

const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
if ( data != nullptr ) {
return data->Width;
Expand All @@ -1277,6 +1293,16 @@ FontCharsClass::Get_Char_Width (WCHAR ch)
int
FontCharsClass::Get_Char_Spacing (WCHAR ch)
{
const uint16 normalized_char = static_cast<uint16>(ch);
const WCHAR glyph = static_cast<WCHAR>(normalized_char);

if ( normalized_char >= 0xE000 && normalized_char <= 0xF8FF && AlternateBrandFont && this != AlternateBrandFont ) {
return AlternateBrandFont->Get_Char_Spacing( glyph );
}
if ( normalized_char >= 256 && AlternateUnicodeFont && this != AlternateUnicodeFont ) {
return AlternateUnicodeFont->Get_Char_Spacing( glyph );
}

const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
if ( data != nullptr ) {
if ( data->Width != 0 ) {
Expand All @@ -1296,6 +1322,18 @@ FontCharsClass::Get_Char_Spacing (WCHAR ch)
void
FontCharsClass::Blit_Char (WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, int y)
{
const uint16 normalized_char = static_cast<uint16>(ch);
const WCHAR glyph = static_cast<WCHAR>(normalized_char);

if ( normalized_char >= 0xE000 && normalized_char <= 0xF8FF && AlternateBrandFont && this != AlternateBrandFont ) {
AlternateBrandFont->Blit_Char( glyph, dest_ptr, dest_stride, x, y );
return;
}
if ( normalized_char >= 256 && AlternateUnicodeFont && this != AlternateUnicodeFont ) {
AlternateUnicodeFont->Blit_Char( glyph, dest_ptr, dest_stride, x, y );
return;
}

const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
if ( data != nullptr && data->Width != 0 ) {

Expand Down Expand Up @@ -1324,6 +1362,35 @@ FontCharsClass::Blit_Char (WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, i
}


////////////////////////////////////////////////////////////////////////////////////
//
// Has_Glyph
//
// GeneralsX @feature felipebraz 27/09/2026 Test if font contains a valid glyph mapping
////////////////////////////////////////////////////////////////////////////////////
bool
FontCharsClass::Has_Glyph (WCHAR ch) const
{
#if defined(_WIN32)
if ( MemDC == nullptr ) {
return false;
}
WORD glyph_index = 0;
if ( ::GetGlyphIndicesW( MemDC, &ch, 1, &glyph_index, GGI_MARK_NONEXISTING_GLYPHS ) == GDI_ERROR ) {
return false;
}
return glyph_index != 0xFFFF;
#elif defined(SAGE_USE_FREETYPE)
if ( FTFace == nullptr ) {
return false;
}
return FT_Get_Char_Index( FTFace, ch ) != 0;
#else
return false;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#endif
}


////////////////////////////////////////////////////////////////////////////////////
//
// Update_Current_Buffer
Expand Down Expand Up @@ -1719,6 +1786,13 @@ FontCharsClass::Locate_Font_FontConfig (const char *font_name)
strcmp( normalized, "liberationmono" ) == 0 ||
strcmp( normalized, "liberationmonobold" ) == 0);

// GeneralsX @feature felipebraz 26/09/2026 Font Awesome 6 Brands font resolution
bool is_fa_brands = (strcmp( normalized, "fontawesome6brands" ) == 0 ||
strcmp( normalized, "fontawesomebrands" ) == 0 ||
strcmp( normalized, "fabrands400" ) == 0 ||
strcmp( normalized, "fabrands" ) == 0 ||
strcmp( normalized, "fontawesome" ) == 0);

if ( is_arial ) {
if ( is_bold ) {
candidates[candidate_count++] = "LiberationSans-Bold";
Expand All @@ -1743,6 +1817,11 @@ FontCharsClass::Locate_Font_FontConfig (const char *font_name)
candidates[candidate_count++] = "LiberationMono-Regular";
candidates[candidate_count++] = "liberationmono-regular";
candidates[candidate_count++] = "LiberationMono";
} else if ( is_fa_brands ) {
candidates[candidate_count++] = "fa-brands-400";
candidates[candidate_count++] = "fa-brands-400.ttf";
candidates[candidate_count++] = "FontAwesome6Brands-Regular";
candidates[candidate_count++] = "Font Awesome 6 Brands";
}

//
Expand All @@ -1769,14 +1848,31 @@ FontCharsClass::Locate_Font_FontConfig (const char *font_name)
FcResult result = FcResultNoMatch;
FcPattern *font = FcFontMatch( config, pattern, &result );
if ( font != nullptr && result == FcResultMatch ) {
FcChar8 *file_path = nullptr;
if ( FcPatternGetString( font, FC_FILE, 0, &file_path ) == FcResultMatch && file_path != nullptr ) {
if ( Platform::IsFileReadable( (const char*)file_path ) ) {
FreetypeFontPath = (const char*)file_path;
FcPatternDestroy( font );
FcPatternDestroy( pattern );
FcConfigDestroy( config );
return FreetypeFontPath;
// GeneralsX @bugfix felipebraz 27/09/2026 Guard brand queries against generic sans fallbacks from FcFontMatch
bool match_valid = true;
if ( is_fa_brands ) {
FcChar8 *family = nullptr;
if ( FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch && family != nullptr ) {
if ( strstr( (const char*)family, "Font Awesome" ) == nullptr &&
strstr( (const char*)family, "FontAwesome" ) == nullptr &&
strstr( (const char*)family, "Brands" ) == nullptr ) {
match_valid = false;
}
} else {
match_valid = false;
}
}

if ( match_valid ) {
FcChar8 *file_path = nullptr;
if ( FcPatternGetString( font, FC_FILE, 0, &file_path ) == FcResultMatch && file_path != nullptr ) {
if ( Platform::IsFileReadable( (const char*)file_path ) ) {
FreetypeFontPath = (const char*)file_path;
FcPatternDestroy( font );
FcPatternDestroy( pattern );
FcConfigDestroy( config );
return FreetypeFontPath;
}
}
}
FcPatternDestroy( font );
Expand Down Expand Up @@ -2003,6 +2099,10 @@ FontCharsClass::Store_Freetype_Char (WCHAR ch)
const int skip_cols = (glyph->bitmap_left < 0) ? -glyph->bitmap_left : 0;
const int max_cols = char_width - x_offset;
for ( unsigned int row = 0; row < glyph->bitmap.rows; row++ ) {
// GeneralsX @bugfix felipebraz 26/09/2026 Guard row write bounds against CharHeight
if ( (y_offset + static_cast<int>(row)) >= CharHeight ) {
break;
}
int src_index = row * glyph->bitmap.pitch;
int dst_index = (y_offset + row) * char_width;

Expand Down
4 changes: 4 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class FontCharsClass : public RefCountClass

// TR: Hack for unicode font support
FontCharsClass *AlternateUnicodeFont;
// GeneralsX @feature felipebraz 26/09/2026 Fallback font for brand/symbolic glyphs (Font Awesome)
FontCharsClass *AlternateBrandFont;


bool Initialize_GDI_Font( const char *font_name, int point_size, bool is_bold );
Expand All @@ -112,6 +114,8 @@ class FontCharsClass : public RefCountClass
int Get_Extra_Overlap() {return PixelOverlap;}

void Blit_Char( WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, int y );
// GeneralsX @feature felipebraz 27/09/2026 Test if font contains a valid glyph mapping
bool Has_Glyph( WCHAR ch ) const;

private:

Expand Down
Loading
Loading