diff --git a/kitty/core_text.m b/kitty/core_text.m index aae74ec8b..68e1de0b9 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -640,7 +640,18 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u PyObject* face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) { - RAII_CoreFoundation(CTFontDescriptorRef, desc, font_descriptor_from_python(descriptor)); + RAII_CoreFoundation(CTFontDescriptorRef, desc, NULL); + if (builtin_nerd_font_descriptor) { + PyObject *psname = PyDict_GetItemString(descriptor, "postscript_name"); + if (psname && PyUnicode_CompareWithASCIIString(psname, "SymbolsNFM") == 0) { + RAII_PyObject(path, get_path_for_font_descriptor(builtin_nerd_font_descriptor)); + PyObject *dpath = PyDict_GetItemString(descriptor, "path"); + if (dpath && PyUnicode_Compare(path, dpath) == 0) { + desc = builtin_nerd_font_descriptor; CFRetain(desc); + } + } + } + if (!desc) desc = font_descriptor_from_python(descriptor); if (!desc) return NULL; RAII_CoreFoundation(CTFontRef, font, CTFontCreateWithFontDescriptor(desc, fg ? scaled_point_sz(fg) : 12, NULL)); if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; } @@ -672,8 +683,7 @@ new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) { PyObject* specialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts UNUSED, double dpi_x UNUSED, double dpi_y UNUSED) { - Py_INCREF(base_descriptor); - return base_descriptor; + return PyDict_Copy(base_descriptor); } struct RenderBuffers { @@ -1135,7 +1145,7 @@ set_builtin_nerd_font(PyObject UNUSED *self, PyObject *pypath) { if (builtin_nerd_font_descriptor) CFRelease(builtin_nerd_font_descriptor); builtin_nerd_font_descriptor = CFArrayGetValueAtIndex(descriptors, 0); CFRetain(builtin_nerd_font_descriptor); - Py_RETURN_NONE; + return font_descriptor_to_python(builtin_nerd_font_descriptor); } static PyMethodDef module_methods[] = { diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index ed6d1e442..6ce12bedd 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -426,7 +426,7 @@ def fc_match_postscript_name( def add_font_file(path: str) -> bool: ... -def set_builtin_nerd_font(path: str) -> None: ... +def set_builtin_nerd_font(path: str) -> Union[CoreTextFont, FontConfigPattern]: ... class FeatureData(TypedDict): diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index 2dd061ae6..ffc12cb1a 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -426,6 +426,12 @@ specialize_font_descriptor(PyObject *base_descriptor, double font_sz_in_pts, dou AP(FcPatternAddDouble, FC_DPI, (dpi_x + dpi_y) / 2.0, "dpi"); ans = _fc_match(pat); FcPatternDestroy(pat); pat = NULL; + if (!ans) return NULL; + // fontconfig returns a completely random font if the base descriptor + // points to a font that fontconfig hasnt indexed, for example the builting + // NERD font + PyObject *new_path = PyDict_GetItemString(ans, "path"); + if (!new_path || PyObject_RichCompareBool(p, new_path, Py_EQ) != 1) { Py_CLEAR(ans); ans = PyDict_Copy(base_descriptor); if (!ans) return NULL; } if (face_idx > 0) { // For some reason FcFontMatch sets the index to zero, so manually restore it. @@ -538,6 +544,7 @@ set_builtin_nerd_font(PyObject UNUSED *self, PyObject *pypath) { copy(hinting); copy(hint_style); #undef copy if (PyDict_SetItemString(builtin_nerd_font.descriptor, "path", pypath) != 0) goto end; + if (PyDict_SetItemString(builtin_nerd_font.descriptor, "index", PyLong_FromLong(0)) != 0) goto end; } end: if (pat) FcPatternDestroy(pat); @@ -546,7 +553,8 @@ end: Py_CLEAR(builtin_nerd_font.descriptor); return NULL; } - Py_RETURN_NONE; + Py_INCREF(builtin_nerd_font.descriptor); + return builtin_nerd_font.descriptor; } diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index 9734149ce..ea25c690d 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -31,6 +31,7 @@ from kitty.types import _T from kitty.typing import CoreTextFont, FontConfigPattern from kitty.utils import log_error +from . import family_name_to_key from .common import get_font_files if is_macos: @@ -40,6 +41,7 @@ else: FontObject = Union[CoreTextFont, FontConfigPattern] current_faces: List[Tuple[FontObject, bool, bool]] = [] +builtin_nerd_font_descriptor: Optional[FontObject] = None def font_for_family(family: str) -> Tuple[FontObject, bool, bool]: @@ -145,6 +147,10 @@ def create_symbol_map(opts: Options) -> Tuple[Tuple[int, int, int], ...]: for family in val.values(): if family not in family_map: font, bold, italic = font_for_family(family) + fkey = family_name_to_key(family) + if fkey in ('symbolsnfm', 'symbols nerd font mono') and font['postscript_name'] != 'SymbolsNFM' and builtin_nerd_font_descriptor: + font = builtin_nerd_font_descriptor + bold = italic = False family_map[family] = count count += 1 current_faces.append((font, bold, italic)) @@ -172,8 +178,8 @@ def dump_font_debug() -> None: log_error(' ' + s.identify_for_debug()) -def set_font_family(opts: Optional[Options] = None, override_font_size: Optional[float] = None) -> None: - global current_faces +def set_font_family(opts: Optional[Options] = None, override_font_size: Optional[float] = None, add_builtin_nerd_font: bool = False) -> None: + global current_faces, builtin_nerd_font_descriptor opts = opts or defaults sz = override_font_size or opts.font_size font_map = get_font_files(opts) @@ -185,6 +191,12 @@ def set_font_family(opts: Optional[Options] = None, override_font_size: Optional indices[k] = len(current_faces) current_faces.append((font_map[k], 'b' in k, 'i' in k)) before = len(current_faces) + if add_builtin_nerd_font: + builtin_nerd_font_path = os.path.join(fonts_dir, 'SymbolsNerdFontMono-Regular.ttf') + if os.path.exists(builtin_nerd_font_path): + builtin_nerd_font_descriptor = set_builtin_nerd_font(builtin_nerd_font_path) + else: + log_error(f'No builtin NERD font found in {fonts_dir}') sm = create_symbol_map(opts) ns = create_narrow_symbols(opts) num_symbol_fonts = len(current_faces) - before @@ -195,15 +207,6 @@ def set_font_family(opts: Optional[Options] = None, override_font_size: Optional ) -def add_application_fonts() -> None: - for font in ('SymbolsNerdFontMono-Regular.ttf',): - path = os.path.join(fonts_dir, font) - if os.path.exists(path): - set_builtin_nerd_font(path) - else: - log_error(f'No builtin NERD font found in {fonts_dir}') - - if TYPE_CHECKING: CBufType = ctypes.Array[ctypes.c_ubyte] else: diff --git a/kitty/main.py b/kitty/main.py index b07817eaf..f5b060608 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -44,7 +44,7 @@ from .fast_data_types import ( set_options, ) from .fonts.box_drawing import set_scale -from .fonts.render import add_application_fonts, dump_font_debug, set_font_family +from .fonts.render import dump_font_debug, set_font_family from .options.types import Options from .options.utils import DELETE_ENV_VAR from .os_window_size import edge_spacing, initial_window_size_func @@ -248,8 +248,7 @@ class AppRunner: set_scale(opts.box_drawing_scale) set_options(opts, is_wayland(), args.debug_rendering, args.debug_font_fallback) try: - set_font_family(opts) - add_application_fonts() + set_font_family(opts, add_builtin_nerd_font=True) _run_app(opts, args, bad_lines, talk_fd) finally: set_options(None)