Refactor font code
DRYer with global state stored in fewer places. And only round-tripping through python for font selection, not face creation.
This commit is contained in:
parent
98e93cb4bd
commit
e5d411b80d
@ -5,6 +5,7 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "state.h"
|
||||
#include "fonts.h"
|
||||
#include <structmember.h>
|
||||
#include <stdint.h>
|
||||
@ -111,13 +112,13 @@ free_font(void *f) {
|
||||
}
|
||||
|
||||
static inline PyObject*
|
||||
ft_face(CTFontRef font, float pt_sz, float xdpi, float ydpi) {
|
||||
ft_face(CTFontRef font) {
|
||||
const char *psname = convert_cfstring(CTFontCopyPostScriptName(font), 1);
|
||||
NSURL *url = (NSURL*)CTFontCopyAttribute(font, kCTFontURLAttribute);
|
||||
PyObject *path = PyUnicode_FromString([[url path] UTF8String]);
|
||||
[url release];
|
||||
if (path == NULL) { CFRelease(font); return NULL; }
|
||||
PyObject *ans = ft_face_from_path_and_psname(path, psname, (void*)font, free_font, true, 3, pt_sz, xdpi, ydpi, CTFontGetLeading(font));
|
||||
PyObject *ans = ft_face_from_path_and_psname(path, psname, (void*)font, free_font, true, 3, CTFontGetLeading(font));
|
||||
Py_DECREF(path);
|
||||
if (ans == NULL) { CFRelease(font); }
|
||||
return ans;
|
||||
@ -141,43 +142,43 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
face_for_text(PyObject UNUSED *self, PyObject UNUSED *args) {
|
||||
char *text;
|
||||
PyObject *lp;
|
||||
float pt_sz, xdpi, ydpi;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(args, "sO!fffpp", &text, &PyLong_Type, &lp, &pt_sz, &xdpi, &ydpi, &bold, &italic)) return NULL;
|
||||
PyObject*
|
||||
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) {
|
||||
PyObject *lp = PyObject_CallMethod(base_face, "extra_data", NULL);
|
||||
if (lp == NULL) return NULL;
|
||||
CTFontRef font = PyLong_AsVoidPtr(lp);
|
||||
Py_CLEAR(lp);
|
||||
static char text[128];
|
||||
cell_as_utf8(cell, true, text, ' ');
|
||||
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
|
||||
if (str == NULL) return PyErr_NoMemory();
|
||||
CTFontRef new_font = find_substitute_face(str, font);
|
||||
CFRelease(str);
|
||||
if (new_font == NULL) return NULL;
|
||||
return ft_face(new_font, pt_sz, xdpi, ydpi);
|
||||
return ft_face(new_font);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
create_face(PyObject UNUSED *self, PyObject *args) {
|
||||
PyObject *descriptor;
|
||||
float point_sz, xdpi, ydpi;
|
||||
if(!PyArg_ParseTuple(args, "Offf", &descriptor, &point_sz, &xdpi, &ydpi)) return NULL;
|
||||
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor) {
|
||||
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
|
||||
if (!desc) return NULL;
|
||||
float scaled_point_sz = ((xdpi + ydpi) / 144.0) * point_sz;
|
||||
float scaled_point_sz = ((global_state.logical_dpi_x + global_state.logical_dpi_y) / 144.0) * global_state.font_sz_in_pts;
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz, NULL);
|
||||
CFRelease(desc); desc = NULL;
|
||||
if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; }
|
||||
return ft_face(font, point_sz, xdpi, ydpi);
|
||||
return ft_face(font);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
specialize_font_descriptor(PyObject *base_descriptor) {
|
||||
Py_INCREF(base_descriptor);
|
||||
return base_descriptor;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(coretext_all_fonts, METH_NOARGS),
|
||||
METHODB(face_for_text, METH_VARARGS),
|
||||
METHODB(create_face, METH_VARARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "data-types.h"
|
||||
#include "state.h"
|
||||
#include "lineops.h"
|
||||
#include "fonts.h"
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#ifndef FC_COLOR
|
||||
#define FC_COLOR "color"
|
||||
@ -117,16 +119,16 @@ end:
|
||||
return ans;
|
||||
}
|
||||
|
||||
static Py_UCS4 char_buf[1024];
|
||||
|
||||
static inline void
|
||||
add_charset(PyObject *characters, FcPattern *pat) {
|
||||
add_charset(FcPattern *pat, size_t num) {
|
||||
FcCharSet *charset = NULL;
|
||||
if (PyUnicode_READY(characters) != 0) goto end;
|
||||
if (PyUnicode_GET_LENGTH(characters) > 0) {
|
||||
if (num) {
|
||||
charset = FcCharSetCreate();
|
||||
if (charset == NULL) { PyErr_NoMemory(); 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))) {
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
if (!FcCharSetAddChar(charset, char_buf[i])) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to add character to fontconfig charset");
|
||||
goto end;
|
||||
}
|
||||
@ -142,11 +144,10 @@ fc_match(PyObject UNUSED *self, PyObject *args) {
|
||||
char *family = NULL;
|
||||
int bold = 0, italic = 0, allow_bitmapped_fonts = 0;
|
||||
double size_in_pts = 0, dpi = 0;
|
||||
PyObject *characters = NULL;
|
||||
FcPattern *pat = NULL;
|
||||
PyObject *ans = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|zpppdO!d", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &PyUnicode_Type, &characters, &dpi)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "|zpppdd", &family, &bold, &italic, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL;
|
||||
pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
|
||||
@ -159,7 +160,6 @@ fc_match(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 (characters) add_charset(characters, pat);
|
||||
ans = _fc_match(pat);
|
||||
|
||||
end:
|
||||
@ -167,22 +167,36 @@ end:
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
fc_font(PyObject UNUSED *self, PyObject *args) {
|
||||
double size_in_pts, dpi;
|
||||
int index;
|
||||
char *path;
|
||||
PyObject *ans = NULL, *chars = NULL;
|
||||
if (!PyArg_ParseTuple(args, "ddsi|O!", &size_in_pts, &dpi, &path, &index, &PyUnicode_Type, &chars)) return NULL;
|
||||
PyObject*
|
||||
specialize_font_descriptor(PyObject *base_descriptor) {
|
||||
PyObject *p = PyDict_GetItemString(base_descriptor, "path"), *ans = NULL;
|
||||
PyObject *idx = PyDict_GetItemString(base_descriptor, "index");
|
||||
if (p == NULL) { PyErr_SetString(PyExc_ValueError, "Base descriptor has no path"); return NULL; }
|
||||
if (idx == NULL) { PyErr_SetString(PyExc_ValueError, "Base descriptor has no index"); return NULL; }
|
||||
FcPattern *pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
if (size_in_pts > 0) { AP(FcPatternAddDouble, FC_SIZE, size_in_pts, "size"); }
|
||||
if (dpi > 0) { AP(FcPatternAddDouble, FC_DPI, dpi, "dpi"); }
|
||||
AP(FcPatternAddString, FC_FILE, (const FcChar8*)path, "path");
|
||||
AP(FcPatternAddInteger, FC_INDEX, index, "index");
|
||||
if (chars) add_charset(chars, pat);
|
||||
AP(FcPatternAddString, FC_FILE, (const FcChar8*)PyUnicode_AsUTF8(p), "path");
|
||||
AP(FcPatternAddInteger, FC_INDEX, PyLong_AsLong(idx), "index");
|
||||
AP(FcPatternAddDouble, FC_SIZE, global_state.font_sz_in_pts, "size");
|
||||
AP(FcPatternAddDouble, FC_DPI, (global_state.logical_dpi_x + global_state.logical_dpi_y) / 2.0, "dpi");
|
||||
ans = _fc_match(pat);
|
||||
end:
|
||||
if (pat != NULL) FcPatternDestroy(pat);
|
||||
return ans;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic) {
|
||||
PyObject *ans = NULL;
|
||||
FcPattern *pat = FcPatternCreate();
|
||||
if (pat == NULL) return PyErr_NoMemory();
|
||||
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)"monospace", "family");
|
||||
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||
size_t num = cell_as_unicode(cell, true, char_buf, ' ');
|
||||
add_charset(pat, num);
|
||||
PyObject *d = _fc_match(pat);
|
||||
if (d) { ans = face_from_descriptor(d); Py_CLEAR(d); }
|
||||
end:
|
||||
if (pat != NULL) FcPatternDestroy(pat);
|
||||
return ans;
|
||||
@ -192,7 +206,6 @@ end:
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(fc_list, METH_VARARGS),
|
||||
METHODB(fc_match, METH_VARARGS),
|
||||
METHODB(fc_font, METH_VARARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
@ -149,12 +149,24 @@ sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height) {
|
||||
sprite_tracker.x = 0; sprite_tracker.y = 0; sprite_tracker.z = 0;
|
||||
}
|
||||
|
||||
static inline PyObject*
|
||||
desc_to_face(PyObject *desc) {
|
||||
PyObject *d = specialize_font_descriptor(desc);
|
||||
if (d == NULL) return NULL;
|
||||
PyObject *ans = face_from_descriptor(d);
|
||||
Py_DECREF(d);
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
alloc_font(Font *f, PyObject *face, bool bold, bool italic) {
|
||||
f->face = face; Py_INCREF(face);
|
||||
alloc_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face) {
|
||||
PyObject *face;
|
||||
if (is_face) { face = descriptor; Py_INCREF(face); }
|
||||
else { face = desc_to_face(descriptor); if (face == NULL) return false; }
|
||||
f->face = face;
|
||||
f->hb_font = harfbuzz_font_for_face(face);
|
||||
if (f->hb_font == NULL) return false;
|
||||
if (f->hb_font == NULL) { PyErr_NoMemory(); return false; }
|
||||
f->bold = bold; f->italic = italic;
|
||||
return true;
|
||||
}
|
||||
@ -178,7 +190,6 @@ clear_font(Font *f) {
|
||||
|
||||
static Font medium_font = {0}, bold_font = {0}, italic_font = {0}, bi_font = {0}, box_font = {0};
|
||||
static Font fallback_fonts[256] = {{0}};
|
||||
static PyObject *get_fallback_font = NULL;
|
||||
typedef enum { FONT, BLANK_FONT, BOX_FONT, MISSING_FONT } FontType;
|
||||
|
||||
typedef struct {
|
||||
@ -206,16 +217,9 @@ python_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t* buf)
|
||||
|
||||
|
||||
static inline PyObject*
|
||||
update_cell_metrics(float pt_sz, float xdpi, float ydpi) {
|
||||
#define CALL(f) { if ((f)->face) { if(!set_size_for_face((f)->face, pt_sz, xdpi, ydpi)) return NULL; (f)->hb_font = harfbuzz_font_for_face((f)->face); } clear_sprite_map((f)); }
|
||||
CALL(&medium_font); CALL(&bold_font); CALL(&italic_font); CALL(&bi_font); CALL(&box_font);
|
||||
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) {
|
||||
CALL(fallback_fonts + i);
|
||||
}
|
||||
for (size_t i = 0; i < symbol_map_fonts_count; i++) {
|
||||
CALL(symbol_map_fonts + i);
|
||||
}
|
||||
#undef CALL
|
||||
update_cell_metrics() {
|
||||
#define CALL(f, desired_height) { if ((f)->face) { if(!set_size_for_face((f)->face, desired_height)) return NULL; (f)->hb_font = harfbuzz_font_for_face((f)->face); } clear_sprite_map((f)); }
|
||||
CALL(&medium_font, 0); CALL(&bold_font, 0); CALL(&italic_font, 0); CALL(&bi_font, 0); CALL(&box_font, 0);
|
||||
cell_metrics(medium_font.face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
||||
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
||||
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
||||
@ -227,14 +231,20 @@ update_cell_metrics(float pt_sz, float xdpi, float ydpi) {
|
||||
global_state.cell_width = cell_width; global_state.cell_height = cell_height;
|
||||
free(canvas); canvas = malloc(CELLS_IN_CANVAS * cell_width * cell_height);
|
||||
if (canvas == NULL) return PyErr_NoMemory();
|
||||
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) {
|
||||
CALL(fallback_fonts + i, cell_height);
|
||||
}
|
||||
for (size_t i = 0; i < symbol_map_fonts_count; i++) {
|
||||
CALL(symbol_map_fonts + i, cell_height);
|
||||
}
|
||||
return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness);
|
||||
#undef CALL
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_font_size(PyObject UNUSED *m, PyObject *args) {
|
||||
float pt_sz, xdpi, ydpi;
|
||||
if (!PyArg_ParseTuple(args, "fff", &pt_sz, &xdpi, &ydpi)) return NULL;
|
||||
return update_cell_metrics(pt_sz, xdpi, ydpi);
|
||||
if (!PyArg_ParseTuple(args, "f", &global_state.font_sz_in_pts)) return NULL;
|
||||
return update_cell_metrics();
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@ -260,13 +270,16 @@ fallback_font(Cell *cell) {
|
||||
return fallback_fonts + i;
|
||||
}
|
||||
}
|
||||
if (get_fallback_font == NULL || i == (sizeof(fallback_fonts)/sizeof(fallback_fonts[0])-1)) return NULL;
|
||||
Py_UCS4 buf[10];
|
||||
size_t n = cell_as_unicode(cell, true, buf, ' ');
|
||||
PyObject *face = PyObject_CallFunction(get_fallback_font, "NOO", PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n), bold ? Py_True : Py_False, italic ? Py_True : Py_False);
|
||||
if (i == (sizeof(fallback_fonts)/sizeof(fallback_fonts[0])-1)) { return NULL; }
|
||||
Font* base_font;
|
||||
if (bold) base_font = italic ? &bi_font : &bold_font;
|
||||
else base_font = italic ? &italic_font : &medium_font;
|
||||
if (!base_font->face) base_font = &medium_font;
|
||||
PyObject *face = create_fallback_face(base_font->face, cell, bold, italic);
|
||||
if (face == NULL) { PyErr_Print(); return NULL; }
|
||||
if (face == Py_None) { Py_DECREF(face); return NULL; }
|
||||
if (!alloc_font(fallback_fonts + i, face, bold, italic)) { Py_DECREF(face); fatal("Out of memory"); }
|
||||
if (!alloc_font(fallback_fonts + i, face, bold, italic, true)) { Py_DECREF(face); fatal("Out of memory"); }
|
||||
set_size_for_face(face, cell_height);
|
||||
Py_DECREF(face);
|
||||
return fallback_fonts + i;
|
||||
}
|
||||
@ -526,15 +539,14 @@ render_line(Line *line) {
|
||||
static PyObject*
|
||||
set_font(PyObject UNUSED *m, PyObject *args) {
|
||||
PyObject *sm, *smf, *medium, *bold = NULL, *italic = NULL, *bi = NULL;
|
||||
float xdpi, ydpi, pt_sz;
|
||||
Py_CLEAR(get_fallback_font); Py_CLEAR(box_drawing_function);
|
||||
if (!PyArg_ParseTuple(args, "OOO!O!fffO|OOO", &get_fallback_font, &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL;
|
||||
Py_INCREF(get_fallback_font); Py_INCREF(box_drawing_function);
|
||||
Py_CLEAR(box_drawing_function);
|
||||
if (!PyArg_ParseTuple(args, "OO!O!fO|OOO", &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &global_state.font_sz_in_pts, &medium, &bold, &italic, &bi)) return NULL;
|
||||
Py_INCREF(box_drawing_function);
|
||||
clear_font(&medium_font); clear_font(&bold_font); clear_font(&italic_font); clear_font(&bi_font); clear_sprite_map(&box_font);
|
||||
if (!alloc_font(&medium_font, medium, false, false)) return PyErr_NoMemory();
|
||||
if (bold && !alloc_font(&bold_font, bold, true, false)) return PyErr_NoMemory();
|
||||
if (italic && !alloc_font(&italic_font, italic, false, true)) return PyErr_NoMemory();
|
||||
if (bi && !alloc_font(&bi_font, bi, true, true)) return PyErr_NoMemory();
|
||||
if (!alloc_font(&medium_font, medium, false, false, false)) return NULL;
|
||||
if (bold && !alloc_font(&bold_font, bold, true, false, false)) return NULL;
|
||||
if (italic && !alloc_font(&italic_font, italic, false, true, false)) return NULL;
|
||||
if (bi && !alloc_font(&bi_font, bi, true, true, false)) return NULL;
|
||||
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) clear_font(fallback_fonts + i);
|
||||
for (size_t i = 0; symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i);
|
||||
free(symbol_maps); free(symbol_map_fonts); symbol_maps = NULL; symbol_map_fonts = NULL;
|
||||
@ -551,7 +563,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
|
||||
PyObject *face;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
|
||||
if (!alloc_font(symbol_map_fonts + i, face, bold != 0, italic != 0)) return PyErr_NoMemory();
|
||||
if (!alloc_font(symbol_map_fonts + i, face, bold != 0, italic != 0, false)) return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < symbol_maps_count; i++) {
|
||||
unsigned int left, right, font_idx;
|
||||
@ -559,14 +571,13 @@ set_font(PyObject UNUSED *m, PyObject *args) {
|
||||
symbol_maps[i].left = left; symbol_maps[i].right = right; symbol_maps[i].font_idx = font_idx;
|
||||
}
|
||||
}
|
||||
return update_cell_metrics(pt_sz, xdpi, ydpi);
|
||||
return update_cell_metrics();
|
||||
}
|
||||
|
||||
static void
|
||||
finalize(void) {
|
||||
Py_CLEAR(python_send_to_gpu_impl);
|
||||
free(canvas);
|
||||
Py_CLEAR(get_fallback_font);
|
||||
Py_CLEAR(box_drawing_function);
|
||||
free_font(&medium_font); free_font(&bold_font); free_font(&italic_font); free_font(&bi_font); free_font(&box_font);
|
||||
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) free_font(fallback_fonts + i);
|
||||
@ -685,6 +696,25 @@ error:
|
||||
#undef SET
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
||||
PyObject *text;
|
||||
int bold, italic;
|
||||
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
|
||||
static Py_UCS4 char_buf[16];
|
||||
if (!PyUnicode_AsUCS4(text, char_buf, sizeof(char_buf)/sizeof(char_buf[0]), 1)) return NULL;
|
||||
Cell cell = {0};
|
||||
cell.ch = char_buf[0];
|
||||
if (PyUnicode_GetLength(text) > 1) cell.cc |= char_buf[1] & CC_MASK;
|
||||
if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16;
|
||||
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
|
||||
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
|
||||
Font *ans = fallback_font(&cell);
|
||||
if (ans == NULL) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
||||
return ans->face;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(set_font_size, METH_VARARGS),
|
||||
METHODB(set_font, METH_VARARGS),
|
||||
@ -696,6 +726,7 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(set_send_sprite_to_gpu, METH_O),
|
||||
METHODB(current_fonts, METH_NOARGS),
|
||||
METHODB(test_render_line, METH_VARARGS),
|
||||
METHODB(get_fallback_font, METH_VARARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
bool face_has_codepoint(PyObject *, char_type);
|
||||
hb_font_t* harfbuzz_font_for_face(PyObject*);
|
||||
bool set_size_for_face(PyObject*, float, float, float);
|
||||
bool set_size_for_face(PyObject*, unsigned int);
|
||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||
void sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z);
|
||||
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, uint8_t *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline);
|
||||
@ -24,5 +24,8 @@ void render_line(Line *line);
|
||||
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
||||
void sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height);
|
||||
typedef void (*free_extra_data_func)(void*);
|
||||
PyObject* ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float size_in_pts, float xdpi, float ydpi, float);
|
||||
PyObject* ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_data, free_extra_data_func fed, int hinting, int hintstyle, float size_in_pts, float xdpi, float ydpi, float);
|
||||
PyObject* ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float);
|
||||
PyObject* ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_data, free_extra_data_func fed, int hinting, int hintstyle, float);
|
||||
PyObject* specialize_font_descriptor(PyObject *base_descriptor);
|
||||
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic);
|
||||
PyObject* face_from_descriptor(PyObject*);
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
|
||||
import re
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from kitty.fast_data_types import coretext_all_fonts, create_face, face_for_text
|
||||
from kitty.fast_data_types import coretext_all_fonts
|
||||
from kitty.utils import safe_print
|
||||
|
||||
attr_map = {(False, False): 'font_family',
|
||||
@ -43,7 +42,7 @@ def list_fonts():
|
||||
yield {'family': f, 'full_name': fn, 'is_monospace': is_mono}
|
||||
|
||||
|
||||
def find_best_match(family, bold, italic):
|
||||
def find_best_match(family, bold=False, italic=False):
|
||||
q = re.sub(r'\s+', ' ', family.lower())
|
||||
font_map = all_fonts_map()
|
||||
|
||||
@ -77,7 +76,7 @@ def find_best_match(family, bold, italic):
|
||||
}
|
||||
|
||||
|
||||
def resolve_family(f, main_family, bold, italic):
|
||||
def resolve_family(f, main_family, bold=False, italic=False):
|
||||
if (bold or italic) and f == 'auto':
|
||||
f = main_family
|
||||
if f.lower() == 'monospace':
|
||||
@ -85,67 +84,20 @@ def resolve_family(f, main_family, bold, italic):
|
||||
return f
|
||||
|
||||
|
||||
FaceDescription = namedtuple(
|
||||
'FaceDescription', 'resolved_family family bold italic'
|
||||
)
|
||||
|
||||
|
||||
def face_description(family, main_family, bold=False, italic=False):
|
||||
return FaceDescription(
|
||||
resolve_family(family, main_family, bold, italic), family, bold, italic
|
||||
)
|
||||
|
||||
|
||||
def get_face(family, font_size, xdpi, ydpi, bold=False, italic=False):
|
||||
descriptor = find_best_match(family, bold, italic)
|
||||
return create_face(descriptor, font_size, xdpi, ydpi)
|
||||
|
||||
|
||||
def get_font_files(opts):
|
||||
ans = {}
|
||||
for (bold, italic), attr in attr_map.items():
|
||||
face = face_description(
|
||||
getattr(opts, attr), opts.font_family, bold, italic
|
||||
)
|
||||
face = find_best_match(resolve_family(getattr(opts, attr), opts.font_family, bold, italic), bold, italic)
|
||||
key = {(False, False): 'medium',
|
||||
(True, False): 'bold',
|
||||
(False, True): 'italic',
|
||||
(True, True): 'bi'}[(bold, italic)]
|
||||
ans[key] = face
|
||||
if key == 'medium':
|
||||
save_medium_face.family = face.resolved_family
|
||||
get_font_files.medium_family = face['family']
|
||||
return ans
|
||||
|
||||
|
||||
def face_from_font(font, pt_sz=11.0, xdpi=72.0, ydpi=72.0):
|
||||
return get_face(font.resolved_family, pt_sz, xdpi, ydpi, bold=font.bold, italic=font.italic)
|
||||
|
||||
|
||||
def save_medium_face(face):
|
||||
save_medium_face.face = face
|
||||
|
||||
|
||||
def font_for_text(text, current_font_family, pt_sz, xdpi, ydpi, bold=False, italic=False):
|
||||
return face_for_text(text, save_medium_face.face.extra_data(), pt_sz, xdpi, ydpi, bold, italic)
|
||||
|
||||
|
||||
def font_for_family(family):
|
||||
ans = face_description(family, save_medium_face.family)
|
||||
return ans, ans.bold, ans.italic
|
||||
|
||||
|
||||
def test_font_matching(
|
||||
name='Menlo', bold=False, italic=False, dpi=72.0, font_size=11.0
|
||||
):
|
||||
all_fonts = create_font_map(coretext_all_fonts())
|
||||
face = get_face(all_fonts, name, 'Menlo', font_size, dpi, dpi, bold, italic)
|
||||
return face
|
||||
|
||||
|
||||
def test_family_matching(name='Menlo', dpi=72.0, font_size=11.0):
|
||||
for bold in (False, True):
|
||||
for italic in (False, True):
|
||||
face = get_face(
|
||||
name, font_size, dpi, dpi, bold, italic
|
||||
)
|
||||
print(bold, italic, face)
|
||||
ans = find_best_match(resolve_family(family, get_font_files.medium_family))
|
||||
return ans, ans['bold'], ans['italic']
|
||||
|
||||
@ -6,8 +6,8 @@ import re
|
||||
from functools import lru_cache
|
||||
|
||||
from kitty.fast_data_types import (
|
||||
FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD, FC_WEIGHT_REGULAR, Face,
|
||||
fc_list, fc_match, fc_font
|
||||
FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD, FC_WEIGHT_REGULAR,
|
||||
fc_list, fc_match,
|
||||
)
|
||||
|
||||
attr_map = {(False, False): 'font_family',
|
||||
@ -66,21 +66,12 @@ def find_best_match(family, bold=False, italic=False, monospaced=True):
|
||||
return fc_match(family, bold, italic)
|
||||
|
||||
|
||||
def face_from_font(font, pt_sz=11.0, xdpi=96.0, ydpi=96.0):
|
||||
font = fc_font(pt_sz, (xdpi + ydpi) / 2.0, font['path'], font.get('index', 0))
|
||||
return Face(font['path'], font.get('index', 0), font.get('hinting', False), font.get('hint_style', 0), pt_sz, xdpi, ydpi)
|
||||
|
||||
|
||||
def resolve_family(f, main_family, bold, italic):
|
||||
if (bold or italic) and f == 'auto':
|
||||
f = main_family
|
||||
return f
|
||||
|
||||
|
||||
def save_medium_face(face):
|
||||
pass
|
||||
|
||||
|
||||
def get_font_files(opts):
|
||||
ans = {}
|
||||
for (bold, italic), attr in attr_map.items():
|
||||
@ -91,18 +82,9 @@ def get_font_files(opts):
|
||||
(False, True): 'italic',
|
||||
(True, True): 'bi'}[(bold, italic)]
|
||||
ans[key] = font
|
||||
if key == 'medium':
|
||||
save_medium_face.medium_font = font
|
||||
return ans
|
||||
|
||||
|
||||
def font_for_family(family):
|
||||
ans = find_best_match(family, monospaced=False)
|
||||
return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN
|
||||
|
||||
|
||||
def font_for_text(text, current_font_family='monospace', pt_sz=11.0, xdpi=96.0, ydpi=96.0, bold=False, italic=False):
|
||||
ans = fc_match('monospace', bold, italic, False, pt_sz, str(text), (xdpi + ydpi) / 2.0)
|
||||
if ans is not None:
|
||||
ans = face_from_font(ans, pt_sz, xdpi, ydpi)
|
||||
return ans
|
||||
|
||||
@ -10,21 +10,16 @@ from math import ceil, floor, pi, sin, sqrt
|
||||
from kitty.config import defaults
|
||||
from kitty.constants import isosx
|
||||
from kitty.fast_data_types import (
|
||||
Screen, change_wcwidth, send_prerendered_sprites, set_font, set_font_size,
|
||||
set_send_sprite_to_gpu, sprite_map_set_limits, test_render_line
|
||||
Screen, change_wcwidth, get_fallback_font, send_prerendered_sprites,
|
||||
set_font, set_font_size, set_logical_dpi, set_send_sprite_to_gpu,
|
||||
sprite_map_set_limits, test_render_line
|
||||
)
|
||||
from kitty.fonts.box_drawing import render_box_char, render_missing_glyph
|
||||
from kitty.utils import get_logical_dpi
|
||||
|
||||
if isosx:
|
||||
from .core_text import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
||||
from .core_text import get_font_files, font_for_family
|
||||
else:
|
||||
from .fontconfig import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
||||
|
||||
|
||||
def create_face(font):
|
||||
s = set_font_family.state
|
||||
return face_from_font(font, s.pt_sz, s.xdpi, s.ydpi)
|
||||
from .fontconfig import get_font_files, font_for_family
|
||||
|
||||
|
||||
def create_symbol_map(opts):
|
||||
@ -34,58 +29,42 @@ def create_symbol_map(opts):
|
||||
for family in val.values():
|
||||
if family not in family_map:
|
||||
font, bold, italic = font_for_family(family)
|
||||
o = create_face(font)
|
||||
family_map[family] = len(faces)
|
||||
faces.append((o, bold, italic))
|
||||
faces.append((font, bold, italic))
|
||||
sm = tuple((a, b, family_map[f]) for (a, b), f in val.items())
|
||||
return sm, tuple(faces)
|
||||
|
||||
|
||||
FontState = namedtuple(
|
||||
'FontState',
|
||||
'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness'
|
||||
'family pt_sz cell_width cell_height baseline underline_position underline_thickness'
|
||||
)
|
||||
|
||||
|
||||
def get_fallback_font(text, bold, italic):
|
||||
state = set_font_family.state
|
||||
return font_for_text(
|
||||
text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold,
|
||||
italic
|
||||
)
|
||||
|
||||
|
||||
def set_font_family(opts=None, override_font_size=None, override_dpi=None):
|
||||
def set_font_family(opts=None, override_font_size=None):
|
||||
opts = opts or defaults
|
||||
sz = override_font_size or opts.font_size
|
||||
xdpi, ydpi = get_logical_dpi(override_dpi)
|
||||
set_font_family.state = FontState('', sz, xdpi, ydpi, 0, 0, 0, 0, 0)
|
||||
font_map = get_font_files(opts)
|
||||
faces = [create_face(font_map['medium'])]
|
||||
save_medium_face(faces[0])
|
||||
faces = [font_map['medium']]
|
||||
for k in 'bold italic bi'.split():
|
||||
if k in font_map:
|
||||
faces.append(create_face(font_map[k]))
|
||||
sm, sfaces = create_symbol_map(opts)
|
||||
faces.append(font_map[k])
|
||||
sm, sfonts = create_symbol_map(opts)
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font(
|
||||
get_fallback_font, render_box_drawing, sm, sfaces, sz, xdpi, ydpi, *faces
|
||||
render_box_drawing, sm, sfonts, sz, *faces
|
||||
)
|
||||
set_font_family.state = FontState(
|
||||
opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline,
|
||||
opts.font_family, sz, cell_width, cell_height, baseline,
|
||||
underline_position, underline_thickness
|
||||
)
|
||||
return cell_width, cell_height
|
||||
|
||||
|
||||
def resize_fonts(new_sz, xdpi=None, ydpi=None):
|
||||
def resize_fonts(new_sz):
|
||||
s = set_font_family.state
|
||||
xdpi = xdpi or s.xdpi
|
||||
ydpi = ydpi or s.ydpi
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(
|
||||
new_sz, xdpi, ydpi
|
||||
)
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz)
|
||||
set_font_family.state = FontState(
|
||||
s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline,
|
||||
s.family, new_sz, cell_width, cell_height, baseline,
|
||||
underline_position, underline_thickness
|
||||
)
|
||||
return cell_width, cell_height
|
||||
@ -175,6 +154,7 @@ def render_box_drawing(codepoint):
|
||||
|
||||
|
||||
def setup_for_testing(family='monospace', size=11.0, dpi=96.0):
|
||||
from kitty.utils import get_logical_dpi
|
||||
opts = defaults._replace(font_family=family)
|
||||
sprites = {}
|
||||
|
||||
@ -183,7 +163,9 @@ def setup_for_testing(family='monospace', size=11.0, dpi=96.0):
|
||||
|
||||
sprite_map_set_limits(100000, 100)
|
||||
set_send_sprite_to_gpu(send_to_gpu)
|
||||
cell_width, cell_height = set_font_family(opts, override_dpi=(dpi, dpi), override_font_size=size)
|
||||
set_logical_dpi(dpi, dpi)
|
||||
get_logical_dpi((dpi, dpi))
|
||||
cell_width, cell_height = set_font_family(opts, override_font_size=size)
|
||||
prerender()
|
||||
return sprites, cell_width, cell_height
|
||||
|
||||
@ -230,8 +212,9 @@ def test_render_string(text='Hello, world!', family='monospace', size=144.0, dpi
|
||||
|
||||
|
||||
def test_fallback_font(qtext=None, bold=False, italic=False):
|
||||
set_font_family(override_dpi=(96.0, 96.0))
|
||||
trials = (qtext,) if qtext else ('你好', 'He\u0347\u0305', '\U0001F929')
|
||||
set_logical_dpi(96.0, 96.0)
|
||||
set_font_family()
|
||||
trials = (qtext,) if qtext else ('你', 'He\u0347\u0305', '\U0001F929')
|
||||
for text in trials:
|
||||
f = get_fallback_font(text, bold, italic)
|
||||
try:
|
||||
|
||||
@ -116,12 +116,12 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
||||
}
|
||||
|
||||
bool
|
||||
set_size_for_face(PyObject *s, float pt_sz, float xdpi, float ydpi) {
|
||||
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height) {
|
||||
Face *self = (Face*)s;
|
||||
FT_UInt w = (FT_UInt)(ceilf(pt_sz * 64));
|
||||
if (self->char_width == w && self->char_height == w && self->xdpi == (FT_UInt)xdpi && self->ydpi == (FT_UInt)ydpi) return true;
|
||||
((Face*)self)->size_in_pts = pt_sz;
|
||||
return set_font_size(self, w, w, (FT_UInt)xdpi, (FT_UInt) ydpi);
|
||||
FT_UInt w = (FT_UInt)(ceilf(global_state.font_sz_in_pts * 64));
|
||||
if (self->char_width == w && self->char_height == w && self->xdpi == (FT_UInt)global_state.logical_dpi_x && self->ydpi == (FT_UInt)global_state.logical_dpi_x) return true;
|
||||
((Face*)self)->size_in_pts = global_state.font_sz_in_pts;
|
||||
return set_font_size(self, w, w, self->xdpi, self->ydpi);
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -136,13 +136,13 @@ get_load_flags(int hinting, int hintstyle, int base) {
|
||||
|
||||
|
||||
static inline bool
|
||||
init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, float size_in_pts, float xdpi, float ydpi) {
|
||||
init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle) {
|
||||
#define CPY(n) self->n = self->face->n;
|
||||
CPY(units_per_EM); CPY(ascender); CPY(descender); CPY(height); CPY(max_advance_width); CPY(max_advance_height); CPY(underline_position); CPY(underline_thickness);
|
||||
#undef CPY
|
||||
self->is_scalable = FT_IS_SCALABLE(self->face);
|
||||
self->hinting = hinting; self->hintstyle = hintstyle;
|
||||
if (!set_size_for_face((PyObject*)self, size_in_pts, xdpi, ydpi)) return false;
|
||||
if (!set_size_for_face((PyObject*)self, 0)) return false;
|
||||
self->harfbuzz_font = hb_ft_font_create(self->face, NULL);
|
||||
if (self->harfbuzz_font == NULL) { PyErr_NoMemory(); return false; }
|
||||
hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT));
|
||||
@ -154,12 +154,12 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, float size_
|
||||
}
|
||||
|
||||
PyObject*
|
||||
ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float size_in_pts, float xdpi, float ydpi, float apple_leading) {
|
||||
ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float apple_leading) {
|
||||
Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);
|
||||
if (ans == NULL) return NULL;
|
||||
int error = FT_New_Memory_Face(library, data, sz, 0, &ans->face);
|
||||
if(error) { set_freetype_error("Failed to load memory face, with error:", error); Py_CLEAR(ans); return NULL; }
|
||||
if (!init_ft_face(ans, path, hinting, hintstyle, size_in_pts, xdpi, ydpi)) { Py_CLEAR(ans); return NULL; }
|
||||
if (!init_ft_face(ans, path, hinting, hintstyle)) { Py_CLEAR(ans); return NULL; }
|
||||
ans->extra_data = extra_data;
|
||||
ans->free_extra_data = fed;
|
||||
ans->apple_leading = apple_leading;
|
||||
@ -186,35 +186,40 @@ load_from_path_and_psname(const char *path, const char* psname, Face *ans) {
|
||||
}
|
||||
|
||||
PyObject*
|
||||
ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_data, free_extra_data_func fed, int hinting, int hintstyle, float size_in_pts, float xdpi, float ydpi, float apple_leading) {
|
||||
ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_data, free_extra_data_func fed, int hinting, int hintstyle, float apple_leading) {
|
||||
if (PyUnicode_READY(path) != 0) return NULL;
|
||||
Face *ans = (Face*)Face_Type.tp_alloc(&Face_Type, 0);
|
||||
if (!ans) return NULL;
|
||||
if (!load_from_path_and_psname(PyUnicode_AsUTF8(path), psname, ans)) { Py_CLEAR(ans); return NULL; }
|
||||
if (!init_ft_face(ans, path, hinting, hintstyle, size_in_pts, xdpi, ydpi)) { Py_CLEAR(ans); return NULL; }
|
||||
if (!init_ft_face(ans, path, hinting, hintstyle)) { Py_CLEAR(ans); return NULL; }
|
||||
ans->extra_data = extra_data;
|
||||
ans->free_extra_data = fed;
|
||||
ans->apple_leading = apple_leading;
|
||||
return (PyObject*)ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
Face *self;
|
||||
#ifndef __APPLE__
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor) {
|
||||
#define D(key, conv) { PyObject *t = PyDict_GetItemString(descriptor, #key); if (t == NULL) return NULL; key = conv(t); t = NULL; }
|
||||
char *path;
|
||||
int error, hinting, hintstyle;
|
||||
long index;
|
||||
float size_in_pts, xdpi, ydpi;
|
||||
if (!PyArg_ParseTuple(args, "sliifff", &path, &index, &hinting, &hintstyle, &size_in_pts, &xdpi, &ydpi)) return NULL;
|
||||
|
||||
self = (Face *)type->tp_alloc(type, 0);
|
||||
bool hinting;
|
||||
long hint_style;
|
||||
D(path, PyUnicode_AsUTF8);
|
||||
D(index, PyLong_AsLong);
|
||||
D(hinting, PyObject_IsTrue);
|
||||
D(hint_style, PyLong_AsLong);
|
||||
#undef D
|
||||
Face *self = (Face *)Face_Type.tp_alloc(&Face_Type, 0);
|
||||
if (self != NULL) {
|
||||
error = FT_New_Face(library, path, index, &(self->face));
|
||||
int error = FT_New_Face(library, path, index, &(self->face));
|
||||
if(error) { set_freetype_error("Failed to load face, with error:", error); Py_CLEAR(self); return NULL; }
|
||||
if (!init_ft_face(self, PyTuple_GET_ITEM(args, 0), hinting, hintstyle, size_in_pts, xdpi, ydpi)) { Py_CLEAR(self); return NULL; }
|
||||
if (!init_ft_face(self, PyDict_GetItemString(descriptor, "path"), hinting, hint_style)) { Py_CLEAR(self); return NULL; }
|
||||
}
|
||||
return (PyObject*)self;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
dealloc(Face* self) {
|
||||
@ -439,7 +444,6 @@ PyTypeObject Face_Type = {
|
||||
.tp_doc = "FreeType Font face",
|
||||
.tp_methods = methods,
|
||||
.tp_members = members,
|
||||
.tp_new = new,
|
||||
.tp_repr = (reprfunc)repr,
|
||||
};
|
||||
|
||||
|
||||
17
kitty/line.c
17
kitty/line.c
@ -181,6 +181,23 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char)
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type zero_char) {
|
||||
size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf);
|
||||
if (include_cc) {
|
||||
char_type cc = cell->cc;
|
||||
Py_UCS4 cc1 = cc & CC_MASK, cc2;
|
||||
if (cc1) {
|
||||
n += encode_utf8(cc1, buf + n);
|
||||
cc2 = cc >> 16;
|
||||
if (cc2) { n += encode_utf8(cc2, buf + n); }
|
||||
}
|
||||
}
|
||||
buf[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) {
|
||||
size_t n = 0;
|
||||
|
||||
@ -55,6 +55,7 @@ index_type line_url_end_at(Line *self, index_type x);
|
||||
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
||||
unsigned int line_length(Line *self);
|
||||
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type);
|
||||
size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type);
|
||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||
|
||||
void linebuf_init_line(LineBuf *, index_type);
|
||||
|
||||
@ -72,6 +72,7 @@ typedef struct {
|
||||
bool application_focused;
|
||||
double cursor_blink_zero_time, last_mouse_activity_at;
|
||||
double logical_dpi_x, logical_dpi_y;
|
||||
float font_sz_in_pts;
|
||||
double mouse_x, mouse_y;
|
||||
bool mouse_button_pressed[20];
|
||||
int viewport_width, viewport_height;
|
||||
|
||||
@ -6,11 +6,12 @@ from collections import OrderedDict
|
||||
|
||||
from kitty.constants import isosx
|
||||
from kitty.fast_data_types import (
|
||||
set_send_sprite_to_gpu, sprite_map_set_layout, sprite_map_set_limits,
|
||||
test_render_line, test_sprite_position_for, wcwidth
|
||||
set_logical_dpi, set_send_sprite_to_gpu, sprite_map_set_layout,
|
||||
sprite_map_set_limits, test_render_line, test_sprite_position_for, wcwidth
|
||||
)
|
||||
from kitty.fonts.box_drawing import box_chars
|
||||
from kitty.fonts.render import render_string, set_font_family, prerender
|
||||
from kitty.fonts.render import prerender, render_string, set_font_family
|
||||
from kitty.utils import get_logical_dpi
|
||||
|
||||
from . import BaseTest
|
||||
|
||||
@ -25,7 +26,9 @@ class Rendering(BaseTest):
|
||||
self.sprites[(x, y, z)] = data
|
||||
|
||||
set_send_sprite_to_gpu(send_to_gpu)
|
||||
self.cell_width, self.cell_height = set_font_family(override_dpi=(96.0, 96.0))
|
||||
set_logical_dpi(96.0, 96.0)
|
||||
get_logical_dpi((96.0, 96.0))
|
||||
self.cell_width, self.cell_height = set_font_family()
|
||||
prerender()
|
||||
self.assertEqual([k[0] for k in self.sprites], [0, 1, 2, 3, 4])
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user