Linux: Allow using fonts with spacing=90 in addition to fonts with spacing=100

Fixes #1968
This commit is contained in:
Kovid Goyal 2019-09-15 06:08:40 +05:30
parent 41b0f8852f
commit bc302dad23
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
4 changed files with 40 additions and 28 deletions

View file

@ -22,6 +22,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Fix incorrect rendering of some symbols when followed by a space while using
the PowerLine font which does not have a space glyph (:iss:`1225`)
- Linux: Allow using fonts with spacing=90 in addition to fonts with
spacing=100 (:iss:`1968`)
0.14.4 [2019-08-31]
---------------------

View file

@ -152,7 +152,7 @@ only monospace fonts, since every cell in the grid has to be the same size. If
your font is not listed in ``kitty list-fonts`` it means that it is not
monospace. On Linux you can list all monospace fonts with::
fc-list : family spacing | grep spacing=100
fc-list : family spacing | grep -e spacing=100 -e spacing=90
Note that the spacing property is calculated by fontconfig based on actual
glyph widths in the font. If for some reason fontconfig concludes your favorite

View file

@ -83,19 +83,19 @@ font_set(FcFontSet *fs) {
static PyObject*
fc_list(PyObject UNUSED *self, PyObject *args) {
int allow_bitmapped_fonts = 0, only_monospaced_fonts = 1;
int allow_bitmapped_fonts = 0, spacing = -1;
PyObject *ans = NULL;
FcObjectSet *os = NULL;
FcPattern *pat = NULL;
FcFontSet *fs = NULL;
if (!PyArg_ParseTuple(args, "|pp", &only_monospaced_fonts, &allow_bitmapped_fonts)) return NULL;
if (!PyArg_ParseTuple(args, "|ip", &spacing, &allow_bitmapped_fonts)) return NULL;
pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory();
if (!allow_bitmapped_fonts) {
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
AP(FcPatternAddBool, FC_SCALABLE, true, "scalable");
}
if (only_monospaced_fonts) AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing");
if (spacing > -1) AP(FcPatternAddInteger, FC_SPACING, spacing, "spacing");
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; }
fs = FcFontList(NULL, pat, os);
@ -147,21 +147,21 @@ end:
static PyObject*
fc_match(PyObject UNUSED *self, PyObject *args) {
char *family = NULL;
int bold = 0, italic = 0, allow_bitmapped_fonts = 0, monospaced = 0;
int bold = 0, italic = 0, allow_bitmapped_fonts = 0, spacing = FC_MONO;
double size_in_pts = 0, dpi = 0;
FcPattern *pat = NULL;
PyObject *ans = NULL;
if (!PyArg_ParseTuple(args, "|zppppdd", &family, &bold, &italic, &monospaced, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL;
if (!PyArg_ParseTuple(args, "|zppppdd", &family, &bold, &italic, &spacing, &allow_bitmapped_fonts, &size_in_pts, &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 (monospaced) {
if (spacing >= FC_DUAL) {
// pass the family,monospace as the family parameter to fc-match,
// which will fallback to using monospace if the family does not match.
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)"monospace", "family");
AP(FcPatternAddInteger, FC_SPACING, FC_MONO, "spacing");
AP(FcPatternAddInteger, FC_SPACING, spacing, "spacing");
}
if (!allow_bitmapped_fonts) {
AP(FcPatternAddBool, FC_OUTLINE, true, "outline");
@ -243,5 +243,9 @@ init_fontconfig_library(PyObject *module) {
PyModule_AddIntMacro(module, FC_WEIGHT_BOLD);
PyModule_AddIntMacro(module, FC_SLANT_ITALIC);
PyModule_AddIntMacro(module, FC_SLANT_ROMAN);
PyModule_AddIntMacro(module, FC_PROPORTIONAL);
PyModule_AddIntMacro(module, FC_DUAL);
PyModule_AddIntMacro(module, FC_MONO);
PyModule_AddIntMacro(module, FC_CHARCELL);
return true;
}

View file

@ -7,7 +7,7 @@
from kitty.fast_data_types import (
FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD, FC_WEIGHT_REGULAR,
fc_list, fc_match as fc_match_impl,
fc_list, fc_match as fc_match_impl, FC_DUAL, FC_MONO
)
attr_map = {(False, False): 'font_family',
@ -32,15 +32,19 @@ def create_font_map(all_fonts):
@lru_cache()
def all_fonts_map(monospaced=True):
return create_font_map(fc_list(monospaced))
if monospaced:
ans = fc_list(FC_DUAL) + fc_list(FC_MONO)
else:
ans = fc_list()
return create_font_map(ans)
def list_fonts():
for fd in fc_list(False):
for fd in fc_list():
f = fd.get('family')
if f:
fn = fd.get('full_name') or (f + ' ' + fd.get('style', '')).strip()
is_mono = fd.get('spacing') == 'MONO'
is_mono = fd.get('spacing') in ('MONO', 'DUAL')
yield {'family': f, 'full_name': fn, 'is_monospace': is_mono}
@ -49,8 +53,8 @@ def family_name_to_key(family):
@lru_cache()
def fc_match(family, bold, italic):
return fc_match_impl(family, bold, italic)
def fc_match(family, bold, italic, spacing=FC_MONO):
return fc_match_impl(family, bold, italic, spacing)
def find_best_match(family, bold=False, italic=False, monospaced=True):
@ -71,20 +75,21 @@ def score(candidate):
return candidates[0]
# Use fc-match to see if we can find a monospaced font that matches family
possibility = fc_match(family, False, False)
for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')):
val = possibility.get(key)
if val:
candidates = font_map[map_key].get(family_name_to_key(val))
if candidates:
if len(candidates) == 1:
# happens if the family name is an alias, so we search with
# the actual family name to see if we can find all the
# fonts in the family.
family_name_candidates = font_map['family_map'].get(family_name_to_key(candidates[0]['family']))
if family_name_candidates and len(family_name_candidates) > 1:
candidates = family_name_candidates
return sorted(candidates, key=score)[0]
for spacing in (FC_MONO, FC_DUAL):
possibility = fc_match(family, False, False, spacing)
for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')):
val = possibility.get(key)
if val:
candidates = font_map[map_key].get(family_name_to_key(val))
if candidates:
if len(candidates) == 1:
# happens if the family name is an alias, so we search with
# the actual family name to see if we can find all the
# fonts in the family.
family_name_candidates = font_map['family_map'].get(family_name_to_key(candidates[0]['family']))
if family_name_candidates and len(family_name_candidates) > 1:
candidates = family_name_candidates
return sorted(candidates, key=score)[0]
# Use fc-match with a generic family
family = 'monospace' if monospaced else 'sans-serif'