Read strikethrough info from font when using FreeType

This reads the strikethrough position and thickness from the font so it
is rendered correctly.

This is only implemented for FreeType, not Core Text, because I didn't
find any way to get the info from Core Text, and I don't have a Mac to
test it on either. When using Core Text or when the font doesn't provide
the info, the same approximation as before is used.
This commit is contained in:
Trygve Aaberge 2020-03-28 05:38:11 +01:00
parent 6e13509720
commit 931e91f1a7
6 changed files with 42 additions and 16 deletions

View File

@ -312,7 +312,7 @@ harfbuzz_font_for_face(PyObject* s) {
}
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) {
// See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html
CTFace *self = (CTFace*)s;
#define count (128 - 32)
@ -332,6 +332,8 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
*underline_position = (unsigned int)floor(self->ascent - self->underline_position + 0.5);
*underline_thickness = (unsigned int)ceil(MAX(0.1, self->underline_thickness));
*baseline = (unsigned int)self->ascent;
*strikethrough_position = (unsigned int)floor(*baseline * 0.65);
*strikethrough_thickness = *underline_thickness;
// float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5));
// Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate.
#define W "AQWMH_gyl "

View File

@ -902,7 +902,7 @@ def set_font_data(
box_drawing_func: Callable[[int, int, int, float],
Tuple[int, Union[bytearray, bytes, Array]]],
prerender_func: Callable[
[int, int, int, int, int, float, float, float, float],
[int, int, int, int, int, int, int, float, float, float, float],
Tuple[Union[Array, int], ...]],
descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,

View File

@ -85,7 +85,7 @@ typedef struct {
typedef struct {
FONTS_DATA_HEAD
id_type id;
unsigned int baseline, underline_position, underline_thickness;
unsigned int baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
size_t fonts_capacity, fonts_count, fallback_fonts_count;
ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx;
Font *fonts;
@ -422,8 +422,8 @@ python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigne
static inline void
calc_cell_metrics(FontGroup *fg) {
unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness;
cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness);
if (!cell_width) fatal("Failed to calculate cell width for the specified font");
unsigned int before_cell_height = cell_height;
int cw = cell_width, ch = cell_height;
@ -455,7 +455,7 @@ calc_cell_metrics(FontGroup *fg) {
}
sprite_tracker_set_layout(&fg->sprite_tracker, cell_width, cell_height);
fg->cell_width = cell_width; fg->cell_height = cell_height;
fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness;
fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness, fg->strikethrough_position = strikethrough_position, fg->strikethrough_thickness = strikethrough_thickness;
free(fg->canvas);
fg->canvas = calloc(CELLS_IN_CANVAS * fg->cell_width * fg->cell_height, sizeof(pixel));
if (!fg->canvas) fatal("Out of memory allocating canvas for font group");
@ -1243,7 +1243,7 @@ send_prerendered_sprites(FontGroup *fg) {
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas);
do_increment(fg, &error);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->strikethrough_position, fg->strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) {
x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;

View File

@ -25,7 +25,7 @@ int get_glyph_width(PyObject *, glyph_index);
bool is_glyph_empty(PyObject *, glyph_index);
hb_font_t* harfbuzz_font_for_face(PyObject*);
bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE);
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
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, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, bool center_glyph);
PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg);
PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE);

View File

@ -191,7 +191,9 @@ def render_special(
cell_width: int = 0, cell_height: int = 0,
baseline: int = 0,
underline_position: int = 0,
underline_thickness: int = 0
underline_thickness: int = 0,
strikethrough_position: int = 0,
strikethrough_thickness: int = 0
) -> ctypes.Array:
underline_position = min(underline_position, cell_height - underline_thickness)
CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
@ -216,8 +218,7 @@ def render_special(
t = max(1, min(cell_height - underline_position - 1, t))
dl([add_line, add_line, add_dline, add_curl][underline], underline_position, t, cell_height)
if strikethrough:
pos = int(0.65 * baseline)
dl(add_line, pos, underline_thickness, cell_height)
dl(add_line, strikethrough_position, strikethrough_thickness, cell_height)
return ans
@ -268,6 +269,8 @@ def prerender_function(
baseline: int,
underline_position: int,
underline_thickness: int,
strikethrough_position: int,
strikethrough_thickness: int,
cursor_beam_thickness: float,
cursor_underline_thickness: float,
dpi_x: float,
@ -276,7 +279,8 @@ def prerender_function(
# Pre-render the special underline, strikethrough and missing and cursor cells
f = partial(
render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline,
underline_position=underline_position, underline_thickness=underline_thickness)
underline_position=underline_position, underline_thickness=underline_thickness,
strikethrough_position=strikethrough_position, strikethrough_thickness=strikethrough_thickness)
c = partial(
render_cursor, cursor_beam_thickness=cursor_beam_thickness,
cursor_underline_thickness=cursor_underline_thickness, cell_width=cell_width,

View File

@ -22,12 +22,13 @@
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_TRUETYPE_TABLES_H
typedef struct {
PyObject_HEAD
FT_Face face;
unsigned int units_per_EM;
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness;
int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness;
int hinting, hintstyle, index;
bool is_scalable, has_color;
float size_in_pts;
@ -207,6 +208,12 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_
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));
TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2);
if (os2 != NULL) {
self->strikethrough_position = os2->yStrikeoutPosition;
self->strikethrough_thickness = os2->yStrikeoutSize;
}
self->path = path;
Py_INCREF(self->path);
self->index = self->face->face_index & 0xFFFF;
@ -263,11 +270,11 @@ static PyObject *
repr(Face *self) {
const char *ps_name = FT_Get_Postscript_Name(self->face);
return PyUnicode_FromFormat(
"Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i)",
"Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i, strikethrough_position=%i, strikethrough_thickness=%i)",
self->face->family_name ? self->face->family_name : "", self->face->style_name ? self->face->style_name : "",
ps_name ? ps_name: "",
self->path, self->index, self->is_scalable ? Py_True : Py_False, self->has_color ? Py_True : Py_False,
self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness
self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness, self->strikethrough_position, self->strikethrough_thickness
);
}
@ -292,13 +299,24 @@ calc_cell_width(Face *self) {
}
void
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) {
Face *self = (Face*)s;
*cell_width = calc_cell_width(self);
*cell_height = calc_cell_height(self, true);
*baseline = font_units_to_pixels_y(self, self->ascender);
*underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position)));
*underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness));
if (self->strikethrough_position != 0) {
*strikethrough_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position)));
} else {
*strikethrough_position = (unsigned int)floor(*baseline * 0.65);
}
if (self->strikethrough_thickness != 0) {
*strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness));
} else {
*strikethrough_thickness = *underline_thickness;
}
}
unsigned int
@ -659,6 +677,8 @@ static PyMemberDef members[] = {
MEM(max_advance_height, T_INT),
MEM(underline_position, T_INT),
MEM(underline_thickness, T_INT),
MEM(strikethrough_position, T_INT),
MEM(strikethrough_thickness, T_INT),
MEM(is_scalable, T_BOOL),
MEM(path, T_OBJECT_EX),
{NULL} /* Sentinel */