mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-09 23:54:20 +02:00
Implement postscript variation name prefix for CoreText as well
This commit is contained in:
@@ -32,7 +32,7 @@ typedef struct {
|
||||
float ascent, descent, leading, underline_position, underline_thickness, point_sz, scaled_point_sz;
|
||||
CTFontRef ct_font;
|
||||
hb_font_t *hb_font;
|
||||
PyObject *family_name, *full_name, *postscript_name, *path;
|
||||
PyObject *family_name, *full_name, *postscript_name, *path, *name_lookup_table;
|
||||
} CTFace;
|
||||
PyTypeObject CTFace_Type;
|
||||
static CTFontRef window_title_font = nil;
|
||||
@@ -107,6 +107,7 @@ dealloc(CTFace* self) {
|
||||
self->hb_font = NULL;
|
||||
self->ct_font = NULL;
|
||||
Py_CLEAR(self->family_name); Py_CLEAR(self->full_name); Py_CLEAR(self->postscript_name); Py_CLEAR(self->path);
|
||||
Py_CLEAR(self->name_lookup_table);
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
@@ -781,16 +782,57 @@ render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *inf
|
||||
return do_render(self->ct_font, self->units_per_em, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg, center_glyph);
|
||||
}
|
||||
|
||||
// Name table
|
||||
|
||||
static uint16_t byteswap(uint16_t x) { return (x << 8) | (x >> 8); }
|
||||
|
||||
// Boilerplate {{{
|
||||
static bool
|
||||
ensure_name_table(CTFace *self) {
|
||||
if (self->name_lookup_table) return true;
|
||||
RAII_CoreFoundation(CFDataRef, cftable, CTFontCopyTable(self->ct_font, kCTFontTableName, kCTFontTableOptionNoOptions));
|
||||
const uint8_t *table = cftable ? CFDataGetBytePtr(cftable) : NULL;
|
||||
size_t table_len = 0;
|
||||
if (!table || (table_len = CFDataGetLength(cftable)) < 9 * sizeof(uint16_t)) {
|
||||
self->name_lookup_table = PyDict_New();
|
||||
return true;
|
||||
}
|
||||
RAII_PyObject(ans, PyDict_New());
|
||||
// OpenType tables are big-endian for god knows what reason so need to byteswap
|
||||
#define next byteswap(*(p++))
|
||||
uint16_t *p = (uint16_t*)table; p++;
|
||||
uint16_t num_of_name_records = next, storage_offset = next;
|
||||
const uint8_t *storage = table + storage_offset, *slimit = table + table_len;
|
||||
if (storage >= slimit) {
|
||||
self->name_lookup_table = PyDict_New();
|
||||
return true;
|
||||
}
|
||||
for (; num_of_name_records > 0 && p + 6 <= (uint16_t*)slimit; num_of_name_records--) {
|
||||
uint16_t platform_id = next, encoding_id = next, language_id = next, name_id = next, length = next, offset = next;
|
||||
const uint8_t *s = storage + offset;
|
||||
if (s + length <= slimit && !add_font_name_record(
|
||||
ans, platform_id, encoding_id, language_id, name_id, (const char*)(s), length)) return NULL;
|
||||
}
|
||||
self->name_lookup_table = ans; Py_INCREF(ans);
|
||||
return true;
|
||||
#undef next
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
display_name(CTFace *self) {
|
||||
CFStringRef dn = CTFontCopyDisplayName(self->ct_font);
|
||||
return convert_cfstring(dn, true);
|
||||
get_best_name(CTFace *self, PyObject *nameid) {
|
||||
if (!ensure_name_table(self)) return NULL;
|
||||
return get_best_name_from_name_table(self->name_lookup_table, nameid);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_get_best_name(CTFace *self, unsigned long nameid) {
|
||||
RAII_PyObject(key, PyLong_FromUnsignedLong(nameid));
|
||||
return key ? get_best_name(self, key) : NULL;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Variations {{{
|
||||
|
||||
static const char*
|
||||
tag_to_string(uint32_t tag, uint8_t bytes[5]) {
|
||||
bytes[0] = (tag >> 24) & 0xff;
|
||||
@@ -864,12 +906,23 @@ get_variable_data(CTFace *self) {
|
||||
PyTuple_SET_ITEM(named_styles, actual_num, ns); actual_num++;
|
||||
}
|
||||
_PyTuple_Resize(&named_styles, actual_num);
|
||||
return Py_BuildValue("{sO sO}", "axes", axes, "named_styles", named_styles); //, "named_styles", named_styles);
|
||||
return Py_BuildValue("{sO sO sN}", "axes", axes, "named_styles", named_styles, "variations_postscript_name_prefix", _get_best_name(self, 25));
|
||||
}
|
||||
// }}}
|
||||
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyObject*
|
||||
display_name(CTFace *self) {
|
||||
CFStringRef dn = CTFontCopyDisplayName(self->ct_font);
|
||||
return convert_cfstring(dn, true);
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
METHODB(display_name, METH_NOARGS),
|
||||
METHODB(get_variable_data, METH_NOARGS),
|
||||
METHODB(get_best_name, METH_O),
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
72
kitty/font-names.c
Normal file
72
kitty/font-names.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* font-names.c
|
||||
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "fonts.h"
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
add_font_name_record(PyObject *table, uint16_t platform_id, uint16_t encoding_id, uint16_t language_id, uint16_t name_id, const char *string, uint16_t string_len) {
|
||||
RAII_PyObject(key, PyLong_FromUnsignedLong((unsigned long)name_id));
|
||||
if (!key) return false;
|
||||
RAII_PyObject(list, PyDict_GetItem(table, key));
|
||||
if (list == NULL) {
|
||||
list = PyList_New(0);
|
||||
if (!list) return false;
|
||||
if (PyDict_SetItem(table, key, list) != 0) return false;
|
||||
} else Py_INCREF(list);
|
||||
RAII_PyObject(value, Py_BuildValue("(H H H y#)", platform_id, encoding_id, language_id, string, (Py_ssize_t)string_len));
|
||||
if (!value) return false;
|
||||
if (PyList_Append(list, value) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
get_best_name_from_name_table(PyObject *table, PyObject *name_id) {
|
||||
PyObject *namerecs = PyDict_GetItem(table, name_id);
|
||||
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("");
|
||||
|
||||
}
|
||||
@@ -42,6 +42,11 @@ typedef void (*free_extra_data_func)(void*);
|
||||
StringCanvas render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline);
|
||||
StringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text);
|
||||
|
||||
bool
|
||||
add_font_name_record(PyObject *table, uint16_t platform_id, uint16_t encoding_id, uint16_t language_id, uint16_t name_id, const char *string, uint16_t string_len);
|
||||
PyObject*
|
||||
get_best_name_from_name_table(PyObject *table, PyObject *name_id);
|
||||
|
||||
static inline void
|
||||
right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {
|
||||
pixel *src;
|
||||
|
||||
@@ -705,18 +705,6 @@ extra_data(PyObject *self, PyObject *a UNUSED) {
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -726,54 +714,16 @@ ensure_name_table(Face *self) {
|
||||
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;
|
||||
if (!add_font_name_record(ans, temp.platform_id, temp.encoding_id, temp.language_id, temp.name_id, (const char*)temp.string, temp.string_len)) return NULL;
|
||||
}
|
||||
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("");
|
||||
return get_best_name_from_name_table(self->name_lookup_table, nameid);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
||||
Reference in New Issue
Block a user