From ddb128f10d0cda2be9173185a4c9253e09ba9b97 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Tue, 10 Jun 2025 11:26:05 +0100 Subject: [PATCH] Codechange: Use ProviderManager interface to register FontCache factories. --- src/fontcache.cpp | 36 ++--- src/fontcache.h | 28 ++++ src/fontcache/freetypefontcache.cpp | 202 ++++++++++++++-------------- src/fontcache/spritefontcache.cpp | 14 ++ src/os/macosx/font_osx.cpp | 156 +++++++++++---------- src/os/windows/font_win32.cpp | 178 ++++++++++++------------ 6 files changed, 337 insertions(+), 277 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index c93440801d..6608abf156 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -12,7 +12,6 @@ #include "fontdetection.h" #include "blitter/factory.hpp" #include "gfx_layout.h" -#include "fontcache/spritefontcache.h" #include "openttd.h" #include "settings_func.h" #include "strings_func.h" @@ -28,6 +27,18 @@ static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8}; FontCacheSettings _fcsettings; +/** + * Try loading a font with any fontcache factory. + * @param fs Font size to load. + * @param fonttype Font type requested. + */ +/* static */ void FontProviderManager::LoadFont(FontSize fs, FontType fonttype) +{ + for (auto &provider : FontProviderManager::GetProviders()) { + provider->LoadFont(fs, fonttype); + } +} + /** * Create a new font cache. * @param fs The size of the font. @@ -85,7 +96,7 @@ int GetCharacterHeight(FontSize size) /* static */ void FontCache::InitializeFontCaches() { for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) { - if (FontCache::caches[fs] == nullptr) new SpriteFontCache(fs); /* FontCache inserts itself into to the cache. */ + if (FontCache::caches[fs] == nullptr) FontProviderManager::LoadFont(fs, FontType::Sprite); /* FontCache inserts itself into to the cache. */ } } @@ -136,15 +147,6 @@ void SetFont(FontSize fontsize, const std::string &font, uint size) if (_save_config) SaveToConfig(); } -#ifdef WITH_FREETYPE -extern void LoadFreeTypeFont(FontSize fs); -extern void UninitFreeType(); -#elif defined(_WIN32) -extern void LoadWin32Font(FontSize fs); -#elif defined(WITH_COCOA) -extern void LoadCoreTextFont(FontSize fs); -#endif - /** * Test if a font setting uses the default font. * @return true iff the font is not configured and no fallback font data is present. @@ -223,13 +225,7 @@ void InitFontCache(FontSizes fontsizes) FontCache *fc = FontCache::Get(fs); if (fc->HasParent()) delete fc; -#ifdef WITH_FREETYPE - LoadFreeTypeFont(fs); -#elif defined(_WIN32) - LoadWin32Font(fs); -#elif defined(WITH_COCOA) - LoadCoreTextFont(fs); -#endif + FontProviderManager::LoadFont(fs, FontType::TrueType); } } @@ -242,10 +238,6 @@ void UninitFontCache() FontCache *fc = FontCache::Get(fs); if (fc->HasParent()) delete fc; } - -#ifdef WITH_FREETYPE - UninitFreeType(); -#endif /* WITH_FREETYPE */ } #if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) diff --git a/src/fontcache.h b/src/fontcache.h index c5a5ff873b..374f2f8766 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -10,6 +10,7 @@ #ifndef FONTCACHE_H #define FONTCACHE_H +#include "provider_manager.h" #include "string_type.h" #include "spritecache.h" @@ -208,6 +209,33 @@ void UninitFontCache(); bool GetFontAAState(); void SetFont(FontSize fontsize, const std::string &font, uint size); +/** Different types of font that can be loaded. */ +enum class FontType : uint8_t { + Sprite, ///< Bitmap sprites from GRF files. + TrueType, ///< Scalable TrueType fonts. +}; + +/** Factory for FontCaches. */ +class FontCacheFactory : public BaseProvider { +public: + FontCacheFactory(std::string_view name, std::string_view description) : BaseProvider(name, description) + { + ProviderManager::Register(*this); + } + + virtual ~FontCacheFactory() + { + ProviderManager::Unregister(*this); + } + + virtual void LoadFont(FontSize fs, FontType fonttype) = 0; +}; + +class FontProviderManager : ProviderManager { +public: + static void LoadFont(FontSize fs, FontType fonttype); +}; + /* Implemented in spritefontcache.cpp */ void InitializeUnicodeGlyphMap(); void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite); diff --git a/src/fontcache/freetypefontcache.cpp b/src/fontcache/freetypefontcache.cpp index 80bb2cda6d..23d622aa92 100644 --- a/src/fontcache/freetypefontcache.cpp +++ b/src/fontcache/freetypefontcache.cpp @@ -46,9 +46,6 @@ public: const void *GetOSHandle() override { return &face; } }; -FT_Library _ft_library = nullptr; - - /** * Create a new FreeTypeFontCache. * @param fs The font size that is going to be cached. @@ -113,93 +110,6 @@ void FreeTypeFontCache::SetFontSize(int pixels) } } -static FT_Error LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size) -{ - Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name); - - /* Attempt to select the unicode character map */ - FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode); - if (error == FT_Err_Ok) goto found_face; // Success - - if (error == FT_Err_Invalid_CharMap_Handle) { - /* Try to pick a different character map instead. We default to - * the first map, but platform_id 0 encoding_id 0 should also - * be unicode (strange system...) */ - FT_CharMap found = face->charmaps[0]; - int i; - - for (i = 0; i < face->num_charmaps; i++) { - FT_CharMap charmap = face->charmaps[i]; - if (charmap->platform_id == 0 && charmap->encoding_id == 0) { - found = charmap; - } - } - - if (found != nullptr) { - error = FT_Set_Charmap(face, found); - if (error == FT_Err_Ok) goto found_face; - } - } - - FT_Done_Face(face); - return error; - -found_face: - new FreeTypeFontCache(fs, face, size); - return FT_Err_Ok; -} - -/** - * Loads the freetype font. - * First type to load the fontname as if it were a path. If that fails, - * try to resolve the filename of the font using fontconfig, where the - * format is 'font family name' or 'font family name, font style'. - * @param fs The font size to load. - */ -void LoadFreeTypeFont(FontSize fs) -{ - FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); - - std::string font = GetFontCacheFontName(fs); - if (font.empty()) return; - - if (_ft_library == nullptr) { - if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) { - ShowInfo("Unable to initialize FreeType, using sprite fonts instead"); - return; - } - - Debug(fontcache, 2, "Initialized"); - } - - FT_Face face = nullptr; - - /* If font is an absolute path to a ttf, try loading that first. */ - int32_t index = 0; - if (settings->os_handle != nullptr) index = *static_cast(settings->os_handle); - FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face); - - if (error != FT_Err_Ok) { - /* Check if font is a relative filename in one of our search-paths. */ - std::string full_font = FioFindFullPath(BASE_DIR, font); - if (!full_font.empty()) { - error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face); - } - } - - /* Try loading based on font face name (OS-wide fonts). */ - if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face); - - if (error == FT_Err_Ok) { - error = LoadFont(fs, face, font, GetFontCacheFontSize(fs)); - if (error != FT_Err_Ok) { - ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font, FontSizeToName(fs), error); - } - } else { - FT_Done_Face(face); - } -} - /** * Free everything that was allocated for this font cache. */ @@ -296,14 +206,110 @@ GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback) return glyph; } -/** - * Free everything allocated w.r.t. freetype. - */ -void UninitFreeType() -{ - FT_Done_FreeType(_ft_library); - _ft_library = nullptr; -} +FT_Library _ft_library = nullptr; + +class FreeTypeFontCacheFactory : public FontCacheFactory { +public: + FreeTypeFontCacheFactory() : FontCacheFactory("freetype", "FreeType font provider") {} + + virtual ~FreeTypeFontCacheFactory() + { + FT_Done_FreeType(_ft_library); + _ft_library = nullptr; + } + + /** + * Loads the freetype font. + * First type to load the fontname as if it were a path. If that fails, + * try to resolve the filename of the font using fontconfig, where the + * format is 'font family name' or 'font family name, font style'. + * @param fs The font size to load. + */ + void LoadFont(FontSize fs, FontType fonttype) override + { + if (fonttype != FontType::TrueType) return; + + FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); + + std::string font = GetFontCacheFontName(fs); + if (font.empty()) return; + + if (_ft_library == nullptr) { + if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) { + ShowInfo("Unable to initialize FreeType, using sprite fonts instead"); + return; + } + + Debug(fontcache, 2, "Initialized"); + } + + FT_Face face = nullptr; + + /* If font is an absolute path to a ttf, try loading that first. */ + int32_t index = 0; + if (settings->os_handle != nullptr) index = *static_cast(settings->os_handle); + FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face); + + if (error != FT_Err_Ok) { + /* Check if font is a relative filename in one of our search-paths. */ + std::string full_font = FioFindFullPath(BASE_DIR, font); + if (!full_font.empty()) { + error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face); + } + } + + /* Try loading based on font face name (OS-wide fonts). */ + if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face); + + if (error == FT_Err_Ok) { + error = LoadFont(fs, face, font, GetFontCacheFontSize(fs)); + if (error != FT_Err_Ok) { + ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font, FontSizeToName(fs), error); + } + } else { + FT_Done_Face(face); + } + } + +private: + static FT_Error LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size) + { + Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name); + + /* Attempt to select the unicode character map */ + FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode); + if (error == FT_Err_Ok) goto found_face; // Success + + if (error == FT_Err_Invalid_CharMap_Handle) { + /* Try to pick a different character map instead. We default to + * the first map, but platform_id 0 encoding_id 0 should also + * be unicode (strange system...) */ + FT_CharMap found = face->charmaps[0]; + int i; + + for (i = 0; i < face->num_charmaps; i++) { + FT_CharMap charmap = face->charmaps[i]; + if (charmap->platform_id == 0 && charmap->encoding_id == 0) { + found = charmap; + } + } + + if (found != nullptr) { + error = FT_Set_Charmap(face, found); + if (error == FT_Err_Ok) goto found_face; + } + } + + FT_Done_Face(face); + return error; + + found_face: + new FreeTypeFontCache(fs, face, size); + return FT_Err_Ok; + } +}; + +static FreeTypeFontCacheFactory s_freetype_fontcache_factory; #if !defined(WITH_FONTCONFIG) diff --git a/src/fontcache/spritefontcache.cpp b/src/fontcache/spritefontcache.cpp index 2d46d0f106..eab13ae388 100644 --- a/src/fontcache/spritefontcache.cpp +++ b/src/fontcache/spritefontcache.cpp @@ -91,6 +91,20 @@ bool SpriteFontCache::GetDrawGlyphShadow() return false; } +class SpriteFontCacheFactory : public FontCacheFactory { +public: + SpriteFontCacheFactory() : FontCacheFactory("sprite", "Sprite font provider") {} + + void LoadFont(FontSize fs, FontType fonttype) override + { + if (fonttype != FontType::Sprite) return; + + new SpriteFontCache(fs); + } +}; + +static SpriteFontCacheFactory s_sprite_fontcache_factory; + /** * Set the SpriteID for a unicode character. * @param fs Font size to set. diff --git a/src/os/macosx/font_osx.cpp b/src/os/macosx/font_osx.cpp index 6588eb7507..5815d5c197 100644 --- a/src/os/macosx/font_osx.cpp +++ b/src/os/macosx/font_osx.cpp @@ -287,88 +287,98 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa) return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite(); } -static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name) -{ - if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr; +class CoreTextFontCacheFactory : public FontCacheFactory { +public: + CoreTextFontCacheFactory() : FontCacheFactory("coretext", "CoreText font loader") {} - /* Might be a font file name, try load it. Direct font loading is - * only supported starting on OSX 10.6. */ - CFAutoRelease path; + /** + * Loads the TrueType font. + * If a CoreText font description is present, e.g. from the automatic font + * fallback search, use it. Otherwise, try to resolve it by font name. + * @param fs The font size to load. + */ + void LoadFont(FontSize fs, FontType fonttype) + { + if (fonttype != FontType::TrueType) return; - /* See if this is an absolute path. */ - if (FileExists(font_name)) { - path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8)); - } else { - /* Scan the search-paths to see if it can be found. */ - std::string full_font = FioFindFullPath(BASE_DIR, font_name); - if (!full_font.empty()) { - path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8)); + FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); + + std::string font = GetFontCacheFontName(fs); + if (font.empty()) return; + + CFAutoRelease font_ref; + + if (settings->os_handle != nullptr) { + font_ref.reset(static_cast(const_cast(settings->os_handle))); + CFRetain(font_ref.get()); // Increase ref count to match a later release. } - } - if (path) { - /* Try getting a font descriptor to see if the system can use it. */ - CFAutoRelease url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); - CFAutoRelease descs(CTFontManagerCreateFontDescriptorsFromURL(url.get())); - - if (descs && CFArrayGetCount(descs.get()) > 0) { - CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0); - CFRetain(font_ref); - return font_ref; + if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) { + /* Might be a font file name, try load it. */ + font_ref.reset(LoadFontFromFile(font)); + if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs)); } - } - return nullptr; -} + if (!font_ref) { + CFAutoRelease name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8)); -/** - * Loads the TrueType font. - * If a CoreText font description is present, e.g. from the automatic font - * fallback search, use it. Otherwise, try to resolve it by font name. - * @param fs The font size to load. - */ -void LoadCoreTextFont(FontSize fs) -{ - FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); + /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return + * something, no matter the name. As such, we can't use it to check for existence. + * We instead query the list of all font descriptors that match the given name which + * does not do this stupid name fallback. */ + CFAutoRelease name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0)); + CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks)); + CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get())); - std::string font = GetFontCacheFontName(fs); - if (font.empty()) return; - - CFAutoRelease font_ref; - - if (settings->os_handle != nullptr) { - font_ref.reset(static_cast(const_cast(settings->os_handle))); - CFRetain(font_ref.get()); // Increase ref count to match a later release. - } - - if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) { - /* Might be a font file name, try load it. */ - font_ref.reset(LoadFontFromFile(font)); - if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs)); - } - - if (!font_ref) { - CFAutoRelease name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8)); - - /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return - * something, no matter the name. As such, we can't use it to check for existence. - * We instead query the list of all font descriptors that match the given name which - * does not do this stupid name fallback. */ - CFAutoRelease name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0)); - CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks)); - CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get())); - - /* Assume the first result is the one we want. */ - if (descs && CFArrayGetCount(descs.get()) > 0) { - font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0)); - CFRetain(font_ref.get()); + /* Assume the first result is the one we want. */ + if (descs && CFArrayGetCount(descs.get()) > 0) { + font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0)); + CFRetain(font_ref.get()); + } } + + if (!font_ref) { + ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs)); + return; + } + + new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs)); } - if (!font_ref) { - ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs)); - return; - } +private: + static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name) + { + if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr; - new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs)); -} + /* Might be a font file name, try load it. Direct font loading is + * only supported starting on OSX 10.6. */ + CFAutoRelease path; + + /* See if this is an absolute path. */ + if (FileExists(font_name)) { + path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8)); + } else { + /* Scan the search-paths to see if it can be found. */ + std::string full_font = FioFindFullPath(BASE_DIR, font_name); + if (!full_font.empty()) { + path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8)); + } + } + + if (path) { + /* Try getting a font descriptor to see if the system can use it. */ + CFAutoRelease url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); + CFAutoRelease descs(CTFontManagerCreateFontDescriptorsFromURL(url.get())); + + if (descs && CFArrayGetCount(descs.get()) > 0) { + CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0); + CFRetain(font_ref); + return font_ref; + } + } + + return nullptr; + } +}; + +static CoreTextFontCacheFactory s_coretext_fontcache_Factory; diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 6358ca4d3d..df4dcdbcd3 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -293,99 +293,109 @@ void Win32FontCache::ClearFontCache() return allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END ? this->parent->MapCharToGlyph(key) : 0; } +class Win32FontCacheFactory : FontCacheFactory { +public: + Win32FontCacheFactory() : FontCacheFactory("win32", "Win32 font loader") {} -static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont) -{ - wchar_t fontPath[MAX_PATH] = {}; + /** + * Loads the GDI font. + * If a GDI font description is present, e.g. from the automatic font + * fallback search, use it. Otherwise, try to resolve it by font name. + * @param fs The font size to load. + */ + void LoadFont(FontSize fs, FontType fonttype) override + { + if (fonttype != FontType::TrueType) return; - /* See if this is an absolute path. */ - if (FileExists(font_name)) { - convert_to_fs(font_name, fontPath); - } else { - /* Scan the search-paths to see if it can be found. */ - std::string full_font = FioFindFullPath(BASE_DIR, font_name); - if (!full_font.empty()) { - convert_to_fs(font_name, fontPath); + FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); + + std::string font = GetFontCacheFontName(fs); + if (font.empty()) return; + + LOGFONT logfont{}; + logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH; + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + + if (settings->os_handle != nullptr) { + logfont = *(const LOGFONT *)settings->os_handle; + } else if (font.find('.') != std::string::npos) { + /* Might be a font file name, try load it. */ + if (!TryLoadFontFromFile(font, logfont)) { + ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs)); + } } + + if (logfont.lfFaceName[0] == 0) { + logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. + convert_to_fs(font, logfont.lfFaceName); + } + + LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font); } - if (fontPath[0] != 0) { - if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { - /* Try a nice little undocumented function first for getting the internal font name. - * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ - static LibraryLoader _gdi32("gdi32.dll"); - typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); - static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW"); +private: + static void LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name) + { + HFONT font = CreateFontIndirect(&logfont); + if (font == nullptr) { + ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError()); + return; + } + DeleteObject(font); - if (GetFontResourceInfo != nullptr) { - /* Try to query an array of LOGFONTs that describe the file. */ - DWORD len = 0; - if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { - LOGFONT *buf = (LOGFONT *)new uint8_t[len]; - if (GetFontResourceInfo(fontPath, &len, buf, 2)) { - logfont = *buf; // Just use first entry. + new Win32FontCache(fs, logfont, size); + } + + static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont) + { + wchar_t fontPath[MAX_PATH] = {}; + + /* See if this is an absolute path. */ + if (FileExists(font_name)) { + convert_to_fs(font_name, fontPath); + } else { + /* Scan the search-paths to see if it can be found. */ + std::string full_font = FioFindFullPath(BASE_DIR, font_name); + if (!full_font.empty()) { + convert_to_fs(font_name, fontPath); + } + } + + if (fontPath[0] != 0) { + if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { + /* Try a nice little undocumented function first for getting the internal font name. + * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ + static LibraryLoader _gdi32("gdi32.dll"); + typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); + static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW"); + + if (GetFontResourceInfo != nullptr) { + /* Try to query an array of LOGFONTs that describe the file. */ + DWORD len = 0; + if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { + LOGFONT *buf = (LOGFONT *)new uint8_t[len]; + if (GetFontResourceInfo(fontPath, &len, buf, 2)) { + logfont = *buf; // Just use first entry. + } + delete[](uint8_t *)buf; } - delete[](uint8_t *)buf; + } + + /* No dice yet. Use the file name as the font face name, hoping it matches. */ + if (logfont.lfFaceName[0] == 0) { + wchar_t fname[_MAX_FNAME]; + _wsplitpath(fontPath, nullptr, nullptr, fname, nullptr); + + wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE); + logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. } } - - /* No dice yet. Use the file name as the font face name, hoping it matches. */ - if (logfont.lfFaceName[0] == 0) { - wchar_t fname[_MAX_FNAME]; - _wsplitpath(fontPath, nullptr, nullptr, fname, nullptr); - - wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE); - logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - } } + + return logfont.lfFaceName[0] != 0; } +}; - return logfont.lfFaceName[0] != 0; -} - -static void LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name) -{ - HFONT font = CreateFontIndirect(&logfont); - if (font == nullptr) { - ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError()); - return; - } - DeleteObject(font); - - new Win32FontCache(fs, logfont, size); -} -/** - * Loads the GDI font. - * If a GDI font description is present, e.g. from the automatic font - * fallback search, use it. Otherwise, try to resolve it by font name. - * @param fs The font size to load. - */ -void LoadWin32Font(FontSize fs) -{ - FontCacheSubSetting *settings = GetFontCacheSubSetting(fs); - - std::string font = GetFontCacheFontName(fs); - if (font.empty()) return; - - LOGFONT logfont{}; - logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH; - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - - if (settings->os_handle != nullptr) { - logfont = *(const LOGFONT *)settings->os_handle; - } else if (font.find('.') != std::string::npos) { - /* Might be a font file name, try load it. */ - if (!TryLoadFontFromFile(font, logfont)) { - ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs)); - } - } - - if (logfont.lfFaceName[0] == 0) { - logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - convert_to_fs(font, logfont.lfFaceName); - } - - LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font); -} +static Win32FontCacheFactory s_win32_fontcache_factory;