Allow querying fontconfig for multiple characters
This commit is contained in:
parent
91963095d0
commit
c841106902
@ -13,7 +13,7 @@ get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
||||
char *family;
|
||||
int bold, italic, allow_bitmapped_fonts, index = 0, hint_style=0, weight=0, slant=0;
|
||||
double size_in_pts, dpi;
|
||||
unsigned int character;
|
||||
PyObject *characters;
|
||||
FcBool hinting, scalable, outline;
|
||||
FcChar8 *path = NULL;
|
||||
FcPattern *pat = NULL, *match = NULL;
|
||||
@ -21,7 +21,8 @@ get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
||||
FcCharSet *charset = NULL;
|
||||
PyObject *ans = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "spppdId", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &character, &dpi)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "spppdO!d", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &PyUnicode_Type, &characters, &dpi)) return NULL;
|
||||
if (PyUnicode_READY(characters) != 0) return NULL;
|
||||
pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
|
||||
@ -35,10 +36,16 @@ get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
||||
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 (character > 0) {
|
||||
if (PyUnicode_GET_LENGTH(characters) > 0) {
|
||||
charset = FcCharSetCreate();
|
||||
if (charset == NULL) { PyErr_NoMemory(); goto end; }
|
||||
if (!FcCharSetAddChar(charset, character)) { PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset"); goto end; }
|
||||
int kind = PyUnicode_KIND(characters); void *data = PyUnicode_DATA(characters);
|
||||
for (int i = 0; i < PyUnicode_GET_LENGTH(characters); i++) {
|
||||
if (!FcCharSetAddChar(charset, PyUnicode_READ(kind, data, i))) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
|
||||
}
|
||||
#undef AP
|
||||
|
||||
@ -27,10 +27,11 @@ def to_bool(x):
|
||||
return x.lower() == 'true'
|
||||
|
||||
|
||||
def font_not_found(err, char):
|
||||
def font_not_found(err, chars):
|
||||
msg = 'Failed to find font'
|
||||
if char is not None:
|
||||
msg = 'Failed to find font for character U+{:X}, error from fontconfig: {}'. format(ord(char[0]), err)
|
||||
if chars:
|
||||
chars = ', '.join('U+{:X}'.format(ord(c)) for c in chars)
|
||||
msg = 'Failed to find font for characters U+{:X}, error from fontconfig: {}'. format(chars, err)
|
||||
return FontNotFound(msg)
|
||||
|
||||
|
||||
@ -40,16 +41,16 @@ def get_font(
|
||||
italic=False,
|
||||
allow_bitmaped_fonts=False,
|
||||
size_in_pts=None,
|
||||
character=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,
|
||||
0 if character is None else ord(character[0]), dpi or 0
|
||||
characters or '', dpi or 0
|
||||
)
|
||||
except KeyError as err:
|
||||
raise font_not_found(err, character)
|
||||
raise font_not_found(err, characters)
|
||||
|
||||
return Font(
|
||||
path, hinting, hintstyle, bold, italic, scalable, outline, weight,
|
||||
@ -57,9 +58,9 @@ def get_font(
|
||||
)
|
||||
|
||||
|
||||
def find_font_for_character(
|
||||
def find_font_for_characters(
|
||||
family,
|
||||
char,
|
||||
chars,
|
||||
bold=False,
|
||||
italic=False,
|
||||
allow_bitmaped_fonts=False,
|
||||
@ -70,14 +71,14 @@ def find_font_for_character(
|
||||
family,
|
||||
bold,
|
||||
italic,
|
||||
character=char,
|
||||
characters=chars,
|
||||
allow_bitmaped_fonts=allow_bitmaped_fonts,
|
||||
size_in_pts=size_in_pts,
|
||||
dpi=dpi
|
||||
)
|
||||
if not ans.face or not os.path.exists(ans.face):
|
||||
raise FontNotFound(
|
||||
'Failed to find font for character U+{:X}'.format(ord(char[0]))
|
||||
'Failed to find font for characters: {!r}'.format(chars)
|
||||
)
|
||||
return ans
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ from kitty.fonts.box_drawing import render_missing_glyph
|
||||
from kitty.utils import ceil_int, get_logical_dpi, safe_print, wcwidth, adjust_line_height
|
||||
|
||||
from .fontconfig import (
|
||||
FontNotFound, find_font_for_character, font_for_family, get_font_files
|
||||
FontNotFound, find_font_for_characters, font_for_family, get_font_files
|
||||
)
|
||||
|
||||
current_font_family = current_font_family_name = cff_size = cell_width = cell_height = baseline = None
|
||||
@ -41,9 +41,9 @@ def calc_cell_width(font, face):
|
||||
|
||||
|
||||
@lru_cache(maxsize=2**10)
|
||||
def font_for_char(char, bold=False, italic=False, allow_bitmaped_fonts=False):
|
||||
def font_for_text(char, bold=False, italic=False, allow_bitmaped_fonts=False):
|
||||
if allow_bitmaped_fonts:
|
||||
return find_font_for_character(
|
||||
return find_font_for_characters(
|
||||
current_font_family_name,
|
||||
char,
|
||||
bold,
|
||||
@ -52,7 +52,7 @@ def font_for_char(char, bold=False, italic=False, allow_bitmaped_fonts=False):
|
||||
size_in_pts=cff_size['width'] / 64,
|
||||
dpi=(cff_size['hres'] + cff_size['vres']) / 2
|
||||
)
|
||||
return find_font_for_character(
|
||||
return find_font_for_characters(
|
||||
current_font_family_name, char, bold, italic
|
||||
)
|
||||
|
||||
@ -104,7 +104,7 @@ def set_font_family(opts, override_font_size=None):
|
||||
face.underline_thickness, face.units_per_EM, size_in_pts, dpi[1]
|
||||
)
|
||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||
font_for_char.cache_clear()
|
||||
font_for_text.cache_clear()
|
||||
alt_face_cache.clear()
|
||||
return cell_width, cell_height
|
||||
|
||||
@ -152,22 +152,29 @@ def render_using_face(font, face, text, width, italic, bold):
|
||||
)
|
||||
|
||||
|
||||
def face_for_char(ch, bold=False, italic=False):
|
||||
def font_has_text(font, text):
|
||||
for c in text:
|
||||
if not font.face.get_char_index(c):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def face_for_text(text, bold=False, italic=False):
|
||||
key = 'regular'
|
||||
if bold:
|
||||
key = 'bi' if italic else 'bold'
|
||||
elif italic:
|
||||
key = 'italic'
|
||||
font = symbol_map.get(ch)
|
||||
if font is None or not font.face.get_char_index(ch):
|
||||
font = symbol_map.get(text[0])
|
||||
if font is None or not font_has_text(font, text):
|
||||
font = current_font_family.get(key) or current_font_family['regular']
|
||||
face = font.face
|
||||
if not face.get_char_index(ch):
|
||||
if not font_has_text(font, text):
|
||||
try:
|
||||
font = font_for_char(ch, bold, italic)
|
||||
font = font_for_text(text, bold, italic)
|
||||
except FontNotFound:
|
||||
font = font_for_char(
|
||||
ch, bold, italic, allow_bitmaped_fonts=True
|
||||
font = font_for_text(
|
||||
text, bold, italic, allow_bitmaped_fonts=True
|
||||
)
|
||||
face = alt_face_cache.get(font)
|
||||
if face is None:
|
||||
@ -180,12 +187,12 @@ def face_for_char(ch, bold=False, italic=False):
|
||||
|
||||
|
||||
def render_char(text, bold=False, italic=False, width=1):
|
||||
font, face = face_for_char(text[0], bold, italic)
|
||||
font, face = face_for_text(text[0], bold, italic)
|
||||
return render_using_face(font, face, text, width, italic, bold)
|
||||
|
||||
|
||||
def render_complex_char(text, bold=False, italic=False, width=1):
|
||||
font, face = face_for_char(text[0], bold, italic)
|
||||
font, face = face_for_text(text, bold, italic)
|
||||
import pprint
|
||||
pprint.pprint(face.shape(text, font.hinting, font.hintstyle))
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user