2424
2525#include " safeguards.h"
2626
27- bool SetFallbackFont (FontCacheSettings *settings, const std::string &language_isocode, int , MissingGlyphSearcher *callback )
27+ static void EnumerateCoreFextFonts ( const std::string &language_isocode, int ntries, std::function< bool ( int , CTFontDescriptorRef, CTFontSymbolicTraits)> enum_func )
2828{
2929 /* Determine fallback font using CoreText. This uses the language isocode
3030 * to find a suitable font. CoreText is available from 10.5 onwards. */
@@ -55,9 +55,12 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
5555 CFAutoRelease<CFSetRef> mandatory_attribs (CFSetCreate (kCFAllocatorDefault , const_cast <const void **>(reinterpret_cast <const void *const *>(&kCTFontLanguagesAttribute )), 1 , &kCFTypeSetCallBacks ));
5656 CFAutoRelease<CFArrayRef> descs (CTFontDescriptorCreateMatchingFontDescriptors (lang_desc.get (), mandatory_attribs.get ()));
5757
58- bool result = false ;
59- for (int tries = 0 ; tries < 2 ; tries++) {
60- for (CFIndex i = 0 ; descs.get () != nullptr && i < CFArrayGetCount (descs.get ()); i++) {
58+ /* Nothing to see here. */
59+ if (descs == nullptr ) return ;
60+
61+ CFIndex count = CFArrayGetCount (descs.get ());
62+ for (int tries = 0 ; tries < ntries; tries++) {
63+ for (CFIndex i = 0 ; i < count; i++) {
6164 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex (descs.get (), i);
6265
6366 /* Get font traits. */
@@ -67,34 +70,44 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
6770
6871 /* Skip symbol fonts and vertical fonts. */
6972 if ((symbolic_traits & kCTFontClassMaskTrait ) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait )) continue ;
70- /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
71- if (symbolic_traits & kCTFontBoldTrait ) continue ;
72- /* Select monospaced fonts if asked for. */
73- if (((symbolic_traits & kCTFontMonoSpaceTrait ) == kCTFontMonoSpaceTrait ) != callback->Monospace ()) continue ;
74-
75- /* Get font name. */
76- char name[128 ];
77- CFAutoRelease<CFStringRef> font_name ((CFStringRef)CTFontDescriptorCopyAttribute (font, kCTFontDisplayNameAttribute ));
78- CFStringGetCString (font_name.get (), name, lengthof (name), kCFStringEncodingUTF8 );
79-
80- /* Serif fonts usually look worse on-screen with only small
81- * font sizes. As such, we try for a sans-serif font first.
82- * If we can't find one in the first try, try all fonts. */
83- if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait ) != (CTFontStylisticClass)kCTFontSansSerifClass ) continue ;
84-
85- /* There are some special fonts starting with an '.' and the last
86- * resort font that aren't usable. Skip them. */
87- if (name[0 ] == ' .' || strncmp (name, " LastResort" , 10 ) == 0 ) continue ;
88-
89- /* Save result. */
90- callback->SetFontNames (settings, name);
91- if (!callback->FindMissingGlyphs ()) {
92- Debug (fontcache, 2 , " CT-Font for {}: {}" , language_isocode, name);
93- result = true ;
94- break ;
95- }
73+
74+ bool continue_enumerating = enum_func (tries, font, symbolic_traits);
75+ if (!continue_enumerating) return ;
9676 }
9777 }
78+ }
79+
80+ bool SetFallbackFont (FontCacheSettings *settings, const std::string &language_isocode, int , MissingGlyphSearcher *callback)
81+ {
82+ bool result = false ;
83+ EnumerateCoreFextFonts (language_isocode, 2 , [&settings, &language_isocode, &callback, &result](int tries, CTFontDescriptorRef font, CTFontSymbolicTraits symbolic_traits) {
84+ /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
85+ if (symbolic_traits & kCTFontBoldTrait ) return true ;
86+ /* Select monospaced fonts if asked for. */
87+ if (((symbolic_traits & kCTFontMonoSpaceTrait ) == kCTFontMonoSpaceTrait ) != callback->Monospace ()) return true ;
88+
89+ /* Get font name. */
90+ char name[128 ];
91+ CFAutoRelease<CFStringRef> font_name ((CFStringRef)CTFontDescriptorCopyAttribute (font, kCTFontDisplayNameAttribute ));
92+ CFStringGetCString (font_name.get (), name, lengthof (name), kCFStringEncodingUTF8 );
93+
94+ /* Serif fonts usually look worse on-screen with only small
95+ * font sizes. As such, we try for a sans-serif font first.
96+ * If we can't find one in the first try, try all fonts. */
97+ if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait ) != (CTFontStylisticClass)kCTFontSansSerifClass ) return true ;
98+
99+ /* There are some special fonts starting with an '.' and the last
100+ * resort font that aren't usable. Skip them. */
101+ if (name[0 ] == ' .' || strncmp (name, " LastResort" , 10 ) == 0 ) return true ;
102+
103+ /* Save result. */
104+ callback->SetFontNames (settings, name);
105+ if (!callback->FindMissingGlyphs ()) {
106+ Debug (fontcache, 2 , " CT-Font for {}: {}" , language_isocode, name);
107+ result = true ;
108+ return false ;
109+ }
110+ });
98111
99112 if (!result) {
100113 /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
@@ -382,3 +395,67 @@ void LoadCoreTextFont(FontSize fs, const std::string &file_name, uint size)
382395 new CoreTextFontCache (fs, std::move (font_ref), size);
383396 }
384397}
398+
399+ class MacOSFontSearcher : public FontSearcher {
400+ public:
401+ std::vector<std::string> ListFamilies (const std::string &language_isocode, int winlangid) override ;
402+ std::vector<FontFamily> ListStyles (const std::string &language_isocode, int winlangid, std::string_view family) override ;
403+ };
404+
405+ std::vector<std::string> MacOSFontSearcher::ListFamilies (const std::string &language_isocode, int )
406+ {
407+ std::vector<std::string> families;
408+
409+ EnumerateCoreFextFonts (language_isocode, 1 , [&families](int , CTFontDescriptorRef font, CTFontSymbolicTraits) {
410+ /* Get font name. */
411+ char family[128 ];
412+ CFAutoRelease<CFStringRef> font_name ((CFStringRef)CTFontDescriptorCopyAttribute (font, kCTFontFamilyNameAttribute ));
413+ CFStringGetCString (font_name.get (), family, std::size (family), kCFStringEncodingUTF8 );
414+
415+ /* There are some special fonts starting with an '.' and the last resort font that aren't usable. Skip them. */
416+ if (family[0 ] == ' .' || strncmp (family, " LastResort" , 10 ) == 0 ) return true ;
417+
418+ if (std::find (std::begin (families), std::end (families), family) == std::end (families)) {
419+ families.push_back (family);
420+ }
421+
422+ return true ;
423+ });
424+
425+ return families;
426+ }
427+
428+ std::vector<FontFamily> MacOSFontSearcher::ListStyles (const std::string &language_isocode, int , std::string_view family)
429+ {
430+ std::vector<FontFamily> styles;
431+
432+ EnumerateCoreFextFonts (language_isocode, 1 , [&styles, &family](int , CTFontDescriptorRef font, CTFontSymbolicTraits) {
433+ /* Get font name. */
434+ char font_family[128 ];
435+ CFAutoRelease<CFStringRef> family_name ((CFStringRef)CTFontDescriptorCopyAttribute (font, kCTFontFamilyNameAttribute ));
436+ CFStringGetCString (family_name.get (), family, std::size (family), kCFStringEncodingUTF8 );
437+
438+ if (family != font_family) return true ;
439+
440+ /* There are some special fonts starting with an '.' and the last resort font that aren't usable. Skip them. */
441+ if (font_family[0 ] == ' .' || strncmp (font_family, " LastResort" , 10 ) == 0 ) return true ;
442+
443+ char style[128 ];
444+ CFAutoRelease<CFStringRef> style_name ((CFStringRef)CTFontDescriptorCopyAttribute (font, kCTFontStyleNameAttribute ));
445+ CFStringGetCString (style_name.get (), style, std::size (style), kCFStringEncodingUTF8 );
446+
447+ CFAutoRelease<CFDictionaryRef> traits ((CFDictionaryRef)CTFontDescriptorCopyAttribute (font, kCTFontTraitsAttribute ));
448+ float weight = 0 .0f ;
449+ CFNumberGetValue ((CFNumberRef)CFDictionaryGetValue (traits.get (), kCTFontWeightTrait ), kCFNumberFloatType , &weight);
450+ float slant = 0 .0f ;
451+ CFNumberGetValue ((CFNumberRef)CFDictionaryGetValue (traits.get (), kCTFontSlantTrait ), kCFNumberFloatType , &slant);
452+
453+ styles.emplace_back (font_family, style, static_cast <int >(slant * 100 ), static_cast <int >(weight * 100 ));
454+
455+ return true ;
456+ });
457+
458+ return styles;
459+ }
460+
461+ MacOSFontSearcher _macosfs_instance;
0 commit comments