Mercurial > pygame
view src/font.c @ 3172:d1b367fc08bc tests-returncode
Use a return code of 1 when the tests fail.
| author | Thomas Kluyver <takowl@gmail.com> |
|---|---|
| date | Wed, 09 Jan 2013 10:13:57 +0000 |
| parents | 6cc8196e0181 |
| children |
line wrap: on
line source
/* pygame - Python Game Library Copyright (C) 2000-2001 Pete Shinners This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pete Shinners pete@shinners.org */ /* * font module for pygame */ #define PYGAMEAPI_FONT_INTERNAL #include "font.h" #include <stdio.h> #include <string.h> #include "pygame.h" #include "pgcompat.h" #include "doc/font_doc.h" #include "structmember.h" /* Require SDL_ttf 2.0.6 or later for rwops support */ #ifdef TTF_MAJOR_VERSION #define FONT_HAVE_RWOPS 1 #else #define FONT_HAVE_RWOPS 0 #endif #if PY3 #define RAISE_TEXT_TYPE_ERROR() \ RAISE(PyExc_TypeError, "text must be a unicode or bytes"); #else #define RAISE_TEXT_TYPE_ERROR() \ RAISE(PyExc_TypeError, "text must be a string or unicode"); #endif /* For filtering out UCS-4 and larger characters when Python is * built with Py_UNICODE_WIDE. */ #if defined(Py_UNICODE_WIDE) #define IS_UCS_2(c) ((c) < 0x10000L) #else #define IS_UCS_2(c) 1 #endif static PyTypeObject PyFont_Type; static PyObject* PyFont_New (TTF_Font*); #define PyFont_Check(x) ((x)->ob_type == &PyFont_Type) static int font_initialized = 0; static const char font_defaultname[] = "freesansbold.ttf"; static const char pkgdatamodule_name[] = "pygame.pkgdata"; static const char resourcefunc_name[] = "getResource"; /* */ static int utf_8_needs_UCS_4(const char *str) { static const Uint8 first = '\xF0'; while (*str) { if ((Uint8)*str >= first) { return 1; } ++str; } return 0; } /* Return an encoded file path, a file-like object or a NULL pointer. * May raise a Python error. Use PyErr_Occurred to check. */ static PyObject* font_resource (const char *filename) { PyObject* pkgdatamodule = NULL; PyObject* resourcefunc = NULL; PyObject* result = NULL; PyObject* tmp; pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name); if (pkgdatamodule == NULL) { return NULL; } resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name); Py_DECREF(pkgdatamodule); if (resourcefunc == NULL) { return NULL; } result = PyObject_CallFunction(resourcefunc, "s", filename); Py_DECREF(resourcefunc); if (result == NULL) { return NULL; } #if PY3 tmp = PyObject_GetAttrString(result, "name"); if (tmp != NULL) { Py_DECREF(result); result = tmp; } else if (!PyErr_ExceptionMatches(PyExc_MemoryError)) { PyErr_Clear(); } #else if (PyFile_Check(result)) { tmp = PyFile_Name(result); Py_INCREF(tmp); Py_DECREF(result); result = tmp; } #endif tmp = RWopsEncodeFilePath(result, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } else if (tmp != Py_None) { Py_DECREF(result); result = tmp; } else { Py_DECREF(tmp); } return result; } static void font_autoquit (void) { if (font_initialized) { font_initialized = 0; TTF_Quit (); } } static PyObject* font_autoinit (PyObject* self) { if (!font_initialized) { PyGame_RegisterQuit (font_autoquit); if (TTF_Init ()) return PyInt_FromLong (0); font_initialized = 1; } return PyInt_FromLong (font_initialized); } static PyObject* fontmodule_quit (PyObject* self) { font_autoquit (); Py_RETURN_NONE; } static PyObject* fontmodule_init (PyObject* self) { PyObject* result; int istrue; result = font_autoinit (self); istrue = PyObject_IsTrue (result); Py_DECREF (result); if (!istrue) return RAISE (PyExc_SDLError, SDL_GetError ()); Py_RETURN_NONE; } static PyObject* get_init (PyObject* self) { return PyInt_FromLong (font_initialized); } /* font object methods */ static PyObject* font_get_height (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong (TTF_FontHeight (font)); } static PyObject* font_get_descent (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong (TTF_FontDescent (font)); } static PyObject* font_get_ascent (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong (TTF_FontAscent (font)); } static PyObject* font_get_linesize (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong (TTF_FontLineSkip (font)); } static PyObject* font_get_bold (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong ((TTF_GetFontStyle (font) & TTF_STYLE_BOLD) != 0); } static PyObject* font_set_bold (PyObject* self, PyObject* args) { TTF_Font* font = PyFont_AsFont (self); int style, val; if (!PyArg_ParseTuple (args, "i", &val)) return NULL; style = TTF_GetFontStyle (font); if (val) style |= TTF_STYLE_BOLD; else style &= ~TTF_STYLE_BOLD; TTF_SetFontStyle (font, style); Py_RETURN_NONE; } static PyObject* font_get_italic (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong ((TTF_GetFontStyle (font) & TTF_STYLE_ITALIC) != 0); } static PyObject* font_set_italic (PyObject* self, PyObject* args) { TTF_Font* font = PyFont_AsFont (self); int style, val; if (!PyArg_ParseTuple (args, "i", &val)) return NULL; style = TTF_GetFontStyle (font); if(val) style |= TTF_STYLE_ITALIC; else style &= ~TTF_STYLE_ITALIC; TTF_SetFontStyle (font, style); Py_RETURN_NONE; } static PyObject* font_get_underline (PyObject* self) { TTF_Font* font = PyFont_AsFont (self); return PyInt_FromLong ((TTF_GetFontStyle (font) & TTF_STYLE_UNDERLINE) != 0); } static PyObject* font_set_underline (PyObject* self, PyObject* args) { TTF_Font* font = PyFont_AsFont (self); int style, val; if (!PyArg_ParseTuple (args, "i", &val)) return NULL; style = TTF_GetFontStyle (font); if(val) style |= TTF_STYLE_UNDERLINE; else style &= ~TTF_STYLE_UNDERLINE; TTF_SetFontStyle (font, style); Py_RETURN_NONE; } static PyObject* font_render(PyObject* self, PyObject* args) { TTF_Font* font = PyFont_AsFont (self); int aa; PyObject* text, *final; PyObject* fg_rgba_obj, *bg_rgba_obj = NULL; Uint8 rgba[4]; SDL_Surface* surf; SDL_Color foreg, backg; int just_return; if (!PyArg_ParseTuple(args, "OiO|O", &text, &aa, &fg_rgba_obj, &bg_rgba_obj)) { return NULL; } if (!RGBAFromColorObj(fg_rgba_obj, rgba)) { return RAISE(PyExc_TypeError, "Invalid foreground RGBA argument"); } foreg.r = rgba[0]; foreg.g = rgba[1]; foreg.b = rgba[2]; if (bg_rgba_obj != NULL) { if (!RGBAFromColorObj(bg_rgba_obj, rgba)) { bg_rgba_obj = NULL; backg.r = 0; backg.g = 0; backg.b = 0; backg.unused = 0; } else { backg.r = rgba[0]; backg.g = rgba[1]; backg.b = rgba[2]; backg.unused = 0; } } else { backg.r = 0; backg.g = 0; backg.b = 0; backg.unused = 0; } just_return = PyObject_Not(text); if (just_return) { int height = TTF_FontHeight(font); if (just_return == -1 || !(PyUnicode_Check(text) || Bytes_Check(text) || text == Py_None)) { PyErr_Clear(); return RAISE_TEXT_TYPE_ERROR(); } surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, height, 32, 0xff<<16, 0xff<<8, 0xff, 0); if (surf == NULL) { return RAISE(PyExc_SDLError, SDL_GetError()); } if (bg_rgba_obj != NULL) { Uint32 c = SDL_MapRGB(surf->format, backg.r, backg.g, backg.b); SDL_FillRect(surf, NULL, c); } else { SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0); } } else if (PyUnicode_Check(text)) { PyObject *bytes = PyUnicode_AsEncodedString(text, "utf-8", "replace"); const char *astring = NULL; if (!bytes) { return NULL; } astring = Bytes_AsString(bytes); if (strlen(astring) != Bytes_GET_SIZE(bytes)) { Py_DECREF(bytes); return RAISE(PyExc_ValueError, "A null character was found in the text"); } if (utf_8_needs_UCS_4(astring)) { Py_DECREF(bytes); return RAISE(PyExc_UnicodeError, "A Unicode character above '\\uFFFF' was found;" " not supported"); } if (aa) { if (bg_rgba_obj == NULL) { surf = TTF_RenderUTF8_Blended(font, astring, foreg); } else { surf = TTF_RenderUTF8_Shaded(font, astring, foreg, backg); } } else { surf = TTF_RenderUTF8_Solid(font, astring, foreg); } Py_DECREF(bytes); } else if (Bytes_Check(text)) { const char *astring = Bytes_AsString(text); if (strlen(astring) != Bytes_GET_SIZE(text)) { return RAISE(PyExc_ValueError, "A null character was found in the text"); } if (aa) { if (bg_rgba_obj == NULL) { surf = TTF_RenderText_Blended(font, astring, foreg); } else { surf = TTF_RenderText_Shaded(font, astring, foreg, backg); } } else { surf = TTF_RenderText_Solid(font, astring, foreg); } } else { return RAISE_TEXT_TYPE_ERROR(); } if (surf == NULL) { return RAISE(PyExc_SDLError, TTF_GetError()); } if (!aa && (bg_rgba_obj != NULL) && !just_return) { /* turn off transparancy */ SDL_SetColorKey(surf, 0, 0); surf->format->palette->colors[0].r = backg.r; surf->format->palette->colors[0].g = backg.g; surf->format->palette->colors[0].b = backg.b; } final = PySurface_New(surf); if (final == NULL) { SDL_FreeSurface(surf); } return final; } static PyObject* font_size(PyObject* self, PyObject* args) { TTF_Font* font = PyFont_AsFont(self); int w, h; PyObject *text; const char *string; if (!PyArg_ParseTuple(args, "O", &text)) { return NULL; } if (PyUnicode_Check(text)) { PyObject* bytes = PyUnicode_AsEncodedString(text, "utf-8", "strict"); int ecode; if (!bytes) { return NULL; } string = Bytes_AS_STRING(bytes); ecode = TTF_SizeUTF8(font, string, &w, &h); Py_DECREF(bytes); if (ecode) { return RAISE (PyExc_SDLError, TTF_GetError()); } } else if (Bytes_Check(text)) { string = Bytes_AS_STRING(text); if (TTF_SizeText(font, string, &w, &h)) { return RAISE (PyExc_SDLError, TTF_GetError()); } } else { return RAISE_TEXT_TYPE_ERROR(); } return Py_BuildValue("(ii)", w, h); } static PyObject* font_metrics(PyObject* self, PyObject* args) { TTF_Font *font = PyFont_AsFont (self); PyObject *list; PyObject *textobj; Py_ssize_t length; Py_ssize_t i; int minx; int maxx; int miny; int maxy; int advance; PyObject *unicodeobj; PyObject *listitem; Py_UNICODE *buffer; Py_UNICODE ch; if (!PyArg_ParseTuple(args, "O", &textobj)) { return NULL; } if (PyUnicode_Check (textobj)) { unicodeobj = textobj; Py_INCREF (unicodeobj); } else if (Bytes_Check (textobj)) { unicodeobj = PyUnicode_FromEncodedObject(textobj, "latin-1", NULL); if (!unicodeobj) { return NULL; } } else { return RAISE_TEXT_TYPE_ERROR (); } length = PyUnicode_GET_SIZE(unicodeobj); list = PyList_New(length); if (!list) { Py_DECREF (unicodeobj); return NULL; } buffer = PyUnicode_AS_UNICODE(unicodeobj); for (i = 0; i != length; ++i) { ch = buffer[i]; /* TODO: * TTF_GlyphMetrics() seems to return a value for any character, * using the default invalid character, if the char is not found. */ if (IS_UCS_2(ch) && /* conditional and */ !TTF_GlyphMetrics(font, (Uint16) ch, &minx, &maxx, &miny, &maxy, &advance)) { listitem = Py_BuildValue("(iiiii)", minx, maxx, miny, maxy, advance); if (!listitem) { Py_DECREF(list); Py_DECREF(unicodeobj); return NULL; } } else { /* Not UCS-2 or no matching metrics. */ Py_INCREF(Py_None); listitem = Py_None; } PyList_SET_ITEM(list, i, listitem); } Py_DECREF(unicodeobj); return list; } static PyMethodDef font_methods[] = { { "get_height", (PyCFunction) font_get_height, METH_NOARGS, DOC_FONTGETHEIGHT }, { "get_descent", (PyCFunction) font_get_descent, METH_NOARGS, DOC_FONTGETDESCENT }, { "get_ascent", (PyCFunction) font_get_ascent, METH_NOARGS, DOC_FONTGETASCENT }, { "get_linesize", (PyCFunction) font_get_linesize, METH_NOARGS, DOC_FONTGETLINESIZE }, { "get_bold", (PyCFunction) font_get_bold, METH_NOARGS, DOC_FONTGETBOLD }, { "set_bold", font_set_bold, METH_VARARGS, DOC_FONTSETBOLD }, { "get_italic", (PyCFunction) font_get_italic, METH_NOARGS, DOC_FONTGETITALIC }, { "set_italic", font_set_italic, METH_VARARGS, DOC_FONTSETITALIC }, { "get_underline", (PyCFunction) font_get_underline, METH_NOARGS, DOC_FONTGETUNDERLINE }, { "set_underline", font_set_underline, METH_VARARGS, DOC_FONTSETUNDERLINE }, { "metrics", font_metrics, METH_VARARGS, DOC_FONTMETRICS }, { "render", font_render, METH_VARARGS, DOC_FONTRENDER }, { "size", font_size, METH_VARARGS, DOC_FONTSIZE }, { NULL, NULL, 0, NULL } }; /*font object internals*/ static void font_dealloc (PyFontObject* self) { TTF_Font* font = PyFont_AsFont (self); if (font && font_initialized) TTF_CloseFont (font); if (self->weakreflist) PyObject_ClearWeakRefs ((PyObject*) self); Py_TYPE(self)->tp_free ((PyObject*) self); } static int font_init(PyFontObject *self, PyObject *args, PyObject *kwds) { int fontsize; TTF_Font *font = NULL; PyObject *obj; PyObject *oencoded; self->font = NULL; if (!PyArg_ParseTuple(args, "Oi", &obj, &fontsize)) { return -1; } if (!font_initialized) { RAISE(PyExc_SDLError, "font not initialized"); return -1; } Py_INCREF(obj); if (fontsize <= 1) { fontsize = 1; } if (obj == Py_None) { Py_DECREF(obj); obj = font_resource(font_defaultname); if (obj == NULL) { if (PyErr_Occurred() == NULL) { PyErr_Format(PyExc_RuntimeError, "default font '%.1024s' not found", font_defaultname); } goto error; } fontsize = (int)(fontsize * .6875); if (fontsize <= 1) { fontsize = 1; } } else { oencoded = RWopsEncodeFilePath(obj, NULL); if (oencoded == NULL) { goto error; } if (oencoded == Py_None) { Py_DECREF(oencoded); } else { Py_DECREF(obj); obj = oencoded; } } if (Bytes_Check(obj)) { const char *filename = Bytes_AS_STRING(obj); FILE *test; /*check if it is a valid file, else SDL_ttf segfaults*/ test = fopen(filename, "rb"); if (test == NULL) { PyObject *tmp = NULL; if (strcmp(filename, font_defaultname) == 0) { /* filename is the default font; get it's resource */ tmp = font_resource(font_defaultname); } if (tmp == NULL) { if (PyErr_Occurred() == NULL) { PyErr_Format(PyExc_IOError, "unable to read font file '%.1024s'", filename); } goto error; } Py_DECREF(obj); obj = tmp; if (Bytes_Check(obj)) { filename = Bytes_AS_STRING(obj); test = fopen(filename, "rb"); if (test == NULL) { PyErr_Format(PyExc_IOError, "unable to read font file '%.1024s'", filename); goto error; } } } if (Bytes_Check(obj)) { fclose(test); Py_BEGIN_ALLOW_THREADS; font = TTF_OpenFont(filename, fontsize); Py_END_ALLOW_THREADS; } } if (font == NULL) { #if FONT_HAVE_RWOPS SDL_RWops *rw = RWopsFromFileObject(obj); if (rw == NULL) { goto error; } if (RWopsCheckObject(rw)) { font = TTF_OpenFontIndexRW(rw, 1, fontsize, 0); } else { Py_BEGIN_ALLOW_THREADS; font = TTF_OpenFontIndexRW(rw, 1, fontsize, 0); Py_END_ALLOW_THREADS; } #else RAISE (PyExc_NotImplementedError, "nonstring fonts require SDL_ttf-2.0.6"); goto error; #endif } if (font == NULL) { RAISE(PyExc_RuntimeError, SDL_GetError()); goto error; } Py_DECREF(obj); self->font = font; return 0; error: Py_DECREF(obj); return -1; } static PyTypeObject PyFont_Type = { TYPE_HEAD (NULL, 0) "pygame.font.Font", sizeof(PyFontObject), 0, (destructor)font_dealloc, 0, 0, /*getattr*/ 0, 0, 0, 0, NULL, 0, (hashfunc)NULL, (ternaryfunc)NULL, (reprfunc)NULL, 0L,0L,0L, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ DOC_PYGAMEFONTFONT, /* Documentation string */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ offsetof(PyFontObject, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ font_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)font_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; //PyType_GenericNew, /* tp_new */ /*font module methods*/ static PyObject* get_default_font (PyObject* self) { return Text_FromUTF8 (font_defaultname); } static PyMethodDef _font_methods[] = { { "__PYGAMEinit__", (PyCFunction) font_autoinit, METH_NOARGS, "auto initialize function for font" }, { "init", (PyCFunction) fontmodule_init, METH_NOARGS, DOC_PYGAMEFONTINIT }, { "quit", (PyCFunction) fontmodule_quit, METH_NOARGS, DOC_PYGAMEFONTQUIT }, { "get_init", (PyCFunction) get_init, METH_NOARGS, DOC_PYGAMEFONTGETINIT }, { "get_default_font", (PyCFunction) get_default_font, METH_NOARGS, DOC_PYGAMEFONTGETDEFAULTFONT }, { NULL, NULL, 0, NULL } }; static PyObject* PyFont_New (TTF_Font* font) { PyFontObject* fontobj; if (!font) return RAISE (PyExc_RuntimeError, "unable to load font."); fontobj = (PyFontObject *) PyFont_Type.tp_new (&PyFont_Type, NULL, NULL); if (fontobj) fontobj->font = font; return (PyObject*) fontobj; } MODINIT_DEFINE (font) { PyObject *module, *apiobj; static void* c_api[PYGAMEAPI_FONT_NUMSLOTS]; #if PY3 static struct PyModuleDef _module = { PyModuleDef_HEAD_INIT, "font", DOC_PYGAMEFONT, -1, _font_methods, NULL, NULL, NULL, NULL }; #endif /* imported needed apis; Do this first so if there is an error the module is not loaded. */ import_pygame_base (); if (PyErr_Occurred ()) { MODINIT_ERROR; } import_pygame_color (); if (PyErr_Occurred ()) { MODINIT_ERROR; } import_pygame_surface (); if (PyErr_Occurred ()) { MODINIT_ERROR; } import_pygame_rwobject (); if (PyErr_Occurred ()) { MODINIT_ERROR; } /* type preparation */ if (PyType_Ready (&PyFont_Type) < 0) { MODINIT_ERROR; } PyFont_Type.tp_new = PyType_GenericNew; #if PY3 module = PyModule_Create (&_module); #else module = Py_InitModule3 (MODPREFIX "font", _font_methods, DOC_PYGAMEFONT); #endif if (module == NULL) { MODINIT_ERROR; } Py_INCREF ((PyObject*) &PyFont_Type); if (PyModule_AddObject (module, "FontType", (PyObject *) &PyFont_Type) == -1) { Py_DECREF ((PyObject *) &PyFont_Type); DECREF_MOD (module); MODINIT_ERROR; } Py_INCREF ((PyObject*) &PyFont_Type); if (PyModule_AddObject (module, "Font", (PyObject *) &PyFont_Type) == -1) { Py_DECREF ((PyObject *) &PyFont_Type); DECREF_MOD (module); MODINIT_ERROR; } /* export the c api */ c_api[0] = &PyFont_Type; c_api[1] = PyFont_New; c_api[2] = &font_initialized; apiobj = encapsulate_api (c_api, "font"); if (apiobj == NULL) { DECREF_MOD (module); MODINIT_ERROR; } if (PyModule_AddObject (module, PYGAMEAPI_LOCAL_ENTRY, apiobj) == -1) { Py_DECREF (apiobj); DECREF_MOD (module); MODINIT_ERROR; } MODINIT_RETURN (module); }
