From 512f2a2258601c215581385fe463d71aaa027f86 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 15 Jun 2024 21:11:17 +0100 Subject: [PATCH] Add: Native Win32 font search implementation. --- src/os/windows/font_win32.cpp | 108 ++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index b2eac91314..77d925a277 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -40,9 +40,8 @@ struct EFCParam { bool Add(const std::wstring_view &font) { - for (const auto &entry : this->fonts) { - if (font.compare(entry) == 0) return false; - } + if (font.starts_with('@')) return false; + if (std::find(std::begin(this->fonts), std::end(this->fonts), font) != std::end(this->fonts)) return false; this->fonts.emplace_back(font); @@ -99,7 +98,6 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &, int winla return ret == 0; } - /** * Create a new Win32FontCache. * @param fs The font size that is going to be cached. @@ -380,3 +378,105 @@ void LoadWin32Font(FontSize fs) LoadWin32Font(fs, logfont, settings->size, font_name); } + +/** + * Win32 implementation of FontSearcher. + */ +class Win32FontSearcher : public FontSearcher { +public: + std::vector ListFamilies(const std::string &language_isocode, int winlangid) override; + std::vector ListStyles(const std::string &language_isocode, int winlangid, std::string_view font_family) override; +}; + +/** + * State passed between EnumFontFamiliesEx and our list families callback. + */ +struct EFCListFamiliesParam : EFCParam { + std::vector families; ///< List of families found. +}; + +static int CALLBACK ListFamiliesFontCallback(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam) +{ + EFCListFamiliesParam &info = *reinterpret_cast(lParam); + + /* Only use TrueType fonts */ + if (!(type & TRUETYPE_FONTTYPE)) return 1; + /* Skip duplicates */ + if (!info.Add(lpelfe->elfFullName)) return 1; + /* Don't use SYMBOL fonts */ + if (lpelfe->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1; + /* The font has to have at least one of the supported locales to be usable. */ + if ((metric->ntmFontSig.fsCsb[0] & info.locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info.locale.lsCsbSupported[1]) == 0) return 1; + + LOGFONT &lf = lpelfe->elfLogFont; + info.families.emplace_back(FS2OTTD(lf.lfFaceName)); + + return 1; +} + +std::vector Win32FontSearcher::ListFamilies(const std::string &, int winlangid) +{ + EFCListFamiliesParam info; + if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, reinterpret_cast(&info.locale), sizeof(info.locale) / sizeof(wchar_t)) == 0) { + /* Invalid langid or some other mysterious error, can't determine fallback font. */ + Debug(fontcache, 1, "Can't get locale info for fallback font (langid=0x{:x})", winlangid); + return info.families; + } + + LOGFONT lf{}; + lf.lfCharSet = DEFAULT_CHARSET; + + HDC dc = GetDC(nullptr); + EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)&ListFamiliesFontCallback, reinterpret_cast(&info), 0); + ReleaseDC(nullptr, dc); + + return info.families; +} + +/** + * State passed between EnumFontFamiliesEx and our list styles callback. + */ +struct EFCListStylesParam : EFCParam { + std::vector styles; ///< List of styles for the family. +}; + +static int CALLBACK ListStylesFontCallback(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam) +{ + EFCListStylesParam &info = *reinterpret_cast(lParam); + + /* Only use TrueType fonts */ + if (!(type & TRUETYPE_FONTTYPE)) return 1; + /* Skip duplicates */ + if (!info.Add(lpelfe->elfFullName)) return 1; + /* Don't use SYMBOL fonts */ + if (lpelfe->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1; + /* The font has to have at least one of the supported locales to be usable. */ + if ((metric->ntmFontSig.fsCsb[0] & info.locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info.locale.lsCsbSupported[1]) == 0) return 1; + + LOGFONT &lf = lpelfe->elfLogFont; + info.styles.emplace_back(FS2OTTD(lf.lfFaceName), FS2OTTD(lpelfe->elfStyle), lf.lfItalic, lf.lfWeight); + + return 1; +} + +std::vector Win32FontSearcher::ListStyles(const std::string &, int winlangid, std::string_view font_family) +{ + EFCListStylesParam info; + if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, reinterpret_cast(&info.locale), sizeof(info.locale) / sizeof(wchar_t)) == 0) { + /* Invalid langid or some other mysterious error, can't determine fallback font. */ + Debug(fontcache, 1, "Can't get locale info for fallback font (langid=0x{:x})", winlangid); + return info.styles; + } + + LOGFONT lf{}; + lf.lfCharSet = DEFAULT_CHARSET; + convert_to_fs(font_family, lf.lfFaceName, std::size(lf.lfFaceName)); + + HDC dc = GetDC(nullptr); + EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)&ListStylesFontCallback, reinterpret_cast(&info), 0); + ReleaseDC(nullptr, dc); + + return info.styles; +} + +static Win32FontSearcher _win32fs_instance;