diff --git a/kitty/core_text.m b/kitty/core_text.m index 5fa6b3551..9c41cf53c 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -98,8 +98,10 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) { unsigned int straits = [traits[(id)kCTFontSymbolicTrait] unsignedIntValue]; float weightVal = [traits[(id)kCTFontWeightTrait] floatValue]; float widthVal = [traits[(id)kCTFontWidthTrait] floatValue]; + NSDictionary *variation = (NSDictionary *)CTFontDescriptorCopyAttribute(descriptor, kCTFontVariationAttribute); - PyObject *ans = Py_BuildValue("{ssssssss sOsOsOsOsOsO sfsfsI}", + PyObject *ans = Py_BuildValue("{ssssssssss sOsOsOsOsOsOsO sfsfsI}", + "descriptor_type", "core_text", "path", [[url path] UTF8String], "postscript_name", [psName UTF8String], "family", [family UTF8String], @@ -111,6 +113,7 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) { "expanded", (straits & kCTFontExpandedTrait) != 0 ? Py_True : Py_False, "condensed", (straits & kCTFontCondensedTrait) != 0 ? Py_True : Py_False, "color_glyphs", (straits & kCTFontColorGlyphsTrait) != 0 ? Py_True : Py_False, + "variable", variation ? Py_True : Py_False, "weight", weightVal, "width", widthVal, @@ -121,6 +124,7 @@ font_descriptor_to_python(CTFontDescriptorRef descriptor) { [family release]; [style release]; [traits release]; + if (variation) [variation release]; return ans; } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 0d9836cb9..2354fcc9f 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1,6 +1,6 @@ import termios from ctypes import Array, c_ubyte -from typing import Any, Callable, Dict, Iterator, List, NewType, Optional, Tuple, TypedDict, Union, overload +from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload from kitty.boss import Boss from kitty.fonts import FontFeature, VariableData @@ -373,6 +373,7 @@ def default_color_table() -> Tuple[int, ...]: class FontConfigPattern(TypedDict): + descriptor_type: Literal['fontconfig'] path: str index: int family: str @@ -422,6 +423,7 @@ class Face: class CoreTextFont(TypedDict): + descriptor_type: Literal['core_text'] path: str postscript_name: str family: str @@ -432,6 +434,7 @@ class CoreTextFont(TypedDict): condensed: bool color_glyphs: bool monospace: bool + variable: bool weight: float width: float traits: int diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index 7f29435bf..a9b027181 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -150,7 +150,7 @@ pyspacing(int val) { static PyObject* pattern_as_dict(FcPattern *pat) { - PyObject *ans = PyDict_New(), *p = NULL, *list = NULL; + PyObject *ans = Py_BuildValue("{ss}", "descriptor_type", "fontconfig"), *p = NULL, *list = NULL; if (ans == NULL) return NULL; #define PS(x) PyUnicode_Decode((const char*)x, strlen((const char*)x), "UTF-8", "replace") diff --git a/kitty/fonts/__init__.py b/kitty/fonts/__init__.py index 3fcfb319c..fd8a73e72 100644 --- a/kitty/fonts/__init__.py +++ b/kitty/fonts/__init__.py @@ -1,5 +1,7 @@ from enum import Enum, IntEnum, auto -from typing import Any, NamedTuple, Optional, Tuple, TypedDict +from typing import NamedTuple, Optional, Tuple, TypedDict, Union + +from kitty.typing import CoreTextFont, FontConfigPattern class ListedFont(TypedDict): @@ -8,7 +10,7 @@ class ListedFont(TypedDict): postscript_name: str is_monospace: bool is_variable: bool - descriptor: Any + descriptor: Union[FontConfigPattern, CoreTextFont] class VariableAxis(TypedDict): diff --git a/kitty/fonts/core_text.py b/kitty/fonts/core_text.py index 64e0a827d..40668ed62 100644 --- a/kitty/fonts/core_text.py +++ b/kitty/fonts/core_text.py @@ -5,7 +5,7 @@ import re from typing import Dict, Generator, Iterable, List, Optional, Tuple from kitty.fast_data_types import coretext_all_fonts -from kitty.fonts import FontFeature +from kitty.fonts import FontFeature, VariableData from kitty.options.types import Options from kitty.typing import CoreTextFont from kitty.utils import log_error @@ -47,7 +47,8 @@ def list_fonts() -> Generator[ListedFont, None, None]: if f: fn = f'{f} {fd.get("style", "")}'.strip() is_mono = bool(fd['monospace']) - yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono} + yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono, + 'is_variable': fd['variable'], 'descriptor': fd} def find_font_features(postscript_name: str) -> Tuple[FontFeature, ...]: @@ -114,3 +115,9 @@ def get_font_files(opts: Options) -> Dict[str, CoreTextFont]: def font_for_family(family: str) -> Tuple[CoreTextFont, bool, bool]: ans = find_best_match(resolve_family(family, getattr(get_font_files, 'medium_family'))) return ans, ans['bold'], ans['italic'] + + +def get_variable_data_for_descriptor(f: ListedFont) -> VariableData: + d = f['descriptor'] + assert d['descriptor_type'] == 'core_text' + return d['variable_data'] diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index 8ff38175d..71aed3967 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -177,5 +177,7 @@ def font_for_family(family: str) -> Tuple[FontConfigPattern, bool, bool]: return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN -def get_variable_data_for_descriptor(fd: FontConfigPattern) -> VariableData: - return Face(descriptor=fd).get_variable_data() +def get_variable_data_for_descriptor(f: ListedFont) -> VariableData: + d = f['descriptor'] + assert d['descriptor_type'] == 'fontconfig' + return Face(descriptor=d).get_variable_data() diff --git a/kitty/fonts/list.py b/kitty/fonts/list.py index b3d552026..2fdfa515d 100644 --- a/kitty/fonts/list.py +++ b/kitty/fonts/list.py @@ -11,7 +11,7 @@ from kitty.types import run_once from . import ListedFont if is_macos: - from .core_text import list_fonts + from .core_text import get_variable_data_for_descriptor, list_fonts else: from .fontconfig import get_variable_data_for_descriptor, list_fonts @@ -58,7 +58,7 @@ def create_family_groups(monospaced: bool = True) -> Dict[str, List[ListedFont]] def show_variable(f: ListedFont, psnames: bool) -> None: - vd = get_variable_data_for_descriptor(f['descriptor']) + vd = get_variable_data_for_descriptor(f) p = f"{italic(f['full_name'])} {variable_font_label('Variable font')}" print(indented(p)) print(indented(variable_font_label('Axes of variation'), level=2))