From c27c731ffb22af7bd013838269ea2b71ca490230 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 15 May 2024 22:09:59 +0530 Subject: [PATCH] Get font selection for the cascadia code variable fonts working --- kitty/fonts/common.py | 2 +- kitty/fonts/core_text.py | 15 ++++++++----- kitty/fonts/fontconfig.py | 13 ++++++----- kitty_tests/fonts.py | 47 +++++++++++++++++++++++++-------------- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/kitty/fonts/common.py b/kitty/fonts/common.py index 23615cdc7..baab070c5 100644 --- a/kitty/fonts/common.py +++ b/kitty/fonts/common.py @@ -102,7 +102,7 @@ def find_medium_variant(font: Descriptor) -> Descriptor: vd = get_variable_data_for_descriptor(font) for i, ns in enumerate(vd['named_styles']): if ns['name'] == 'Regular': - set_named_style(ns['psname'], font, vd) + set_named_style(ns['psname'] or ns['name'], font, vd) return font axis_values = {} for i, ax in enumerate(vd['axes']): diff --git a/kitty/fonts/core_text.py b/kitty/fonts/core_text.py index e7c60857f..0558d6976 100644 --- a/kitty/fonts/core_text.py +++ b/kitty/fonts/core_text.py @@ -7,7 +7,7 @@ from collections import defaultdict from functools import lru_cache from typing import Dict, Generator, Iterable, List, NamedTuple, Optional, Sequence, Tuple -from kitty.fast_data_types import coretext_all_fonts +from kitty.fast_data_types import CTFace, coretext_all_fonts from kitty.fonts import FontSpec, family_name_to_key from kitty.options.types import Options from kitty.typing import CoreTextFont @@ -109,9 +109,9 @@ def weight_range_for_family(family: str) -> WeightRange: bold = w elif s == 'bold' and bold == wr.bold: bold = w - elif s == 'medium': + elif s == 'regular': medium = w - elif s == 'regular' and medium == wr.medium: + elif s == 'medium' and medium == wr.medium: medium = w return WeightRange(mini, maxi, medium, bold) @@ -148,15 +148,18 @@ class CTScorer(Scorer): families = {x['family'] for x in candidates} if len(families) == 1: wr = weight_range_for_family(next(iter(families))) - if wr.is_valid and wr.minimum < 0 and wr.maximum <= 0: # Operator Mono is an example of this craziness + if wr.is_valid and wr.medium < 0: # Operator Mono is an example of this craziness self.weight_range = wr candidates = sorted(candidates, key=self.score) if dump: print(self) + if self.weight_range: + print(self.weight_range) for x in candidates: assert x['descriptor_type'] == 'core_text' - print(x['postscript_name'], f'bold={x["bold"]}', f'italic={x["italic"]}', f'weight={x["weight"]:.2f}', f'slant={x["slant"]:.2f}') - print(self.score(x)) + print(CTFace(descriptor=x).postscript_name(), + f'bold={x["bold"]}', f'italic={x["italic"]}', f'weight={x["weight"]:.2f}', f'slant={x["slant"]:.2f}') + print(' ', self.score(x)) print() return candidates diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index cd5b2cc2d..f0ba93a49 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -12,6 +12,7 @@ from kitty.fast_data_types import ( FC_SLANT_ROMAN, FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, + Face, fc_list, ) from kitty.fast_data_types import ( @@ -117,9 +118,9 @@ def weight_range_for_family(family: str) -> WeightRange: bold = w elif s == 'bold' and bold == wr.bold: bold = w - elif s == 'medium': + elif s == 'regular': medium = w - elif s == 'regular' and medium == wr.medium: + elif s == 'medium' and medium == wr.medium: medium = w return WeightRange(mini, maxi, medium, bold) @@ -148,15 +149,17 @@ class FCScorer(Scorer): families = {x['family'] for x in candidates} if len(families) == 1: wr = weight_range_for_family(next(iter(families))) - if wr.is_valid and wr.maximum < 100: # Operator Mono is an example of this craziness + if wr.is_valid and wr.medium < 100: # Operator Mono and Cascadia Code are examples self.weight_range = wr candidates = sorted(candidates, key=self.score) if dump: print(self) + if self.weight_range: + print(self.weight_range) for x in candidates: assert x['descriptor_type'] == 'fontconfig' - print(x['postscript_name'], f'weight={x["weight"]}', f'slant={x["slant"]}') - print(self.score(x)) + print(Face(descriptor=x).postscript_name(), f'weight={x["weight"]}', f'slant={x["slant"]}') + print(' ', self.score(x)) print() return candidates diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index f24f38b7d..5926b332c 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -25,19 +25,13 @@ class Selection(BaseTest): opts = Options() fonts_map = all_fonts_map(True) names = set(fonts_map['family_map']) | set(fonts_map['variable_map']) - def has(x: str) -> bool: - return family_name_to_key(x) in names - has_source_code_pro = has('Source Code Pro') - has_source_code_vf = has('sourcecodeVf') - has_fira_code = has('Fira Code') - has_hack = has('Hack') - has_operator_mono = has('Operator Mono') - del fonts_map, has + del fonts_map def s(family: str, *expected: str) -> None: opts.font_family = parse_font_spec(family) ff = get_font_files(opts) actual = tuple(face_from_descriptor(ff[x]).postscript_name() for x in ('medium', 'bold', 'italic', 'bi')) # type: ignore + del ff with self.subTest(spec=family): self.ae(expected, actual) @@ -45,15 +39,34 @@ class Selection(BaseTest): for family in (family, f'family="{family}"'): s(family, *expected) - if has_source_code_pro: - both('Source Code Pro', 'SourceCodePro-Regular', 'SourceCodePro-Semibold', 'SourceCodePro-It', 'SourceCodePro-SemiboldIt') - if has_source_code_vf: - both('sourcecodeVf', 'SourceCodeVF-Regular', 'SourceCodeVF-Semibold', 'SourceCodeVF-Italic', 'SourceCodeVF-SemiboldItalic') - if has_fira_code: - both('fira code', 'FiraCodeRoman-Regular', 'FiraCodeRoman-SemiBold', 'FiraCodeRoman-Regular', 'FiraCodeRoman-SemiBold') - if has_hack: - both('hack', 'Hack-Regular', 'Hack-Bold', 'Hack-Italic', 'Hack-BoldItalic') - if has_operator_mono: + def has(family): + return family_name_to_key(family) in names + + def t(family, psprefix, bold='Bold', italic='Italic', bi='', reg='Regular'): + if has(family): + bi = bi or bold + italic + if reg: + reg = '-' + reg + both(family, f'{psprefix}{reg}', f'{psprefix}-{bold}', f'{psprefix}-{italic}', f'{psprefix}-{bi}') + + t('Source Code Pro', 'SourceCodePro', 'Semibold', 'It') + t('sourcecodeVf', 'SourceCodeVF', 'Semibold') + t('fira code', 'FiraCodeRoman', 'SemiBold', 'Regular', 'SemiBold') + t('hack', 'Hack') + t('fantasque sans mono', 'FantasqueSansMono') + t('jetbrains mono', 'JetBrainsMono', 'SemiBold') + t('consolas', 'Consolas', reg='') + if has('cascadia code'): + if is_macos: + both('cascadia code', 'CascadiaCode-Regular', 'CascadiaCode-Regular_SemiBold', 'CascadiaCode-Italic', 'CascadiaCode-Italic_SemiBold-Italic') + else: + both('cascadia code', 'CascadiaCodeRoman-Regular', 'CascadiaCodeRoman-SemiBold', 'CascadiaCode-Italic', 'CascadiaCode-SemiBoldItalic') + if has('cascadia mono'): + if is_macos: + both('cascadia mono', 'CascadiaMono-Regular', 'CascadiaMono-Regular_SemiBold', 'CascadiaMono-Italic', 'CascadiaMono-Italic_SemiBold-Italic') + else: + both('cascadia mono', 'CascadiaMonoRoman-Regular', 'CascadiaMonoRoman-SemiBold', 'CascadiaMono-Italic', 'CascadiaMono-SemiBoldItalic') + if has('operator mono'): both('operator mono', 'OperatorMono-Medium', 'OperatorMono-Bold', 'OperatorMono-MediumItalic', 'OperatorMono-BoldItalic')