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;
|
char *family;
|
||||||
int bold, italic, allow_bitmapped_fonts, index = 0, hint_style=0, weight=0, slant=0;
|
int bold, italic, allow_bitmapped_fonts, index = 0, hint_style=0, weight=0, slant=0;
|
||||||
double size_in_pts, dpi;
|
double size_in_pts, dpi;
|
||||||
unsigned int character;
|
PyObject *characters;
|
||||||
FcBool hinting, scalable, outline;
|
FcBool hinting, scalable, outline;
|
||||||
FcChar8 *path = NULL;
|
FcChar8 *path = NULL;
|
||||||
FcPattern *pat = NULL, *match = NULL;
|
FcPattern *pat = NULL, *match = NULL;
|
||||||
@ -21,7 +21,8 @@ get_fontconfig_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
FcCharSet *charset = NULL;
|
FcCharSet *charset = NULL;
|
||||||
PyObject *ans = 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();
|
pat = FcPatternCreate();
|
||||||
if (pat == NULL) return PyErr_NoMemory();
|
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 (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
||||||
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||||
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||||
if (character > 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; }
|
||||||
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");
|
AP(FcPatternAddCharSet, FC_CHARSET, charset, "charset");
|
||||||
}
|
}
|
||||||
#undef AP
|
#undef AP
|
||||||
|
|||||||
@ -27,10 +27,11 @@ def to_bool(x):
|
|||||||
return x.lower() == 'true'
|
return x.lower() == 'true'
|
||||||
|
|
||||||
|
|
||||||
def font_not_found(err, char):
|
def font_not_found(err, chars):
|
||||||
msg = 'Failed to find font'
|
msg = 'Failed to find font'
|
||||||
if char is not None:
|
if chars:
|
||||||
msg = 'Failed to find font for character U+{:X}, error from fontconfig: {}'. format(ord(char[0]), err)
|
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)
|
return FontNotFound(msg)
|
||||||
|
|
||||||
|
|
||||||
@ -40,16 +41,16 @@ def get_font(
|
|||||||
italic=False,
|
italic=False,
|
||||||
allow_bitmaped_fonts=False,
|
allow_bitmaped_fonts=False,
|
||||||
size_in_pts=None,
|
size_in_pts=None,
|
||||||
character=None,
|
characters='',
|
||||||
dpi=None
|
dpi=None
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
path, index, hintstyle, hinting, scalable, outline, weight, slant = get_fontconfig_font(
|
path, index, hintstyle, hinting, scalable, outline, weight, slant = get_fontconfig_font(
|
||||||
family, bold, italic, allow_bitmaped_fonts, size_in_pts or 0,
|
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:
|
except KeyError as err:
|
||||||
raise font_not_found(err, character)
|
raise font_not_found(err, characters)
|
||||||
|
|
||||||
return Font(
|
return Font(
|
||||||
path, hinting, hintstyle, bold, italic, scalable, outline, weight,
|
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,
|
family,
|
||||||
char,
|
chars,
|
||||||
bold=False,
|
bold=False,
|
||||||
italic=False,
|
italic=False,
|
||||||
allow_bitmaped_fonts=False,
|
allow_bitmaped_fonts=False,
|
||||||
@ -70,14 +71,14 @@ def find_font_for_character(
|
|||||||
family,
|
family,
|
||||||
bold,
|
bold,
|
||||||
italic,
|
italic,
|
||||||
character=char,
|
characters=chars,
|
||||||
allow_bitmaped_fonts=allow_bitmaped_fonts,
|
allow_bitmaped_fonts=allow_bitmaped_fonts,
|
||||||
size_in_pts=size_in_pts,
|
size_in_pts=size_in_pts,
|
||||||
dpi=dpi
|
dpi=dpi
|
||||||
)
|
)
|
||||||
if not ans.face or not os.path.exists(ans.face):
|
if not ans.face or not os.path.exists(ans.face):
|
||||||
raise FontNotFound(
|
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
|
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 kitty.utils import ceil_int, get_logical_dpi, safe_print, wcwidth, adjust_line_height
|
||||||
|
|
||||||
from .fontconfig import (
|
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
|
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)
|
@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:
|
if allow_bitmaped_fonts:
|
||||||
return find_font_for_character(
|
return find_font_for_characters(
|
||||||
current_font_family_name,
|
current_font_family_name,
|
||||||
char,
|
char,
|
||||||
bold,
|
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,
|
size_in_pts=cff_size['width'] / 64,
|
||||||
dpi=(cff_size['hres'] + cff_size['vres']) / 2
|
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
|
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]
|
face.underline_thickness, face.units_per_EM, size_in_pts, dpi[1]
|
||||||
)
|
)
|
||||||
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
|
||||||
font_for_char.cache_clear()
|
font_for_text.cache_clear()
|
||||||
alt_face_cache.clear()
|
alt_face_cache.clear()
|
||||||
return cell_width, cell_height
|
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'
|
key = 'regular'
|
||||||
if bold:
|
if bold:
|
||||||
key = 'bi' if italic else 'bold'
|
key = 'bi' if italic else 'bold'
|
||||||
elif italic:
|
elif italic:
|
||||||
key = 'italic'
|
key = 'italic'
|
||||||
font = symbol_map.get(ch)
|
font = symbol_map.get(text[0])
|
||||||
if font is None or not font.face.get_char_index(ch):
|
if font is None or not font_has_text(font, text):
|
||||||
font = current_font_family.get(key) or current_font_family['regular']
|
font = current_font_family.get(key) or current_font_family['regular']
|
||||||
face = font.face
|
face = font.face
|
||||||
if not face.get_char_index(ch):
|
if not font_has_text(font, text):
|
||||||
try:
|
try:
|
||||||
font = font_for_char(ch, bold, italic)
|
font = font_for_text(text, bold, italic)
|
||||||
except FontNotFound:
|
except FontNotFound:
|
||||||
font = font_for_char(
|
font = font_for_text(
|
||||||
ch, bold, italic, allow_bitmaped_fonts=True
|
text, bold, italic, allow_bitmaped_fonts=True
|
||||||
)
|
)
|
||||||
face = alt_face_cache.get(font)
|
face = alt_face_cache.get(font)
|
||||||
if face is None:
|
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):
|
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)
|
return render_using_face(font, face, text, width, italic, bold)
|
||||||
|
|
||||||
|
|
||||||
def render_complex_char(text, bold=False, italic=False, width=1):
|
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
|
import pprint
|
||||||
pprint.pprint(face.shape(text, font.hinting, font.hintstyle))
|
pprint.pprint(face.shape(text, font.hinting, font.hintstyle))
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user