Improve rendering of italic fonts in linux

Now rather than down-size characters from italic fonts that do not fit
into the cell width, trim the left and right margins to make them fit,
instead.
This commit is contained in:
Kovid Goyal 2017-01-25 20:30:39 +05:30
parent b4bf0eb794
commit 0427f30c2a
2 changed files with 58 additions and 16 deletions

View File

@ -100,27 +100,27 @@ def render_char(text, bold=False, italic=False, width=1):
face = alt_face_cache[font] = Face(font.face)
set_char_size(face, **cff_size)
bitmap = render_to_bitmap(font, face, text)
if width == 1 and bitmap.width > cell_width * 1.1:
# rescale the font size so that the glyph is visible in a single
# cell and hope somebody updates libc's wcwidth
sz = cff_size.copy()
sz['width'] = int(sz['width'] * cell_width / bitmap.width)
# Preserve aspect ratio
sz['height'] = int(sz['height'] * cell_width / bitmap.width)
try:
set_char_size(face, **sz)
bitmap = render_to_bitmap(font, face, text)
finally:
set_char_size(face, **cff_size)
if width == 1 and bitmap.width > cell_width:
extra = bitmap.width - cell_width
if italic and extra < cell_width // 2:
bitmap = face.trim_to_width(bitmap, cell_width)
elif extra > max(2, 0.1 * cell_width):
# rescale the font size so that the glyph is visible in a single
# cell and hope somebody updates libc's wcwidth
sz = cff_size.copy()
sz['width'] = int(sz['width'] * cell_width / bitmap.width)
# Preserve aspect ratio
sz['height'] = int(sz['height'] * cell_width / bitmap.width)
try:
set_char_size(face, **sz)
bitmap = render_to_bitmap(font, face, text)
finally:
set_char_size(face, **cff_size)
m = face.glyph_metrics()
return CharBitmap(bitmap.buffer, ceil_int(abs(m.horiBearingX) / 64),
ceil_int(abs(m.horiBearingY) / 64), ceil_int(m.horiAdvance / 64), bitmap.rows, bitmap.width)
def is_wide_char(bitmap_char):
return min(bitmap_char.advance, bitmap_char.columns) > cell_width * 1.1
def place_char_in_cell(bitmap_char):
# We want the glyph to be positioned inside the cell based on the bearingX
# and bearingY values, making sure that it does not overflow the cell.

View File

@ -184,6 +184,46 @@ bitmap(Face *self) {
return ans;
}
static PyObject*
trim_to_width(Face UNUSED *self, PyObject *args) {
#define trim_to_width_doc "Trim edges from the supplied bitmap to make it fit in the specified cell-width"
PyObject *bitmap, *t;
unsigned long cell_width, rows, width, rtrim = 0, extra, ltrim;
unsigned char *src, *dest;
bool column_has_text = false;
if (!PyArg_ParseTuple(args, "O!k", &BitmapType, &bitmap, &cell_width)) return NULL;
rows = PyLong_AsUnsignedLong(PyStructSequence_GET_ITEM(bitmap, 0));
width = PyLong_AsUnsignedLong(PyStructSequence_GET_ITEM(bitmap, 1));
extra = width - cell_width;
if (extra >= cell_width) { PyErr_SetString(PyExc_ValueError, "Too large for trimming"); return NULL; }
PyObject *ans = PyStructSequence_New(&BitmapType);
if (ans == NULL) return PyErr_NoMemory();
src = (unsigned char*)PyByteArray_AS_STRING(PyStructSequence_GET_ITEM(bitmap, 3));
PyObject *abuf = PyByteArray_FromStringAndSize(NULL, cell_width * rows);
if (abuf == NULL) { Py_CLEAR(ans); return PyErr_NoMemory(); }
dest = (unsigned char*)PyByteArray_AS_STRING(abuf);
PyStructSequence_SET_ITEM(ans, 1, PyLong_FromUnsignedLong(cell_width));
PyStructSequence_SET_ITEM(ans, 2, PyLong_FromUnsignedLong(cell_width));
PyStructSequence_SET_ITEM(ans, 3, abuf);
#define COPY(which) t = PyStructSequence_GET_ITEM(bitmap, which); Py_INCREF(t); PyStructSequence_SET_ITEM(ans, which, t);
COPY(0); COPY(4); COPY(5); COPY(6);
#undef COPY
for (long x = width - 1; !column_has_text && x > -1 && rtrim < extra; x--) {
for (unsigned long y = 0; y < rows * width; y += width) {
if (src[x + y] > 200) { column_has_text = true; break; }
}
if (!column_has_text) rtrim++;
}
rtrim = MIN(extra, rtrim);
ltrim = extra - rtrim;
for (unsigned long y = 0; y < rows; y++) {
memcpy(dest + y*cell_width, src + ltrim + y*width, cell_width);
}
return ans;
}
// Boilerplate {{{
static PyMemberDef members[] = {
@ -205,6 +245,7 @@ static PyMethodDef methods[] = {
METHOD(get_char_index, METH_VARARGS)
METHOD(glyph_metrics, METH_NOARGS)
METHOD(bitmap, METH_NOARGS)
METHOD(trim_to_width, METH_VARARGS)
{NULL} /* Sentinel */
};
@ -234,6 +275,7 @@ init_freetype_library(PyObject *m) {
if (PyStructSequence_InitType2(&GlpyhMetricsType, &gm_desc) != 0) return false;
if (PyStructSequence_InitType2(&BitmapType, &bm_desc) != 0) return false;
PyModule_AddObject(m, "GlyphMetrics", (PyObject*)&GlpyhMetricsType);
PyModule_AddObject(m, "Bitmap", (PyObject*)&BitmapType);
PyModule_AddIntMacro(m, FT_LOAD_RENDER);
PyModule_AddIntMacro(m, FT_LOAD_TARGET_NORMAL);
PyModule_AddIntMacro(m, FT_LOAD_TARGET_LIGHT);