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
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
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: 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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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;