diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 22bcbae9c8b..2800647d69a 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -981,6 +981,14 @@ def fill(width, height): start[1], ) + def getmetrics(self): + """ + :return: A tuple of the maximum font ascent (the distance from the baseline to + the highest outline point) and maximum descent (the distance from the + baseline to the lowest outline point, a negative value) + """ + return self.font.ascent, self.font.descent + class TransposedFont: """Wrapper for writing rotated or mirrored text""" diff --git a/src/_imagingft.c b/src/_imagingft.c index 736613b719f..c895a888974 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -381,6 +381,42 @@ getfamily(PyObject *self_, PyObject *args, PyObject *kw) { return NULL; } +static FT_Pos +family_getascender(FontFamily *family) { + int i; + FT_Pos ascender = 0; + for (i = 0; i < family->font_count; i++) { + ascender = (ascender > family->faces[i]->size->metrics.ascender) + ? ascender + : family->faces[i]->size->metrics.ascender; + } + return ascender; +} + +static FT_Pos +family_getdescender(FontFamily *family) { + int i; + FT_Pos descender = 0; + for (i = 0; i < family->font_count; i++) { + descender = (descender < family->faces[i]->size->metrics.descender) + ? descender + : family->faces[i]->size->metrics.descender; + } + return descender; +} + +static FT_Pos +family_getheight(FontFamily *family) { + int i; + FT_Pos height = 0; + for (i = 0; i < family->font_count; i++) { + height = (height > family->faces[i]->size->metrics.height) + ? height + : family->faces[i]->size->metrics.height; + } + return height; +} + static int font_getchar(PyObject *string, int index, FT_ULong *char_out) { if (PyUnicode_Check(string)) { @@ -928,16 +964,16 @@ bounding_box_and_anchors(FontFamily *family, const char *anchor, int horizontal_ } switch (anchor[1]) { case 'a': // ascender - y_anchor = PIXEL(family->faces[0]->size->metrics.ascender); + // this should be consistent with getmetrics() + y_anchor = PIXEL(family_getascender(family)); break; case 't': // top y_anchor = y_max; break; case 'm': // middle (ascender + descender) / 2 + // this should be consistent with getmetrics() y_anchor = PIXEL( - (family->faces[0]->size->metrics.ascender + - family->faces[0]->size->metrics.descender) / - 2); + (family_getascender(family) + family_getdescender(family)) / 2); break; case 's': // horizontal baseline y_anchor = 0; @@ -946,7 +982,8 @@ bounding_box_and_anchors(FontFamily *family, const char *anchor, int horizontal_ y_anchor = y_min; break; case 'd': // descender - y_anchor = PIXEL(family->faces[0]->size->metrics.descender); + // this should be consistent with getmetrics() + y_anchor = PIXEL(family_getdescender(family)); break; default: goto bad_anchor; @@ -1794,6 +1831,33 @@ static PyMethodDef family_methods[] = { */ {NULL, NULL}}; +static PyObject * +family_getattr_ascent(FontFamilyObject *self, void *closure) { + return PyLong_FromLong(PIXEL(family_getascender(&self->data))); +} + +static PyObject * +family_getattr_descent(FontFamilyObject *self, void *closure) { + return PyLong_FromLong(-PIXEL(family_getdescender(&self->data))); +} + +static PyObject * +family_getattr_height(FontFamilyObject *self, void *closure) { + return PyLong_FromLong(PIXEL(family_getheight(&self->data))); +} + + +static struct PyGetSetDef family_getsetters[] = { + //{"family", (getter)font_getattr_family}, + //{"style", (getter)font_getattr_style}, + {"ascent", (getter)family_getattr_ascent}, + {"descent", (getter)family_getattr_descent}, + {"height", (getter)family_getattr_height}, + //{"x_ppem", (getter)font_getattr_x_ppem}, + //{"y_ppem", (getter)font_getattr_y_ppem}, + //{"glyphs", (getter)font_getattr_glyphs}, + {NULL}}; + static PyTypeObject FontFamily_Type = { PyVarObject_HEAD_INIT(NULL, 0) "FontFamily", @@ -1825,7 +1889,7 @@ static PyTypeObject FontFamily_Type = { 0, /*tp_iternext*/ family_methods, /*tp_methods*/ 0, /*tp_members*/ - 0, /*TODO tp_getset*/ + family_getsetters, /* tp_getset*/ }; static PyMethodDef _functions[] = {