Allow querying fontconfig for multiple characters

This commit is contained in:
Kovid Goyal 2017-10-25 13:01:39 +05:30
parent 91963095d0
commit c841106902
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 43 additions and 28 deletions

View File

@ -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

View File

@ -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

View File

@ -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))