From 233061aaa2517b185ceee2832f637036ea0e1c3b Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 15 Jun 2024 21:11:18 +0100 Subject: [PATCH] Change: WIP Change interface to list all families and styles at once. --- src/fontcache.cpp | 36 +++++++++++++++ src/fontdetection.h | 16 ++++++- src/os/macosx/font_osx.cpp | 43 +++++------------- src/os/unix/font_unix.cpp | 66 ++++++--------------------- src/os/windows/font_win32.cpp | 86 +++++++++++------------------------ 5 files changed, 101 insertions(+), 146 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 88a7fdfb9f..93b4e69f04 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -255,6 +255,42 @@ FontSearcher::~FontSearcher() FontSearcher::instance = nullptr; } +std::vector FontSearcher::ListFamilies(const std::string &language_isocode, int winlangid) +{ + std::vector families; + + if (this->cached_language_isocode != language_isocode || this->cached_winlangid != winlangid) { + this->UpdateCachedFonts(language_isocode, winlangid); + this->cached_language_isocode = language_isocode; + this->cached_winlangid = winlangid; + } + + for (const FontFamily &ff : this->cached_fonts) { + if (std::find(std::begin(families), std::end(families), ff.family) != std::end(families)) continue; + families.push_back(ff.family); + } + + return families; +} + +std::vector> FontSearcher::ListStyles(const std::string &language_isocode, int winlangid, std::string_view family) +{ + std::vector> styles; + + if (this->cached_language_isocode != language_isocode || this->cached_winlangid != winlangid) { + this->UpdateCachedFonts(language_isocode, winlangid); + this->cached_language_isocode = language_isocode; + this->cached_winlangid = winlangid; + } + + for (const FontFamily &ff : this->cached_fonts) { + if (ff.family != family) continue; + styles.emplace_back(std::ref(ff)); + } + + return styles; +} + #if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) bool SetFallbackFont(FontCacheSettings *, const std::string &, int, MissingGlyphSearcher *) { return false; } diff --git a/src/fontdetection.h b/src/fontdetection.h index fa71cd7728..4556f18f5d 100644 --- a/src/fontdetection.h +++ b/src/fontdetection.h @@ -61,13 +61,20 @@ public: */ static inline FontSearcher *GetFontSearcher() { return FontSearcher::instance; } + /** + * Update cached font information. + * @param language_isocode the language, e.g. en_GB. + * @param winlangid the language ID windows style. + */ + virtual void UpdateCachedFonts(const std::string &language_isocode, int winlangid) = 0; + /** * List available fonts. * @param language_isocode the language, e.g. en_GB. * @param winlangid the language ID windows style. * @return vector containing font family names. */ - virtual std::vector ListFamilies(const std::string &language_isocode, int winlangid) = 0; + std::vector ListFamilies(const std::string &language_isocode, int winlangid); /** * List available styles for a font family. @@ -76,7 +83,12 @@ public: * @param font_family The font family to list. * @return vector containing style information for the family. */ - virtual std::vector ListStyles(const std::string &language_isocode, int winlangid, std::string_view font_family) = 0; + std::vector> ListStyles(const std::string &language_isocode, int winlangid, std::string_view family); + +protected: + std::vector cached_fonts; + std::string cached_language_isocode; + int cached_winlangid; private: static inline FontSearcher *instance = nullptr; diff --git a/src/os/macosx/font_osx.cpp b/src/os/macosx/font_osx.cpp index d2c3bba685..e6b1704c74 100644 --- a/src/os/macosx/font_osx.cpp +++ b/src/os/macosx/font_osx.cpp @@ -389,61 +389,40 @@ void LoadCoreTextFont(FontSize fs) class CoreTextFontSearcher : 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; + void UpdateCachedFonts(const std::string &language_isocode, int winlangid) override; }; -std::vector CoreTextFontSearcher::ListFamilies(const std::string &language_isocode, int) +void CoreTextFontSearcher::UpdateCachedFonts(const std::string &language_isocode, int) { - std::vector families; + this->cached_fonts.clear(); - EnumerateCoreFextFonts(language_isocode, 1, [&families](int, CTFontDescriptorRef font, CTFontSymbolicTraits) { - /* Get font name. */ - char family[128]; - CFAutoRelease font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute)); - CFStringGetCString(font_name.get(), family, std::size(family), kCFStringEncodingUTF8); - - /* There are some special fonts starting with an '.' and the last resort font that aren't usable. Skip them. */ - if (family[0] == '.' || strncmp(family, "LastResort", 10) == 0) return true; - - if (std::find(std::begin(families), std::end(families), family) == std::end(families)) { - families.push_back(family); - } - - return true; - }); - - return families; -} - -std::vector CoreTextFontSearcher::ListStyles(const std::string &language_isocode, int, std::string_view font_family) -{ - std::vector styles; - - EnumerateCoreFextFonts(language_isocode, 1, [&styles, &font_family](int, CTFontDescriptorRef font, CTFontSymbolicTraits) { + EnumerateCoreFextFonts(language_isocode, 1, [this](int, CTFontDescriptorRef font, CTFontSymbolicTraits) { /* Get font name. */ char family[128]; CFAutoRelease family_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute)); CFStringGetCString(family_name.get(), family, std::size(family), kCFStringEncodingUTF8); - if (font_family != family) return true; - char style[128]; CFAutoRelease style_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute)); CFStringGetCString(style_name.get(), style, std::size(style), kCFStringEncodingUTF8); + /* Don't add duplicate fonts. */ + std::string_view sv_family = family; + std::string_view sv_style = style; + if (std::any_of(std::begin(this->cached_fonts), std::end(this->cached_fonts), [&sv_family, &sv_style](const FontFamily &ff) { return ff.family == sv_family && ff.style == sv_style; })) return true; + CFAutoRelease traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)); float weight = 0.0f; CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait), kCFNumberFloatType, &weight); float slant = 0.0f; CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSlantTrait), kCFNumberFloatType, &slant); - styles.emplace_back(family, style, static_cast(slant * 100), static_cast(weight * 100)); + this->cached_fonts.emplace_back(sv_family, sv_style, static_cast(slant * 100), static_cast(weight * 100)); return true; }); - return styles; + std::sort(std::begin(this->cached_fonts), std::end(this->cached_fonts), FontFamilySorter); } CoreTextFontSearcher _coretextfs_instance; diff --git a/src/os/unix/font_unix.cpp b/src/os/unix/font_unix.cpp index f78dc80981..085be73b06 100644 --- a/src/os/unix/font_unix.cpp +++ b/src/os/unix/font_unix.cpp @@ -196,15 +196,14 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is */ class FontConfigFontSearcher : 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; + void UpdateCachedFonts(const std::string &language_isocode, int winlangid) override; }; -std::vector FontConfigFontSearcher::ListFamilies(const std::string &language_isocode, int) +void FontConfigFontSearcher::UpdateCachedFonts(const std::string &language_isocode, int) { - std::vector families; + this->cached_fonts.clear(); - if (!FcInit()) return families; + if (!FcInit()) return; FcConfig *fc_instance = FcConfigReference(nullptr); assert(fc_instance != nullptr); @@ -216,51 +215,6 @@ std::vector FontConfigFontSearcher::ListFamilies(const std::string /* We want to know this attributes. */ FcObjectSet *os = FcObjectSetCreate(); FcObjectSetAdd(os, FC_FAMILY); - /* Get the list of filenames matching the wanted language. */ - FcFontSet *fs = FcFontList(nullptr, pat, os); - - /* We don't need these anymore. */ - FcObjectSetDestroy(os); - FcPatternDestroy(pat); - - if (fs != nullptr) { - families.reserve(fs->nfont); - for (const FcPattern *font : std::span(fs->fonts, fs->nfont)) { - FcChar8 *family; - if (FcPatternGetString(font, FC_FAMILY, 0, &family) != FcResultMatch) continue; - - /* Check if the family already exists. */ - std::string_view sv_family = reinterpret_cast(family); - if (std::find(std::begin(families), std::end(families), sv_family) != std::end(families)) continue; - - families.emplace_back(sv_family); - } - - /* Clean up the list of filenames. */ - FcFontSetDestroy(fs); - } - - FcConfigDestroy(fc_instance); - return families; -} - -std::vector FontConfigFontSearcher::ListStyles(const std::string &language_isocode, int, std::string_view font_family) -{ - std::vector styles; - - if (!FcInit()) return styles; - - FcConfig *fc_instance = FcConfigReference(nullptr); - assert(fc_instance != nullptr); - - std::string lang = GetFontConfigLanguage(language_isocode); - - /* First create a pattern to match the wanted language. */ - FcPattern *pat = FcNameParse(reinterpret_cast(lang.c_str())); - FcPatternAddString(pat, FC_FAMILY, reinterpret_cast(std::string(font_family).c_str())); - /* We want to know these attributes. */ - FcObjectSet *os = FcObjectSetCreate(); - FcObjectSetAdd(os, FC_FAMILY); FcObjectSetAdd(os, FC_STYLE); FcObjectSetAdd(os, FC_SLANT); FcObjectSetAdd(os, FC_WEIGHT); @@ -272,7 +226,7 @@ std::vector FontConfigFontSearcher::ListStyles(const std::string &la FcPatternDestroy(pat); if (fs != nullptr) { - styles.reserve(fs->nfont); + this->cached_fonts.reserve(fs->nfont); for (const FcPattern *font : std::span(fs->fonts, fs->nfont)) { FcChar8 *family; FcChar8 *style; @@ -284,7 +238,12 @@ std::vector FontConfigFontSearcher::ListStyles(const std::string &la if (FcPatternGetInteger(font, FC_SLANT, 0, &slant) != FcResultMatch) continue; if (FcPatternGetInteger(font, FC_WEIGHT, 0, &weight) != FcResultMatch) continue; - styles.emplace_back(reinterpret_cast(family), reinterpret_cast(style), slant, weight); + /* Don't add duplicate fonts. */ + std::string_view sv_family = reinterpret_cast(family); + std::string_view sv_style = reinterpret_cast(style); + if (std::any_of(std::begin(this->cached_fonts), std::end(this->cached_fonts), [&sv_family, &sv_style](const FontFamily &ff) { return ff.family == sv_family && ff.style == sv_style; })) continue; + + this->cached_fonts.emplace_back(sv_family, sv_style, slant, weight); } /* Clean up the list of filenames. */ @@ -292,7 +251,8 @@ std::vector FontConfigFontSearcher::ListStyles(const std::string &la } FcConfigDestroy(fc_instance); - return styles; + + std::sort(std::begin(this->cached_fonts), std::end(this->cached_fonts), FontFamilySorter); } static FontConfigFontSearcher _fcfs_instance; diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 77d925a277..7ee5993284 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -34,7 +34,7 @@ struct EFCParam { FontCacheSettings *settings; - LOCALESIGNATURE locale; + LOCALESIGNATURE locale; MissingGlyphSearcher *callback; std::vector fonts; @@ -384,20 +384,31 @@ void LoadWin32Font(FontSize fs) */ 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; + void UpdateCachedFonts(const std::string &language_isocode, int winlangid) override; }; /** - * State passed between EnumFontFamiliesEx and our list families callback. + * State passed between EnumFontFamiliesEx and our list fonts callbacks. */ -struct EFCListFamiliesParam : EFCParam { - std::vector families; ///< List of families found. +struct EFCListFontsParam : EFCParam { + std::vector &fonts; + + explicit EFCListFontsParam(std::vector &fonts) : fonts(fonts) {} }; +static int CALLBACK ListStylesFontCallback(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *, DWORD, LPARAM lParam) +{ + EFCListFontsParam &info = *reinterpret_cast(lParam); + + LOGFONT &lf = lpelfe->elfLogFont; + info.fonts.emplace_back(FS2OTTD(lf.lfFaceName), FS2OTTD(lpelfe->elfStyle), lf.lfItalic, lf.lfWeight); + + return 1; +} + static int CALLBACK ListFamiliesFontCallback(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam) { - EFCListFamiliesParam &info = *reinterpret_cast(lParam); + EFCListFontsParam &info = *reinterpret_cast(lParam); /* Only use TrueType fonts */ if (!(type & TRUETYPE_FONTTYPE)) return 1; @@ -409,18 +420,23 @@ static int CALLBACK ListFamiliesFontCallback(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRI 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)); + + HDC dc = GetDC(nullptr); + EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)&ListStylesFontCallback, reinterpret_cast(&info), 0); + ReleaseDC(nullptr, dc); return 1; } -std::vector Win32FontSearcher::ListFamilies(const std::string &, int winlangid) +void Win32FontSearcher::UpdateCachedFonts(const std::string &, int winlangid) { - EFCListFamiliesParam info; + EFCListFontsParam info(this->cached_fonts); + this->cached_fonts.clear(); + 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; + return; } LOGFONT lf{}; @@ -429,54 +445,6 @@ std::vector Win32FontSearcher::ListFamilies(const std::string &, in 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;