From e646596c5bacfe86d14f01ecadcf38378ff8a0c7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 20 Mar 2024 20:01:17 +0530 Subject: [PATCH] macOS: When CoreText fails to find a fallback font for a character in the first Private Use Unicode Area, preferentially use the NERD font, if available, for it Fixes #6043 --- docs/changelog.rst | 3 +++ kitty/core_text.m | 46 +++++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index dab909f38..da83cdf11 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -56,6 +56,9 @@ Detailed list of changes - macOS: Fix text rendered with fallback fonts not respecting bold/italic styling (:disc:`7241`) +- macOS: When CoreText fails to find a fallback font for a character in the first Private Use Unicode Area, preferentially use the NERD font, if available, for it (:iss:`6043`) + + 0.33.0 [2024-03-12] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/core_text.m b/kitty/core_text.m index ff8af760e..e3ebd24cb 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -184,12 +184,17 @@ glyph_id_for_codepoint_ctfont(CTFontRef ct_font, char_type ch) { return glyphs[0]; } +static bool +cf_string_equals(CFStringRef a, CFStringRef b) { return CFStringCompare(a, b, 0) == kCFCompareEqualTo; } + +#define LAST_RESORT_FONT_NAME "LastResort" + static bool is_last_resort_font(CTFontRef new_font) { CFStringRef name = CTFontCopyPostScriptName(new_font); - CFComparisonResult cr = CFStringCompare(name, CFSTR("LastResort"), 0); + bool ans = cf_string_equals(name, CFSTR(LAST_RESORT_FONT_NAME)); CFRelease(name); - return cr == kCFCompareEqualTo; + return ans; } static CTFontRef @@ -197,26 +202,29 @@ manually_search_fallback_fonts(CTFontRef current_font, CPUCell *cell) { CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection()); CTFontRef ans = NULL; const CFIndex count = CFArrayGetCount(fonts); + char_type ch = cell->ch ? cell->ch : ' '; + bool in_first_pua = 0xe000 <= ch && ch <= 0xf8ff; + // preferentially load from NERD fonts for (CFIndex i = 0; i < count; i++) { CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i); + CFStringRef name = CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute); + bool is_last_resort_font = cf_string_equals(name, CFSTR(LAST_RESORT_FONT_NAME)); + bool is_nerd_font = cf_string_equals(name, CFSTR("SymbolsNFM")); + CFRelease(name); + if (is_last_resort_font) continue; CTFontRef new_font = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(current_font), NULL); - if (new_font) { - if (!is_last_resort_font(new_font)) { - char_type ch = cell->ch ? cell->ch : ' '; - bool found = true; - if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false; - for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i] && found; i++) { - ch = codepoint_for_mark(cell->cc_idx[i]); - if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false; - } - if (found) { - ans = new_font; - if (global_state.debug_font_fallback) fprintf(stderr, "Manually found fallback font.\n"); - break; - } - } - CFRelease(new_font); + bool found = true; + if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false; + for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i] && found; i++) { + char_type cch = codepoint_for_mark(cell->cc_idx[i]); + if (!glyph_id_for_codepoint_ctfont(new_font, cch)) found = false; } + if (found) { + // preferentially use NERD font for glyphs in first PUA + if (!ans || is_nerd_font) { if (ans) CFRelease(ans); ans = new_font; CFRetain(ans); } + if (is_nerd_font || !in_first_pua) break; + } + CFRelease(new_font); } CFRelease(fonts); return ans; @@ -241,7 +249,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font, CPUCell *cpu_cell) { new_font = manually_search_fallback_fonts(old_font, cpu_cell); if (new_font) return new_font; } - PyErr_Format(PyExc_ValueError, "Failed to find fallback CTFont other than the LastResort font for: %s", [(NSString *)str UTF8String]); + PyErr_Format(PyExc_ValueError, "Failed to find fallback CTFont other than the %s font for: %s", LAST_RESORT_FONT_NAME, [(NSString *)str UTF8String]); return NULL; } return new_font;