mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-24 01:08:10 +02:00
Use fc-list instead of fc-match to select fonts
This is because there is no way to force fc-match to return a monospace font. So if the user specifies a non-existent font we end up with some non-monospaced font, which looks terrible. Also, this makes the font selection logic or macOS and Linux similar.
This commit is contained in:
@@ -28,20 +28,20 @@ pattern_as_dict(FcPattern *pat) {
|
|||||||
#define PS(x) PyUnicode_FromString((char*)x)
|
#define PS(x) PyUnicode_FromString((char*)x)
|
||||||
#define G(type, get, which, conv, name) { \
|
#define G(type, get, which, conv, name) { \
|
||||||
type out; PyObject *p; \
|
type out; PyObject *p; \
|
||||||
if (get(pat, which, 0, &out) == FcResultMatch) { p = conv(out); if (p == NULL) { Py_CLEAR(ans); return NULL; } \
|
if (get(pat, which, 0, &out) == FcResultMatch) { \
|
||||||
} else { p = Py_None; Py_INCREF(p); } \
|
p = conv(out); if (p == NULL) { Py_CLEAR(ans); return NULL; } \
|
||||||
if (PyDict_SetItemString(ans, #name, p) != 0) { Py_CLEAR(p); Py_CLEAR(ans); return NULL; } \
|
if (PyDict_SetItemString(ans, #name, p) != 0) { Py_CLEAR(p); Py_CLEAR(ans); return NULL; } \
|
||||||
Py_CLEAR(p); \
|
Py_CLEAR(p); \
|
||||||
}
|
}}
|
||||||
#define S(which, key) G(FcChar8*, FcPatternGetString, which, PS, key)
|
#define S(which, key) G(FcChar8*, FcPatternGetString, which, PS, key)
|
||||||
#define I(which, key) G(int, FcPatternGetInteger, which, PyLong_FromLong, key)
|
#define I(which, key) G(int, FcPatternGetInteger, which, PyLong_FromLong, key)
|
||||||
#define B(which, key) G(int, FcPatternGetInteger, which, pybool, key)
|
#define B(which, key) G(int, FcPatternGetBool, which, pybool, key)
|
||||||
#define E(which, key, conv) G(int, FcPatternGetInteger, which, conv, key)
|
#define E(which, key, conv) G(int, FcPatternGetInteger, which, conv, key)
|
||||||
S(FC_FILE, path);
|
S(FC_FILE, path);
|
||||||
S(FC_FAMILY, family);
|
S(FC_FAMILY, family);
|
||||||
S(FC_STYLE, style);
|
S(FC_STYLE, style);
|
||||||
S(FC_FULLNAME, fullname);
|
S(FC_FULLNAME, full_name);
|
||||||
S(FC_POSTSCRIPT_NAME, psname);
|
S(FC_POSTSCRIPT_NAME, postscript_name);
|
||||||
I(FC_WEIGHT, weight);
|
I(FC_WEIGHT, weight);
|
||||||
I(FC_SLANT, slant);
|
I(FC_SLANT, slant);
|
||||||
I(FC_HINT_STYLE, hint_style);
|
I(FC_HINT_STYLE, hint_style);
|
||||||
@@ -73,8 +73,10 @@ font_set(FcFontSet *fs) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define AP(func, which, in, desc) if (!func(pat, which, in)) { PyErr_Format(PyExc_ValueError, "Failed to add %s to fontconfig pattern", desc, NULL); goto end; }
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
list_fontconfig_fonts(PyObject UNUSED *self, PyObject *args) {
|
fc_list(PyObject UNUSED *self, PyObject *args) {
|
||||||
int allow_bitmapped_fonts = 0, only_monospaced_fonts = 1;
|
int allow_bitmapped_fonts = 0, only_monospaced_fonts = 1;
|
||||||
PyObject *ans = NULL;
|
PyObject *ans = NULL;
|
||||||
FcObjectSet *os = NULL;
|
FcObjectSet *os = NULL;
|
||||||
@@ -83,13 +85,11 @@ list_fontconfig_fonts(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (!PyArg_ParseTuple(args, "|pp", &only_monospaced_fonts, &allow_bitmapped_fonts)) return NULL;
|
if (!PyArg_ParseTuple(args, "|pp", &only_monospaced_fonts, &allow_bitmapped_fonts)) return NULL;
|
||||||
pat = FcPatternCreate();
|
pat = FcPatternCreate();
|
||||||
if (pat == NULL) return PyErr_NoMemory();
|
if (pat == NULL) return PyErr_NoMemory();
|
||||||
#define AP(func, which, in, desc) if (!func(pat, which, in)) { PyErr_Format(PyExc_ValueError, "Failed to add %s to fontconfig pattern", desc, NULL); goto end; }
|
|
||||||
if (!allow_bitmapped_fonts) {
|
if (!allow_bitmapped_fonts) {
|
||||||
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
|
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
|
||||||
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
|
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
|
||||||
}
|
}
|
||||||
if (only_monospaced_fonts) AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing");
|
if (only_monospaced_fonts) AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing");
|
||||||
#undef AP
|
|
||||||
os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_FULLNAME, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_HINT_STYLE, FC_INDEX, FC_HINTING, FC_SCALABLE, FC_OUTLINE, FC_COLOR, FC_SPACING, NULL);
|
os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_FULLNAME, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_HINT_STYLE, FC_INDEX, FC_HINTING, FC_SCALABLE, FC_OUTLINE, FC_COLOR, FC_SPACING, NULL);
|
||||||
if (!os) { PyErr_SetString(PyExc_ValueError, "Failed to create fontconfig object set"); goto end; }
|
if (!os) { PyErr_SetString(PyExc_ValueError, "Failed to create fontconfig object set"); goto end; }
|
||||||
fs = FcFontList(NULL, pat, os);
|
fs = FcFontList(NULL, pat, os);
|
||||||
@@ -102,34 +102,25 @@ end:
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static inline PyObject*
|
||||||
get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
_fc_match(FcPattern *pat) {
|
||||||
char *family;
|
FcPattern *match = NULL;
|
||||||
int bold, italic, allow_bitmapped_fonts, index = 0, hint_style=0, weight=0, slant=0;
|
|
||||||
double size_in_pts, dpi;
|
|
||||||
PyObject *characters;
|
|
||||||
FcBool hinting, scalable, outline;
|
|
||||||
FcChar8 *path = NULL;
|
|
||||||
FcPattern *pat = NULL, *match = NULL;
|
|
||||||
FcResult result;
|
|
||||||
FcCharSet *charset = NULL;
|
|
||||||
PyObject *ans = NULL;
|
PyObject *ans = NULL;
|
||||||
|
FcResult result;
|
||||||
|
FcConfigSubstitute(NULL, pat, FcMatchPattern);
|
||||||
|
FcDefaultSubstitute(pat);
|
||||||
|
match = FcFontMatch(NULL, pat, &result);
|
||||||
|
if (match == NULL) { PyErr_SetString(PyExc_KeyError, "FcFontMatch() failed"); goto end; }
|
||||||
|
ans = pattern_as_dict(match);
|
||||||
|
end:
|
||||||
|
if (match) FcPatternDestroy(match);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "spppdO!d", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &PyUnicode_Type, &characters, &dpi)) return NULL;
|
static inline void
|
||||||
if (PyUnicode_READY(characters) != 0) return NULL;
|
add_charset(PyObject *characters, FcPattern *pat) {
|
||||||
pat = FcPatternCreate();
|
FcCharSet *charset = NULL;
|
||||||
if (pat == NULL) return PyErr_NoMemory();
|
if (PyUnicode_READY(characters) != 0) goto end;
|
||||||
|
|
||||||
#define AP(func, which, in, desc) if (!func(pat, which, in)) { PyErr_Format(PyExc_RuntimeError, "Failed to add %s to fontconfig patter", desc, NULL); goto end; }
|
|
||||||
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family");
|
|
||||||
if (!allow_bitmapped_fonts) {
|
|
||||||
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
|
|
||||||
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
|
|
||||||
}
|
|
||||||
if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, "size"); }
|
|
||||||
if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
|
||||||
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
|
||||||
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
|
||||||
if (PyUnicode_GET_LENGTH(characters) > 0) {
|
if (PyUnicode_GET_LENGTH(characters) > 0) {
|
||||||
charset = FcCharSetCreate();
|
charset = FcCharSetCreate();
|
||||||
if (charset == NULL) { PyErr_NoMemory(); goto end; }
|
if (charset == NULL) { PyErr_NoMemory(); goto end; }
|
||||||
@@ -142,42 +133,66 @@ get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
|
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
|
||||||
}
|
}
|
||||||
#undef AP
|
end:
|
||||||
FcConfigSubstitute(NULL, pat, FcMatchPattern);
|
if (charset != NULL) FcCharSetDestroy(charset);
|
||||||
FcDefaultSubstitute(pat);
|
}
|
||||||
match = FcFontMatch(NULL, pat, &result);
|
|
||||||
if (match == NULL) { PyErr_SetString(PyExc_KeyError, "FcFontMatch() failed"); goto end; }
|
|
||||||
|
|
||||||
#define GI(func, which, out, desc) \
|
static PyObject*
|
||||||
if (func(match, which, 0, & out) != FcResultMatch) { \
|
fc_match(PyObject UNUSED *self, PyObject *args) {
|
||||||
PyErr_Format(PyExc_RuntimeError, "Failed to get %s from match object", desc, NULL); goto end; \
|
char *family = NULL;
|
||||||
|
int bold = 0, italic = 0, allow_bitmapped_fonts = 0;
|
||||||
|
double size_in_pts = 0, dpi = 0;
|
||||||
|
PyObject *characters = NULL;
|
||||||
|
FcPattern *pat = NULL;
|
||||||
|
PyObject *ans = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "|zpppdO!d", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &PyUnicode_Type, &characters, &dpi)) return NULL;
|
||||||
|
pat = FcPatternCreate();
|
||||||
|
if (pat == NULL) return PyErr_NoMemory();
|
||||||
|
|
||||||
|
if (family && strlen(family) > 0) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family");
|
||||||
|
if (!allow_bitmapped_fonts) {
|
||||||
|
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
|
||||||
|
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
|
||||||
}
|
}
|
||||||
|
if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, "size"); }
|
||||||
GI(FcPatternGetString, FC_FILE, path, "file path");
|
if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
||||||
GI(FcPatternGetInteger, FC_INDEX, index, "face index");
|
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||||
GI(FcPatternGetInteger, FC_WEIGHT, weight, "weight");
|
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||||
GI(FcPatternGetInteger, FC_SLANT, slant, "slant");
|
if (characters) add_charset(characters, pat);
|
||||||
GI(FcPatternGetInteger, FC_HINT_STYLE, hint_style, "hint style");
|
ans = _fc_match(pat);
|
||||||
GI(FcPatternGetBool, FC_HINTING, hinting, "hinting");
|
|
||||||
GI(FcPatternGetBool, FC_SCALABLE, scalable, "scalable");
|
|
||||||
GI(FcPatternGetBool, FC_OUTLINE, outline, "outline");
|
|
||||||
#undef GI
|
|
||||||
|
|
||||||
#define BP(x) (x ? Py_True : Py_False)
|
|
||||||
ans = Py_BuildValue("siiOOOii", path, index, hint_style, BP(hinting), BP(scalable), BP(outline), weight, slant);
|
|
||||||
#undef BP
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (pat != NULL) FcPatternDestroy(pat);
|
if (pat != NULL) FcPatternDestroy(pat);
|
||||||
if (match != NULL) FcPatternDestroy(match);
|
|
||||||
if (charset != NULL) FcCharSetDestroy(charset);
|
|
||||||
if (PyErr_Occurred()) return NULL;
|
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
fc_font(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
double size_in_pts, dpi;
|
||||||
|
int index;
|
||||||
|
char *path;
|
||||||
|
PyObject *ans = NULL, *chars = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "ddsi|O!", &size_in_pts, &dpi, &path, &index, &PyUnicode_Type, &chars)) return NULL;
|
||||||
|
FcPattern *pat = FcPatternCreate();
|
||||||
|
if (pat == NULL) return PyErr_NoMemory();
|
||||||
|
if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, "size"); }
|
||||||
|
if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
||||||
|
AP(FcPatternAddString, FC_FILE, (const FcChar8*)path, "path");
|
||||||
|
AP(FcPatternAddInteger, FC_INDEX, index, "index");
|
||||||
|
if (chars) add_charset(chars, pat);
|
||||||
|
ans = _fc_match(pat);
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (pat != NULL) FcPatternDestroy(pat);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef AP
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
METHODB(list_fontconfig_fonts, METH_VARARGS),
|
METHODB(fc_list, METH_VARARGS),
|
||||||
METHODB(get_fontconfig_font, METH_VARARGS),
|
METHODB(fc_match, METH_VARARGS),
|
||||||
|
METHODB(fc_font, METH_VARARGS),
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -192,5 +207,11 @@ init_fontconfig_library(PyObject *module) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
|
PyModule_AddIntMacro(module, FC_WEIGHT_REGULAR);
|
||||||
|
PyModule_AddIntMacro(module, FC_WEIGHT_MEDIUM);
|
||||||
|
PyModule_AddIntMacro(module, FC_WEIGHT_SEMIBOLD);
|
||||||
|
PyModule_AddIntMacro(module, FC_WEIGHT_BOLD);
|
||||||
|
PyModule_AddIntMacro(module, FC_SLANT_ITALIC);
|
||||||
|
PyModule_AddIntMacro(module, FC_SLANT_ROMAN);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ def font_for_text(text, current_font_family, pt_sz, xdpi, ydpi, bold=False, ital
|
|||||||
|
|
||||||
|
|
||||||
def font_for_family(family):
|
def font_for_family(family):
|
||||||
return face_description(family, save_medium_face.family)
|
ans = face_description(family, save_medium_face.family)
|
||||||
|
return ans, ans.bold, ans.italic
|
||||||
|
|
||||||
|
|
||||||
def test_font_matching(
|
def test_font_matching(
|
||||||
|
|||||||
@@ -2,128 +2,93 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
import re
|
||||||
from collections import namedtuple
|
from functools import lru_cache
|
||||||
|
|
||||||
from kitty.fast_data_types import Face, get_fontconfig_font
|
from kitty.fast_data_types import (
|
||||||
|
FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD, FC_WEIGHT_REGULAR, Face,
|
||||||
|
fc_list, fc_match, fc_font
|
||||||
def face_from_font(font, pt_sz, xdpi, ydpi):
|
|
||||||
return Face(font.path, font.index, font.hinting, font.hintstyle, pt_sz, xdpi, ydpi)
|
|
||||||
|
|
||||||
|
|
||||||
Font = namedtuple(
|
|
||||||
'Font',
|
|
||||||
'path hinting hintstyle bold italic scalable outline weight slant index'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
attr_map = {(False, False): 'font_family',
|
||||||
class FontNotFound(ValueError):
|
(True, False): 'bold_font',
|
||||||
pass
|
(False, True): 'italic_font',
|
||||||
|
(True, True): 'bold_italic_font'}
|
||||||
|
|
||||||
|
|
||||||
def to_bool(x):
|
def create_font_map(all_fonts):
|
||||||
return x.lower() == 'true'
|
ans = {'family_map': {}, 'ps_map': {}, 'full_map': {}}
|
||||||
|
for x in all_fonts:
|
||||||
|
f = (x['family'] or '').lower()
|
||||||
def font_not_found(err, chars):
|
full = (x['full_name'] or '').lower()
|
||||||
msg = 'Failed to find font'
|
ps = (x['postscript_name'] or '').lower()
|
||||||
if chars:
|
ans['family_map'].setdefault(f, []).append(x)
|
||||||
chars = ', '.join('U+{:X}'.format(ord(c)) for c in chars)
|
ans['ps_map'].setdefault(ps, []).append(x)
|
||||||
msg = 'Failed to find font for characters U+{:X}, error from fontconfig: {}'. format(chars, err)
|
ans['full_map'].setdefault(full, []).append(x)
|
||||||
return FontNotFound(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def get_font(
|
|
||||||
family='monospace',
|
|
||||||
bold=False,
|
|
||||||
italic=False,
|
|
||||||
allow_bitmaped_fonts=False,
|
|
||||||
size_in_pts=None,
|
|
||||||
characters='',
|
|
||||||
dpi=None
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
path, index, hintstyle, hinting, scalable, outline, weight, slant = get_fontconfig_font(
|
|
||||||
family, bold, italic, allow_bitmaped_fonts, size_in_pts or 0,
|
|
||||||
characters or '', dpi or 0
|
|
||||||
)
|
|
||||||
except KeyError as err:
|
|
||||||
raise font_not_found(err, characters)
|
|
||||||
|
|
||||||
return Font(
|
|
||||||
path, hinting, hintstyle, bold, italic, scalable, outline, weight,
|
|
||||||
slant, index
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def find_font_for_characters(
|
|
||||||
family,
|
|
||||||
chars,
|
|
||||||
bold=False,
|
|
||||||
italic=False,
|
|
||||||
allow_bitmaped_fonts=False,
|
|
||||||
size_in_pts=None,
|
|
||||||
dpi=None
|
|
||||||
):
|
|
||||||
ans = get_font(
|
|
||||||
family,
|
|
||||||
bold,
|
|
||||||
italic,
|
|
||||||
characters=chars,
|
|
||||||
allow_bitmaped_fonts=allow_bitmaped_fonts,
|
|
||||||
size_in_pts=size_in_pts,
|
|
||||||
dpi=dpi
|
|
||||||
)
|
|
||||||
if not ans.path or not os.path.exists(ans.path):
|
|
||||||
raise FontNotFound(
|
|
||||||
'Failed to find font for characters: {!r}'.format(chars)
|
|
||||||
)
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def font_for_text(text, current_font_family, pt_sz, xdpi, ydpi, bold=False, italic=False):
|
@lru_cache()
|
||||||
dpi = (xdpi + ydpi) / 2
|
def all_fonts_map(monospaced=True):
|
||||||
try:
|
return create_font_map(fc_list(monospaced))
|
||||||
return find_font_for_characters(current_font_family, text, bold=bold, italic=italic, size_in_pts=pt_sz, dpi=dpi)
|
|
||||||
except FontNotFound:
|
|
||||||
return find_font_for_characters(current_font_family, text, bold=bold, italic=italic, size_in_pts=pt_sz, dpi=dpi, allow_bitmaped_fonts=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_font_information(family, bold=False, italic=False):
|
def find_best_match(family, bold=False, italic=False, monospaced=True):
|
||||||
return get_font(family, bold, italic)
|
q = re.sub(r'\s+', ' ', family.lower())
|
||||||
|
font_map = all_fonts_map(monospaced)
|
||||||
|
|
||||||
|
def score(candidate):
|
||||||
|
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate['weight'])
|
||||||
|
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate['slant'])
|
||||||
|
monospace_match = 0 if candidate['spacing'] == 'MONO' else 1
|
||||||
|
return bold_score + italic_score, monospace_match
|
||||||
|
|
||||||
|
# First look for an exact match
|
||||||
|
for selector in ('ps_map', 'full_map', 'family_map'):
|
||||||
|
candidates = font_map[selector].get(q)
|
||||||
|
if candidates:
|
||||||
|
candidates.sort(key=score)
|
||||||
|
return candidates[0]
|
||||||
|
|
||||||
|
# Use fc-match with a generic family
|
||||||
|
family = 'monospace' if monospaced else 'sans-serif'
|
||||||
|
return fc_match(family, bold, italic)
|
||||||
|
|
||||||
|
|
||||||
|
def face_from_font(font, pt_sz=11.0, xdpi=96.0, ydpi=96.0):
|
||||||
|
font = fc_font(pt_sz, (xdpi + ydpi) / 2.0, font['path'], font['index'])
|
||||||
|
return Face(font['path'], font['index'], font['hinting'], font['hint_style'], pt_sz, xdpi, ydpi)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_family(f, main_family, bold, italic):
|
||||||
|
if (bold or italic) and f == 'auto':
|
||||||
|
f = main_family
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def save_medium_face(face):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_font_files(opts):
|
def get_font_files(opts):
|
||||||
ans = {}
|
ans = {}
|
||||||
attr_map = {
|
for (bold, italic), attr in attr_map.items():
|
||||||
'bold': 'bold_font',
|
rf = resolve_family(getattr(opts, attr), opts.font_family, bold, italic)
|
||||||
'italic': 'italic_font',
|
font = find_best_match(rf, bold, italic)
|
||||||
'bi': 'bold_italic_font'
|
key = {(False, False): 'medium',
|
||||||
}
|
(True, False): 'bold',
|
||||||
|
(False, True): 'italic',
|
||||||
def get_family(key=None):
|
(True, True): 'bi'}[(bold, italic)]
|
||||||
ans = getattr(opts, attr_map.get(key, 'font_family'))
|
ans[key] = font
|
||||||
if ans == 'auto' and key:
|
if key == 'medium':
|
||||||
ans = get_family()
|
save_medium_face.medium_font = font
|
||||||
return ans
|
|
||||||
|
|
||||||
n = get_font_information(get_family())
|
|
||||||
ans['medium'] = n
|
|
||||||
|
|
||||||
def do(key):
|
|
||||||
b = get_font_information(
|
|
||||||
get_family(key),
|
|
||||||
bold=key in ('bold', 'bi'),
|
|
||||||
italic=key in ('italic', 'bi')
|
|
||||||
)
|
|
||||||
if b.path != n.path:
|
|
||||||
ans[key] = b
|
|
||||||
|
|
||||||
do('bold'), do('italic'), do('bi')
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def font_for_family(family):
|
def font_for_family(family):
|
||||||
ans = get_font_information(family)
|
ans = find_best_match(family)
|
||||||
return ans
|
return ans, ans['weight'] >= FC_WEIGHT_BOLD, ans['slant'] != FC_SLANT_ROMAN
|
||||||
|
|
||||||
|
|
||||||
|
def font_for_text(text, current_font_family='monospace', pt_sz=11.0, xdpi=96.0, ydpi=96.0, bold=False, italic=False):
|
||||||
|
return fc_match('monospace', bold, italic, False, pt_sz, str(text), (xdpi + ydpi) / 2.0)
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ from kitty.utils import get_logical_dpi
|
|||||||
if isosx:
|
if isosx:
|
||||||
from .core_text import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
from .core_text import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
||||||
else:
|
else:
|
||||||
from .fontconfig import get_font_files, font_for_text, face_from_font, font_for_family
|
from .fontconfig import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
||||||
|
|
||||||
def save_medium_face(f):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def create_face(font):
|
def create_face(font):
|
||||||
@@ -35,10 +32,10 @@ def create_symbol_map(opts):
|
|||||||
faces = []
|
faces = []
|
||||||
for family in val.values():
|
for family in val.values():
|
||||||
if family not in family_map:
|
if family not in family_map:
|
||||||
font = font_for_family(family)
|
font, bold, italic = font_for_family(family)
|
||||||
o = create_face(font)
|
o = create_face(font)
|
||||||
family_map[family] = len(faces)
|
family_map[family] = len(faces)
|
||||||
faces.append((o, font.bold, font.italic))
|
faces.append((o, bold, italic))
|
||||||
sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())
|
sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())
|
||||||
return sm, tuple(faces)
|
return sm, tuple(faces)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user