mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-09 23:54:20 +02:00
Code to read features from GSUB/GPOS tables
This commit is contained in:
@@ -946,6 +946,21 @@ get_variation(CTFace *self) {
|
||||
return variation_to_python(src);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_features(CTFace *self, PyObject *a UNUSED) {
|
||||
RAII_PyObject(output, PyFrozenSet_New(NULL)); if (!output) return NULL;
|
||||
RAII_CoreFoundation(CFDataRef, cftable, CTFontCopyTable(self->ct_font, kCTFontTableGSUB, kCTFontTableOptionNoOptions));
|
||||
const uint8_t *table = cftable ? CFDataGetBytePtr(cftable) : NULL;
|
||||
size_t table_len = cftable ? CFDataGetLength(cftable) : 0;
|
||||
if (!read_features_from_font_table(table, table_len, output)) return NULL;
|
||||
RAII_CoreFoundation(CFDataRef, cfpostable, CTFontCopyTable(self->ct_font, kCTFontTableGPOS, kCTFontTableOptionNoOptions));
|
||||
table = cfpostable ? CFDataGetBytePtr(cfpostable) : NULL;
|
||||
table_len = cfpostable ? CFDataGetLength(cfpostable) : 0;
|
||||
if (!read_features_from_font_table(table, table_len, output)) return NULL;
|
||||
Py_INCREF(output); return output;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
get_variable_data(CTFace *self) {
|
||||
if (!ensure_name_table(self)) return NULL;
|
||||
@@ -989,6 +1004,7 @@ static PyMethodDef methods[] = {
|
||||
METHODB(display_name, METH_NOARGS),
|
||||
METHODB(postscript_name, METH_NOARGS),
|
||||
METHODB(get_variable_data, METH_NOARGS),
|
||||
METHODB(get_features, METH_NOARGS),
|
||||
METHODB(get_variation, METH_NOARGS),
|
||||
METHODB(identify_for_debug, METH_NOARGS),
|
||||
METHODB(set_size, METH_VARARGS),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import termios
|
||||
from ctypes import Array, c_ubyte
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload, FrozenSet
|
||||
|
||||
from kitty.boss import Boss
|
||||
from kitty.fonts import FontFeature, VariableData
|
||||
@@ -433,6 +433,7 @@ class Face:
|
||||
def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ...
|
||||
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
||||
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
||||
def get_features(self) -> FrozenSet[str]: ...
|
||||
|
||||
|
||||
class CoreTextFont(TypedDict):
|
||||
@@ -467,6 +468,7 @@ class CTFace:
|
||||
def set_size(self, sz_in_pts: float, dpi_x: float, dpi_y: float) -> None: ...
|
||||
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
||||
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
||||
def get_features(self) -> FrozenSet[str]: ...
|
||||
|
||||
|
||||
def coretext_all_fonts(monospaced_only: bool) -> Tuple[CoreTextFont, ...]:
|
||||
|
||||
@@ -119,6 +119,28 @@ read_name_font_table(const uint8_t *table, size_t table_len) {
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
read_features_from_font_table(const uint8_t *table, size_t table_len, PyObject *output) {
|
||||
if (table_len < 20) return true;
|
||||
const uint16_t *p = (uint16_t*)table;
|
||||
const uint8_t *limit = table + table_len;
|
||||
uint16_t major_version = next, minor_version = next, script_list_offset = next, feature_list_offset = next;
|
||||
(void)major_version; (void)minor_version; (void)script_list_offset;
|
||||
const uint8_t *feature_list_table = table + feature_list_offset;
|
||||
char tag_buf[5] = {0};
|
||||
if (feature_list_table + 2 >= limit) return true;
|
||||
p = (uint16_t*)feature_list_table;
|
||||
uint16_t feature_count = next;
|
||||
const uint8_t *pos = (uint8_t*)p;
|
||||
for (uint16_t i = 0; i < feature_count && pos + 4 < limit; pos += 6, i++) {
|
||||
memcpy(tag_buf, pos, 4);
|
||||
RAII_PyObject(tag, PyUnicode_FromString(tag_buf));
|
||||
if (!tag) return false;
|
||||
if (PySet_Add(output, tag) != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
read_STAT_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output) {
|
||||
RAII_PyObject(design_axes, PyTuple_New(0));
|
||||
|
||||
@@ -52,6 +52,8 @@ bool
|
||||
read_fvar_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);
|
||||
bool
|
||||
read_STAT_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);
|
||||
bool
|
||||
read_features_from_font_table(const uint8_t *table, size_t table_len, PyObject *output);
|
||||
|
||||
static inline void
|
||||
right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {
|
||||
|
||||
@@ -470,16 +470,19 @@ def develop(family: str = '') -> None:
|
||||
opts = Options()
|
||||
opts.font_family = parse_font_spec(family)
|
||||
ff = get_font_files(opts)
|
||||
def s(d: Descriptor) -> str:
|
||||
return str(face_from_descriptor(d))
|
||||
def s(name: str, d: Descriptor) -> None:
|
||||
f = face_from_descriptor(d)
|
||||
print(name, str(f))
|
||||
features = f.get_features()
|
||||
print(' Features :', ' '.join(sorted(features)))
|
||||
|
||||
print('Medium :', s(ff['medium']))
|
||||
s('Medium :', ff['medium'])
|
||||
print()
|
||||
print('Bold :', s(ff['bold']))
|
||||
s('Bold :', ff['bold'])
|
||||
print()
|
||||
print('Italic :', s(ff['italic']))
|
||||
s('Italic :', ff['italic'])
|
||||
print()
|
||||
print('Bold-Italic:', s(ff['bi']))
|
||||
s('Bold-Italic:', ff['bi'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -842,6 +842,31 @@ get_variation(Face *self, PyObject *a UNUSED) {
|
||||
Py_INCREF(ans); return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_features(Face *self, PyObject *a UNUSED) {
|
||||
FT_Error err;
|
||||
FT_ULong length = 0;
|
||||
RAII_PyObject(output, PyFrozenSet_New(NULL)); if (!output) return NULL;
|
||||
if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, NULL, &length)) == 0) {
|
||||
RAII_ALLOC(uint8_t, table, malloc(length));
|
||||
if (!table) return PyErr_NoMemory();
|
||||
if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, table, &length))) {
|
||||
set_freetype_error("Failed to load the GSUB table from font with error:", err); return NULL;
|
||||
}
|
||||
if (!read_features_from_font_table(table, length, output)) return NULL;
|
||||
}
|
||||
length = 0;
|
||||
if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'P', 'O', 'S'), 0, NULL, &length)) == 0) {
|
||||
RAII_ALLOC(uint8_t, table, malloc(length));
|
||||
if (!table) return PyErr_NoMemory();
|
||||
if ((err = FT_Load_Sfnt_Table(self->face, FT_MAKE_TAG('G', 'P', 'O', 'S'), 0, table, &length))) {
|
||||
set_freetype_error("Failed to load the GSUB table from font with error:", err); return NULL;
|
||||
}
|
||||
if (!read_features_from_font_table(table, length, output)) return NULL;
|
||||
}
|
||||
Py_INCREF(output); return output;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_variable_data(Face *self, PyObject *a UNUSED) {
|
||||
if (!ensure_name_table(self)) return NULL;
|
||||
@@ -992,6 +1017,7 @@ static PyMethodDef methods[] = {
|
||||
METHODB(identify_for_debug, METH_NOARGS),
|
||||
METHODB(extra_data, METH_NOARGS),
|
||||
METHODB(get_variable_data, METH_NOARGS),
|
||||
METHODB(get_features, METH_NOARGS),
|
||||
METHODB(get_variation, METH_NOARGS),
|
||||
METHODB(get_best_name, METH_O),
|
||||
METHODB(set_size, METH_VARARGS),
|
||||
|
||||
Reference in New Issue
Block a user