1 /*
2   pygame - Python Game Library
3   Copyright (C) 2000-2001  Pete Shinners
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Library General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Library General Public License for more details.
14 
15   You should have received a copy of the GNU Library General Public
16   License along with this library; if not, write to the Free
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19   Pete Shinners
20   pete@shinners.org
21 */
22 
23 /*
24  *  font module for pygame
25  */
26 #define PYGAMEAPI_FONT_INTERNAL
27 #include "font.h"
28 
29 #include <stdio.h>
30 #include <string.h>
31 
32 #include "pygame.h"
33 
34 #include "pgcompat.h"
35 
36 #include "doc/font_doc.h"
37 
38 #include "structmember.h"
39 
40 /* Require SDL_ttf 2.0.6 or later for rwops support */
41 #ifdef TTF_MAJOR_VERSION
42 #define FONT_HAVE_RWOPS 1
43 #else
44 #define FONT_HAVE_RWOPS 0
45 #endif
46 
47 #ifndef SDL_TTF_VERSION_ATLEAST
48 #define SDL_TTF_COMPILEDVERSION \
49     SDL_VERSIONNUM(SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL)
50 #define SDL_TTF_VERSION_ATLEAST(X, Y, Z) \
51     (SDL_TTF_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
52 #endif
53 
54 #define RAISE_TEXT_TYPE_ERROR() \
55     RAISE(PyExc_TypeError, "text must be a unicode or bytes");
56 
57 /* For filtering out UCS-4 and larger characters when Python is
58  * built with Py_UNICODE_WIDE.
59  */
60 #if defined(PYPY_VERSION)
61 #define Py_UNICODE_IS_SURROGATE(ch) (0xD800 <= (ch) && (ch) <= 0xDFFF)
62 #endif
63 
64 static PyTypeObject PyFont_Type;
65 static PyObject *
66 PyFont_New(TTF_Font *);
67 #define PyFont_Check(x) ((x)->ob_type == &PyFont_Type)
68 
69 static int font_initialized = 0;
70 static unsigned int current_ttf_generation = 0;
71 static const char font_defaultname[] = "freesansbold.ttf";
72 static const char pkgdatamodule_name[] = "pygame.pkgdata";
73 static const char resourcefunc_name[] = "getResource";
74 
75 /*
76  */
77 static int
utf_8_needs_UCS_4(const char * str)78 utf_8_needs_UCS_4(const char *str)
79 {
80     static const Uint8 first = '\xF0';
81 
82     while (*str) {
83         if ((Uint8)*str >= first) {
84             return 1;
85         }
86         ++str;
87     }
88     return 0;
89 }
90 
91 static PyObject *
pg_open_obj(PyObject * obj,const char * mode)92 pg_open_obj(PyObject *obj, const char *mode)
93 {
94     PyObject *result;
95     PyObject *open;
96     PyObject *bltins = PyImport_ImportModule(BUILTINS_MODULE);
97     if (!bltins)
98         return NULL;
99     open = PyObject_GetAttrString(bltins, "open");
100     Py_DECREF(bltins);
101     if (!open)
102         return NULL;
103 
104     result = PyObject_CallFunction(open, "Os", obj, mode);
105     Py_DECREF(open);
106     return result;
107 }
108 
109 /* Return an encoded file path, a file-like object or a NULL pointer.
110  * May raise a Python error. Use PyErr_Occurred to check.
111  */
112 static PyObject *
font_resource(const char * filename)113 font_resource(const char *filename)
114 {
115     PyObject *pkgdatamodule = NULL;
116     PyObject *resourcefunc = NULL;
117     PyObject *result = NULL;
118     PyObject *tmp;
119 
120     pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name);
121     if (pkgdatamodule == NULL) {
122         return NULL;
123     }
124 
125     resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name);
126     Py_DECREF(pkgdatamodule);
127     if (resourcefunc == NULL) {
128         return NULL;
129     }
130 
131     result = PyObject_CallFunction(resourcefunc, "s", filename);
132     Py_DECREF(resourcefunc);
133     if (result == NULL) {
134         return NULL;
135     }
136 
137     tmp = PyObject_GetAttrString(result, "name");
138     if (tmp != NULL) {
139         PyObject *closeret;
140         if (!(closeret = PyObject_CallMethod(result, "close", NULL))) {
141             Py_DECREF(result);
142             Py_DECREF(tmp);
143             return NULL;
144         }
145         Py_DECREF(closeret);
146         Py_DECREF(result);
147         result = tmp;
148     }
149     else if (!PyErr_ExceptionMatches(PyExc_MemoryError)) {
150         PyErr_Clear();
151     }
152 
153     tmp = pg_EncodeString(result, "UTF-8", NULL, NULL);
154     if (tmp == NULL) {
155         Py_DECREF(result);
156         return NULL;
157     }
158     else if (tmp != Py_None) {
159         Py_DECREF(result);
160         result = tmp;
161     }
162     else {
163         Py_DECREF(tmp);
164     }
165 
166     return result;
167 }
168 
169 static PyObject *
fontmodule_init(PyObject * self)170 fontmodule_init(PyObject *self)
171 {
172     if (!font_initialized) {
173         if (TTF_Init())
174             return RAISE(pgExc_SDLError, SDL_GetError());
175 
176         font_initialized = 1;
177     }
178     Py_RETURN_NONE;
179 }
180 
181 static PyObject *
fontmodule_quit(PyObject * self)182 fontmodule_quit(PyObject *self)
183 {
184     if (font_initialized) {
185         TTF_Quit();
186         font_initialized = 0;
187         current_ttf_generation++;
188     }
189     Py_RETURN_NONE;
190 }
191 
192 static PyObject *
get_init(PyObject * self)193 get_init(PyObject *self)
194 {
195     return PyBool_FromLong(font_initialized);
196 }
197 
198 /* font object methods */
199 static PyObject *
font_get_height(PyObject * self,PyObject * args)200 font_get_height(PyObject *self, PyObject *args)
201 {
202     TTF_Font *font = PyFont_AsFont(self);
203     return PyInt_FromLong(TTF_FontHeight(font));
204 }
205 
206 static PyObject *
font_get_descent(PyObject * self,PyObject * args)207 font_get_descent(PyObject *self, PyObject *args)
208 {
209     TTF_Font *font = PyFont_AsFont(self);
210     return PyInt_FromLong(TTF_FontDescent(font));
211 }
212 
213 static PyObject *
font_get_ascent(PyObject * self,PyObject * args)214 font_get_ascent(PyObject *self, PyObject *args)
215 {
216     TTF_Font *font = PyFont_AsFont(self);
217     return PyInt_FromLong(TTF_FontAscent(font));
218 }
219 
220 static PyObject *
font_get_linesize(PyObject * self,PyObject * args)221 font_get_linesize(PyObject *self, PyObject *args)
222 {
223     TTF_Font *font = PyFont_AsFont(self);
224     return PyInt_FromLong(TTF_FontLineSkip(font));
225 }
226 
227 static PyObject *
_font_get_style_flag_as_py_bool(PyObject * self,int flag)228 _font_get_style_flag_as_py_bool(PyObject *self, int flag)
229 {
230     TTF_Font *font = PyFont_AsFont(self);
231     return PyBool_FromLong((TTF_GetFontStyle(font) & flag) != 0);
232 }
233 
234 static void
_font_set_or_clear_style_flag(TTF_Font * font,int flag,int set_flag)235 _font_set_or_clear_style_flag(TTF_Font *font, int flag, int set_flag)
236 {
237     int style = TTF_GetFontStyle(font);
238     if (set_flag)
239         style |= flag;
240     else
241         style &= ~flag;
242     TTF_SetFontStyle(font, style);
243 }
244 
245 /* Implements getter for the bold attribute */
246 static PyObject *
font_getter_bold(PyObject * self,void * closure)247 font_getter_bold(PyObject *self, void *closure)
248 {
249     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_BOLD);
250 }
251 
252 /* Implements setter for the bold attribute */
253 static int
font_setter_bold(PyObject * self,PyObject * value,void * closure)254 font_setter_bold(PyObject *self, PyObject *value, void *closure)
255 {
256     TTF_Font *font = PyFont_AsFont(self);
257     int val;
258 
259     DEL_ATTR_NOT_SUPPORTED_CHECK("bold", value);
260 
261     val = PyObject_IsTrue(value);
262     if (val == -1) {
263         return -1;
264     }
265 
266     _font_set_or_clear_style_flag(font, TTF_STYLE_BOLD, val);
267     return 0;
268 }
269 
270 /* Implements get_bold() */
271 static PyObject *
font_get_bold(PyObject * self,PyObject * args)272 font_get_bold(PyObject *self, PyObject *args)
273 {
274     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_BOLD);
275 }
276 
277 /* Implements set_bold(bool) */
278 static PyObject *
font_set_bold(PyObject * self,PyObject * args)279 font_set_bold(PyObject *self, PyObject *args)
280 {
281     TTF_Font *font = PyFont_AsFont(self);
282     int val;
283     if (!PyArg_ParseTuple(args, "p", &val))
284         return NULL;
285 
286     _font_set_or_clear_style_flag(font, TTF_STYLE_BOLD, val);
287 
288     Py_RETURN_NONE;
289 }
290 
291 /* Implements getter for the italic attribute */
292 static PyObject *
font_getter_italic(PyObject * self,void * closure)293 font_getter_italic(PyObject *self, void *closure)
294 {
295     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_ITALIC);
296 }
297 
298 /* Implements setter for the italic attribute */
299 static int
font_setter_italic(PyObject * self,PyObject * value,void * closure)300 font_setter_italic(PyObject *self, PyObject *value, void *closure)
301 {
302     TTF_Font *font = PyFont_AsFont(self);
303     int val;
304 
305     DEL_ATTR_NOT_SUPPORTED_CHECK("italic", value);
306 
307     val = PyObject_IsTrue(value);
308     if (val == -1) {
309         return -1;
310     }
311 
312     _font_set_or_clear_style_flag(font, TTF_STYLE_ITALIC, val);
313     return 0;
314 }
315 
316 /* Implements get_italic() */
317 static PyObject *
font_get_italic(PyObject * self,PyObject * args)318 font_get_italic(PyObject *self, PyObject *args)
319 {
320     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_ITALIC);
321 }
322 
323 /* Implements set_italic(bool) */
324 static PyObject *
font_set_italic(PyObject * self,PyObject * args)325 font_set_italic(PyObject *self, PyObject *args)
326 {
327     TTF_Font *font = PyFont_AsFont(self);
328     int val;
329 
330     if (!PyArg_ParseTuple(args, "p", &val))
331         return NULL;
332 
333     _font_set_or_clear_style_flag(font, TTF_STYLE_ITALIC, val);
334 
335     Py_RETURN_NONE;
336 }
337 
338 /* Implements getter for the underline attribute */
339 static PyObject *
font_getter_underline(PyObject * self,void * closure)340 font_getter_underline(PyObject *self, void *closure)
341 {
342     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_UNDERLINE);
343 }
344 
345 /* Implements setter for the underline attribute */
346 static int
font_setter_underline(PyObject * self,PyObject * value,void * closure)347 font_setter_underline(PyObject *self, PyObject *value, void *closure)
348 {
349     TTF_Font *font = PyFont_AsFont(self);
350     int val;
351 
352     DEL_ATTR_NOT_SUPPORTED_CHECK("underline", value);
353 
354     val = PyObject_IsTrue(value);
355     if (val == -1) {
356         return -1;
357     }
358 
359     _font_set_or_clear_style_flag(font, TTF_STYLE_UNDERLINE, val);
360     return 0;
361 }
362 
363 /* Implements get_underline() */
364 static PyObject *
font_get_underline(PyObject * self,PyObject * args)365 font_get_underline(PyObject *self, PyObject *args)
366 {
367     return _font_get_style_flag_as_py_bool(self, TTF_STYLE_UNDERLINE);
368 }
369 
370 /* Implements set_underline(bool) */
371 static PyObject *
font_set_underline(PyObject * self,PyObject * args)372 font_set_underline(PyObject *self, PyObject *args)
373 {
374     TTF_Font *font = PyFont_AsFont(self);
375     int val;
376 
377     if (!PyArg_ParseTuple(args, "p", &val))
378         return NULL;
379 
380     _font_set_or_clear_style_flag(font, TTF_STYLE_UNDERLINE, val);
381 
382     Py_RETURN_NONE;
383 }
384 
385 static PyObject *
font_render(PyObject * self,PyObject * args)386 font_render(PyObject *self, PyObject *args)
387 {
388     TTF_Font *font = PyFont_AsFont(self);
389     int aa;
390     PyObject *text, *final;
391     PyObject *fg_rgba_obj, *bg_rgba_obj = NULL;
392     Uint8 rgba[] = {0, 0, 0, 0};
393     SDL_Surface *surf;
394     SDL_Color foreg, backg;
395     int just_return;
396 
397     if (!PyArg_ParseTuple(args, "OpO|O", &text, &aa, &fg_rgba_obj,
398                           &bg_rgba_obj)) {
399         return NULL;
400     }
401 
402     if (!pg_RGBAFromFuzzyColorObj(fg_rgba_obj, rgba)) {
403         /* Exception already set for us */
404         return NULL;
405     }
406     foreg.r = rgba[0];
407     foreg.g = rgba[1];
408     foreg.b = rgba[2];
409     foreg.a = SDL_ALPHA_OPAQUE;
410     if (bg_rgba_obj == Py_None) {
411         /* Explicit None is the same as not passing a color for us */
412         bg_rgba_obj = NULL;
413     }
414     if (bg_rgba_obj != NULL) {
415         if (!pg_RGBAFromFuzzyColorObj(bg_rgba_obj, rgba)) {
416             /* Exception already set for us */
417             return NULL;
418         }
419         else {
420             backg.r = rgba[0];
421             backg.g = rgba[1];
422             backg.b = rgba[2];
423             backg.a = SDL_ALPHA_OPAQUE;
424         }
425     }
426     else {
427         backg.r = 0;
428         backg.g = 0;
429         backg.b = 0;
430         backg.a = SDL_ALPHA_OPAQUE;
431     }
432 
433     just_return = PyObject_Not(text);
434     if (just_return) {
435         int height = TTF_FontHeight(font);
436 
437         if (just_return == -1 ||
438             !(PyUnicode_Check(text) || Bytes_Check(text) || text == Py_None)) {
439             PyErr_Clear();
440             return RAISE_TEXT_TYPE_ERROR();
441         }
442         surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, height, 32, 0xff << 16,
443                                     0xff << 8, 0xff, 0);
444         if (surf == NULL) {
445             return RAISE(pgExc_SDLError, SDL_GetError());
446         }
447         if (bg_rgba_obj != NULL) {
448             Uint32 c = SDL_MapRGB(surf->format, backg.r, backg.g, backg.b);
449             SDL_FillRect(surf, NULL, c);
450         }
451         else {
452             SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
453         }
454     }
455     else if (PyUnicode_Check(text)) {
456         PyObject *bytes = PyUnicode_AsEncodedString(text, "utf-8", "replace");
457         const char *astring = NULL;
458 
459         if (!bytes) {
460             return NULL;
461         }
462         astring = Bytes_AsString(bytes);
463         if (strlen(astring) != (size_t)Bytes_GET_SIZE(bytes)) {
464             Py_DECREF(bytes);
465             return RAISE(PyExc_ValueError,
466                          "A null character was found in the text");
467         }
468 #if !SDL_TTF_VERSION_ATLEAST(2, 0, 15)
469         if (utf_8_needs_UCS_4(astring)) {
470             Py_DECREF(bytes);
471             return RAISE(PyExc_UnicodeError,
472                          "A Unicode character above '\\uFFFF' was found;"
473                          " not supported with SDL_ttf version below 2.0.15");
474         }
475 #endif
476         if (aa) {
477             if (bg_rgba_obj == NULL) {
478                 surf = TTF_RenderUTF8_Blended(font, astring, foreg);
479             }
480             else {
481                 surf = TTF_RenderUTF8_Shaded(font, astring, foreg, backg);
482             }
483         }
484         else {
485             surf = TTF_RenderUTF8_Solid(font, astring, foreg);
486         }
487         Py_DECREF(bytes);
488     }
489     else if (Bytes_Check(text)) {
490         const char *astring = Bytes_AsString(text);
491 
492         if (strlen(astring) != (size_t)Bytes_GET_SIZE(text)) {
493             return RAISE(PyExc_ValueError,
494                          "A null character was found in the text");
495         }
496         if (aa) {
497             if (bg_rgba_obj == NULL) {
498                 surf = TTF_RenderText_Blended(font, astring, foreg);
499             }
500             else {
501                 surf = TTF_RenderText_Shaded(font, astring, foreg, backg);
502             }
503         }
504         else {
505             surf = TTF_RenderText_Solid(font, astring, foreg);
506         }
507     }
508     else {
509         return RAISE_TEXT_TYPE_ERROR();
510     }
511     if (surf == NULL) {
512         return RAISE(pgExc_SDLError, TTF_GetError());
513     }
514     if (!aa && (bg_rgba_obj != NULL) && !just_return) {
515         /* turn off transparency */
516         SDL_SetColorKey(surf, 0, 0);
517         surf->format->palette->colors[0].r = backg.r;
518         surf->format->palette->colors[0].g = backg.g;
519         surf->format->palette->colors[0].b = backg.b;
520     }
521     final = (PyObject *)pgSurface_New(surf);
522     if (final == NULL) {
523         SDL_FreeSurface(surf);
524     }
525     return final;
526 }
527 
528 static PyObject *
font_size(PyObject * self,PyObject * args)529 font_size(PyObject *self, PyObject *args)
530 {
531     TTF_Font *font = PyFont_AsFont(self);
532     int w, h;
533     PyObject *text;
534     const char *string;
535 
536     if (!PyArg_ParseTuple(args, "O", &text)) {
537         return NULL;
538     }
539 
540     if (PyUnicode_Check(text)) {
541         PyObject *bytes = PyUnicode_AsEncodedString(text, "utf-8", "strict");
542         int ecode;
543 
544         if (!bytes) {
545             return NULL;
546         }
547         string = Bytes_AS_STRING(bytes);
548         ecode = TTF_SizeUTF8(font, string, &w, &h);
549         Py_DECREF(bytes);
550         if (ecode) {
551             return RAISE(pgExc_SDLError, TTF_GetError());
552         }
553     }
554     else if (Bytes_Check(text)) {
555         string = Bytes_AS_STRING(text);
556         if (TTF_SizeText(font, string, &w, &h)) {
557             return RAISE(pgExc_SDLError, TTF_GetError());
558         }
559     }
560     else {
561         return RAISE_TEXT_TYPE_ERROR();
562     }
563     return Py_BuildValue("(ii)", w, h);
564 }
565 
566 static PyObject *
font_metrics(PyObject * self,PyObject * args)567 font_metrics(PyObject *self, PyObject *args)
568 {
569     TTF_Font *font = PyFont_AsFont(self);
570     PyObject *list;
571     PyObject *textobj;
572     Py_ssize_t length;
573     Py_ssize_t i;
574     int minx;
575     int maxx;
576     int miny;
577     int maxy;
578     int advance;
579     PyObject *obj;
580     PyObject *listitem;
581     Uint16 *buffer;
582     Uint16 ch;
583     PyObject *temp;
584     int surrogate;
585 
586     if (!PyArg_ParseTuple(args, "O", &textobj)) {
587         return NULL;
588     }
589 
590     if (PyUnicode_Check(textobj)) {
591         obj = textobj;
592         Py_INCREF(obj);
593     }
594     else if (Bytes_Check(textobj)) {
595         obj = PyUnicode_FromEncodedObject(textobj, "UTF-8", NULL);
596         if (!obj) {
597             return NULL;
598         }
599     }
600     else {
601         return RAISE_TEXT_TYPE_ERROR();
602     }
603     temp = PyUnicode_AsUTF16String(obj);
604     Py_DECREF(obj);
605     if (!temp)
606         return NULL;
607     obj = temp;
608 
609     list = PyList_New(0);
610     if (!list) {
611         Py_DECREF(obj);
612         return NULL;
613     }
614     buffer = (Uint16 *)Bytes_AS_STRING(obj);
615     length = Bytes_GET_SIZE(obj) / sizeof(Uint16);
616     for (i = 1 /* skip BOM */; i < length; i++) {
617         ch = buffer[i];
618         surrogate = Py_UNICODE_IS_SURROGATE(ch);
619         /* TODO:
620          * TTF_GlyphMetrics() seems to return a value for any character,
621          * using the default invalid character, if the char is not found.
622          */
623         if (!surrogate && /* conditional and */
624             !TTF_GlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy,
625                               &advance)) {
626             listitem =
627                 Py_BuildValue("(iiiii)", minx, maxx, miny, maxy, advance);
628             if (!listitem) {
629                 Py_DECREF(list);
630                 Py_DECREF(obj);
631                 return NULL;
632             }
633         }
634         else {
635             /* Not UCS-2 or no matching metrics. */
636             Py_INCREF(Py_None);
637             listitem = Py_None;
638             if (surrogate)
639                 i++;
640         }
641         if (0 != PyList_Append(list, listitem)) {
642             Py_DECREF(list);
643             Py_DECREF(listitem);
644             Py_DECREF(obj);
645             return NULL; /* Exception already set. */
646         }
647         Py_DECREF(listitem);
648     }
649     Py_DECREF(obj);
650     return list;
651 }
652 
653 /**
654  * Getters and setters for the pgFontObject.
655  */
656 static PyGetSetDef font_getsets[] = {
657     {"bold", (getter)font_getter_bold, (setter)font_setter_bold, DOC_FONTBOLD,
658      NULL},
659     {"italic", (getter)font_getter_italic, (setter)font_setter_italic,
660      DOC_FONTITALIC, NULL},
661     {"underline", (getter)font_getter_underline, (setter)font_setter_underline,
662      DOC_FONTUNDERLINE, NULL},
663     {NULL, NULL, NULL, NULL, NULL}};
664 
665 static PyMethodDef font_methods[] = {
666     {"get_height", font_get_height, METH_NOARGS, DOC_FONTGETHEIGHT},
667     {"get_descent", font_get_descent, METH_NOARGS, DOC_FONTGETDESCENT},
668     {"get_ascent", font_get_ascent, METH_NOARGS, DOC_FONTGETASCENT},
669     {"get_linesize", font_get_linesize, METH_NOARGS, DOC_FONTGETLINESIZE},
670 
671     {"get_bold", font_get_bold, METH_NOARGS, DOC_FONTGETBOLD},
672     {"set_bold", font_set_bold, METH_VARARGS, DOC_FONTSETBOLD},
673     {"get_italic", font_get_italic, METH_NOARGS, DOC_FONTGETITALIC},
674     {"set_italic", font_set_italic, METH_VARARGS, DOC_FONTSETITALIC},
675     {"get_underline", font_get_underline, METH_NOARGS, DOC_FONTGETUNDERLINE},
676     {"set_underline", font_set_underline, METH_VARARGS, DOC_FONTSETUNDERLINE},
677 
678     {"metrics", font_metrics, METH_VARARGS, DOC_FONTMETRICS},
679     {"render", font_render, METH_VARARGS, DOC_FONTRENDER},
680     {"size", font_size, METH_VARARGS, DOC_FONTSIZE},
681 
682     {NULL, NULL, 0, NULL}};
683 
684 /*font object internals*/
685 static void
font_dealloc(PyFontObject * self)686 font_dealloc(PyFontObject *self)
687 {
688     TTF_Font *font = PyFont_AsFont(self);
689     if (font && font_initialized) {
690         if (self->ttf_init_generation != current_ttf_generation) {
691             // Since TTF_Font is a private structure
692             // it's impossible to access face field in a common way.
693             int** face_pp = font;
694             *face_pp = NULL;
695         }
696         TTF_CloseFont(font);
697         self->font = NULL;
698     }
699 
700     if (self->weakreflist)
701         PyObject_ClearWeakRefs((PyObject *)self);
702     Py_TYPE(self)->tp_free((PyObject *)self);
703 }
704 
705 static int
font_init(PyFontObject * self,PyObject * args,PyObject * kwds)706 font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
707 {
708     int fontsize;
709     TTF_Font *font = NULL;
710     PyObject *obj;
711     PyObject *test;
712     PyObject *oencoded = NULL;
713     SDL_RWops *rw;
714 
715     const char *filename;
716 
717     self->font = NULL;
718     if (!PyArg_ParseTuple(args, "Oi", &obj, &fontsize)) {
719         return -1;
720     }
721 
722     if (!font_initialized) {
723         PyErr_SetString(pgExc_SDLError, "font not initialized");
724         return -1;
725     }
726 
727     /* Incref obj, needs to be decref'd later */
728     Py_INCREF(obj);
729 
730     if (fontsize <= 1) {
731         fontsize = 1;
732     }
733 
734     if (obj == Py_None) {
735         /* default font */
736         Py_DECREF(obj);
737         obj = font_resource(font_defaultname);
738         if (obj == NULL) {
739             if (PyErr_Occurred() == NULL) {
740                 PyErr_Format(PyExc_RuntimeError,
741                              "default font '%.1024s' not found",
742                              font_defaultname);
743             }
744             goto error;
745         }
746         fontsize = (int)(fontsize * .6875);
747         if (fontsize <= 1)
748             fontsize = 1;
749     }
750 
751     /* SDL accepts UTF8 */
752     oencoded = pg_EncodeString(obj, "UTF8", NULL, NULL);
753     if (!oencoded || oencoded == Py_None) {
754         /* got a file object, or an error */
755         Py_XDECREF(oencoded);
756         oencoded = NULL;
757         PyErr_Clear();
758         goto fileobject;
759     }
760     filename = Bytes_AS_STRING(oencoded);
761 
762 #if FONT_HAVE_RWOPS
763     /* Try opening the path through RWops first */
764     if (filename) {
765         rw = SDL_RWFromFile(filename, "rb");
766         if (rw != NULL) {
767             Py_BEGIN_ALLOW_THREADS;
768             font = TTF_OpenFontIndexRW(rw, 1, fontsize, 0);
769             Py_END_ALLOW_THREADS;
770         }
771         else {
772             /*
773             PyErr_Format(PyExc_IOError,
774                                  "unable to read font file '%.1024s'",
775                                  filename);
776             goto error;
777             */
778 
779             /* silently ignore this failure. We will try opening the path
780                with fopen (pg_open_obj) again later.
781                RWops can open assets bundled with P4A on Android, but not
782                font_resource() paths */
783         }
784         if (font != NULL)
785             goto success;
786     }
787 #endif
788 
789     if (font == NULL) {
790         /*check if it is a valid file, else SDL_ttf segfaults*/
791         test = pg_open_obj(obj, "rb");
792         if (test == NULL) {
793             if (strcmp(filename, font_defaultname) == 0) {
794                 PyObject *tmp;
795                 PyErr_Clear();
796                 tmp = font_resource(font_defaultname);
797                 if (tmp == NULL) {
798                     if (!PyErr_Occurred()) {
799                         PyErr_Format(PyExc_IOError,
800                                      "unable to read font file '%.1024s'",
801                                      filename);
802                     }
803                     goto error;
804                 }
805                 Py_DECREF(obj);
806                 obj = tmp;
807                 filename = Bytes_AS_STRING(obj);
808                 test = pg_open_obj(obj, "rb");
809             }
810             if (test == NULL) {
811                 if (!PyErr_Occurred()) {
812                     PyErr_Format(PyExc_IOError,
813                                  "unable to read font file '%.1024s'",
814                                  filename);
815                 }
816                 goto error;
817             }
818         }
819         {
820             PyObject *tmp;
821             if (!(tmp = PyObject_CallMethod(test, "close", NULL))) {
822                 Py_DECREF(test);
823                 goto error;
824             }
825             Py_DECREF(tmp);
826         }
827         Py_DECREF(test);
828         /* opened file (test) is not used for loading,
829            SDL_TTF fopens the file _again_.*/
830 
831         Py_BEGIN_ALLOW_THREADS;
832         font = TTF_OpenFont(filename, fontsize);
833         Py_END_ALLOW_THREADS;
834     }
835 
836 fileobject:
837     if (font == NULL) {
838 #if FONT_HAVE_RWOPS
839         rw = pgRWops_FromFileObject(obj);
840 
841         if (rw == NULL) {
842             goto error;
843         }
844 
845         Py_BEGIN_ALLOW_THREADS;
846         font = TTF_OpenFontIndexRW(rw, 1, fontsize, 0);
847         Py_END_ALLOW_THREADS;
848 #else
849         PyErr_SetString(PyExc_NotImplementedError,
850                         "nonstring fonts require SDL_ttf-2.0.6");
851         goto error;
852 #endif
853     }
854 
855     if (font == NULL) {
856         PyErr_SetString(PyExc_RuntimeError, SDL_GetError());
857         goto error;
858     }
859 
860 success:
861     Py_XDECREF(oencoded);
862     Py_DECREF(obj);
863     self->font = font;
864     self->ttf_init_generation = current_ttf_generation;
865     return 0;
866 
867 error:
868     Py_XDECREF(oencoded);
869     Py_XDECREF(obj);
870     return -1;
871 }
872 
873 static PyTypeObject PyFont_Type = {
874     PyVarObject_HEAD_INIT(NULL, 0) "pygame.font.Font",
875     sizeof(PyFontObject),
876     0,
877     (destructor)font_dealloc,
878     0,
879     0, /*getattr*/
880     0,
881     0,
882     0,
883     0,
884     NULL,
885     0,
886     (hashfunc)NULL,
887     (ternaryfunc)NULL,
888     (reprfunc)NULL,
889     0L,
890     0L,
891     0L,
892     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
893     DOC_PYGAMEFONTFONT,                       /* Documentation string */
894     0,                                        /* tp_traverse */
895     0,                                        /* tp_clear */
896     0,                                        /* tp_richcompare */
897     offsetof(PyFontObject, weakreflist),      /* tp_weaklistoffset */
898     0,                                        /* tp_iter */
899     0,                                        /* tp_iternext */
900     font_methods,                             /* tp_methods */
901     0,                                        /* tp_members */
902     font_getsets,                             /* tp_getset */
903     0,                                        /* tp_base */
904     0,                                        /* tp_dict */
905     0,                                        /* tp_descr_get */
906     0,                                        /* tp_descr_set */
907     0,                                        /* tp_dictoffset */
908     (initproc)font_init,                      /* tp_init */
909     0,                                        /* tp_alloc */
910     0,                                        /* tp_new */
911 };
912 
913 //    PyType_GenericNew,                        /* tp_new */
914 
915 /*font module methods*/
916 static PyObject *
get_default_font(PyObject * self)917 get_default_font(PyObject *self)
918 {
919     return Text_FromUTF8(font_defaultname);
920 }
921 
922 static PyMethodDef _font_methods[] = {
923     {"init", (PyCFunction)fontmodule_init, METH_NOARGS, DOC_PYGAMEFONTINIT},
924     {"quit", (PyCFunction)fontmodule_quit, METH_NOARGS, DOC_PYGAMEFONTQUIT},
925     {"get_init", (PyCFunction)get_init, METH_NOARGS, DOC_PYGAMEFONTGETINIT},
926     {"get_default_font", (PyCFunction)get_default_font, METH_NOARGS,
927      DOC_PYGAMEFONTGETDEFAULTFONT},
928     {NULL, NULL, 0, NULL}};
929 
930 static PyObject *
PyFont_New(TTF_Font * font)931 PyFont_New(TTF_Font *font)
932 {
933     PyFontObject *fontobj;
934 
935     if (!font)
936         return RAISE(PyExc_RuntimeError, "unable to load font.");
937     fontobj = (PyFontObject *)PyFont_Type.tp_new(&PyFont_Type, NULL, NULL);
938 
939     if (fontobj)
940         fontobj->font = font;
941 
942     return (PyObject *)fontobj;
943 }
944 
MODINIT_DEFINE(font)945 MODINIT_DEFINE(font)
946 {
947     PyObject *module, *apiobj;
948     static void *c_api[PYGAMEAPI_FONT_NUMSLOTS];
949 
950     static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT,
951                                          "font",
952                                          DOC_PYGAMEFONT,
953                                          -1,
954                                          _font_methods,
955                                          NULL,
956                                          NULL,
957                                          NULL,
958                                          NULL};
959 
960     /* imported needed apis; Do this first so if there is an error
961        the module is not loaded.
962     */
963     import_pygame_base();
964     if (PyErr_Occurred()) {
965         MODINIT_ERROR;
966     }
967     import_pygame_color();
968     if (PyErr_Occurred()) {
969         MODINIT_ERROR;
970     }
971     import_pygame_surface();
972     if (PyErr_Occurred()) {
973         MODINIT_ERROR;
974     }
975     import_pygame_rwobject();
976     if (PyErr_Occurred()) {
977         MODINIT_ERROR;
978     }
979 
980     /* type preparation */
981     if (PyType_Ready(&PyFont_Type) < 0) {
982         MODINIT_ERROR;
983     }
984     PyFont_Type.tp_new = PyType_GenericNew;
985 
986     module = PyModule_Create(&_module);
987     if (module == NULL) {
988         MODINIT_ERROR;
989     }
990 
991     Py_INCREF((PyObject *)&PyFont_Type);
992     if (PyModule_AddObject(module, "FontType", (PyObject *)&PyFont_Type) ==
993         -1) {
994         Py_DECREF((PyObject *)&PyFont_Type);
995         DECREF_MOD(module);
996         MODINIT_ERROR;
997     }
998 
999     Py_INCREF((PyObject *)&PyFont_Type);
1000     if (PyModule_AddObject(module, "Font", (PyObject *)&PyFont_Type) == -1) {
1001         Py_DECREF((PyObject *)&PyFont_Type);
1002         DECREF_MOD(module);
1003         MODINIT_ERROR;
1004     }
1005 
1006 #if SDL_TTF_VERSION_ATLEAST(2, 0, 15)
1007     /* So people can check for UCS4 support. */
1008     if (PyModule_AddIntConstant(module, "UCS4", 1)) {
1009         DECREF_MOD(module);
1010         MODINIT_ERROR;
1011     }
1012 #endif
1013 
1014     /* export the c api */
1015     c_api[0] = &PyFont_Type;
1016     c_api[1] = PyFont_New;
1017     c_api[2] = &font_initialized;
1018     apiobj = encapsulate_api(c_api, "font");
1019     if (apiobj == NULL) {
1020         DECREF_MOD(module);
1021         MODINIT_ERROR;
1022     }
1023     if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj) == -1) {
1024         Py_DECREF(apiobj);
1025         DECREF_MOD(module);
1026         MODINIT_ERROR;
1027     }
1028     MODINIT_RETURN(module);
1029 }
1030