Make font rendering tests use a text font rather than system font
This commit is contained in:
parent
850dcec4d7
commit
3dca2687d8
5 changed files with 149 additions and 126 deletions
|
|
@ -1137,7 +1137,7 @@ def set_font_data(
|
|||
prerender_func: Callable[
|
||||
[int, int, int, int, int, int, int, float, float, float, float],
|
||||
Tuple[Tuple[int, ...], Tuple[Array[c_ubyte], ...]]],
|
||||
descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
|
||||
descriptor_for_idx: Callable[[int], Tuple[Union[FontObject|str], bool, bool]],
|
||||
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
|
||||
symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,
|
||||
narrow_symbols: Tuple[Tuple[int, int, int], ...],
|
||||
|
|
|
|||
|
|
@ -979,7 +979,7 @@ render_group(
|
|||
|
||||
#define sendtogpu { FontCellMetrics scaled = fg->fcm; current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b); fg->fcm = scaled; }
|
||||
|
||||
if (scale == 1.f) {
|
||||
if (scale == 1.f && rf.scale == 1 && !rf.subscale_n) {
|
||||
for (unsigned i = 0; i < num_cells; i++) {
|
||||
if (!sp[i]->rendered) {
|
||||
sp[i]->rendered = true;
|
||||
|
|
@ -1757,7 +1757,8 @@ initialize_font(FontGroup *fg, unsigned int desc_idx, const char *ftype) {
|
|||
if (d == NULL) { PyErr_Print(); fatal("Failed for %s font", ftype); }
|
||||
bool bold = PyObject_IsTrue(PyTuple_GET_ITEM(d, 1));
|
||||
bool italic = PyObject_IsTrue(PyTuple_GET_ITEM(d, 2));
|
||||
PyObject *face = desc_to_face(PyTuple_GET_ITEM(d, 0), (FONTS_DATA_HANDLE)fg);
|
||||
PyObject *x = PyTuple_GET_ITEM(d, 0);
|
||||
PyObject *face = PyUnicode_Check(x) ? face_from_path(PyUnicode_AsUTF8(x), 0, (FONTS_DATA_HANDLE)fg) : desc_to_face(x, (FONTS_DATA_HANDLE)fg);
|
||||
Py_CLEAR(d);
|
||||
if (face == NULL) { PyErr_Print(); fatal("Failed to convert descriptor to face for %s font", ftype); }
|
||||
size_t idx = fg->fonts_count++;
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ def thickness(level: int = 1, horizontal: bool = True) -> int:
|
|||
return int(math.ceil(pts * (_dpi / 72.0)))
|
||||
|
||||
|
||||
def draw_hline(buf: BufType, width: int, x1: int, x2: int, y: int, level: int, supersample_factor: int = 1) -> None:
|
||||
def draw_hline(buf: BufType, width: int, height: int, x1: int, x2: int, y: int, level: int, supersample_factor: int = 1) -> None:
|
||||
' Draw a horizontal line between [x1, x2) centered at y with the thickness given by level and supersample factor '
|
||||
sz = int(supersample_factor * thickness(level=level, horizontal=False))
|
||||
start = y - sz // 2
|
||||
for y in range(start, start + sz):
|
||||
start = max(0, y - sz // 2)
|
||||
for y in range(start, min(start + sz, height)):
|
||||
offset = y * width
|
||||
for x in range(x1, x2):
|
||||
buf[offset + x] = 255
|
||||
|
|
@ -41,15 +41,15 @@ def draw_hline(buf: BufType, width: int, x1: int, x2: int, y: int, level: int, s
|
|||
def draw_vline(buf: BufType, width: int, y1: int, y2: int, x: int, level: int, supersample_factor: float = 1.0) -> None:
|
||||
' Draw a vertical line between [y1, y2) centered at x with the thickness given by level and supersample factor '
|
||||
sz = int(supersample_factor * thickness(level=level, horizontal=True))
|
||||
start = x - sz // 2
|
||||
for x in range(start, start + sz):
|
||||
start = max(0, x - sz // 2)
|
||||
for x in range(start, min(start + sz, width)):
|
||||
for y in range(y1, y2):
|
||||
buf[x + y * width] = 255
|
||||
|
||||
|
||||
def half_hline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', extend_by: int = 0) -> None:
|
||||
x1, x2 = (0, extend_by + width // 2) if which == 'left' else (width // 2 - extend_by, width)
|
||||
draw_hline(buf, width, x1, x2, height // 2, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2, level)
|
||||
|
||||
|
||||
def half_vline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'top', extend_by: int = 0) -> None:
|
||||
|
|
@ -59,7 +59,7 @@ def half_vline(buf: BufType, width: int, height: int, level: int = 1, which: str
|
|||
|
||||
def get_holes(sz: int, hole_sz: int, num: int) -> list[tuple[int, ...]]:
|
||||
all_holes_use = (num + 1) * hole_sz
|
||||
individual_block_size = (sz - all_holes_use) // (num + 1)
|
||||
individual_block_size = max(1, (sz - all_holes_use) // (num + 1))
|
||||
half_hole_sz = hole_sz // 2
|
||||
pos = - half_hole_sz
|
||||
holes = []
|
||||
|
|
@ -354,7 +354,7 @@ def fading_hline(buf: SSByteArray, width: int, height: int, level: int = 1, num:
|
|||
factor = buf.supersample_factor
|
||||
y = (height // 2 // factor) * factor
|
||||
for x1, x2 in get_fading_lines(width, num, fade):
|
||||
draw_hline(buf, width, x1, x2, y, level, supersample_factor = factor)
|
||||
draw_hline(buf, width, height, x1, x2, y, level, supersample_factor = factor)
|
||||
|
||||
|
||||
@supersampled()
|
||||
|
|
@ -622,9 +622,9 @@ def commit(buf: SSByteArray, width: int, height: int, level: int = 1, scale: flo
|
|||
|
||||
for line in lines:
|
||||
if line == 'horizontal' or line == 'right':
|
||||
draw_hline(buf, width, hwidth, width, hheight, level, supersample_factor=factor)
|
||||
draw_hline(buf, width, height, hwidth, width, hheight, level, supersample_factor=factor)
|
||||
if line == 'horizontal' or line == 'left':
|
||||
draw_hline(buf, width, 0, hwidth, hheight, level, supersample_factor=factor)
|
||||
draw_hline(buf, width, height, 0, hwidth, hheight, level, supersample_factor=factor)
|
||||
if line == 'vertical' or line == 'down':
|
||||
draw_vline(buf, width, hheight, height, hwidth, level, supersample_factor=factor)
|
||||
if line == 'vertical' or line == 'up':
|
||||
|
|
@ -639,9 +639,9 @@ def half_dhline(buf: BufType, width: int, height: int, level: int = 1, which: st
|
|||
x1, x2 = (0, width // 2) if which == 'left' else (width // 2, width)
|
||||
gap = thickness(level + 1, horizontal=False)
|
||||
if only != 'bottom':
|
||||
draw_hline(buf, width, x1, x2, height // 2 - gap, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2 - gap, level)
|
||||
if only != 'top':
|
||||
draw_hline(buf, width, x1, x2, height // 2 + gap, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2 + gap, level)
|
||||
return height // 2 - gap, height // 2 + gap
|
||||
|
||||
|
||||
|
|
@ -692,12 +692,12 @@ def dcorner(buf: BufType, width: int, height: int, level: int = 1, which: str =
|
|||
x2 += vgap
|
||||
else:
|
||||
x1 -= vgap
|
||||
draw_hline(buf, width, x1, x2, height // 2 + ydelta, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2 + ydelta, level)
|
||||
if hw == 'left':
|
||||
x2 -= 2 * vgap
|
||||
else:
|
||||
x1 += 2 * vgap
|
||||
draw_hline(buf, width, x1, x2, height // 2 - ydelta, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2 - ydelta, level)
|
||||
y1, y2 = (0, height // 2) if vw == 'top' else (height // 2, height)
|
||||
xdelta = vgap if hw == 'right' else -vgap
|
||||
yd = thickness(level, horizontal=True) // 2
|
||||
|
|
@ -717,7 +717,7 @@ def dpip(buf: BufType, width: int, height: int, level: int = 1, which: str = '
|
|||
if which in '╟╢':
|
||||
left, right = dvline(buf, width, height)
|
||||
x1, x2 = (0, left) if which == '╢' else (right, width)
|
||||
draw_hline(buf, width, x1, x2, height // 2, level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2, level)
|
||||
else:
|
||||
top, bottom = dhline(buf, width, height)
|
||||
y1, y2 = (0, top) if which == '╧' else (bottom, height)
|
||||
|
|
@ -730,7 +730,7 @@ def inner_corner(buf: BufType, width: int, height: int, which: str = 'tl', level
|
|||
vthick = thickness(level, horizontal=True) // 2
|
||||
x1, x2 = (0, width // 2 - hgap + vthick + 1) if 'l' in which else (width // 2 + hgap - vthick, width)
|
||||
yd = -1 if 't' in which else 1
|
||||
draw_hline(buf, width, x1, x2, height // 2 + (yd * vgap), level)
|
||||
draw_hline(buf, width, height, x1, x2, height // 2 + (yd * vgap), level)
|
||||
y1, y2 = (0, height // 2 - vgap) if 't' in which else (height // 2 + vgap, height)
|
||||
xd = -1 if 'l' in which else 1
|
||||
draw_vline(buf, width, y1, y2, width // 2 + (xd * hgap), level)
|
||||
|
|
|
|||
|
|
@ -163,8 +163,14 @@ def create_narrow_symbols(opts: Options) -> tuple[tuple[int, int, int], ...]:
|
|||
return tuple((a, b, v) for (a, b), v in coalesce_symbol_maps(opts.narrow_symbols).items())
|
||||
|
||||
|
||||
def descriptor_for_idx(idx: int) -> tuple[FontObject, bool, bool]:
|
||||
return current_faces[idx]
|
||||
descriptor_overrides: dict[int, tuple[str, bool, bool]] = {}
|
||||
|
||||
|
||||
def descriptor_for_idx(idx: int) -> tuple[Union[FontObject | str], bool, bool]:
|
||||
ans = descriptor_overrides.get(idx)
|
||||
if ans is None:
|
||||
return current_faces[idx]
|
||||
return ans
|
||||
|
||||
|
||||
def dump_font_debug() -> None:
|
||||
|
|
@ -431,10 +437,12 @@ def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: f
|
|||
|
||||
class setup_for_testing:
|
||||
|
||||
def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0):
|
||||
def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, main_face_path: str = ''):
|
||||
self.family, self.size, self.dpi = family, size, dpi
|
||||
self.main_face_path = main_face_path
|
||||
|
||||
def __enter__(self) -> tuple[dict[tuple[int, int, int], bytes], int, int]:
|
||||
global descriptor_overrides
|
||||
opts = defaults._replace(font_family=parse_font_spec(self.family), font_size=self.size)
|
||||
set_options(opts)
|
||||
sprites = {}
|
||||
|
|
@ -444,6 +452,10 @@ def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:
|
|||
|
||||
sprite_map_set_limits(100000, 100)
|
||||
set_send_sprite_to_gpu(send_to_gpu)
|
||||
self.orig_desc_overrides = descriptor_overrides
|
||||
descriptor_overrides = {}
|
||||
if self.main_face_path:
|
||||
descriptor_overrides[0] = self.main_face_path, False, False
|
||||
try:
|
||||
set_font_family(opts)
|
||||
cell_width, cell_height = create_test_font_group(self.size, self.dpi, self.dpi)
|
||||
|
|
@ -453,6 +465,8 @@ def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:
|
|||
raise
|
||||
|
||||
def __exit__(self, *args: Any) -> None:
|
||||
global descriptor_overrides
|
||||
descriptor_overrides = self.orig_desc_overrides
|
||||
set_send_sprite_to_gpu(None)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from functools import partial
|
||||
from functools import lru_cache, partial
|
||||
from math import ceil
|
||||
|
||||
from kitty.constants import is_macos, read_kitty_resource
|
||||
|
|
@ -32,6 +32,11 @@ def parse_font_spec(spec):
|
|||
return FontSpec.from_setting(spec)
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def testing_font_data(name):
|
||||
return read_kitty_resource(name, __name__.rpartition('.')[0])
|
||||
|
||||
|
||||
class Selection(BaseTest):
|
||||
|
||||
def test_font_selection(self):
|
||||
|
|
@ -165,28 +170,110 @@ def t(x, **kw):
|
|||
self.ae(face_from_descriptor(ff['medium']).applied_features(), {'dlig': 'dlig', 'test': 'test=3'})
|
||||
self.ae(face_from_descriptor(ff['bold']).applied_features(), {'dlig': 'dlig', 'test': 'test=3'})
|
||||
|
||||
def block_helpers(s, sprites, cell_width, cell_height):
|
||||
block_size = cell_width * cell_height * 4
|
||||
|
||||
class Rendering(BaseTest):
|
||||
def full_block():
|
||||
return b'\xff' * block_size
|
||||
|
||||
def empty_block():
|
||||
return b'\0' * block_size
|
||||
|
||||
def half_block(first=b'\xff', second=b'\0', swap=False):
|
||||
frac = 0.5
|
||||
height = ceil(frac * cell_height)
|
||||
rest = cell_height - height
|
||||
if swap:
|
||||
height, rest = rest, height
|
||||
first, second = second, first
|
||||
return (first * (height * cell_width * 4)) + (second * rest * cell_width * 4)
|
||||
|
||||
def quarter_block():
|
||||
frac = 0.5
|
||||
height = ceil(frac * cell_height)
|
||||
width = ceil(frac * cell_width)
|
||||
ans = array.array('I', b'\0' * block_size)
|
||||
for y in range(height):
|
||||
pos = cell_width * y
|
||||
for x in range(width):
|
||||
ans[pos + x] = 0xffffffff
|
||||
return ans.tobytes()
|
||||
|
||||
def upper_half_block():
|
||||
return half_block()
|
||||
|
||||
def lower_half_block():
|
||||
return half_block(swap=True)
|
||||
|
||||
def block_as_str(a):
|
||||
pixels = array.array('I', a)
|
||||
def row(y):
|
||||
pos = y * cell_width
|
||||
return ' '.join(f'{int(pixels[pos + x] != 0)}' for x in range(cell_width))
|
||||
return '\n'.join(row(y) for y in range(cell_height))
|
||||
|
||||
def assert_blocks(a, b, msg=''):
|
||||
if a != b:
|
||||
msg = msg or 'block not equal'
|
||||
if len(a) != len(b):
|
||||
assert_blocks.__msg = msg + f'block lengths not equal: {len(a)/4} != {len(b)/4}'
|
||||
else:
|
||||
assert_blocks.__msg = msg + '\n' + block_as_str(a) + '\n\n' + block_as_str(b)
|
||||
del a, b
|
||||
raise AssertionError(assert_blocks.__msg)
|
||||
|
||||
def multiline_render(text, scale=1, width=1, **kw):
|
||||
s.reset()
|
||||
draw_multicell(s, text, scale=scale, width=width, **kw)
|
||||
ans = []
|
||||
for y in range(scale):
|
||||
line = s.line(y)
|
||||
test_render_line(line)
|
||||
for x in range(width * scale):
|
||||
ans.append(sprites[line.sprite_at(x)])
|
||||
return ans
|
||||
|
||||
def block_test(*expected, **kw):
|
||||
for i, (expected, actual) in enumerate(zip(expected, multiline_render(kw.pop('text', '█'), **kw), strict=True)):
|
||||
assert_blocks(expected(), actual, f'Block {i} is not equal')
|
||||
|
||||
|
||||
return full_block, empty_block, upper_half_block, lower_half_block, quarter_block, block_as_str, block_test
|
||||
|
||||
|
||||
class FontBaseTest(BaseTest):
|
||||
|
||||
font_size = 5.0
|
||||
dpi = 72.
|
||||
font_name = 'FiraCode-Medium.otf'
|
||||
|
||||
def path_for_font(self, name):
|
||||
if name not in self.font_path_cache:
|
||||
with open(os.path.join(self.tdir, name), 'wb') as f:
|
||||
self.font_path_cache[name] = f.name
|
||||
f.write(testing_font_data(name))
|
||||
return self.font_path_cache[name]
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.test_ctx = setup_for_testing()
|
||||
self.test_ctx.__enter__()
|
||||
self.sprites, self.cell_width, self.cell_height = self.test_ctx.__enter__()
|
||||
try:
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
except Exception:
|
||||
self.test_ctx.__exit__()
|
||||
del self.test_ctx
|
||||
raise
|
||||
self.font_path_cache = {}
|
||||
self.tdir = tempfile.mkdtemp()
|
||||
self.addCleanup(self.rmtree_ignoring_errors, self.tdir)
|
||||
path = self.path_for_font(self.font_name) if self.font_name else ''
|
||||
tc = setup_for_testing(size=self.font_size, dpi=self.dpi, main_face_path=path)
|
||||
self.sprites, self.cell_width, self.cell_height = tc.__enter__()
|
||||
self.addCleanup(tc.__exit__)
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
def tearDown(self):
|
||||
self.test_ctx.__exit__()
|
||||
del self.sprites, self.cell_width, self.cell_height, self.test_ctx
|
||||
self.rmtree_ignoring_errors(self.tdir)
|
||||
del self.sprites, self.cell_width, self.cell_height
|
||||
self.font_path_cache = {}
|
||||
super().tearDown()
|
||||
|
||||
|
||||
|
||||
class Rendering(FontBaseTest):
|
||||
|
||||
def test_sprite_map(self):
|
||||
sprite_map_set_limits(10, 2)
|
||||
sprite_map_set_layout(5, 5)
|
||||
|
|
@ -210,86 +297,17 @@ def test_box_drawing(self):
|
|||
self.assertEqual(len(self.sprites) - prerendered, len(box_chars))
|
||||
|
||||
def test_scaled_box_drawing(self):
|
||||
block_size = self.cell_width * self.cell_height * 4
|
||||
|
||||
def full_block():
|
||||
return b'\xff' * block_size
|
||||
|
||||
def empty_block():
|
||||
return b'\0' * block_size
|
||||
|
||||
def half_block(first=b'\xff', second=b'\0'):
|
||||
frac = 0.5
|
||||
height = ceil(frac * self.cell_height)
|
||||
rest = self.cell_height - height
|
||||
return (first * (rest * self.cell_width * 4)) + (second * height * self.cell_width * 4)
|
||||
|
||||
def quarter_block():
|
||||
frac = 0.5
|
||||
height = ceil(frac * self.cell_height)
|
||||
width = ceil(frac * self.cell_width)
|
||||
ans = array.array('I', b'\0' * block_size)
|
||||
self.ae(len(ans), self.cell_width * self.cell_height)
|
||||
for y in range(height):
|
||||
pos = self.cell_width * y
|
||||
for x in range(width):
|
||||
ans[pos + x] = 0xffffffff
|
||||
return ans.tobytes()
|
||||
|
||||
def upper_half_block():
|
||||
return half_block()
|
||||
|
||||
def lower_half_block():
|
||||
return half_block(b'\0', b'\xff')
|
||||
|
||||
def block_as_str(a):
|
||||
pixels = array.array('I', a)
|
||||
def row(y):
|
||||
pos = y * self.cell_width
|
||||
return ' '.join(f'{int(pixels[pos + x] != 0)}' for x in range(self.cell_width))
|
||||
return '\n'.join(row(y) for y in range(self.cell_height))
|
||||
|
||||
def assert_blocks(a, b, msg=''):
|
||||
if a != b:
|
||||
assert_blocks.__msg = (msg or 'block not equal') + '\n' + block_as_str(a) + '\n\n' + block_as_str(b)
|
||||
del a, b
|
||||
raise AssertionError(assert_blocks.__msg)
|
||||
|
||||
s = self.create_screen(cols=8, lines=8, scrollback=0)
|
||||
|
||||
s.reset()
|
||||
before = len(self.sprites)
|
||||
draw_multicell(s, '██', scale=1, subscale_n=1, subscale_d=2, vertical_align=0, width=1)
|
||||
test_render_line(s.line(0))
|
||||
self.ae(len(self.sprites), before + 1)
|
||||
assert_blocks(upper_half_block(), self.sprites[tuple(self.sprites)[before]])
|
||||
|
||||
s.reset()
|
||||
before = len(self.sprites)
|
||||
draw_multicell(s, '█', scale=1, subscale_n=1, subscale_d=2, vertical_align=0)
|
||||
test_render_line(s.line(0))
|
||||
self.ae(len(self.sprites), before + 1)
|
||||
assert_blocks(quarter_block(), self.sprites[tuple(self.sprites)[before]])
|
||||
|
||||
def block_test(a=empty_block, b=empty_block, c=empty_block, d=empty_block, scale=2, half_block=True, vertical_align=0):
|
||||
s.reset()
|
||||
before = len(self.sprites)
|
||||
subscale_n = subscale_d = 0
|
||||
if half_block:
|
||||
subscale_n, subscale_d = 1, 2
|
||||
draw_multicell(s, '█', scale=scale, subscale_n=subscale_n, subscale_d=subscale_d, vertical_align=vertical_align)
|
||||
test_render_line(s.line(0))
|
||||
self.ae(len(self.sprites), before + 2)
|
||||
test_render_line(s.line(1))
|
||||
self.ae(len(self.sprites), before + 4)
|
||||
blocks = tuple(self.sprites)[before:]
|
||||
for i, (expected, actual) in enumerate(zip((a(), b(), c(), d()), blocks)):
|
||||
assert_blocks(expected, self.sprites[actual], f'The {i} block differs')
|
||||
|
||||
block_test(full_block, full_block, full_block, full_block, half_block=False)
|
||||
block_test(a=full_block)
|
||||
block_test(c=full_block, vertical_align=1)
|
||||
block_test(a=lower_half_block, c=upper_half_block, vertical_align=2)
|
||||
full_block, empty_block, upper_half_block, lower_half_block, quarter_block, block_as_str, block_test = block_helpers(
|
||||
s, self.sprites, self.cell_width, self.cell_height)
|
||||
block_test(full_block)
|
||||
block_test(full_block, full_block, full_block, full_block, scale=2)
|
||||
block_test(full_block, empty_block, empty_block, empty_block, scale=2, subscale_n=1, subscale_d=2)
|
||||
block_test(full_block, full_block, empty_block, empty_block, scale=2, subscale_n=1, subscale_d=2, text='██')
|
||||
block_test(empty_block, empty_block, full_block, empty_block, scale=2, subscale_n=1, subscale_d=2, vertical_align=1)
|
||||
block_test(quarter_block, scale=1, subscale_n=1, subscale_d=2)
|
||||
block_test(upper_half_block, scale=1, subscale_n=1, subscale_d=2, text='██')
|
||||
block_test(lower_half_block, scale=1, subscale_n=1, subscale_d=2, text='██', vertical_align=1)
|
||||
|
||||
def test_font_rendering(self):
|
||||
render_string('ab\u0347\u0305你好|\U0001F601|\U0001F64f|\U0001F63a|')
|
||||
|
|
@ -306,18 +324,8 @@ def test_font_rendering(self):
|
|||
|
||||
def test_shaping(self):
|
||||
|
||||
font_path_cache = {}
|
||||
|
||||
def path_for_font(name):
|
||||
if name not in font_path_cache:
|
||||
with open(os.path.join(self.tdir, name), 'wb') as f:
|
||||
font_path_cache[name] = f.name
|
||||
data = read_kitty_resource(name, __name__.rpartition('.')[0])
|
||||
f.write(data)
|
||||
return font_path_cache[name]
|
||||
|
||||
def ss(text, font=None):
|
||||
path = path_for_font(font) if font else None
|
||||
path = self.path_for_font(font) if font else None
|
||||
return shape_string(text, path=path)
|
||||
|
||||
def groups(text, font=None):
|
||||
|
|
|
|||
Loading…
Reference in a new issue