mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-06 16:05:05 +02:00
CoreText: Fix selection of font file with multi-file variant font
Also prefer semibold and use similar scorer as fontconfig
This commit is contained in:
@@ -156,10 +156,9 @@ class FontSpec(NamedTuple):
|
||||
|
||||
class Score(NamedTuple):
|
||||
variable_score: int
|
||||
style_score: int
|
||||
style_score: Union[int, float]
|
||||
monospace_score: int
|
||||
width_score: int
|
||||
weight_distance_from_medium: float = 0
|
||||
|
||||
|
||||
Descriptor = Union[FontConfigPattern, CoreTextFont]
|
||||
|
||||
@@ -26,6 +26,7 @@ if TYPE_CHECKING:
|
||||
def is_variable(descriptor: Descriptor) -> bool: ...
|
||||
def set_named_style(name: str, font: Descriptor, vd: VariableData) -> bool: ...
|
||||
def set_axis_values(tag_map: Dict[str, float], font: Descriptor, vd: VariableData) -> bool: ...
|
||||
def dump_sorted_candidates(bold: bool, italic: bool, candidates: List[Descriptor], scorer: Scorer) -> None: ...
|
||||
else:
|
||||
FontCollectionMapType = FontMap = None
|
||||
if is_macos:
|
||||
@@ -33,6 +34,7 @@ else:
|
||||
from kitty.fonts.core_text import (
|
||||
all_fonts_map,
|
||||
create_scorer,
|
||||
dump_sorted_candidates,
|
||||
find_best_match,
|
||||
find_last_resort_text_font,
|
||||
is_monospace,
|
||||
@@ -45,6 +47,7 @@ else:
|
||||
from kitty.fonts.fontconfig import (
|
||||
all_fonts_map,
|
||||
create_scorer,
|
||||
dump_sorted_candidates,
|
||||
find_best_match,
|
||||
find_last_resort_text_font,
|
||||
is_monospace,
|
||||
@@ -55,6 +58,7 @@ else:
|
||||
def face_from_descriptor(descriptor: Descriptor) -> Face: return Face(descriptor=descriptor)
|
||||
|
||||
|
||||
dump_sorted_candidates
|
||||
cache_for_variable_data_by_path: Dict[str, VariableData] = {}
|
||||
attr_map = {(False, False): 'font_family', (True, False): 'bold_font', (False, True): 'italic_font', (True, True): 'bold_italic_font'}
|
||||
|
||||
|
||||
@@ -79,15 +79,22 @@ def create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = T
|
||||
def score(candidate: Descriptor) -> Score:
|
||||
assert candidate['descriptor_type'] == 'core_text'
|
||||
variable_score = 0 if prefer_variable and candidate['variation'] is not None else 1
|
||||
style_match = 1 if candidate['bold'] == bold and candidate[
|
||||
'italic'
|
||||
] == italic else 0
|
||||
monospace_match = 1 if candidate['monospace'] else 0
|
||||
bold_score = candidate['weight'] # -1 to 1 with 0 being normal
|
||||
if bold_score < 0: # thinner than normal, reject
|
||||
bold_score = 2.0
|
||||
else:
|
||||
if bold:
|
||||
# prefer semibold=0.3 to full bold = 0.4
|
||||
bold_score = abs(bold_score - 0.3)
|
||||
italic_score = candidate['slant'] # -1 to 1 with 0 being upright < 0 being backward slant, abs(slant) == 1 implies 30 deg rotation
|
||||
if italic:
|
||||
if italic_score < 0:
|
||||
italic_score = 2.0
|
||||
else:
|
||||
italic_score = abs(1 - italic_score)
|
||||
monospace_match = 0 if candidate['monospace'] else 1
|
||||
is_regular_width = not candidate['expanded'] and not candidate['condensed']
|
||||
# prefer semi-bold to bold to heavy, less bold means less chance of
|
||||
# overflow
|
||||
weight_distance_from_medium = abs(candidate['weight'])
|
||||
return Score(variable_score, 1 - style_match, 1 - monospace_match, 1 - is_regular_width, weight_distance_from_medium)
|
||||
return Score(variable_score, bold_score + italic_score, monospace_match, 0 if is_regular_width else 1)
|
||||
|
||||
return score
|
||||
|
||||
@@ -99,6 +106,14 @@ def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospa
|
||||
return sorted(candidates, key=scorer)[0]
|
||||
|
||||
|
||||
def dump_sorted_candidates(bold: bool, italic: bool, candidates: List[CoreTextFont], scorer: Scorer) -> None:
|
||||
print(f'{bold=} {italic=}')
|
||||
for x in candidates:
|
||||
print(x['postscript_name'], f'bold={x["bold"]}', f'italic={x["italic"]}', f'weight={x["weight"]:.2f}', f'slant={x["slant"]:.2f}')
|
||||
print(scorer(x))
|
||||
print()
|
||||
|
||||
|
||||
def find_best_match(
|
||||
family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: Optional[CoreTextFont] = None,
|
||||
prefer_variable: bool = False
|
||||
@@ -121,7 +136,8 @@ def find_best_match(
|
||||
log_error(f'The font {family} was not found, falling back to Menlo')
|
||||
q = 'menlo'
|
||||
candidates = font_map['family_map'][q]
|
||||
return sorted(candidates, key=scorer)[0]
|
||||
candidates.sort(key=scorer)
|
||||
return candidates[0]
|
||||
|
||||
|
||||
def get_font_from_spec(
|
||||
|
||||
@@ -97,6 +97,14 @@ def create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = T
|
||||
return score
|
||||
|
||||
|
||||
def dump_sorted_candidates(bold: bool, italic: bool, candidates: List[FontConfigPattern], scorer: Scorer) -> None:
|
||||
print(f'{bold=} {italic=}')
|
||||
for x in candidates:
|
||||
print(x['postscript_name'], f'weight={x["weight"]}', f'slant={x["slant"]}')
|
||||
print(scorer(x))
|
||||
print()
|
||||
|
||||
|
||||
def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:
|
||||
# Use fc-match with a generic family
|
||||
family = 'monospace' if monospaced else 'sans-serif'
|
||||
|
||||
Reference in New Issue
Block a user