Skip to content

Commit 80fe4cb

Browse files
authored
Merge pull request #3328 from pygame-community/ankith26-font-metrics
Add full unicode support to `Font.metrics`
2 parents 5846dcb + d01ceed commit 80fe4cb

File tree

3 files changed

+29
-30
lines changed

3 files changed

+29
-30
lines changed

docs/reST/ref/font.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ solves no longer exists, it will likely be removed in the future.
487487
advance), ...]. None is entered in the list for each unrecognized
488488
character.
489489

490+
.. versionchanged:: 2.5.4 This function now supports all unicode codepoints.
491+
Previously, only a subset that was representable in UCS-2 was supported.
492+
490493
.. ## Font.metrics ##
491494
492495
.. method:: get_italic

src_c/font.c

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -909,62 +909,54 @@ font_metrics(PyObject *self, PyObject *textobj)
909909

910910
TTF_Font *font = PyFont_AsFont(self);
911911
PyObject *list;
912-
Py_ssize_t length;
913912
Py_ssize_t i;
914913
int minx;
915914
int maxx;
916915
int miny;
917916
int maxy;
918917
int advance;
919-
PyObject *obj;
920918
PyObject *listitem;
921-
Uint16 *buffer;
922-
Uint16 ch;
923-
PyObject *temp;
924-
int surrogate;
919+
Py_UCS4 *buffer;
920+
Py_UCS4 ch;
925921
if (!PgFont_GenerationCheck(self)) {
926922
return RAISE_FONT_QUIT_ERROR();
927923
}
928924

929925
if (PyUnicode_Check(textobj)) {
930-
obj = textobj;
931-
Py_INCREF(obj);
926+
Py_INCREF(textobj);
932927
}
933928
else if (PyBytes_Check(textobj)) {
934-
obj = PyUnicode_FromEncodedObject(textobj, "UTF-8", NULL);
935-
if (!obj) {
929+
textobj = PyUnicode_FromEncodedObject(textobj, "UTF-8", NULL);
930+
if (!textobj) {
936931
return NULL;
937932
}
938933
}
939934
else {
940935
return RAISE_TEXT_TYPE_ERROR();
941936
}
942-
temp = PyUnicode_AsUTF16String(obj);
943-
Py_DECREF(obj);
944-
if (!temp)
937+
buffer = PyUnicode_AsUCS4Copy(textobj);
938+
Py_DECREF(textobj);
939+
if (!buffer)
945940
return NULL;
946-
obj = temp;
947941

948942
list = PyList_New(0);
949943
if (!list) {
950-
Py_DECREF(obj);
944+
PyMem_Free(buffer);
951945
return NULL;
952946
}
953-
buffer = (Uint16 *)PyBytes_AS_STRING(obj);
954-
length = PyBytes_GET_SIZE(obj) / sizeof(Uint16);
955-
for (i = 1 /* skip BOM */; i < length; i++) {
956-
ch = buffer[i];
957-
surrogate = Py_UNICODE_IS_SURROGATE(ch);
947+
for (i = 0; (ch = buffer[i]); i++) {
958948
/* TODO:
959949
* TTF_GlyphMetrics() seems to return a value for any character,
960950
* using the default invalid character, if the char is not found.
961951
*/
962952
#if SDL_TTF_VERSION_ATLEAST(3, 0, 0)
963-
if (!surrogate && /* conditional and */
964-
TTF_GetGlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy,
953+
if (TTF_GetGlyphMetrics(font, ch, &minx, &maxx, &miny, &maxy,
954+
&advance))
955+
#elif SDL_TTF_VERSION_ATLEAST(2, 0, 18)
956+
if (!TTF_GlyphMetrics32(font, ch, &minx, &maxx, &miny, &maxy,
965957
&advance))
966958
#else
967-
if (!surrogate && /* conditional and */
959+
if (ch <= 0xFFFF && /* conditional and */
968960
!TTF_GlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy,
969961
&advance))
970962
#endif
@@ -973,26 +965,24 @@ font_metrics(PyObject *self, PyObject *textobj)
973965
Py_BuildValue("(iiiii)", minx, maxx, miny, maxy, advance);
974966
if (!listitem) {
975967
Py_DECREF(list);
976-
Py_DECREF(obj);
968+
PyMem_Free(buffer);
977969
return NULL;
978970
}
979971
}
980972
else {
981-
/* Not UCS-2 or no matching metrics. */
973+
/* Not UCS-2 (and old SDL) or no matching metrics. */
982974
Py_INCREF(Py_None);
983975
listitem = Py_None;
984-
if (surrogate)
985-
i++;
986976
}
987977
if (0 != PyList_Append(list, listitem)) {
988978
Py_DECREF(list);
989979
Py_DECREF(listitem);
990-
Py_DECREF(obj);
980+
PyMem_Free(buffer);
991981
return NULL; /* Exception already set. */
992982
}
993983
Py_DECREF(listitem);
994984
}
995-
Py_DECREF(obj);
985+
PyMem_Free(buffer);
996986
return list;
997987
}
998988

test/font_test.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,13 @@ def test_metrics(self):
416416
bm = f.metrics(u)
417417

418418
self.assertEqual(len(bm), 1)
419-
self.assertIsNone(bm[0])
419+
if (
420+
pygame.font.get_sdl_ttf_version() >= (2, 0, 18)
421+
and pygame_font.__name__ != "pygame.ftfont"
422+
):
423+
self.assertIsNotNone(bm[0])
424+
else:
425+
self.assertIsNone(bm[0])
420426

421427
return # unfinished
422428
# The documentation is useless here. How large a list?

0 commit comments

Comments
 (0)