mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-27 02:31:45 +02:00
Code to get variable data from freetype to python
This commit is contained in:
@@ -416,8 +416,29 @@ def fc_match_postscript_name(
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class VariableAxis(TypedDict):
|
||||||
|
minimum: float
|
||||||
|
maximum: float
|
||||||
|
default: float
|
||||||
|
hidden: bool
|
||||||
|
tag: str
|
||||||
|
strid: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class NamedStyle(TypedDict):
|
||||||
|
axis_values: Tuple[float, ...]
|
||||||
|
name: Optional[str]
|
||||||
|
psname: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
|
class VariableData(TypedDict):
|
||||||
|
axes: Tuple[VariableAxis, ...]
|
||||||
|
named_styles: Tuple[NamedStyle, ...]
|
||||||
|
|
||||||
|
|
||||||
class Face:
|
class Face:
|
||||||
def __init__(self, descriptor: Optional[FontConfigPattern] = None, path: str = '', index: int = 0): ...
|
def __init__(self, descriptor: Optional[FontConfigPattern] = None, path: str = '', index: int = 0): ...
|
||||||
|
def get_variable_data(self) -> VariableData: ...
|
||||||
|
|
||||||
|
|
||||||
class CoreTextFont(TypedDict):
|
class CoreTextFont(TypedDict):
|
||||||
|
|||||||
@@ -178,4 +178,4 @@ def font_for_family(family: str) -> Tuple[FontConfigPattern, bool, bool]:
|
|||||||
|
|
||||||
def get_variable_data_for_descriptor(fd: FontConfigPattern) -> None:
|
def get_variable_data_for_descriptor(fd: FontConfigPattern) -> None:
|
||||||
f = Face(descriptor=fd)
|
f = Face(descriptor=fd)
|
||||||
print(f)
|
print(f.get_variable_axes())
|
||||||
|
|||||||
151
kitty/freetype.c
151
kitty/freetype.c
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include FT_BITMAP_H
|
#include FT_BITMAP_H
|
||||||
#include FT_TRUETYPE_TABLES_H
|
#include FT_TRUETYPE_TABLES_H
|
||||||
|
#include FT_MULTIPLE_MASTERS_H
|
||||||
|
#include FT_SFNT_NAMES_H
|
||||||
|
|
||||||
typedef union FaceIndex {
|
typedef union FaceIndex {
|
||||||
struct {
|
struct {
|
||||||
@@ -46,6 +48,7 @@ typedef struct {
|
|||||||
void *extra_data;
|
void *extra_data;
|
||||||
free_extra_data_func free_extra_data;
|
free_extra_data_func free_extra_data;
|
||||||
float apple_leading;
|
float apple_leading;
|
||||||
|
PyObject *name_lookup_table;
|
||||||
} Face;
|
} Face;
|
||||||
PyTypeObject Face_Type;
|
PyTypeObject Face_Type;
|
||||||
|
|
||||||
@@ -307,6 +310,7 @@ dealloc(Face* self) {
|
|||||||
if (self->face) FT_Done_Face(self->face);
|
if (self->face) FT_Done_Face(self->face);
|
||||||
if (self->extra_data && self->free_extra_data) self->free_extra_data(self->extra_data);
|
if (self->extra_data && self->free_extra_data) self->free_extra_data(self->extra_data);
|
||||||
Py_CLEAR(self->path);
|
Py_CLEAR(self->path);
|
||||||
|
Py_CLEAR(self->name_lookup_table);
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,6 +700,151 @@ extra_data(PyObject *self, PyObject *a UNUSED) {
|
|||||||
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
|
return PyLong_FromVoidPtr(((Face*)self)->extra_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NAME table {{{
|
||||||
|
static PyObject*
|
||||||
|
decode_name_record(PyObject *namerec) {
|
||||||
|
#define d(x) PyLong_AsUnsignedLong(PyTuple_GET_ITEM(namerec, x))
|
||||||
|
unsigned long platform_id = d(0), encoding_id = d(1), language_id = d(2);
|
||||||
|
#undef d
|
||||||
|
const char *encoding = "unicode_escape";
|
||||||
|
if ((platform_id == 3 && encoding_id == 1) || platform_id == 0) encoding = "utf-16-be";
|
||||||
|
else if (platform_id == 1 && encoding_id == 0 && language_id == 0) encoding = "mac-roman";
|
||||||
|
PyObject *b = PyTuple_GET_ITEM(namerec, 3);
|
||||||
|
return PyUnicode_Decode(PyBytes_AS_STRING(b), PyBytes_GET_SIZE(b), encoding, "replace");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ensure_name_table(Face *self) {
|
||||||
|
if (self->name_lookup_table) return true;
|
||||||
|
RAII_PyObject(ans, PyDict_New());
|
||||||
|
if (!ans) return false;
|
||||||
|
FT_SfntName temp;
|
||||||
|
for (FT_UInt i = 0; i < FT_Get_Sfnt_Name_Count(self->face); i++) {
|
||||||
|
FT_Error err = FT_Get_Sfnt_Name(self->face, i, &temp);
|
||||||
|
if (err != 0) continue;
|
||||||
|
RAII_PyObject(key, PyLong_FromUnsignedLong((unsigned long)temp.name_id));
|
||||||
|
if (!key) return false;
|
||||||
|
RAII_PyObject(list, PyDict_GetItem(ans, key));
|
||||||
|
if (list == NULL) {
|
||||||
|
list = PyList_New(0);
|
||||||
|
if (!list) return false;
|
||||||
|
if (PyDict_SetItem(ans, key, list) != 0) return false;
|
||||||
|
} else Py_INCREF(list);
|
||||||
|
RAII_PyObject(value, Py_BuildValue("(H H H y#)", temp.platform_id, temp.encoding_id, temp.language_id, temp.string, (Py_ssize_t)temp.string_len));
|
||||||
|
if (!value) return false;
|
||||||
|
if (PyList_Append(list, value) != 0) return false;
|
||||||
|
}
|
||||||
|
self->name_lookup_table = ans; Py_INCREF(ans);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
namerec_matches(PyObject *namerec, unsigned platform_id, unsigned encoding_id, unsigned language_id) {
|
||||||
|
#define d(x) PyLong_AsUnsignedLong(PyTuple_GET_ITEM(namerec, x))
|
||||||
|
return d(0) == platform_id && d(1) == encoding_id && d(2) == language_id;
|
||||||
|
#undef d
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
find_matching_namerec(PyObject *namerecs, unsigned platform_id, unsigned encoding_id, unsigned language_id) {
|
||||||
|
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(namerecs); i++) {
|
||||||
|
PyObject *namerec = PyList_GET_ITEM(namerecs, i);
|
||||||
|
if (namerec_matches(namerec, platform_id, encoding_id, language_id)) return decode_name_record(namerec);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
get_best_name(Face *self, PyObject *nameid) {
|
||||||
|
if (!ensure_name_table(self)) return NULL;
|
||||||
|
PyObject *namerecs = PyDict_GetItem(self->name_lookup_table, nameid);
|
||||||
|
if (namerecs == NULL) return PyUnicode_FromString("");
|
||||||
|
if (PyList_GET_SIZE(namerecs) == 1) return decode_name_record(PyList_GET_ITEM(namerecs, 0));
|
||||||
|
#define d(...) { PyObject *ans = find_matching_namerec(namerecs, __VA_ARGS__); if (ans != NULL || PyErr_Occurred()) return ans; }
|
||||||
|
d(3, 1, 1033); // Microsoft/Windows/US English
|
||||||
|
d(1, 0, 0); // Mac/Roman/English
|
||||||
|
d(0, 6, 0); // Unicode/SMP/*
|
||||||
|
d(0, 4, 0); // Unicode/SMP/*
|
||||||
|
d(0, 3, 0); // Unicode/BMP/*
|
||||||
|
d(0, 2, 0); // Unicode/10646-BMP/*
|
||||||
|
d(0, 1, 0); // Unicode/1.1/*
|
||||||
|
#undef d
|
||||||
|
return PyUnicode_FromString("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
_get_best_name(Face *self, unsigned long nameid) {
|
||||||
|
RAII_PyObject(key, PyLong_FromUnsignedLong(nameid));
|
||||||
|
return key ? get_best_name(self, key) : NULL;
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
static inline void cleanup_ftmm(FT_MM_Var **p) { if (*p) FT_Done_MM_Var(library, *p); *p = NULL; }
|
||||||
|
|
||||||
|
#define RAII_FTMMVar(name) __attribute__((cleanup(cleanup_ftmm))) FT_MM_Var *name = NULL
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
convert_named_style_to_python(Face *face, const FT_Var_Named_Style *src, unsigned num_of_axes) {
|
||||||
|
RAII_PyObject(axis_values, PyTuple_New(num_of_axes));
|
||||||
|
if (!axis_values) return NULL;
|
||||||
|
for (FT_UInt i = 0; i < num_of_axes; i++) {
|
||||||
|
double val = src->coords[i] / 65536.0;
|
||||||
|
PyObject *pval = PyFloat_FromDouble(val);
|
||||||
|
if (!pval) return NULL;
|
||||||
|
PyTuple_SET_ITEM(axis_values, i, pval);
|
||||||
|
}
|
||||||
|
RAII_PyObject(name, _get_best_name(face, src->strid));
|
||||||
|
if (!name) PyErr_Clear();
|
||||||
|
RAII_PyObject(psname, src->psid == 0xffff ? NULL : _get_best_name(face, src->psid));
|
||||||
|
if (!psname) PyErr_Clear();
|
||||||
|
return Py_BuildValue("{sO sO sO}", "axis_values", axis_values, "name", name ? name : Py_None, "psname", psname ? psname : Py_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
tag_to_string(uint32_t tag) {
|
||||||
|
unsigned char bytes[5] = {0};
|
||||||
|
bytes[0] = (tag >> 24) & 0xff;
|
||||||
|
bytes[1] = (tag >> 16) & 0xff;
|
||||||
|
bytes[2] = (tag >> 8) & 0xff;
|
||||||
|
bytes[3] = (tag) & 0xff;
|
||||||
|
return PyUnicode_DecodeASCII((const char*)bytes, 4, "replace");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
convert_axis_to_python(Face *face, const FT_Var_Axis *src, FT_UInt flags) {
|
||||||
|
RAII_PyObject(strid, _get_best_name(face, src->strid));
|
||||||
|
if (!strid) PyErr_Clear();
|
||||||
|
return Py_BuildValue("{sd sd sd sO ss sN sO}",
|
||||||
|
"minimum", src->minimum / 65536.0, "maximum", src->maximum / 65536.0, "default", src->def / 65536.0,
|
||||||
|
"hidden", flags & FT_VAR_AXIS_FLAG_HIDDEN ? Py_True : Py_False, "name", src->name, "tag", tag_to_string(src->tag),
|
||||||
|
"strid", strid ? strid : Py_None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
get_variable_data(Face *self, PyObject *a UNUSED) {
|
||||||
|
RAII_FTMMVar(mm);
|
||||||
|
FT_Error err = FT_Get_MM_Var(self->face, &mm);
|
||||||
|
if (err) { set_freetype_error("Failed to get variable axis data from font with error:", err); return NULL; }
|
||||||
|
RAII_PyObject(axes, PyTuple_New(mm->num_axis));
|
||||||
|
RAII_PyObject(named_styles, PyTuple_New(mm->num_namedstyles));
|
||||||
|
if (!axes || !named_styles) return NULL;
|
||||||
|
for (FT_UInt i = 0; i < mm->num_namedstyles; i++) {
|
||||||
|
PyObject *s = convert_named_style_to_python(self, mm->namedstyle + i, mm->num_axis);
|
||||||
|
if (!s) return NULL;
|
||||||
|
PyTuple_SET_ITEM(named_styles, i, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FT_UInt i = 0; i < mm->num_axis; i++) {
|
||||||
|
FT_UInt flags;
|
||||||
|
FT_Get_Var_Axis_Flags(mm, i, &flags);
|
||||||
|
PyObject *s = convert_axis_to_python(self, mm->axis + i, flags);
|
||||||
|
|
||||||
|
if (!s) return NULL;
|
||||||
|
PyTuple_SET_ITEM(axes, i, s);
|
||||||
|
}
|
||||||
|
return Py_BuildValue("{sO sO}", "axes", axes, "named_styles", named_styles);
|
||||||
|
}
|
||||||
|
|
||||||
StringCanvas
|
StringCanvas
|
||||||
render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
|
render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
|
||||||
@@ -759,6 +908,8 @@ static PyMemberDef members[] = {
|
|||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
METHODB(display_name, METH_NOARGS),
|
METHODB(display_name, METH_NOARGS),
|
||||||
METHODB(extra_data, METH_NOARGS),
|
METHODB(extra_data, METH_NOARGS),
|
||||||
|
METHODB(get_variable_data, METH_NOARGS),
|
||||||
|
METHODB(get_best_name, METH_O),
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user