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