1 /*
2   pygame - Python Game Library
3   Copyright (C) 2009 Vicent Marti
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 */
20 
21 #define PYGAME_FREETYPE_INTERNAL
22 #define PYGAME_FREETYPE_FONT_INTERNAL
23 
24 #include "freetype.h"
25 
26 #include "freetype/ft_wrap.h"
27 
28 #include "doc/freetype_doc.h"
29 
30 #define MODULE_NAME "_freetype"
31 #define FONT_TYPE_NAME "Font"
32 
33 /*
34  * FreeType module declarations
35  */
36 static const Scale_t FACE_SIZE_NONE = {0, 0};
37 
38 static int
39 _ft_traverse(PyObject *, visitproc, void *);
40 static int
41 _ft_clear(PyObject *);
42 
43 static PyObject *
44 _ft_quit(PyObject *);
45 static PyObject *
46 _ft_init(PyObject *, PyObject *, PyObject *);
47 static PyObject *
48 _ft_get_version(PyObject *, PyObject *);
49 static PyObject *
50 _ft_get_error(PyObject *, PyObject *);
51 static PyObject *
52 _ft_get_init(PyObject *, PyObject *);
53 static PyObject *
54 _ft_autoinit(PyObject *);
55 static PyObject *
56 _ft_get_cache_size(PyObject *, PyObject *);
57 static PyObject *
58 _ft_get_default_resolution(PyObject *, PyObject *);
59 static PyObject *
60 _ft_set_default_resolution(PyObject *, PyObject *);
61 static PyObject *
62 _ft_get_default_font(PyObject *self, PyObject *args);
63 
64 /*
65  * Constructor/init/destructor
66  */
67 static PyObject *
68 _ftfont_new(PyTypeObject *, PyObject *, PyObject *);
69 static void
70 _ftfont_dealloc(pgFontObject *);
71 static PyObject *
72 _ftfont_repr(pgFontObject *);
73 static int
74 _ftfont_init(pgFontObject *, PyObject *, PyObject *);
75 
76 /*
77  * Main methods
78  */
79 static PyObject *
80 _ftfont_getrect(pgFontObject *, PyObject *, PyObject *);
81 static PyObject *
82 _ftfont_getmetrics(pgFontObject *, PyObject *, PyObject *);
83 static PyObject *
84 _ftfont_render(pgFontObject *, PyObject *, PyObject *);
85 static PyObject *
86 _ftfont_render_to(pgFontObject *, PyObject *, PyObject *);
87 static PyObject *
88 _ftfont_render_raw(pgFontObject *, PyObject *, PyObject *);
89 static PyObject *
90 _ftfont_render_raw_to(pgFontObject *, PyObject *, PyObject *);
91 static PyObject *
92 _ftfont_getsizedascender(pgFontObject *, PyObject *);
93 static PyObject *
94 _ftfont_getsizeddescender(pgFontObject *, PyObject *);
95 static PyObject *
96 _ftfont_getsizedheight(pgFontObject *, PyObject *);
97 static PyObject *
98 _ftfont_getsizedglyphheight(pgFontObject *, PyObject *);
99 static PyObject *
100 _ftfont_getsizes(pgFontObject *);
101 
102 /* static PyObject *_ftfont_copy(pgFontObject *); */
103 
104 /*
105  * Getters/setters
106  */
107 static PyObject *
108 _ftfont_getsize(pgFontObject *, void *);
109 static int
110 _ftfont_setsize(pgFontObject *, PyObject *, void *);
111 static PyObject *
112 _ftfont_getstyle(pgFontObject *, void *);
113 static int
114 _ftfont_setstyle(pgFontObject *, PyObject *, void *);
115 static PyObject *
116 _ftfont_getname(pgFontObject *, void *);
117 static PyObject *
118 _ftfont_getpath(pgFontObject *, void *);
119 static PyObject *
120 _ftfont_getscalable(pgFontObject *, void *);
121 static PyObject *
122 _ftfont_getfixedwidth(pgFontObject *, void *);
123 static PyObject *
124 _ftfont_getfixedsizes(pgFontObject *, void *);
125 static PyObject *
126 _ftfont_getstrength(pgFontObject *, void *);
127 static int
128 _ftfont_setstrength(pgFontObject *, PyObject *, void *);
129 static PyObject *
130 _ftfont_getunderlineadjustment(pgFontObject *, void *);
131 static int
132 _ftfont_setunderlineadjustment(pgFontObject *, PyObject *, void *);
133 static PyObject *
134 _ftfont_getrotation(pgFontObject *, void *);
135 static int
136 _ftfont_setrotation(pgFontObject *, PyObject *, void *);
137 static PyObject *
138 _ftfont_getfgcolor(pgFontObject *, void *);
139 static int
140 _ftfont_setfgcolor(pgFontObject *, PyObject *, void *);
141 static PyObject *
142 _ftfont_getbgcolor(pgFontObject *, void *);
143 static int
144 _ftfont_setbgcolor(pgFontObject *, PyObject *, void *);
145 
146 static PyObject *
147 _ftfont_getresolution(pgFontObject *, void *);
148 
149 static PyObject *
150 _ftfont_getfontmetric(pgFontObject *, void *);
151 
152 static PyObject *
153 _ftfont_getstyle_flag(pgFontObject *, void *);
154 static int
155 _ftfont_setstyle_flag(pgFontObject *, PyObject *, void *);
156 
157 static PyObject *
158 _ftfont_getrender_flag(pgFontObject *, void *);
159 static int
160 _ftfont_setrender_flag(pgFontObject *, PyObject *, void *);
161 
162 #if defined(PGFT_DEBUG_CACHE)
163 static PyObject *
164 _ftfont_getdebugcachestats(pgFontObject *, void *);
165 #endif
166 
167 /*
168  * Internal helpers
169  */
170 static PyObject *
171 get_metrics(FontRenderMode *, pgFontObject *, PGFT_String *);
172 static PyObject *
173 load_font_res(const char *);
174 static int
175 parse_dest(PyObject *, int *, int *);
176 static int
177 obj_to_scale(PyObject *, void *);
178 static int
179 objs_to_scale(PyObject *, PyObject *, Scale_t *);
180 static int
181 numbers_to_scale(PyObject *, PyObject *, Scale_t *);
182 static int
183 build_scale(PyObject *, PyObject *, Scale_t *);
184 static FT_UInt
185 number_to_FX6_unsigned(PyObject *);
186 static int
187 obj_to_rotation(PyObject *, void *);
188 static void
189 free_string(PGFT_String *);
190 
191 /*
192  * Auxiliar defines
193  */
194 #define ASSERT_SELF_IS_ALIVE(s)                                          \
195     if (!pgFont_IS_ALIVE(s)) {                                           \
196         return RAISE(PyExc_RuntimeError, MODULE_NAME                     \
197                      "." FONT_TYPE_NAME " instance is not initialized"); \
198     }
199 
200 #define PGFT_CHECK_BOOL(_pyobj, _var)                          \
201     if (_pyobj) {                                              \
202         if (!PyBool_Check(_pyobj)) {                           \
203             PyErr_SetString(PyExc_TypeError,                   \
204                             #_var " must be a boolean value"); \
205             return 0;                                          \
206         }                                                      \
207                                                                \
208         _var = PyObject_IsTrue(_pyobj);                        \
209     }
210 
211 #define DEFAULT_FONT_NAME "freesansbold.ttf"
212 #define PKGDATA_MODULE_NAME "pygame.pkgdata"
213 #define RESOURCE_FUNC_NAME "getResource"
214 
215 static PyObject *
load_font_res(const char * filename)216 load_font_res(const char *filename)
217 {
218     PyObject *load_basicfunc = 0;
219     PyObject *pkgdatamodule = 0;
220     PyObject *resourcefunc = 0;
221     PyObject *result = 0;
222     PyObject *tmp;
223 
224     pkgdatamodule = PyImport_ImportModule(PKGDATA_MODULE_NAME);
225     if (!pkgdatamodule) {
226         goto font_resource_end;
227     }
228 
229     resourcefunc = PyObject_GetAttrString(pkgdatamodule, RESOURCE_FUNC_NAME);
230     if (!resourcefunc) {
231         goto font_resource_end;
232     }
233 
234     result = PyObject_CallFunction(resourcefunc, "s", filename);
235     if (!result) {
236         goto font_resource_end;
237     }
238 
239     tmp = PyObject_GetAttrString(result, "name");
240     if (tmp) {
241         PyObject *closeret;
242         if (!(closeret = PyObject_CallMethod(result, "close", NULL))) {
243             Py_DECREF(result);
244             Py_DECREF(tmp);
245             result = NULL;
246             goto font_resource_end;
247         }
248         Py_DECREF(closeret);
249 
250         Py_DECREF(result);
251         result = tmp;
252     }
253     else {
254         PyErr_Clear();
255     }
256 
257 font_resource_end:
258     Py_XDECREF(pkgdatamodule);
259     Py_XDECREF(resourcefunc);
260     Py_XDECREF(load_basicfunc);
261     return result;
262 }
263 
264 static int
parse_dest(PyObject * dest,int * x,int * y)265 parse_dest(PyObject *dest, int *x, int *y)
266 {
267     PyObject *oi;
268     PyObject *oj;
269     int i, j;
270 
271     if (!PySequence_Check(dest) || /* conditional and */
272         !(PySequence_Size(dest) > 1)) {
273         PyErr_Format(PyExc_TypeError,
274                      "Expected length 2 sequence for dest argument:"
275                      " got type %.1024s",
276                      Py_TYPE(dest)->tp_name);
277         return -1;
278     }
279     oi = PySequence_GetItem(dest, 0);
280     if (!oi) {
281         return -1;
282     }
283     oj = PySequence_GetItem(dest, 1);
284     if (!oj) {
285         Py_DECREF(oi);
286         return -1;
287     }
288     if (!PyNumber_Check(oi) || !PyNumber_Check(oj)) {
289         PyErr_Format(PyExc_TypeError,
290                      "for dest expected a pair of numbers"
291                      "for elements 1 and 2: got types %.1024s and %1024s",
292                      Py_TYPE(oi)->tp_name, Py_TYPE(oj)->tp_name);
293         Py_DECREF(oi);
294         Py_DECREF(oj);
295         return -1;
296     }
297     if (!pg_IntFromObj(oi, &i) || !pg_IntFromObj(oj, &j)){
298         Py_DECREF(oi);
299         Py_DECREF(oj);
300         PyErr_SetString(PyExc_TypeError, "dest expects a pair of numbers");
301         return -1;
302     }
303     Py_DECREF(oi);
304     Py_DECREF(oj);
305     *x = i;
306     *y = j;
307     return 0;
308 }
309 
310 /** Point size PyArg_ParseTuple converter: int -> Scale_t */
311 static int
obj_to_scale(PyObject * o,void * p)312 obj_to_scale(PyObject *o, void *p)
313 {
314     if (PyTuple_Check(o)) {
315         if (PyTuple_GET_SIZE(o) != 2) {
316             PyErr_Format(PyExc_TypeError,
317                          "expected a 2-tuple for size, got %zd-tuple",
318                          PyTuple_GET_SIZE(o));
319             return 0;
320         }
321         return objs_to_scale(PyTuple_GET_ITEM(o, 0), PyTuple_GET_ITEM(o, 1),
322                              (Scale_t *)p);
323     }
324     return objs_to_scale(o, 0, (Scale_t *)p);
325 }
326 
327 static int
objs_to_scale(PyObject * x,PyObject * y,Scale_t * size)328 objs_to_scale(PyObject *x, PyObject *y, Scale_t *size)
329 {
330     PyObject *o;
331     int do_y;
332 
333     for (o = x, do_y = 1; o; o = (do_y--) ? y : 0) {
334         if (!PyLong_Check(o) &&
335             !PyFloat_Check(o)) {
336             if (y) {
337                 PyErr_Format(PyExc_TypeError,
338                              "expected a (float, float) tuple for size"
339                              ", got (%128s, %128s)",
340                              Py_TYPE(x)->tp_name, Py_TYPE(y)->tp_name);
341             }
342             else {
343                 PyErr_Format(PyExc_TypeError,
344                              "expected a float for size, got %128s",
345                              Py_TYPE(o)->tp_name);
346             }
347             return 0;
348         }
349     }
350 
351     return numbers_to_scale(x, y, size);
352 }
353 
354 static int
numbers_to_scale(PyObject * x,PyObject * y,Scale_t * size)355 numbers_to_scale(PyObject *x, PyObject *y, Scale_t *size)
356 {
357     PyObject *o;
358     PyObject *min_obj = 0;
359     PyObject *max_obj = 0;
360     int do_y;
361     int cmp_result;
362     int rval = 0;
363 
364     min_obj = PyFloat_FromDouble(0.0);
365     if (!min_obj)
366         goto finish;
367     max_obj = PyFloat_FromDouble(FX6_TO_DBL(FX6_MAX));
368     if (!max_obj)
369         goto finish;
370 
371     for (o = x, do_y = 1; o; o = (do_y--) ? y : 0) {
372         cmp_result = PyObject_RichCompareBool(o, min_obj, Py_LT);
373         if (cmp_result == -1)
374             goto finish;
375         if (cmp_result == 1) {
376             PyErr_Format(PyExc_OverflowError,
377                          "%128s value is negative"
378                          " while size value is zero or positive",
379                          Py_TYPE(o)->tp_name);
380             goto finish;
381         }
382         cmp_result = PyObject_RichCompareBool(o, max_obj, Py_GT);
383         if (cmp_result == -1)
384             goto finish;
385         if (cmp_result == 1) {
386             PyErr_Format(PyExc_OverflowError,
387                          "%128s value too large to convert to a size value",
388                          Py_TYPE(o)->tp_name);
389             goto finish;
390         }
391     }
392 
393     rval = build_scale(x, y, size);
394 
395 finish:
396     Py_XDECREF(min_obj);
397     Py_XDECREF(max_obj);
398     return rval;
399 }
400 
401 static int
build_scale(PyObject * x,PyObject * y,Scale_t * size)402 build_scale(PyObject *x, PyObject *y, Scale_t *size)
403 {
404     FT_UInt sz_x = 0, sz_y = 0;
405 
406     sz_x = number_to_FX6_unsigned(x);
407     if (PyErr_Occurred()) {
408         return 0;
409     }
410     if (y) {
411         sz_y = number_to_FX6_unsigned(y);
412         if (PyErr_Occurred()) {
413             return 0;
414         }
415     }
416     if (sz_x == 0 && sz_y != 0) {
417         PyErr_SetString(PyExc_ValueError,
418                         "expected zero size height when width is zero");
419         return 0;
420     }
421     size->x = sz_x;
422     size->y = sz_y;
423     return 1;
424 }
425 
426 static FT_UInt
number_to_FX6_unsigned(PyObject * n)427 number_to_FX6_unsigned(PyObject *n)
428 {
429     PyObject *f_obj = PyNumber_Float(n);
430     double f;
431 
432     if (!f_obj)
433         return 0;
434     f = PyFloat_AsDouble(f_obj);
435     Py_XDECREF(f_obj);
436     if (PyErr_Occurred())
437         return 0;
438     return DBL_TO_FX6(f);
439 }
440 
441 /** rotation: int -> Angle_t */
442 int
obj_to_rotation(PyObject * o,void * p)443 obj_to_rotation(PyObject *o, void *p)
444 {
445     PyObject *full_circle_obj = 0;
446     PyObject *angle_obj = 0;
447     long angle;
448     int rval = 0;
449 
450     if (PyLong_Check(o)) {
451         ;
452     }
453     else {
454         PyErr_Format(PyExc_TypeError, "integer rotation expected, got %s",
455                      Py_TYPE(o)->tp_name);
456         goto finish;
457     }
458     full_circle_obj = PyLong_FromLong(360L);
459     if (!full_circle_obj)
460         goto finish;
461     angle_obj = PyNumber_Remainder(o, full_circle_obj);
462     if (!angle_obj)
463         goto finish;
464     angle = PyLong_AsLong(angle_obj);
465     if (angle == -1)
466         goto finish;
467     *(Angle_t *)p = (Angle_t)INT_TO_FX16(angle);
468     rval = 1;
469 
470 finish:
471     Py_XDECREF(full_circle_obj);
472     Py_XDECREF(angle_obj);
473     return rval;
474 }
475 
476 /** This accepts a NULL PGFT_String pointer */
477 static void
free_string(PGFT_String * p)478 free_string(PGFT_String *p)
479 {
480     if (p)
481         _PGFT_FreeString(p);
482 }
483 
484 /*
485  * FREETYPE MODULE METHODS TABLE
486  */
487 static PyMethodDef _ft_methods[] = {
488     {"__PYGAMEinit__", (PyCFunction)_ft_autoinit, METH_NOARGS,
489      "auto initialize function for _freetype"},
490     {"init", (PyCFunction)_ft_init, METH_VARARGS | METH_KEYWORDS,
491      DOC_PYGAMEFREETYPEINIT},
492     {"quit", (PyCFunction)_ft_quit, METH_NOARGS, DOC_PYGAMEFREETYPEQUIT},
493     {"get_init", _ft_get_init, METH_NOARGS,
494      DOC_PYGAMEFREETYPEGETINIT},
495     {"was_init", _ft_get_init, METH_NOARGS,
496      DOC_PYGAMEFREETYPEWASINIT},  // DEPRECATED
497     {"get_error", _ft_get_error, METH_NOARGS,
498      DOC_PYGAMEFREETYPEGETERROR},
499     {"get_version", _ft_get_version, METH_NOARGS,
500      DOC_PYGAMEFREETYPEGETVERSION},
501     {"get_cache_size", _ft_get_cache_size, METH_NOARGS,
502      DOC_PYGAMEFREETYPEGETCACHESIZE},
503     {"get_default_resolution", _ft_get_default_resolution,
504      METH_NOARGS, DOC_PYGAMEFREETYPEGETDEFAULTRESOLUTION},
505     {"set_default_resolution", _ft_set_default_resolution,
506      METH_VARARGS, DOC_PYGAMEFREETYPESETDEFAULTRESOLUTION},
507     {"get_default_font", _ft_get_default_font, METH_NOARGS,
508      DOC_PYGAMEFREETYPEGETDEFAULTFONT},
509 
510     {0, 0, 0, 0}};
511 
512 /*
513  * FREETYPE FONT METHODS TABLE
514  */
515 static PyMethodDef _ftfont_methods[] = {
516     {"get_sized_height", (PyCFunction)_ftfont_getsizedheight, METH_VARARGS,
517      DOC_FONTGETSIZEDHEIGHT},
518     {"get_sized_ascender", (PyCFunction)_ftfont_getsizedascender, METH_VARARGS,
519      DOC_FONTGETSIZEDASCENDER},
520     {"get_sized_descender", (PyCFunction)_ftfont_getsizeddescender,
521      METH_VARARGS, DOC_FONTGETSIZEDDESCENDER},
522     {"get_sized_glyph_height", (PyCFunction)_ftfont_getsizedglyphheight,
523      METH_VARARGS, DOC_FONTGETSIZEDGLYPHHEIGHT},
524     {"get_rect", (PyCFunction)_ftfont_getrect, METH_VARARGS | METH_KEYWORDS,
525      DOC_FONTGETRECT},
526     {"get_metrics", (PyCFunction)_ftfont_getmetrics,
527      METH_VARARGS | METH_KEYWORDS, DOC_FONTGETMETRICS},
528     {"get_sizes", (PyCFunction)_ftfont_getsizes, METH_NOARGS,
529      DOC_FONTGETSIZES},
530     {"render", (PyCFunction)_ftfont_render, METH_VARARGS | METH_KEYWORDS,
531      DOC_FONTRENDER},
532     {"render_to", (PyCFunction)_ftfont_render_to, METH_VARARGS | METH_KEYWORDS,
533      DOC_FONTRENDERTO},
534     {"render_raw", (PyCFunction)_ftfont_render_raw,
535      METH_VARARGS | METH_KEYWORDS, DOC_FONTRENDERRAW},
536     {"render_raw_to", (PyCFunction)_ftfont_render_raw_to,
537      METH_VARARGS | METH_KEYWORDS, DOC_FONTRENDERRAWTO},
538 
539     {0, 0, 0, 0}};
540 
541 /*
542  * FREETYPE FONT GETTERS/SETTERS TABLE
543  */
544 static PyGetSetDef _ftfont_getsets[] = {
545     {"size", (getter)_ftfont_getsize, (setter)_ftfont_setsize, DOC_FONTSIZE,
546      0},
547     {"style", (getter)_ftfont_getstyle, (setter)_ftfont_setstyle,
548      DOC_FONTSTYLE, 0},
549     {"height", (getter)_ftfont_getfontmetric, 0, DOC_FONTHEIGHT,
550      (void *)_PGFT_Font_GetHeight},
551     {"ascender", (getter)_ftfont_getfontmetric, 0, DOC_FONTASCENDER,
552      (void *)_PGFT_Font_GetAscender},
553     {"descender", (getter)_ftfont_getfontmetric, 0, DOC_FONTASCENDER,
554      (void *)_PGFT_Font_GetDescender},
555     {"name", (getter)_ftfont_getname, 0, DOC_FONTNAME, 0},
556     {"path", (getter)_ftfont_getpath, 0, DOC_FONTPATH, 0},
557     {"scalable", (getter)_ftfont_getscalable, 0, DOC_FONTSCALABLE, 0},
558     {"fixed_width", (getter)_ftfont_getfixedwidth, 0, DOC_FONTFIXEDWIDTH, 0},
559     {"fixed_sizes", (getter)_ftfont_getfixedsizes, 0, DOC_FONTFIXEDSIZES, 0},
560     {"antialiased", (getter)_ftfont_getrender_flag,
561      (setter)_ftfont_setrender_flag, DOC_FONTANTIALIASED,
562      (void *)FT_RFLAG_ANTIALIAS},
563     {"kerning", (getter)_ftfont_getrender_flag, (setter)_ftfont_setrender_flag,
564      DOC_FONTKERNING, (void *)FT_RFLAG_KERNING},
565     {"vertical", (getter)_ftfont_getrender_flag,
566      (setter)_ftfont_setrender_flag, DOC_FONTVERTICAL,
567      (void *)FT_RFLAG_VERTICAL},
568     {"pad", (getter)_ftfont_getrender_flag, (setter)_ftfont_setrender_flag,
569      DOC_FONTPAD, (void *)FT_RFLAG_PAD},
570     {"oblique", (getter)_ftfont_getstyle_flag, (setter)_ftfont_setstyle_flag,
571      DOC_FONTOBLIQUE, (void *)FT_STYLE_OBLIQUE},
572     {"strong", (getter)_ftfont_getstyle_flag, (setter)_ftfont_setstyle_flag,
573      DOC_FONTSTRONG, (void *)FT_STYLE_STRONG},
574     {"underline", (getter)_ftfont_getstyle_flag, (setter)_ftfont_setstyle_flag,
575      DOC_FONTUNDERLINE, (void *)FT_STYLE_UNDERLINE},
576     {"wide", (getter)_ftfont_getstyle_flag, (setter)_ftfont_setstyle_flag,
577      DOC_FONTWIDE, (void *)FT_STYLE_WIDE},
578     {"strength", (getter)_ftfont_getstrength, (setter)_ftfont_setstrength,
579      DOC_FONTSTRENGTH, 0},
580     {"underline_adjustment", (getter)_ftfont_getunderlineadjustment,
581      (setter)_ftfont_setunderlineadjustment, DOC_FONTUNDERLINEADJUSTMENT, 0},
582     {"ucs4", (getter)_ftfont_getrender_flag, (setter)_ftfont_setrender_flag,
583      DOC_FONTUCS4, (void *)FT_RFLAG_UCS4},
584     {"use_bitmap_strikes", (getter)_ftfont_getrender_flag,
585      (setter)_ftfont_setrender_flag, DOC_FONTUSEBITMAPSTRIKES,
586      (void *)FT_RFLAG_USE_BITMAP_STRIKES},
587     {"resolution", (getter)_ftfont_getresolution, 0, DOC_FONTRESOLUTION, 0},
588     {"rotation", (getter)_ftfont_getrotation, (setter)_ftfont_setrotation,
589      DOC_FONTROTATION, 0},
590     {"fgcolor", (getter)_ftfont_getfgcolor, (setter)_ftfont_setfgcolor,
591      DOC_FONTFGCOLOR, 0},
592      {"bgcolor", (getter)_ftfont_getbgcolor, (setter)_ftfont_setbgcolor,
593      DOC_FONTBGCOLOR, 0},
594     {"origin", (getter)_ftfont_getrender_flag, (setter)_ftfont_setrender_flag,
595      DOC_FONTORIGIN, (void *)FT_RFLAG_ORIGIN},
596 #if defined(PGFT_DEBUG_CACHE)
597     {"_debug_cache_stats", (getter)_ftfont_getdebugcachestats, 0,
598      "_debug cache fields as a tuple", 0},
599 #endif
600 
601     {0, 0, 0, 0, 0}};
602 
603 /*
604  * FREETYPE FONT BASE TYPE TABLE
605  */
606 #define FULL_TYPE_NAME MODULE_NAME "." FONT_TYPE_NAME
607 
608 PyTypeObject pgFont_Type = {
609     PyVarObject_HEAD_INIT(0,0)
610     FULL_TYPE_NAME,                           /* tp_name */
611     sizeof(pgFontObject),                     /* tp_basicsize */
612     0,                                        /* tp_itemsize */
613     (destructor)_ftfont_dealloc,              /* tp_dealloc */
614     0,                                        /* tp_print */
615     0,                                        /* tp_getattr */
616     0,                                        /* tp_setattr */
617     0,                                        /* tp_compare */
618     (reprfunc)_ftfont_repr,                   /* tp_repr */
619     0,                                        /* tp_as_number */
620     0,                                        /* tp_as_sequence */
621     0,                                        /* tp_as_mapping */
622     0,                                        /* tp_hash */
623     0,                                        /* tp_call */
624     0,                                        /* tp_str */
625     0,                                        /* tp_getattro */
626     0,                                        /* tp_setattro */
627     0,                                        /* tp_as_buffer */
628     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
629     DOC_PYGAMEFREETYPEFONT,                   /* docstring */
630     0,                                        /* tp_traverse */
631     0,                                        /* tp_clear */
632     0,                                        /* tp_richcompare */
633     0,                                        /* tp_weaklistoffset */
634     0,                                        /* tp_iter */
635     0,                                        /* tp_iternext */
636     _ftfont_methods,                          /* tp_methods */
637     0,                                        /* tp_members */
638     _ftfont_getsets,                          /* tp_getset */
639     0,                                        /* tp_base */
640     0,                                        /* tp_dict */
641     0,                                        /* tp_descr_get */
642     0,                                        /* tp_descr_set */
643     0,                                        /* tp_dictoffset */
644     (initproc)_ftfont_init,                   /* tp_init */
645     0,                                        /* tp_alloc */
646     (newfunc)_ftfont_new,                     /* tp_new */
647     0,                                        /* tp_free */
648     0,                                        /* tp_is_gc */
649     0,                                        /* tp_bases */
650     0,                                        /* tp_mro */
651     0,                                        /* tp_cache */
652     0,                                        /* tp_subclasses */
653     0,                                        /* tp_weaklist */
654     0,                                        /* tp_del */
655     0 /* tp_version_tag */
656 };
657 
658 #undef FULL_TYPE_NAME
659 
660 /****************************************************
661  * CONSTRUCTOR/INIT/DESTRUCTOR
662  ****************************************************/
663 static PyObject *
_ftfont_new(PyTypeObject * subtype,PyObject * args,PyObject * kwds)664 _ftfont_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
665 {
666     pgFontObject *obj = (pgFontObject *)(subtype->tp_alloc(subtype, 0));
667 
668     if (obj) {
669         obj->id.open_args.flags = 0;
670         obj->id.open_args.pathname = 0;
671         obj->path = 0;
672         obj->resolution = 0;
673         obj->is_scalable = 0;
674         obj->freetype = 0;
675         obj->_internals = 0;
676         obj->face_size = FACE_SIZE_NONE;
677         obj->style = FT_STYLE_NORMAL;
678         obj->render_flags = FT_RFLAG_DEFAULTS;
679         obj->strength = PGFT_DBL_DEFAULT_STRENGTH;
680         obj->underline_adjustment = 1.0;
681         obj->rotation = 0;
682         obj->transform.xx = FX16_ONE;
683         obj->transform.xy = 0;
684         obj->transform.yx = 0;
685         obj->transform.yy = FX16_ONE;
686         obj->fgcolor[0] = 0; /* rgba opaque black */
687         obj->fgcolor[1] = 0;
688         obj->fgcolor[2] = 0;
689         obj->fgcolor[3] = 255;
690         obj->is_bg_col_set = 0;
691         obj->bgcolor[0] = 0; /* rgba transparent black */
692         obj->bgcolor[1] = 0;
693         obj->bgcolor[2] = 0;
694         obj->bgcolor[3] = 0;
695     }
696     return (PyObject *)obj;
697 }
698 
699 static void
_ftfont_dealloc(pgFontObject * self)700 _ftfont_dealloc(pgFontObject *self)
701 {
702 #ifdef HAVE_PYGAME_SDL_RWOPS
703     SDL_RWops *src = _PGFT_GetRWops(self);
704 #endif
705     _PGFT_UnloadFont(self->freetype, self);
706 #ifdef HAVE_PYGAME_SDL_RWOPS
707     if (src) {
708         pgRWops_ReleaseObject(src);
709     }
710 #endif
711     _PGFT_Quit(self->freetype);
712 
713     Py_XDECREF(self->path);
714     ((PyObject *)self)->ob_type->tp_free((PyObject *)self);
715 }
716 
717 static int
_ftfont_init(pgFontObject * self,PyObject * args,PyObject * kwds)718 _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds)
719 {
720     static char *kwlist[] = {"file",       "size", "font_index",
721                              "resolution", "ucs4", 0};
722 
723     PyObject *file, *original_file;
724     long font_index = 0;
725     Scale_t face_size = self->face_size;
726     int ucs4 = (self->render_flags & FT_RFLAG_UCS4) ? 1 : 0;
727     unsigned resolution = 0;
728     long size = 0;
729     long height = 0;
730     long width = 0;
731     double x_ppem = 0;
732     double y_ppem = 0;
733     int rval = -1;
734     SDL_RWops *source;
735 
736     FreeTypeInstance *ft;
737     ASSERT_GRAB_FREETYPE(ft, -1);
738 
739     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&lIi", kwlist, &file,
740                                      obj_to_scale, (void *)&face_size,
741                                      &font_index, &resolution, &ucs4)) {
742         return -1;
743     }
744 
745     original_file = file;
746 
747     if (self->freetype) {
748         /* Font.__init__ was previously called on this object. Reset */
749         _PGFT_UnloadFont(self->freetype, self);
750         _PGFT_Quit(self->freetype);
751         self->freetype = 0;
752     }
753     Py_XDECREF(self->path);
754     self->path = 0;
755     self->is_scalable = 0;
756 
757     self->face_size = face_size;
758     if (ucs4) {
759         self->render_flags |= FT_RFLAG_UCS4;
760     }
761     else {
762         self->render_flags &= ~FT_RFLAG_UCS4;
763     }
764     if (resolution) {
765         self->resolution = (FT_UInt)resolution;
766     }
767     else {
768         self->resolution = FREETYPE_STATE->resolution;
769     }
770     if (file == Py_None) {
771         file = load_font_res(DEFAULT_FONT_NAME);
772 
773         if (!file) {
774             PyErr_SetString(PyExc_RuntimeError, "Failed to find default font");
775             goto end;
776         }
777     }
778 
779 #if !defined(WIN32) || !defined(HAVE_PYGAME_SDL_RWOPS)
780     file = pg_EncodeString(file, "UTF-8", NULL, NULL);
781     if (!file) {
782         goto end;
783     }
784     if (Bytes_Check(file)) {
785         if (PyUnicode_Check(original_file)) {
786             /* Make sure to save a pure Unicode object to prevent possible
787              * cycles from a derived class. This means no tp_traverse or
788              * tp_clear for the PyFreetypeFont type.
789              */
790             self->path = Object_Unicode(original_file);
791         }
792         else {
793             self->path = PyUnicode_FromEncodedObject(
794                 file, "UTF-8", NULL);
795         }
796         if (!self->path) {
797             goto end;
798         }
799 
800         if (_PGFT_TryLoadFont_Filename(ft, self, Bytes_AS_STRING(file),
801                                        font_index)) {
802             goto end;
803         }
804     } else {
805         PyObject *str = 0;
806         PyObject *path = 0;
807 #ifndef WITH_THREAD
808         goto end;
809 #endif
810         source = pgRWops_FromFileObject(original_file);
811         if (!source) {
812             goto end;
813         }
814 
815         path = PyObject_GetAttrString(original_file, "name");
816         if (!path) {
817             PyErr_Clear();
818             str = Bytes_FromFormat("<%s instance at %p>",
819                                    Py_TYPE(file)->tp_name, (void *)file);
820             if (str) {
821                 self->path =
822                     PyUnicode_FromEncodedObject(str, "ascii", "strict");
823                 Py_DECREF(str);
824             }
825         }
826         else if (PyUnicode_Check(path)) {
827             /* Make sure to save a pure Unicode object to prevent possible
828              * cycles from a derived class. This means no tp_traverse or
829              * tp_clear for the PyFreetypeFont type.
830              */
831             self->path = Object_Unicode(path);
832         }
833         else if (Bytes_Check(path)) {
834             self->path = PyUnicode_FromEncodedObject(
835                 path, "UTF-8", NULL);
836         }
837         else {
838             self->path = Object_Unicode(path);
839         }
840         Py_XDECREF(path);
841         if (!self->path) {
842             goto end;
843         }
844 
845         if (_PGFT_TryLoadFont_RWops(ft, self, source, font_index)) {
846             goto end;
847         }
848     }
849 #else /* WIN32 && HAVE_PYGAME_SDL_RWOPS */
850     /* FT uses fopen(); as a workaround, always use RWops */
851     if (file == original_file)
852         Py_INCREF(file);
853     if (!PG_CHECK_THREADS())
854         goto end;
855     source = pgRWops_FromObject(file);
856     if (!source) {
857         goto end;
858     } else {
859         PyObject *path = 0;
860 
861         if (pgRWops_IsFileObject(source)) {
862             path = PyObject_GetAttrString(file, "name");
863         } else {
864             Py_INCREF(file);
865             path = file;
866         }
867         if (!path) {
868             PyObject *str;
869             PyErr_Clear();
870             str = Bytes_FromFormat("<%s instance at %p>",
871                                    Py_TYPE(file)->tp_name, (void *)file);
872             if (str) {
873                 self->path =
874                     PyUnicode_FromEncodedObject(str, "ascii", "strict");
875                 Py_DECREF(str);
876             }
877         }
878         else if (PyUnicode_Check(path)) {
879             /* Make sure to save a pure Unicode object to prevent possible
880              * cycles from a derived class. This means no tp_traverse or
881              * tp_clear for the PyFreetypeFont type.
882              */
883             self->path = Object_Unicode(path);
884         }
885         else if (Bytes_Check(path)) {
886             self->path = PyUnicode_FromEncodedObject(
887                 path, "UTF-8", NULL);
888         }
889         else {
890             self->path = Object_Unicode(path);
891         }
892         Py_XDECREF(path);
893         if (!self->path) {
894             goto end;
895         }
896 
897         if (_PGFT_TryLoadFont_RWops(ft, self, source, font_index)) {
898             goto end;
899         }
900     }
901 #endif /* WIN32 && HAVE_PYGAME_SDL_RWOPS */
902 
903     if (!self->is_scalable && self->face_size.x == 0) {
904         if (_PGFT_Font_GetAvailableSize(ft, self, 0, &size, &height, &width,
905                                         &x_ppem, &y_ppem)) {
906             self->face_size.x = DBL_TO_FX6(x_ppem);
907             self->face_size.y = DBL_TO_FX6(y_ppem);
908         }
909         else {
910             PyErr_Clear();
911         }
912     }
913 
914     /* Keep the current freetype 2 connection open while this object exists.
915        Otherwise, the freetype library may be closed before the object frees
916        its local resources. See Pygame issue #187
917     */
918     self->freetype = ft;
919     ++ft->ref_count;
920 
921     rval = 0;
922 
923 end:
924     Py_XDECREF(file);
925     return rval;
926 }
927 
928 static PyObject *
_ftfont_repr(pgFontObject * self)929 _ftfont_repr(pgFontObject *self)
930 {
931     if (pgFont_IS_ALIVE(self)) {
932         return PyUnicode_FromFormat("Font('%.1024U')", self->path);
933     }
934     return Text_FromFormat("<uninitialized Font object at %p>", (void *)self);
935 }
936 
937 /****************************************************
938  * GETTERS/SETTERS
939  ****************************************************/
940 
941 /** Generic style attributes */
942 static PyObject *
_ftfont_getstyle_flag(pgFontObject * self,void * closure)943 _ftfont_getstyle_flag(pgFontObject *self, void *closure)
944 {
945     const long style_flag = (long)closure;
946 
947     return PyBool_FromLong(self->style & (FT_UInt16)style_flag);
948 }
949 
950 static int
_ftfont_setstyle_flag(pgFontObject * self,PyObject * value,void * closure)951 _ftfont_setstyle_flag(pgFontObject *self, PyObject *value, void *closure)
952 {
953     const long style_flag = (long)closure;
954 
955     if (!PyBool_Check(value)) {
956         PyErr_SetString(PyExc_TypeError, "The style value must be a boolean");
957         return -1;
958     }
959 
960     if ((style_flag & FT_STYLES_SCALABLE_ONLY) && !self->is_scalable) {
961         if (pgFont_IS_ALIVE(self)) {
962             PyErr_SetString(PyExc_AttributeError,
963                             "this style is unsupported for a bitmap font");
964         }
965         else {
966             PyErr_SetString(PyExc_RuntimeError, MODULE_NAME
967                             "." FONT_TYPE_NAME " instance is not initialized");
968         }
969         return -1;
970     }
971     if (PyObject_IsTrue(value)) {
972         self->style |= (FT_UInt16)style_flag;
973     }
974     else {
975         self->style &= (FT_UInt16)(~style_flag);
976     }
977 
978     return 0;
979 }
980 
981 /** Style attribute */
982 static PyObject *
_ftfont_getstyle(pgFontObject * self,void * closure)983 _ftfont_getstyle(pgFontObject *self, void *closure)
984 {
985     return PyInt_FromLong(self->style);
986 }
987 
988 static int
_ftfont_setstyle(pgFontObject * self,PyObject * value,void * closure)989 _ftfont_setstyle(pgFontObject *self, PyObject *value, void *closure)
990 {
991     FT_UInt32 style;
992 
993     if (!PyInt_Check(value)) {
994         PyErr_SetString(PyExc_TypeError,
995                         "The style value must be an integer"
996                         " from the FT constants module");
997         return -1;
998     }
999 
1000     style = (FT_UInt32)PyInt_AsLong(value);
1001 
1002     if (style == FT_STYLE_DEFAULT) {
1003         /* The Font object's style property is the Font's default style,
1004          * so leave unchanged.
1005          */
1006         return 0;
1007     }
1008     if (_PGFT_CheckStyle(style)) {
1009         PyErr_Format(PyExc_ValueError, "Invalid style value %x", (int)style);
1010         return -1;
1011     }
1012     if ((style & FT_STYLES_SCALABLE_ONLY) && !self->is_scalable) {
1013         if (pgFont_IS_ALIVE(self)) {
1014             PyErr_SetString(PyExc_AttributeError,
1015                             "this style is unsupported for a bitmap font");
1016         }
1017         else {
1018             PyErr_SetString(PyExc_RuntimeError, MODULE_NAME
1019                             "." FONT_TYPE_NAME " instance is not initialized");
1020         }
1021         return -1;
1022     }
1023 
1024     self->style = (FT_UInt16)style;
1025     return 0;
1026 }
1027 
1028 static PyObject *
_ftfont_getstrength(pgFontObject * self,void * closure)1029 _ftfont_getstrength(pgFontObject *self, void *closure)
1030 {
1031     return PyFloat_FromDouble(self->strength);
1032 }
1033 
1034 static int
_ftfont_setstrength(pgFontObject * self,PyObject * value,void * closure)1035 _ftfont_setstrength(pgFontObject *self, PyObject *value, void *closure)
1036 {
1037     PyObject *strengthobj = PyNumber_Float(value);
1038     double strength;
1039 
1040     if (!strengthobj) {
1041         return -1;
1042     }
1043     strength = PyFloat_AS_DOUBLE(strengthobj);
1044     Py_DECREF(strengthobj);
1045     if (strength < 0.0 || strength > 1.0) {
1046         char msg[80];
1047 
1048         sprintf(msg, "strength value %.4e is outside range [0, 1]", strength);
1049         PyErr_SetString(PyExc_ValueError, msg);
1050         return -1;
1051     }
1052     self->strength = strength;
1053     return 0;
1054 }
1055 
1056 static PyObject *
_ftfont_getsize(pgFontObject * self,void * closure)1057 _ftfont_getsize(pgFontObject *self, void *closure)
1058 {
1059     if (self->face_size.y == 0) {
1060         return PyFloat_FromDouble(FX6_TO_DBL(self->face_size.x));
1061     }
1062     return Py_BuildValue("dd", FX6_TO_DBL(self->face_size.x),
1063                          FX6_TO_DBL(self->face_size.y));
1064 }
1065 
1066 static int
_ftfont_setsize(pgFontObject * self,PyObject * value,void * closure)1067 _ftfont_setsize(pgFontObject *self, PyObject *value, void *closure)
1068 {
1069     Scale_t face_size;
1070 
1071     DEL_ATTR_NOT_SUPPORTED_CHECK("size", value);
1072 
1073     if (!obj_to_scale(value, &face_size))
1074         goto error;
1075     self->face_size = face_size;
1076     return 0;
1077 
1078 error:
1079     return -1;
1080 }
1081 
1082 static PyObject *
_ftfont_getunderlineadjustment(pgFontObject * self,void * closure)1083 _ftfont_getunderlineadjustment(pgFontObject *self, void *closure)
1084 {
1085     return PyFloat_FromDouble(self->underline_adjustment);
1086 }
1087 
1088 static int
_ftfont_setunderlineadjustment(pgFontObject * self,PyObject * value,void * closure)1089 _ftfont_setunderlineadjustment(pgFontObject *self, PyObject *value,
1090                                void *closure)
1091 {
1092     PyObject *adjustmentobj;
1093     double adjustment;
1094 
1095     DEL_ATTR_NOT_SUPPORTED_CHECK("underline_adjustment", value);
1096 
1097     adjustmentobj = PyNumber_Float(value);
1098 
1099     if (!adjustmentobj) {
1100         return -1;
1101     }
1102     adjustment = PyFloat_AS_DOUBLE(adjustmentobj);
1103     Py_DECREF(adjustmentobj);
1104     if (adjustment < -2.0 || adjustment > 2.0) {
1105         char msg[100];
1106 
1107         sprintf(msg,
1108                 "underline adjustment value %.4e is outside range [-2.0, 2.0]",
1109                 adjustment);
1110         PyErr_SetString(PyExc_ValueError, msg);
1111         return -1;
1112     }
1113     self->underline_adjustment = adjustment;
1114     return 0;
1115 }
1116 
1117 /** general font attributes */
1118 
1119 static PyObject *
_ftfont_getfontmetric(pgFontObject * self,void * closure)1120 _ftfont_getfontmetric(pgFontObject *self, void *closure)
1121 {
1122     typedef long (*getter)(FreeTypeInstance *, pgFontObject *);
1123     long height;
1124 
1125     ASSERT_SELF_IS_ALIVE(self);
1126     height = ((getter)closure)(self->freetype, self);
1127     if (!height && PyErr_Occurred()) {
1128         return 0;
1129     }
1130     return PyInt_FromLong(height);
1131 }
1132 
1133 static PyObject *
_ftfont_getname(pgFontObject * self,void * closure)1134 _ftfont_getname(pgFontObject *self, void *closure)
1135 {
1136     if (pgFont_IS_ALIVE(self)) {
1137         const char *name = _PGFT_Font_GetName(self->freetype, self);
1138         return name ? Text_FromUTF8(name) : 0;
1139     }
1140     return PyObject_Repr((PyObject *)self);
1141 }
1142 
1143 static PyObject *
_ftfont_getpath(pgFontObject * self,void * closure)1144 _ftfont_getpath(pgFontObject *self, void *closure)
1145 {
1146     PyObject *path = ((pgFontObject *)self)->path;
1147 
1148     if (!path) {
1149         PyErr_SetString(PyExc_AttributeError, "path unavailable");
1150         return 0;
1151     }
1152     Py_INCREF(path);
1153     return path;
1154 }
1155 
1156 static PyObject *
_ftfont_getscalable(pgFontObject * self,void * closure)1157 _ftfont_getscalable(pgFontObject *self, void *closure)
1158 {
1159     ASSERT_SELF_IS_ALIVE(self)
1160     return PyBool_FromLong(self->is_scalable);
1161 }
1162 
1163 static PyObject *
_ftfont_getfixedwidth(pgFontObject * self,void * closure)1164 _ftfont_getfixedwidth(pgFontObject *self, void *closure)
1165 {
1166     long fixed_width;
1167 
1168     ASSERT_SELF_IS_ALIVE(self);
1169     fixed_width =
1170         _PGFT_Font_IsFixedWidth(self->freetype, (pgFontObject *)self);
1171     return fixed_width >= 0 ? PyBool_FromLong(fixed_width) : 0;
1172 }
1173 
1174 static PyObject *
_ftfont_getfixedsizes(pgFontObject * self,void * closure)1175 _ftfont_getfixedsizes(pgFontObject *self, void *closure)
1176 {
1177     long num_fixed_sizes;
1178 
1179     ASSERT_SELF_IS_ALIVE(self);
1180     num_fixed_sizes = _PGFT_Font_NumFixedSizes(self->freetype, self);
1181     return num_fixed_sizes >= 0 ? PyInt_FromLong(num_fixed_sizes) : 0;
1182 }
1183 
1184 /** Generic render flag attributes */
1185 static PyObject *
_ftfont_getrender_flag(pgFontObject * self,void * closure)1186 _ftfont_getrender_flag(pgFontObject *self, void *closure)
1187 {
1188     const long render_flag = (long)closure;
1189 
1190     return PyBool_FromLong(self->render_flags & (FT_UInt16)render_flag);
1191 }
1192 
1193 static int
_ftfont_setrender_flag(pgFontObject * self,PyObject * value,void * closure)1194 _ftfont_setrender_flag(pgFontObject *self, PyObject *value, void *closure)
1195 {
1196     const long render_flag = (long)closure;
1197 
1198     /* Generic setter; We do not know the name of the attribute */
1199     DEL_ATTR_NOT_SUPPORTED_CHECK(NULL, value);
1200 
1201     if (!PyBool_Check(value)) {
1202         PyErr_SetString(PyExc_TypeError, "The style value must be a boolean");
1203         return -1;
1204     }
1205 
1206     if (PyObject_IsTrue(value)) {
1207         self->render_flags |= (FT_UInt16)render_flag;
1208     }
1209     else {
1210         self->render_flags &= (FT_UInt16)(~render_flag);
1211     }
1212 
1213     return 0;
1214 }
1215 
1216 /** resolution pixel size attribute */
1217 static PyObject *
_ftfont_getresolution(pgFontObject * self,void * closure)1218 _ftfont_getresolution(pgFontObject *self, void *closure)
1219 {
1220     return PyLong_FromUnsignedLong((unsigned long)self->resolution);
1221 }
1222 
1223 /** text rotation attribute */
1224 static PyObject *
_ftfont_getrotation(pgFontObject * self,void * closure)1225 _ftfont_getrotation(pgFontObject *self, void *closure)
1226 {
1227     return PyLong_FromLong((long)FX16_ROUND_TO_INT(self->rotation));
1228 }
1229 
1230 static int
_ftfont_setrotation(pgFontObject * self,PyObject * value,void * closure)1231 _ftfont_setrotation(pgFontObject *self, PyObject *value, void *closure)
1232 {
1233 
1234     DEL_ATTR_NOT_SUPPORTED_CHECK("rotation", value);
1235 
1236     if (!self->is_scalable) {
1237         if (pgFont_IS_ALIVE(self)) {
1238             PyErr_SetString(PyExc_AttributeError,
1239                             "rotation is unsupported for a bitmap font");
1240         }
1241         else {
1242             PyErr_SetString(PyExc_RuntimeError, MODULE_NAME
1243                             "." FONT_TYPE_NAME " instance is not initialized");
1244         }
1245         return -1;
1246     }
1247     return obj_to_rotation(value, &self->rotation) ? 0 : -1;
1248 }
1249 
1250 /** default glyph color */
1251 static PyObject *
_ftfont_getfgcolor(pgFontObject * self,void * closure)1252 _ftfont_getfgcolor(pgFontObject *self, void *closure)
1253 {
1254     return pgColor_New(self->fgcolor);
1255 }
1256 
1257 static int
_ftfont_setfgcolor(pgFontObject * self,PyObject * value,void * closure)1258 _ftfont_setfgcolor(pgFontObject *self, PyObject *value, void *closure)
1259 {
1260 
1261     DEL_ATTR_NOT_SUPPORTED_CHECK("fgcolor", value);
1262 
1263     if (!pg_RGBAFromObj(value, self->fgcolor)) {
1264         PyErr_Format(PyExc_AttributeError,
1265                      "unable to convert %128s object to a color",
1266                      Py_TYPE(value)->tp_name);
1267         return -1;
1268     }
1269     return 0;
1270 }
1271 
1272 static PyObject *
_ftfont_getbgcolor(pgFontObject * self,void * closure)1273 _ftfont_getbgcolor(pgFontObject *self, void *closure)
1274 {
1275     return pgColor_New(self->bgcolor);
1276 }
1277 
1278 static int
_ftfont_setbgcolor(pgFontObject * self,PyObject * value,void * closure)1279 _ftfont_setbgcolor(pgFontObject *self, PyObject *value, void *closure)
1280 {
1281 
1282     DEL_ATTR_NOT_SUPPORTED_CHECK("bgcolor", value);
1283 
1284     if (!pg_RGBAFromObj(value, self->bgcolor)) {
1285         PyErr_Format(PyExc_AttributeError,
1286                      "unable to convert %128s object to a color",
1287                      Py_TYPE(value)->tp_name);
1288         return -1;
1289     }
1290     else{
1291         self->is_bg_col_set = 1;
1292     }
1293     return 0;
1294 }
1295 
1296 /** testing and debugging */
1297 #if defined(PGFT_DEBUG_CACHE)
1298 static PyObject *
_ftfont_getdebugcachestats(pgFontObject * self,void * closure)1299 _ftfont_getdebugcachestats(pgFontObject *self, void *closure)
1300 {
1301     /* Yes, this kind of breaches the boundary between the top level
1302      * freetype.c and the lower level ft_text.c. But it is built
1303      * conditionally, and it keeps some of the Python api out
1304      * of ft_text.c and ft_cache.c (hoping to remove the Python
1305      * api completely from ft_text.c and support C modules at some point.)
1306      */
1307     const FontCache *cache = &PGFT_FONT_CACHE(self);
1308 
1309     return Py_BuildValue("kkkkk", (unsigned long)cache->_debug_count,
1310                          (unsigned long)cache->_debug_delete_count,
1311                          (unsigned long)cache->_debug_access,
1312                          (unsigned long)cache->_debug_hit,
1313                          (unsigned long)cache->_debug_miss);
1314 }
1315 #endif
1316 
1317 /****************************************************
1318  * MAIN METHODS
1319  ****************************************************/
1320 static PyObject *
_ftfont_getrect(pgFontObject * self,PyObject * args,PyObject * kwds)1321 _ftfont_getrect(pgFontObject *self, PyObject *args, PyObject *kwds)
1322 {
1323     /* MODIFIED
1324      */
1325     /* keyword list */
1326     static char *kwlist[] = {"text", "style", "rotation", "size", 0};
1327 
1328     PyObject *textobj;
1329     PGFT_String *text = 0;
1330     Scale_t face_size = FACE_SIZE_NONE;
1331     SDL_Rect r;
1332 
1333     FontRenderMode render;
1334     Angle_t rotation = self->rotation;
1335     int style = FT_STYLE_DEFAULT;
1336 
1337     if (!PyArg_ParseTupleAndKeywords(
1338             args, kwds, "O|iO&O&", kwlist, &textobj, &style, obj_to_rotation,
1339             (void *)&rotation, obj_to_scale, (void *)&face_size))
1340         goto error;
1341 
1342     /* Encode text */
1343     if (textobj != Py_None) {
1344         text =
1345             _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1346         if (!text)
1347             goto error;
1348     }
1349 
1350     ASSERT_SELF_IS_ALIVE(self);
1351 
1352     /* Build rendering mode, always anti-aliased by default */
1353     if (_PGFT_BuildRenderMode(self->freetype, self, &render, face_size, style,
1354                               rotation))
1355         goto error;
1356 
1357     if (_PGFT_GetTextRect(self->freetype, self, &render, text, &r))
1358         goto error;
1359     free_string(text);
1360 
1361     return pgRect_New(&r);
1362 
1363 error:
1364     free_string(text);
1365     return 0;
1366 }
1367 
1368 static PyObject *
get_metrics(FontRenderMode * render,pgFontObject * font,PGFT_String * text)1369 get_metrics(FontRenderMode *render, pgFontObject *font, PGFT_String *text)
1370 {
1371     Py_ssize_t length = PGFT_String_GET_LENGTH(text);
1372     PGFT_char *data = PGFT_String_GET_DATA(text);
1373     PyObject *list, *item;
1374     FT_UInt gindex;
1375     long minx, miny;
1376     long maxx, maxy;
1377     double advance_x;
1378     double advance_y;
1379     Py_ssize_t i;
1380 
1381     if (!_PGFT_GetFontSized(font->freetype, font, render->face_size)) {
1382         PyErr_SetString(pgExc_SDLError, _PGFT_GetError(font->freetype));
1383         return 0;
1384     }
1385     list = PyList_New(length);
1386     if (!list) {
1387         return 0;
1388     }
1389     for (i = 0; i < length; ++i) {
1390         if (_PGFT_GetMetrics(font->freetype, font, data[i], render, &gindex,
1391                              &minx, &maxx, &miny, &maxy, &advance_x,
1392                              &advance_y) == 0) {
1393             if (gindex == 0) {
1394                 Py_INCREF(Py_None);
1395                 item = Py_None;
1396             }
1397             else {
1398                 item = Py_BuildValue("lllldd", minx, maxx, miny, maxy,
1399                                      advance_x, advance_y);
1400             }
1401             if (!item) {
1402                 Py_DECREF(list);
1403                 return 0;
1404             }
1405         }
1406         else {
1407             Py_INCREF(Py_None);
1408             item = Py_None;
1409         }
1410         PyList_SET_ITEM(list, i, item);
1411     }
1412 
1413     return list;
1414 }
1415 
1416 static PyObject *
_ftfont_getmetrics(pgFontObject * self,PyObject * args,PyObject * kwds)1417 _ftfont_getmetrics(pgFontObject *self, PyObject *args, PyObject *kwds)
1418 {
1419     /* keyword list */
1420     static char *kwlist[] = {"text", "size", 0};
1421 
1422     FontRenderMode render;
1423     PyObject *list = 0;
1424 
1425     /* arguments */
1426     PyObject *textobj;
1427     PGFT_String *text = 0;
1428     Scale_t face_size = FACE_SIZE_NONE;
1429 
1430     /* parse args */
1431     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&", kwlist, &textobj,
1432                                      obj_to_scale, (void *)&face_size))
1433         goto error;
1434 
1435     /* Encode text */
1436     text = _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1437     if (!text)
1438         goto error;
1439 
1440     ASSERT_SELF_IS_ALIVE(self);
1441 
1442     /*
1443      * Build the render mode with the given size and support
1444      * for rotation/styles/size changes in text
1445      */
1446     if (_PGFT_BuildRenderMode(self->freetype, self, &render, face_size,
1447                               FT_STYLE_DEFAULT, self->rotation))
1448         goto error;
1449 
1450     /* get metrics */
1451     list = get_metrics(&render, self, text);
1452     if (!list)
1453         goto error;
1454     free_string(text);
1455 
1456     return list;
1457 
1458 error:
1459     free_string(text);
1460     Py_XDECREF(list);
1461     return 0;
1462 }
1463 
1464 static PyObject *
_ftfont_getsizedascender(pgFontObject * self,PyObject * args)1465 _ftfont_getsizedascender(pgFontObject *self, PyObject *args)
1466 {
1467     Scale_t face_size = FACE_SIZE_NONE;
1468     long value;
1469 
1470     if (!PyArg_ParseTuple(args, "|O&", obj_to_scale, (void *)&face_size)) {
1471         return 0;
1472     }
1473 
1474     if (face_size.x == 0) {
1475         if (self->face_size.x == 0) {
1476             PyErr_SetString(PyExc_ValueError,
1477                             "No font point size specified"
1478                             " and no default font size in typefont");
1479             return 0;
1480         }
1481 
1482         face_size = self->face_size;
1483     }
1484     value = (long)_PGFT_Font_GetAscenderSized(self->freetype, self, face_size);
1485     if (!value && PyErr_Occurred()) {
1486         return 0;
1487     }
1488     return PyInt_FromLong(value);
1489 }
1490 
1491 static PyObject *
_ftfont_getsizeddescender(pgFontObject * self,PyObject * args)1492 _ftfont_getsizeddescender(pgFontObject *self, PyObject *args)
1493 {
1494     Scale_t face_size = FACE_SIZE_NONE;
1495     long value;
1496 
1497     if (!PyArg_ParseTuple(args, "|O&", obj_to_scale, (void *)&face_size)) {
1498         return 0;
1499     }
1500 
1501     if (face_size.x == 0) {
1502         if (self->face_size.x == 0) {
1503             PyErr_SetString(PyExc_ValueError,
1504                             "No font point size specified"
1505                             " and no default font size in typefont");
1506             return 0;
1507         }
1508 
1509         face_size = self->face_size;
1510     }
1511     value =
1512         (long)_PGFT_Font_GetDescenderSized(self->freetype, self, face_size);
1513     if (!value && PyErr_Occurred()) {
1514         return 0;
1515     }
1516     return PyInt_FromLong(value);
1517 }
1518 
1519 static PyObject *
_ftfont_getsizedheight(pgFontObject * self,PyObject * args)1520 _ftfont_getsizedheight(pgFontObject *self, PyObject *args)
1521 {
1522     Scale_t face_size = FACE_SIZE_NONE;
1523     long value;
1524 
1525     if (!PyArg_ParseTuple(args, "|O&", obj_to_scale, (void *)&face_size)) {
1526         return 0;
1527     }
1528 
1529     if (face_size.x == 0) {
1530         if (self->face_size.x == 0) {
1531             PyErr_SetString(PyExc_ValueError,
1532                             "No font point size specified"
1533                             " and no default font size in typeface");
1534             return 0;
1535         }
1536 
1537         face_size = self->face_size;
1538     }
1539     value = _PGFT_Font_GetHeightSized(self->freetype, self, face_size);
1540     if (!value && PyErr_Occurred()) {
1541         return 0;
1542     }
1543     return PyInt_FromLong(value);
1544 }
1545 
1546 static PyObject *
_ftfont_getsizedglyphheight(pgFontObject * self,PyObject * args)1547 _ftfont_getsizedglyphheight(pgFontObject *self, PyObject *args)
1548 {
1549     Scale_t face_size = FACE_SIZE_NONE;
1550     long value;
1551 
1552     if (!PyArg_ParseTuple(args, "|O&", obj_to_scale, (void *)&face_size)) {
1553         return 0;
1554     }
1555 
1556     if (face_size.x == 0) {
1557         if (self->face_size.x == 0) {
1558             PyErr_SetString(PyExc_ValueError,
1559                             "No font point size specified"
1560                             " and no default font size in typeface");
1561             return 0;
1562         }
1563 
1564         face_size = self->face_size;
1565     }
1566     value =
1567         (long)_PGFT_Font_GetGlyphHeightSized(self->freetype, self, face_size);
1568     if (!value && PyErr_Occurred()) {
1569         return 0;
1570     }
1571     return PyInt_FromLong(value);
1572 }
1573 
1574 static PyObject *
_ftfont_getsizes(pgFontObject * self)1575 _ftfont_getsizes(pgFontObject *self)
1576 {
1577     int nsizes;
1578     int i;
1579     int rc;
1580     long size = 0;
1581     long height = 0, width = 0;
1582     double x_ppem = 0.0, y_ppem = 0.0;
1583     PyObject *size_list = 0;
1584     PyObject *size_item;
1585 
1586     nsizes = _PGFT_Font_NumFixedSizes(self->freetype, self);
1587     if (nsizes < 0)
1588         goto error;
1589     size_list = PyList_New(nsizes);
1590     if (!size_list)
1591         goto error;
1592     for (i = 0; i < nsizes; ++i) {
1593         rc = _PGFT_Font_GetAvailableSize(self->freetype, self, i, &size,
1594                                          &height, &width, &x_ppem, &y_ppem);
1595         if (rc < 0)
1596             goto error;
1597         assert(rc > 0);
1598         size_item =
1599             Py_BuildValue("llldd", size, height, width, x_ppem, y_ppem);
1600         if (!size_item)
1601             goto error;
1602         PyList_SET_ITEM(size_list, i, size_item);
1603     }
1604     return size_list;
1605 
1606 error:
1607     Py_XDECREF(size_list);
1608     return 0;
1609 }
1610 
1611 static PyObject *
_ftfont_render_raw(pgFontObject * self,PyObject * args,PyObject * kwds)1612 _ftfont_render_raw(pgFontObject *self, PyObject *args, PyObject *kwds)
1613 {
1614     /* keyword list */
1615     static char *kwlist[] = {"text", "style", "rotation", "size", "invert", 0};
1616 
1617     FontRenderMode mode;
1618 
1619     /* input arguments */
1620     PyObject *textobj;
1621     PGFT_String *text = 0;
1622     int style = FT_STYLE_DEFAULT;
1623     Angle_t rotation = self->rotation;
1624     Scale_t face_size = FACE_SIZE_NONE;
1625     int invert = 0;
1626 
1627     /* output arguments */
1628     PyObject *rbuffer = 0;
1629     PyObject *rtuple = 0;
1630     int width, height;
1631 
1632     if (!PyArg_ParseTupleAndKeywords(
1633             args, kwds, "O|iO&O&i", kwlist, &textobj, &style, obj_to_rotation,
1634             (void *)&rotation, obj_to_scale, (void *)&face_size, &invert))
1635         goto error;
1636 
1637     /* Encode text */
1638     if (textobj != Py_None) {
1639         text =
1640             _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1641         if (!text)
1642             goto error;
1643     }
1644 
1645     ASSERT_SELF_IS_ALIVE(self);
1646 
1647     /*
1648      * Build the render mode with the given size and no
1649      * rotation/styles/vertical text
1650      */
1651     if (_PGFT_BuildRenderMode(self->freetype, self, &mode, face_size, style,
1652                               rotation))
1653         goto error;
1654 
1655     rbuffer = _PGFT_Render_PixelArray(self->freetype, self, &mode, text,
1656                                       invert, &width, &height);
1657     if (!rbuffer)
1658         goto error;
1659     free_string(text);
1660     rtuple = Py_BuildValue("O(ii)", rbuffer, width, height);
1661     if (!rtuple)
1662         goto error;
1663     Py_DECREF(rbuffer);
1664 
1665     return rtuple;
1666 
1667 error:
1668     free_string(text);
1669     Py_XDECREF(rbuffer);
1670     Py_XDECREF(rtuple);
1671     return 0;
1672 }
1673 
1674 static PyObject *
_ftfont_render_raw_to(pgFontObject * self,PyObject * args,PyObject * kwds)1675 _ftfont_render_raw_to(pgFontObject *self, PyObject *args, PyObject *kwds)
1676 {
1677     /* keyword list */
1678     static char *kwlist[] = {"array",    "text", "dest",   "style",
1679                              "rotation", "size", "invert", 0};
1680 
1681     FontRenderMode mode;
1682 
1683     /* input arguments */
1684     PyObject *arrayobj;
1685     PyObject *textobj;
1686     PGFT_String *text = 0;
1687     PyObject *dest = 0;
1688     int xpos = 0;
1689     int ypos = 0;
1690     int style = FT_STYLE_DEFAULT;
1691     Angle_t rotation = self->rotation;
1692     Scale_t face_size = FACE_SIZE_NONE;
1693     int invert = 0;
1694 
1695     /* output arguments */
1696     SDL_Rect r;
1697 
1698     ASSERT_SELF_IS_ALIVE(self);
1699 
1700     if (!PyArg_ParseTupleAndKeywords(
1701             args, kwds, "OO|OiO&O&i", kwlist, &arrayobj, &textobj, &dest,
1702             &style, obj_to_rotation, (void *)&rotation, obj_to_scale,
1703             (void *)&face_size, &invert))
1704         goto error;
1705 
1706     if (dest && dest != Py_None) {
1707         if (parse_dest(dest, &xpos, &ypos))
1708             goto error;
1709     }
1710 
1711     /* Encode text */
1712     if (textobj != Py_None) {
1713         text =
1714             _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1715         if (!text)
1716             goto error;
1717     }
1718 
1719     /*
1720      * Build the render mode with the given size and no
1721      * rotation/styles/vertical text
1722      */
1723     if (_PGFT_BuildRenderMode(self->freetype, self, &mode, face_size, style,
1724                               rotation))
1725         goto error;
1726 
1727     if (_PGFT_Render_Array(self->freetype, self, &mode, arrayobj, text, invert,
1728                            xpos, ypos, &r))
1729         goto error;
1730     free_string(text);
1731 
1732     return pgRect_New(&r);
1733 
1734 error:
1735     free_string(text);
1736     return 0;
1737 }
1738 
1739 static PyObject *
_ftfont_render(pgFontObject * self,PyObject * args,PyObject * kwds)1740 _ftfont_render(pgFontObject *self, PyObject *args, PyObject *kwds)
1741 {
1742 #ifndef HAVE_PYGAME_SDL_VIDEO
1743 
1744     PyErr_SetString(PyExc_RuntimeError,
1745                     "SDL support is missing. Cannot render on surfonts");
1746     return 0;
1747 
1748 #else
1749     /* keyword list */
1750     static char *kwlist[] = {"text",     "fgcolor", "bgcolor", "style",
1751                              "rotation", "size",    0};
1752 
1753     /* input arguments */
1754     PyObject *textobj = 0;
1755     PGFT_String *text = 0;
1756     Scale_t face_size = FACE_SIZE_NONE;
1757     PyObject *fg_color_obj = 0;
1758     PyObject *bg_color_obj = 0;
1759     Angle_t rotation = self->rotation;
1760     int style = FT_STYLE_DEFAULT;
1761 
1762     /* output arguments */
1763     SDL_Surface *surface = 0;
1764     PyObject *surface_obj = 0;
1765     PyObject *rtuple = 0;
1766     SDL_Rect r;
1767     PyObject *rect_obj = 0;
1768 
1769     FontColor fg_color;
1770     FontColor bg_color;
1771     FontRenderMode render;
1772 
1773     ASSERT_SELF_IS_ALIVE(self);
1774 
1775     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOiO&O&", kwlist,
1776                                      /* required */
1777                                      &textobj,
1778                                      /* optional */
1779                                      &fg_color_obj, &bg_color_obj, &style,
1780                                      obj_to_rotation, (void *)&rotation,
1781                                      obj_to_scale, (void *)&face_size))
1782         goto error;
1783 
1784     if (fg_color_obj == Py_None) {
1785         fg_color_obj = 0;
1786     }
1787     if (bg_color_obj == Py_None) {
1788         bg_color_obj = 0;
1789     }
1790 
1791     if (fg_color_obj) {
1792         if (!pg_RGBAFromFuzzyColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
1793             /* Exception already set for us */
1794             goto error;
1795         }
1796     }
1797     else {
1798         fg_color.r = self->fgcolor[0];
1799         fg_color.g = self->fgcolor[1];
1800         fg_color.b = self->fgcolor[2];
1801         fg_color.a = self->fgcolor[3];
1802     }
1803 
1804     if (bg_color_obj) {
1805         if (!pg_RGBAFromFuzzyColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
1806             /* Exception already set for us */
1807             goto error;
1808         }
1809     }
1810     else{
1811         if (self->is_bg_col_set){
1812             bg_color.r = self->bgcolor[0];
1813             bg_color.g = self->bgcolor[1];
1814             bg_color.b = self->bgcolor[2];
1815             bg_color.a = self->bgcolor[3];
1816         }
1817         else{
1818            bg_color_obj = 0;
1819         }
1820     }
1821 
1822     /* Encode text */
1823     if (textobj != Py_None) {
1824         text =
1825             _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1826         if (!text)
1827             goto error;
1828     }
1829 
1830     if (_PGFT_BuildRenderMode(self->freetype, self, &render, face_size, style,
1831                               rotation))
1832         goto error;
1833 
1834     surface =
1835         _PGFT_Render_NewSurface(
1836             self->freetype, self, &render, text, &fg_color,
1837             (bg_color_obj || self->is_bg_col_set) ? &bg_color : 0, &r);
1838     if (!surface)
1839         goto error;
1840     free_string(text);
1841     surface_obj = (PyObject *)pgSurface_New(surface);
1842     if (!surface_obj)
1843         goto error;
1844 
1845     rect_obj = pgRect_New(&r);
1846     if (!rect_obj)
1847         goto error;
1848     rtuple = PyTuple_Pack(2, surface_obj, rect_obj);
1849     if (!rtuple)
1850         goto error;
1851     Py_DECREF(surface_obj);
1852     Py_DECREF(rect_obj);
1853 
1854     return rtuple;
1855 
1856 error:
1857     free_string(text);
1858     if (surface_obj) {
1859         Py_DECREF(surface_obj);
1860     }
1861     else if (surface) {
1862         SDL_FreeSurface(surface);
1863     }
1864     Py_XDECREF(rect_obj);
1865     Py_XDECREF(rtuple);
1866     return 0;
1867 
1868 #endif  // HAVE_PYGAME_SDL_VIDEO
1869 }
1870 
1871 static PyObject *
_ftfont_render_to(pgFontObject * self,PyObject * args,PyObject * kwds)1872 _ftfont_render_to(pgFontObject *self, PyObject *args, PyObject *kwds)
1873 {
1874 #ifndef HAVE_PYGAME_SDL_VIDEO
1875 
1876     PyErr_SetString(PyExc_RuntimeError,
1877                     "SDL support is missing. Cannot render on surfaces");
1878     return 0;
1879 
1880 #else
1881     /* keyword list */
1882     static char *kwlist[] = {"surf",  "dest",     "text", "fgcolor", "bgcolor",
1883                              "style", "rotation", "size", 0};
1884 
1885     /* input arguments */
1886     PyObject *surface_obj = 0;
1887     PyObject *textobj = 0;
1888     PGFT_String *text = 0;
1889     Scale_t face_size = FACE_SIZE_NONE;
1890     PyObject *dest = 0;
1891     int xpos = 0;
1892     int ypos = 0;
1893     PyObject *fg_color_obj = 0;
1894     PyObject *bg_color_obj = 0;
1895     Angle_t rotation = self->rotation;
1896     int style = FT_STYLE_DEFAULT;
1897     SDL_Surface *surface = 0;
1898 
1899     /* output arguments */
1900     SDL_Rect r;
1901 
1902     FontColor fg_color;
1903     FontColor bg_color;
1904     FontRenderMode render;
1905 
1906     if (!PyArg_ParseTupleAndKeywords(
1907             args, kwds, "O!OO|OOiO&O&", kwlist,
1908             /* required */
1909             &pgSurface_Type, &surface_obj, &dest, &textobj, &fg_color_obj,
1910             /* optional */
1911             &bg_color_obj, &style, obj_to_rotation, (void *)&rotation,
1912             obj_to_scale, (void *)&face_size))
1913         goto error;
1914 
1915     if (fg_color_obj == Py_None) {
1916         fg_color_obj = 0;
1917     }
1918     if (bg_color_obj == Py_None) {
1919         bg_color_obj = 0;
1920     }
1921 
1922     if (parse_dest(dest, &xpos, &ypos))
1923         goto error;
1924     if (fg_color_obj) {
1925         if (!pg_RGBAFromFuzzyColorObj(fg_color_obj, (Uint8 *)&fg_color)) {
1926             /* Exception already set for us */
1927             goto error;
1928         }
1929     }
1930     else {
1931         fg_color.r = self->fgcolor[0];
1932         fg_color.g = self->fgcolor[1];
1933         fg_color.b = self->fgcolor[2];
1934         fg_color.a = self->fgcolor[3];
1935     }
1936     if (bg_color_obj) {
1937         if (!pg_RGBAFromFuzzyColorObj(bg_color_obj, (Uint8 *)&bg_color)) {
1938             /* Exception already set for us */
1939             goto error;
1940         }
1941     }
1942     else{
1943         if (self->is_bg_col_set){
1944             bg_color.r = self->bgcolor[0];
1945             bg_color.g = self->bgcolor[1];
1946             bg_color.b = self->bgcolor[2];
1947             bg_color.a = self->bgcolor[3];
1948         }
1949         else{
1950            bg_color_obj = 0;
1951         }
1952     }
1953 
1954     ASSERT_SELF_IS_ALIVE(self);
1955 
1956     /* Encode text */
1957     if (textobj != Py_None) {
1958         text =
1959             _PGFT_EncodePyString(textobj, self->render_flags & FT_RFLAG_UCS4);
1960         if (!text)
1961             goto error;
1962     }
1963 
1964     if (_PGFT_BuildRenderMode(self->freetype, self, &render, face_size, style,
1965                               rotation))
1966         goto error;
1967 
1968     surface = pgSurface_AsSurface(surface_obj);
1969     if (!surface) {
1970         PyErr_SetString(pgExc_SDLError, "display Surface quit");
1971         goto error;
1972     }
1973     if (_PGFT_Render_ExistingSurface(
1974             self->freetype, self, &render, text,
1975             surface, xpos, ypos, &fg_color,
1976             (bg_color_obj || self->is_bg_col_set) ? &bg_color : 0, &r))
1977         goto error;
1978     free_string(text);
1979 
1980     return pgRect_New(&r);
1981 
1982 error:
1983     free_string(text);
1984     return 0;
1985 #endif  // HAVE_PYGAME_SDL_VIDEO
1986 }
1987 
1988 /****************************************************
1989  * C API CALLS
1990  ****************************************************/
1991 static PyObject *
pgFont_New(const char * filename,long font_index)1992 pgFont_New(const char *filename, long font_index)
1993 {
1994     pgFontObject *font;
1995 
1996     FreeTypeInstance *ft;
1997     ASSERT_GRAB_FREETYPE(ft, 0);
1998 
1999     if (!filename) {
2000         return 0;
2001     }
2002 
2003     font = (pgFontObject *)pgFont_Type.tp_new(&pgFont_Type, 0, 0);
2004 
2005     if (!font) {
2006         return 0;
2007     }
2008 
2009     if (_PGFT_TryLoadFont_Filename(ft, font, filename, font_index)) {
2010         return 0;
2011     }
2012 
2013     return (PyObject *)font;
2014 }
2015 
2016 /****************************************************
2017  * FREETYPE MODULE METHODS
2018  ****************************************************/
2019 
2020 /***************************************************************
2021  *
2022  * Bindings for initialization/cleanup functions
2023  *
2024  * Explicit init/quit functions are required to work around
2025  * some issues regarding module caching and multi-threaded apps.
2026  * It's always good to let the user choose when to initialize
2027  * the module.
2028  *
2029  * TODO: These bindings can be removed once proper threading
2030  * support is in place.
2031  *
2032  ***************************************************************/
2033 
2034 static PyObject *
_ft_autoinit(PyObject * self)2035 _ft_autoinit(PyObject *self)
2036 {
2037     int cache_size = FREETYPE_MOD_STATE(self)->cache_size;
2038 
2039     if (!FREETYPE_MOD_STATE(self)->freetype) {
2040         if (cache_size == 0)
2041             cache_size = PGFT_DEFAULT_CACHE_SIZE;
2042 
2043         if (_PGFT_Init(&(FREETYPE_MOD_STATE(self)->freetype), cache_size))
2044             return RAISE(PyExc_RuntimeError,
2045                             "Failed to initialize freetype library");
2046 
2047         FREETYPE_MOD_STATE(self)->cache_size = cache_size;
2048     }
2049 
2050     Py_RETURN_NONE;
2051 }
2052 
2053 static PyObject *
_ft_quit(PyObject * self)2054 _ft_quit(PyObject *self)
2055 {
2056     _FreeTypeState *state = FREETYPE_STATE;
2057 
2058     if (state->freetype) {
2059         _PGFT_Quit(state->freetype);
2060         state->cache_size = 0;
2061         state->freetype = 0;
2062     }
2063 
2064     Py_RETURN_NONE;
2065 }
2066 
2067 static PyObject *
_ft_init(PyObject * self,PyObject * args,PyObject * kwds)2068 _ft_init(PyObject *self, PyObject *args, PyObject *kwds)
2069 {
2070     static char *kwlist[] = {"cache_size", "resolution", 0};
2071 
2072     unsigned cache_size = 0;
2073     unsigned resolution = 0;
2074     _FreeTypeState *state = FREETYPE_MOD_STATE(self);
2075 
2076     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|II", kwlist, &cache_size,
2077                                      &resolution)) {
2078         return NULL;
2079     }
2080 
2081     if (!state->freetype) {
2082         state->cache_size = cache_size;
2083         state->resolution =
2084             (resolution ? (FT_UInt)resolution : PGFT_DEFAULT_RESOLUTION);
2085         return _ft_autoinit(self);
2086     }
2087 
2088     Py_RETURN_NONE;
2089 }
2090 
2091 static PyObject *
_ft_get_error(PyObject * self,PyObject * args)2092 _ft_get_error(PyObject *self, PyObject *args)
2093 {
2094     FreeTypeInstance *ft;
2095     ASSERT_GRAB_FREETYPE(ft, 0);
2096 
2097     if (ft->_error_msg[0]) {
2098         return Text_FromUTF8(ft->_error_msg);
2099     }
2100 
2101     Py_RETURN_NONE;
2102 }
2103 
2104 static PyObject *
_ft_get_version(PyObject * self,PyObject * args)2105 _ft_get_version(PyObject *self, PyObject *args)
2106 {
2107     /* Return the linked FreeType2 version */
2108     return Py_BuildValue("iii", FREETYPE_MAJOR, FREETYPE_MINOR,
2109                          FREETYPE_PATCH);
2110 }
2111 
2112 static PyObject *
_ft_get_cache_size(PyObject * self,PyObject * args)2113 _ft_get_cache_size(PyObject *self, PyObject *args)
2114 {
2115     return PyLong_FromUnsignedLong(
2116         (unsigned long)(FREETYPE_STATE->cache_size));
2117 }
2118 
2119 static PyObject *
_ft_get_default_resolution(PyObject * self,PyObject * args)2120 _ft_get_default_resolution(PyObject *self, PyObject *args)
2121 {
2122     return PyLong_FromUnsignedLong(
2123         (unsigned long)(FREETYPE_STATE->resolution));
2124 }
2125 
2126 static PyObject *
_ft_set_default_resolution(PyObject * self,PyObject * args)2127 _ft_set_default_resolution(PyObject *self, PyObject *args)
2128 {
2129     unsigned resolution = 0;
2130     _FreeTypeState *state = FREETYPE_MOD_STATE(self);
2131 
2132     if (!PyArg_ParseTuple(args, "|I", &resolution)) {
2133         return 0;
2134     }
2135 
2136     state->resolution =
2137         (resolution ? (FT_UInt)resolution : PGFT_DEFAULT_RESOLUTION);
2138     Py_RETURN_NONE;
2139 }
2140 
2141 static PyObject *
_ft_get_init(PyObject * self,PyObject * args)2142 _ft_get_init(PyObject *self, PyObject *args)
2143 {
2144     return PyBool_FromLong(FREETYPE_MOD_STATE(self)->freetype ? 1 : 0);
2145 }
2146 
2147 static PyObject *
_ft_get_default_font(PyObject * self,PyObject * args)2148 _ft_get_default_font(PyObject *self, PyObject *args)
2149 {
2150     return Text_FromUTF8(DEFAULT_FONT_NAME);
2151 }
2152 
2153 static int
_ft_traverse(PyObject * mod,visitproc visit,void * arg)2154 _ft_traverse(PyObject *mod, visitproc visit, void *arg)
2155 {
2156     return 0;
2157 }
2158 
2159 static int
_ft_clear(PyObject * mod)2160 _ft_clear(PyObject *mod)
2161 {
2162     if (FREETYPE_MOD_STATE(mod)->freetype) {
2163         _PGFT_Quit(FREETYPE_MOD_STATE(mod)->freetype);
2164         FREETYPE_MOD_STATE(mod)->freetype = 0;
2165     }
2166     return 0;
2167 }
2168 
2169 /****************************************************
2170  * FREETYPE MODULE DECLARATION
2171  ****************************************************/
2172 #ifndef PYPY_VERSION
2173 struct PyModuleDef _freetypemodule = {
2174     PyModuleDef_HEAD_INIT,  MODULE_NAME, DOC_PYGAMEFREETYPE,
2175     sizeof(_FreeTypeState), _ft_methods, 0,
2176     _ft_traverse,           _ft_clear,   0};
2177 #else /* PYPY_VERSION */
2178 _FreeTypeState _modstate;
2179 struct PyModuleDef _freetypemodule = {
2180     PyModuleDef_HEAD_INIT,  MODULE_NAME, DOC_PYGAMEFREETYPE,
2181     -1 /* PyModule_GetState() not implemented */, _ft_methods, 0,
2182     _ft_traverse, _ft_clear, 0};
2183 #endif /* PYPY_VERSION */
2184 
MODINIT_DEFINE(_freetype)2185 MODINIT_DEFINE(_freetype)
2186 {
2187     PyObject *module, *apiobj;
2188     static void *c_api[PYGAMEAPI_FREETYPE_NUMSLOTS];
2189 
2190     import_pygame_base();
2191     if (PyErr_Occurred()) {
2192         MODINIT_ERROR;
2193     }
2194 
2195     import_pygame_surface();
2196     if (PyErr_Occurred()) {
2197         MODINIT_ERROR;
2198     }
2199 
2200     import_pygame_color();
2201     if (PyErr_Occurred()) {
2202         MODINIT_ERROR;
2203     }
2204 
2205     import_pygame_rwobject();
2206     if (PyErr_Occurred()) {
2207         MODINIT_ERROR;
2208     }
2209 
2210     import_pygame_rect();
2211     if (PyErr_Occurred()) {
2212         MODINIT_ERROR;
2213     }
2214 
2215     /* type preparation */
2216     if (PyType_Ready(&pgFont_Type) < 0) {
2217         MODINIT_ERROR;
2218     }
2219 
2220     module = PyModule_Create(&_freetypemodule);
2221 
2222     if (!module) {
2223         MODINIT_ERROR;
2224     }
2225 
2226     FREETYPE_MOD_STATE(module)->freetype = 0;
2227     FREETYPE_MOD_STATE(module)->cache_size = 0;
2228     FREETYPE_MOD_STATE(module)->resolution = PGFT_DEFAULT_RESOLUTION;
2229 
2230     Py_INCREF((PyObject *)&pgFont_Type);
2231     if (PyModule_AddObject(module, FONT_TYPE_NAME, (PyObject *)&pgFont_Type) ==
2232         -1) {
2233         Py_DECREF((PyObject *)&pgFont_Type);
2234         DECREF_MOD(module);
2235         MODINIT_ERROR;
2236     }
2237 
2238 #define DEC_CONST(x)                                        \
2239     if (PyModule_AddIntConstant(module, #x, (int)FT_##x)) { \
2240         DECREF_MOD(module);                                 \
2241         MODINIT_ERROR;                                      \
2242     }
2243 
2244     DEC_CONST(STYLE_NORMAL);
2245     DEC_CONST(STYLE_STRONG);
2246     DEC_CONST(STYLE_OBLIQUE);
2247     DEC_CONST(STYLE_UNDERLINE);
2248     DEC_CONST(STYLE_WIDE);
2249     DEC_CONST(STYLE_DEFAULT);
2250 
2251     DEC_CONST(BBOX_EXACT);
2252     DEC_CONST(BBOX_EXACT_GRIDFIT);
2253     DEC_CONST(BBOX_PIXEL);
2254     DEC_CONST(BBOX_PIXEL_GRIDFIT);
2255 
2256     /* export the c api */
2257 #if PYGAMEAPI_FREETYPE_NUMSLOTS != 2
2258 #error Mismatch between number of api slots and actual exports.
2259 #endif
2260     c_api[0] = &pgFont_Type;
2261     c_api[1] = &pgFont_New;
2262 
2263     apiobj = encapsulate_api(c_api, "freetype");
2264     if (!apiobj) {
2265         DECREF_MOD(module);
2266         MODINIT_ERROR;
2267     }
2268 
2269     if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj) == -1) {
2270         Py_DECREF(apiobj);
2271         DECREF_MOD(module);
2272         MODINIT_ERROR;
2273     }
2274 
2275     MODINIT_RETURN(module);
2276 }
2277