From f815d99489ea800207a9eeedf5f56d6aebe6c9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 14 May 2026 02:28:48 -0300 Subject: [PATCH] Some extra fixes. fontconfig is now loaded at runtime. --- include/eepp/graphics/systemfontresolver.hpp | 2 + premake4.lua | 4 - premake5.lua | 3 - src/eepp/graphics/systemfontresolver.cpp | 440 +++++++++++++------ 4 files changed, 299 insertions(+), 150 deletions(-) diff --git a/include/eepp/graphics/systemfontresolver.hpp b/include/eepp/graphics/systemfontresolver.hpp index 81920fa51..1a72370e7 100644 --- a/include/eepp/graphics/systemfontresolver.hpp +++ b/include/eepp/graphics/systemfontresolver.hpp @@ -102,6 +102,8 @@ class EE_API SystemFontResolver { private: void populateFontList() const; + void populateFontListXml() const; + void populateGenericFallbacks() const; static int scoreMatch( const FontQuery& query, const FontDesc& candidate ); diff --git a/premake4.lua b/premake4.lua index c268f1abf..867ff6bcd 100644 --- a/premake4.lua +++ b/premake4.lua @@ -1051,10 +1051,6 @@ function build_eepp( build_name ) links { "bcrypt" } end - if os.is_real("linux") or os.is_real("bsd") then - links { "fontconfig" } - end - files { "src/eepp/core/*.cpp", "src/eepp/math/*.cpp", "src/eepp/system/*.cpp", diff --git a/premake5.lua b/premake5.lua index ae2875290..d44ea951e 100644 --- a/premake5.lua +++ b/premake5.lua @@ -921,9 +921,6 @@ function build_eepp( build_name ) filter { "action:export-compile-commands", "system:macosx" } buildoptions { "-std=c++20" } - filter "system:linux or system:bsd" - links { "fontconfig" } - filter {} end diff --git a/src/eepp/graphics/systemfontresolver.cpp b/src/eepp/graphics/systemfontresolver.cpp index 70ece7242..50ccd6fd2 100644 --- a/src/eepp/graphics/systemfontresolver.cpp +++ b/src/eepp/graphics/systemfontresolver.cpp @@ -1,3 +1,8 @@ +#ifdef __APPLE__ +#include +#include +#endif + #include #include #include @@ -12,19 +17,11 @@ #define NOMINMAX #endif #include +#include #include #pragma comment( lib, "dwrite.lib" ) #endif -#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS -#include -#include -#endif - -#if EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_BSD -#include -#endif - #if EE_PLATFORM == EE_PLATFORM_ANDROID #if __ANDROID_API__ >= 29 #include @@ -564,9 +561,20 @@ void SystemFontResolver::populateFontList() const { desc.italic = ( dwStyle == DWRITE_FONT_STYLE_ITALIC || dwStyle == DWRITE_FONT_STYLE_OBLIQUE ); - DWRITE_PANOSE panose; - dwriteFont->GetPanose( &panose ); - desc.monospace = ( panose.familyKind == 2 && panose.text.panoseProportion == 9 ); + __if_exists( IDWriteFont1 ) { + IDWriteFont1* dwriteFont1 = nullptr; + HRESULT hr1 = dwriteFont->QueryInterface( + __uuidof( IDWriteFont1 ), + reinterpret_cast( &dwriteFont1 ) ); + if ( SUCCEEDED( hr1 ) && dwriteFont1 ) { + DWRITE_PANOSE panose; + dwriteFont1->GetPanose( &panose ); + desc.monospace = + ( panose.familyKind == 2 && + panose.text.panoseProportion == 9 ); + dwriteFont1->Release(); + } + } mFontList.push_back( desc ); } @@ -690,36 +698,34 @@ void SystemFontResolver::populateFontList() const { if ( fontPath.empty() ) continue; - CFNumberRef weightNum = - (CFNumberRef)CTFontDescriptorCopyAttribute( desc, kCTFontWeightTrait ); + CFDictionaryRef traits = + (CFDictionaryRef)CTFontDescriptorCopyAttribute( desc, kCTFontTraitsAttribute ); CGFloat weightVal = 0.0; - if ( weightNum ) { - CFNumberGetValue( weightNum, kCFNumberCGFloatType, &weightVal ); - CFRelease( weightNum ); - } - - CFNumberRef widthNum = - (CFNumberRef)CTFontDescriptorCopyAttribute( desc, kCTFontWidthTrait ); CGFloat widthVal = 0.0; - if ( widthNum ) { - CFNumberGetValue( widthNum, kCFNumberCGFloatType, &widthVal ); - CFRelease( widthNum ); - } - - CFNumberRef slantNum = - (CFNumberRef)CTFontDescriptorCopyAttribute( desc, kCTFontSlantTrait ); CGFloat slantVal = 0.0; - if ( slantNum ) { - CFNumberGetValue( slantNum, kCFNumberCGFloatType, &slantVal ); - CFRelease( slantNum ); - } - - CFNumberRef monoNum = - (CFNumberRef)CTFontDescriptorCopyAttribute( desc, kCTFontMonoSpaceTrait ); int isMono = 0; - if ( monoNum ) { - CFNumberGetValue( monoNum, kCFNumberIntType, &isMono ); - CFRelease( monoNum ); + if ( traits ) { + CFNumberRef weightNum = + (CFNumberRef)CFDictionaryGetValue( traits, kCTFontWeightTrait ); + if ( weightNum ) + CFNumberGetValue( weightNum, kCFNumberCGFloatType, &weightVal ); + + CFNumberRef widthNum = + (CFNumberRef)CFDictionaryGetValue( traits, kCTFontWidthTrait ); + if ( widthNum ) + CFNumberGetValue( widthNum, kCFNumberCGFloatType, &widthVal ); + + CFNumberRef slantNum = + (CFNumberRef)CFDictionaryGetValue( traits, kCTFontSlantTrait ); + if ( slantNum ) + CFNumberGetValue( slantNum, kCFNumberCGFloatType, &slantVal ); + + CFNumberRef monoNum = + (CFNumberRef)CFDictionaryGetValue( traits, kCTFontMonoSpaceTrait ); + if ( monoNum ) + CFNumberGetValue( monoNum, kCFNumberIntType, &isMono ); + + CFRelease( traits ); } FontDesc fontDesc; @@ -741,91 +747,210 @@ void SystemFontResolver::populateFontList() const { } // ===================================================================== -// Platform: Linux / FreeBSD (Fontconfig) +// Platform: Linux / FreeBSD (Fontconfig — dynamically loaded) // ===================================================================== #elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_BSD +#include + +struct FcLib { + void* handle{ nullptr }; + + using FcChar8 = unsigned char; + using FcBool = int; + using FcResult = int; + + struct FcPattern; + struct FcObjectSet; + struct FcFontSet { + int nfont; + int sfont; + FcPattern** fonts; + }; + + static constexpr int FcResultMatch = 0; + + static constexpr int FC_WEIGHT_THIN = 0; + static constexpr int FC_WEIGHT_EXTRALIGHT = 40; + static constexpr int FC_WEIGHT_LIGHT = 50; + static constexpr int FC_WEIGHT_REGULAR = 80; + static constexpr int FC_WEIGHT_MEDIUM = 100; + static constexpr int FC_WEIGHT_SEMIBOLD = 180; + static constexpr int FC_WEIGHT_BOLD = 200; + static constexpr int FC_WEIGHT_EXTRABOLD = 205; + static constexpr int FC_WEIGHT_BLACK = 210; + + static constexpr int FC_WIDTH_ULTRACONDENSED = 50; + static constexpr int FC_WIDTH_EXTRACONDENSED = 63; + static constexpr int FC_WIDTH_CONDENSED = 75; + static constexpr int FC_WIDTH_SEMICONDENSED = 87; + static constexpr int FC_WIDTH_NORMAL = 100; + static constexpr int FC_WIDTH_SEMIEXPANDED = 113; + static constexpr int FC_WIDTH_EXPANDED = 125; + static constexpr int FC_WIDTH_EXTRAEXPANDED = 150; + + static constexpr int FC_SLANT_ROMAN = 0; + static constexpr int FC_SLANT_ITALIC = 100; + static constexpr int FC_SLANT_OBLIQUE = 110; + + static constexpr int FC_MONO = 100; + + FcBool ( *Init )( void ); + FcPattern* ( *PatternCreate )( void ); + FcObjectSet* ( *ObjectSetCreate )( void ); + FcBool ( *ObjectSetAdd )( FcObjectSet*, const char* ); + FcFontSet* ( *FontList )( void*, FcPattern*, FcObjectSet* ); + void ( *PatternDestroy )( FcPattern* ); + void ( *ObjectSetDestroy )( FcObjectSet* ); + void ( *FontSetDestroy )( FcFontSet* ); + FcResult ( *PatternGetString )( const FcPattern*, const char*, int, FcChar8** ); + FcResult ( *PatternGetInteger )( const FcPattern*, const char*, int, int* ); + + bool load() { + handle = Sys::loadObject( "libfontconfig.so.1" ); + if ( !handle ) + handle = Sys::loadObject( "libfontconfig.so" ); + if ( !handle ) + return false; + + Init = ( decltype( Init ) )Sys::loadFunction( handle, "FcInit" ); + PatternCreate = + ( decltype( PatternCreate ) )Sys::loadFunction( handle, "FcPatternCreate" ); + ObjectSetCreate = + ( decltype( ObjectSetCreate ) )Sys::loadFunction( handle, "FcObjectSetCreate" ); + ObjectSetAdd = + ( decltype( ObjectSetAdd ) )Sys::loadFunction( handle, "FcObjectSetAdd" ); + FontList = ( decltype( FontList ) )Sys::loadFunction( handle, "FcFontList" ); + PatternDestroy = + ( decltype( PatternDestroy ) )Sys::loadFunction( handle, "FcPatternDestroy" ); + ObjectSetDestroy = + ( decltype( ObjectSetDestroy ) )Sys::loadFunction( handle, "FcObjectSetDestroy" ); + FontSetDestroy = + ( decltype( FontSetDestroy ) )Sys::loadFunction( handle, "FcFontSetDestroy" ); + PatternGetString = + ( decltype( PatternGetString ) )Sys::loadFunction( handle, "FcPatternGetString" ); + PatternGetInteger = + ( decltype( PatternGetInteger ) )Sys::loadFunction( handle, "FcPatternGetInteger" ); + + return Init && PatternCreate && ObjectSetCreate && ObjectSetAdd && FontList && + PatternDestroy && ObjectSetDestroy && FontSetDestroy && PatternGetString && + PatternGetInteger; + + } + + void unload() { + if ( handle ) { + Sys::unloadObject( handle ); + handle = nullptr; + } + } +}; + +#define FC_W( v ) FcLib::FC_WEIGHT_##v +#define FC_S( v ) FcLib::FC_SLANT_##v +#define FC_WI( v ) FcLib::FC_WIDTH_##v + static FontWeight fcWeightToFontWeight( int fcWeight ) { - if ( fcWeight <= FC_WEIGHT_THIN ) + if ( fcWeight <= FC_W( THIN ) ) return FontWeight::Thin; - if ( fcWeight <= FC_WEIGHT_EXTRALIGHT ) + if ( fcWeight <= FC_W( EXTRALIGHT ) ) return FontWeight::ExtraLight; - if ( fcWeight <= FC_WEIGHT_LIGHT ) + if ( fcWeight <= FC_W( LIGHT ) ) return FontWeight::Light; - if ( fcWeight <= FC_WEIGHT_REGULAR ) + if ( fcWeight <= FC_W( REGULAR ) ) return FontWeight::Normal; - if ( fcWeight <= FC_WEIGHT_MEDIUM ) + if ( fcWeight <= FC_W( MEDIUM ) ) return FontWeight::Medium; - if ( fcWeight <= FC_WEIGHT_SEMIBOLD ) + if ( fcWeight <= FC_W( SEMIBOLD ) ) return FontWeight::SemiBold; - if ( fcWeight <= FC_WEIGHT_BOLD ) + if ( fcWeight <= FC_W( BOLD ) ) return FontWeight::Bold; - if ( fcWeight <= FC_WEIGHT_EXTRABOLD ) + if ( fcWeight <= FC_W( EXTRABOLD ) ) return FontWeight::ExtraBold; return FontWeight::Black; } static FontStretch fcWidthToFontStretch( int fcWidth ) { - if ( fcWidth <= FC_WIDTH_ULTRACONDENSED ) + if ( fcWidth <= FC_WI( ULTRACONDENSED ) ) return FontStretch::UltraCondensed; - if ( fcWidth <= FC_WIDTH_EXTRACONDENSED ) + if ( fcWidth <= FC_WI( EXTRACONDENSED ) ) return FontStretch::ExtraCondensed; - if ( fcWidth <= FC_WIDTH_CONDENSED ) + if ( fcWidth <= FC_WI( CONDENSED ) ) return FontStretch::Condensed; - if ( fcWidth <= FC_WIDTH_SEMICONDENSED ) + if ( fcWidth <= FC_WI( SEMICONDENSED ) ) return FontStretch::SemiCondensed; - if ( fcWidth <= FC_WIDTH_NORMAL ) + if ( fcWidth <= FC_WI( NORMAL ) ) return FontStretch::Normal; - if ( fcWidth <= FC_WIDTH_SEMIEXPANDED ) + if ( fcWidth <= FC_WI( SEMIEXPANDED ) ) return FontStretch::SemiExpanded; - if ( fcWidth <= FC_WIDTH_EXPANDED ) + if ( fcWidth <= FC_WI( EXPANDED ) ) return FontStretch::Expanded; - if ( fcWidth <= FC_WIDTH_EXTRAEXPANDED ) + if ( fcWidth <= FC_WI( EXTRAEXPANDED ) ) return FontStretch::ExtraExpanded; return FontStretch::UltraExpanded; } + + void SystemFontResolver::populateFontList() const { - if ( !FcInit() ) + FcLib fc; + if ( !fc.load() ) { + populateFontListXml(); return; - - FcPattern* pattern = FcPatternCreate(); - FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_FILE, FC_INDEX, FC_WEIGHT, FC_WIDTH, FC_SLANT, - FC_SPACING, nullptr ); - - FcFontSet* fontSet = FcFontList( nullptr, pattern, os ); - - FcPatternDestroy( pattern ); - FcObjectSetDestroy( os ); - - if ( !fontSet ) + } + if ( !fc.Init() ) { + fc.unload(); + populateFontListXml(); return; + } + + FcLib::FcPattern* pattern = fc.PatternCreate(); + FcLib::FcObjectSet* os = fc.ObjectSetCreate(); + fc.ObjectSetAdd( os, "family" ); + fc.ObjectSetAdd( os, "file" ); + fc.ObjectSetAdd( os, "index" ); + fc.ObjectSetAdd( os, "weight" ); + fc.ObjectSetAdd( os, "width" ); + fc.ObjectSetAdd( os, "slant" ); + fc.ObjectSetAdd( os, "spacing" ); + + FcLib::FcFontSet* fontSet = fc.FontList( nullptr, pattern, os ); + + fc.PatternDestroy( pattern ); + fc.ObjectSetDestroy( os ); + + if ( !fontSet ) { + fc.unload(); + populateFontListXml(); + return; + } for ( int i = 0; i < fontSet->nfont; ++i ) { - FcPattern* font = fontSet->fonts[i]; + FcLib::FcPattern* font = fontSet->fonts[i]; - FcChar8* family = nullptr; - if ( FcPatternGetString( font, FC_FAMILY, 0, &family ) != FcResultMatch || !family ) + FcLib::FcChar8* family = nullptr; + if ( fc.PatternGetString( font, "family", 0, &family ) != FcLib::FcResultMatch || + !family ) continue; - FcChar8* file = nullptr; - if ( FcPatternGetString( font, FC_FILE, 0, &file ) != FcResultMatch || !file ) + FcLib::FcChar8* file = nullptr; + if ( fc.PatternGetString( font, "file", 0, &file ) != FcLib::FcResultMatch || !file ) continue; int fcIndex = 0; - FcPatternGetInteger( font, FC_INDEX, 0, &fcIndex ); + fc.PatternGetInteger( font, "index", 0, &fcIndex ); - int fcWeight = FC_WEIGHT_REGULAR; - FcPatternGetInteger( font, FC_WEIGHT, 0, &fcWeight ); + int fcWeight = FC_W( REGULAR ); + fc.PatternGetInteger( font, "weight", 0, &fcWeight ); - int fcWidth = FC_WIDTH_NORMAL; - FcPatternGetInteger( font, FC_WIDTH, 0, &fcWidth ); + int fcWidth = FC_WI( NORMAL ); + fc.PatternGetInteger( font, "width", 0, &fcWidth ); - int fcSlant = FC_SLANT_ROMAN; - FcPatternGetInteger( font, FC_SLANT, 0, &fcSlant ); + int fcSlant = FC_S( ROMAN ); + fc.PatternGetInteger( font, "slant", 0, &fcSlant ); - int fcSpacing = FC_MONO; - FcPatternGetInteger( font, FC_SPACING, 0, &fcSpacing ); + int fcSpacing = FcLib::FC_MONO; + fc.PatternGetInteger( font, "spacing", 0, &fcSpacing ); FontDesc desc; desc.family = reinterpret_cast( family ); @@ -833,13 +958,14 @@ void SystemFontResolver::populateFontList() const { desc.faceIndex = fcIndex >= 0 ? static_cast( fcIndex ) : 0; desc.weight = fcWeightToFontWeight( fcWeight ); desc.stretch = fcWidthToFontStretch( fcWidth ); - desc.italic = ( fcSlant == FC_SLANT_ITALIC || fcSlant == FC_SLANT_OBLIQUE ); - desc.monospace = ( fcSpacing == FC_MONO ); + desc.italic = ( fcSlant == FC_S( ITALIC ) || fcSlant == FC_S( OBLIQUE ) ); + desc.monospace = ( fcSpacing == FcLib::FC_MONO ); mFontList.push_back( desc ); } - FcFontSetDestroy( fontSet ); + fc.FontSetDestroy( fontSet ); + fc.unload(); } // ===================================================================== @@ -864,7 +990,6 @@ void SystemFontResolver::populateFontList() const { goto xml_fallback; xml_fallback: - // XML fallback below populateFontListXml(); } @@ -876,64 +1001,6 @@ void SystemFontResolver::populateFontList() const { #endif -void SystemFontResolver::populateFontListXml() const { - static const char* fontPaths[] = { "/system/etc/fonts.xml", "/system/fonts/fonts.xml", - "/vendor/etc/fonts.xml", nullptr }; - - const char* xmlPath = nullptr; - for ( int i = 0; fontPaths[i]; ++i ) { - if ( FileSystem::fileExists( fontPaths[i] ) ) { - xmlPath = fontPaths[i]; - break; - } - } - - if ( !xmlPath ) - return; - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file( xmlPath ); - if ( !result ) - return; - - pugi::xml_node families = doc.child( "familyset" ); - if ( !families ) - return; - - for ( pugi::xml_node familyNode : families.children( "family" ) ) { - std::string familyName = familyNode.attribute( "name" ).as_string(); - if ( familyName.empty() ) - continue; - - for ( pugi::xml_node fontNode : familyNode.children( "font" ) ) { - std::string fontFile = fontNode.text().as_string(); - if ( fontFile.empty() ) - continue; - - std::string fontPath = "/system/fonts/" + fontFile; - if ( !FileSystem::fileExists( fontPath ) ) - fontPath = fontFile; - - std::string weightStr = fontNode.attribute( "weight" ).as_string(); - FontWeight weight = FontWeight::Normal; - if ( !weightStr.empty() ) - weight = static_cast( std::atoi( weightStr.c_str() ) ); - - std::string styleStr = fontNode.attribute( "style" ).as_string(); - bool italic = ( styleStr == "italic" ); - - FontDesc desc; - desc.family = familyName; - desc.path = fontPath; - desc.faceIndex = 0; - desc.weight = weight; - desc.italic = italic; - - mFontList.push_back( desc ); - } - } -} - // ===================================================================== // Platform: Haiku (BFont) // ===================================================================== @@ -1003,4 +1070,91 @@ void SystemFontResolver::populateFontList() const {} #endif +void SystemFontResolver::populateFontListXml() const { +#if EE_PLATFORM == EE_PLATFORM_ANDROID + static const char* fontPaths[] = { "/system/etc/fonts.xml", "/system/fonts/fonts.xml", + "/vendor/etc/fonts.xml", nullptr }; + + const char* xmlPath = nullptr; + for ( int i = 0; fontPaths[i]; ++i ) { + if ( FileSystem::fileExists( fontPaths[i] ) ) { + xmlPath = fontPaths[i]; + break; + } + } + + if ( !xmlPath ) + return; + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file( xmlPath ); + if ( !result ) + return; + + pugi::xml_node families = doc.child( "familyset" ); + if ( !families ) + return; + + for ( pugi::xml_node familyNode : families.children( "family" ) ) { + std::string familyName = familyNode.attribute( "name" ).as_string(); + if ( familyName.empty() ) + continue; + + for ( pugi::xml_node fontNode : familyNode.children( "font" ) ) { + std::string fontFile = fontNode.text().as_string(); + if ( fontFile.empty() ) + continue; + + std::string fontPath = "/system/fonts/" + fontFile; + if ( !FileSystem::fileExists( fontPath ) ) + fontPath = fontFile; + + std::string weightStr = fontNode.attribute( "weight" ).as_string(); + FontWeight weight = FontWeight::Normal; + if ( !weightStr.empty() ) + weight = static_cast( std::atoi( weightStr.c_str() ) ); + + std::string styleStr = fontNode.attribute( "style" ).as_string(); + bool italic = ( styleStr == "italic" ); + + FontDesc desc; + desc.family = familyName; + desc.path = fontPath; + desc.faceIndex = 0; + desc.weight = weight; + desc.italic = italic; + + mFontList.push_back( desc ); + } + } +#else + static const char* fontDirs[] = { + "/usr/share/fonts", + "/usr/local/share/fonts", + nullptr + }; + + for ( int d = 0; fontDirs[d]; ++d ) { + if ( !FileSystem::isDirectory( std::string( fontDirs[d] ) ) ) + continue; + + auto files = FileSystem::filesGetInPath( std::string( fontDirs[d] ), false, true ); + for ( const auto& file : files ) { + std::string ext = FileSystem::fileExtension( file ); + if ( ext != "ttf" && ext != "otf" && ext != "ttc" && ext != "otc" ) + continue; + + std::string path = std::string( fontDirs[d] ) + FileSystem::getOSSlash() + file; + + FontDesc desc; + desc.family = FileSystem::fileRemoveExtension( file ); + desc.path = path; + desc.faceIndex = 0; + desc.weight = FontWeight::Normal; + mFontList.push_back( desc ); + } + } +#endif +} + }} // namespace EE::Graphics