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
This commit is contained in:
Kovid Goyal
2024-03-20 20:01:17 +05:30
parent f3d9ad3244
commit e646596c5b
2 changed files with 30 additions and 19 deletions

View File

@@ -56,6 +56,9 @@ Detailed list of changes
- macOS: Fix text rendered with fallback fonts not respecting bold/italic styling (:disc:`7241`) - 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] 0.33.0 [2024-03-12]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -184,12 +184,17 @@ glyph_id_for_codepoint_ctfont(CTFontRef ct_font, char_type ch) {
return glyphs[0]; 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 static bool
is_last_resort_font(CTFontRef new_font) { is_last_resort_font(CTFontRef new_font) {
CFStringRef name = CTFontCopyPostScriptName(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); CFRelease(name);
return cr == kCFCompareEqualTo; return ans;
} }
static CTFontRef static CTFontRef
@@ -197,26 +202,29 @@ manually_search_fallback_fonts(CTFontRef current_font, CPUCell *cell) {
CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection()); CFArrayRef fonts = CTFontCollectionCreateMatchingFontDescriptors(all_fonts_collection());
CTFontRef ans = NULL; CTFontRef ans = NULL;
const CFIndex count = CFArrayGetCount(fonts); 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++) { for (CFIndex i = 0; i < count; i++) {
CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, 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); CTFontRef new_font = CTFontCreateWithFontDescriptor(descriptor, CTFontGetSize(current_font), NULL);
if (new_font) { bool found = true;
if (!is_last_resort_font(new_font)) { if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false;
char_type ch = cell->ch ? cell->ch : ' '; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i] && found; i++) {
bool found = true; char_type cch = codepoint_for_mark(cell->cc_idx[i]);
if (!glyph_id_for_codepoint_ctfont(new_font, ch)) found = false; if (!glyph_id_for_codepoint_ctfont(new_font, cch)) 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);
} }
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); CFRelease(fonts);
return ans; 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); new_font = manually_search_fallback_fonts(old_font, cpu_cell);
if (new_font) return new_font; 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 NULL;
} }
return new_font; return new_font;