diff --git a/src_c/_freetype.c b/src_c/_freetype.c index a9a0fc503c..e327c6208e 100644 --- a/src_c/_freetype.c +++ b/src_c/_freetype.c @@ -30,9 +30,17 @@ #define MODULE_NAME "_freetype" #define FONT_TYPE_NAME "Font" +#define DEFAULT_FONT_NAME "freesans" +#define DEFAULT_FONT_FILE "freesansbold.ttf" +#define PKGDATA_MODULE_NAME "pygame.pkgdata" +#define RESOURCE_FUNC_NAME "getResource" + /* * FreeType module declarations */ + +PyObject *_freetypemodule = NULL; + static const Scale_t FACE_SIZE_NONE = {0, 0}; static int @@ -212,10 +220,6 @@ free_string(PGFT_String *); _var = PyObject_IsTrue(_pyobj); \ } -#define DEFAULT_FONT_NAME "freesansbold.ttf" -#define PKGDATA_MODULE_NAME "pygame.pkgdata" -#define RESOURCE_FUNC_NAME "getResource" - static unsigned int current_freetype_generation = 0; #define FreetypeFont_GenerationCheck(x) \ @@ -649,7 +653,7 @@ _ftfont_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) if (obj) { obj->id.open_args.flags = 0; obj->id.open_args.pathname = 0; - obj->path = 0; + obj->path = NULL; obj->resolution = 0; obj->is_scalable = 0; obj->freetype = 0; @@ -692,6 +696,9 @@ _ftfont_dealloc(pgFontObject *self) ((PyObject *)self)->ob_type->tp_free((PyObject *)self); } +FreeTypeInstance *_freetypeinstance; +_FreeTypeState *_freetypestate; + static int _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds) { @@ -711,9 +718,6 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds) int rval = -1; SDL_RWops *source; - FreeTypeInstance *ft; - ASSERT_GRAB_FREETYPE(ft, -1); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&lIi", kwlist, &file, obj_to_scale, (void *)&face_size, &font_index, &resolution, &ucs4)) { @@ -728,25 +732,29 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds) _PGFT_Quit(self->freetype); self->freetype = 0; } + Py_XDECREF(self->path); - self->path = 0; - self->is_scalable = 0; + self->path = NULL; + self->is_scalable = 0; self->face_size = face_size; + if (ucs4) { self->render_flags |= FT_RFLAG_UCS4; } else { self->render_flags &= ~FT_RFLAG_UCS4; } + if (resolution) { self->resolution = (FT_UInt)resolution; } else { - self->resolution = FREETYPE_STATE->resolution; + self->resolution = _freetypestate->resolution; } + if (file == Py_None) { - file = load_font_res(DEFAULT_FONT_NAME); + file = load_font_res(DEFAULT_FONT_FILE); if (!file) { PyErr_SetString(PyExc_RuntimeError, "Failed to find default font"); @@ -813,13 +821,13 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds) goto end; } - if (_PGFT_TryLoadFont_RWops(ft, self, source, font_index)) { + if (_PGFT_TryLoadFont_RWops(_freetypeinstance, self, source, font_index)) { goto end; } if (!self->is_scalable && self->face_size.x == 0) { - if (_PGFT_Font_GetAvailableSize(ft, self, 0, &size, &height, &width, - &x_ppem, &y_ppem)) { + if (_PGFT_Font_GetAvailableSize(_freetypeinstance, self, 0, &size, + &height, &width, &x_ppem, &y_ppem)) { self->face_size.x = DBL_TO_FX6(x_ppem); self->face_size.y = DBL_TO_FX6(y_ppem); } @@ -832,8 +840,8 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds) Otherwise, the freetype library may be closed before the object frees its local resources. See pygame-ce issue #202 */ - self->freetype = ft; - ++ft->ref_count; + self->freetype = _freetypeinstance; + ++_freetypeinstance->ref_count; rval = 0; @@ -2199,6 +2207,14 @@ _ft_get_error(PyObject *self, PyObject *_null) Py_RETURN_NONE; } +#if defined(__EMSCRIPTEN__) || defined(__wasi__) +static PyObject * +_ft_get_version(PyObject *self, PyObject *args, PyObject *kwargs) +{ + return Py_BuildValue("iii", FREETYPE_MAJOR, FREETYPE_MINOR, + FREETYPE_PATCH); +} +#else static PyObject * _ft_get_version(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -2245,6 +2261,7 @@ _ft_get_version(PyObject *self, PyObject *args, PyObject *kwargs) FREETYPE_PATCH); } } +#endif // defined(__EMSCRIPTEN__) || defined(__wasi__) static PyObject * _ft_get_cache_size(PyObject *self, PyObject *_null) @@ -2297,7 +2314,7 @@ _ft_was_init(PyObject *self, PyObject *_null) static PyObject * _ft_get_default_font(PyObject *self, PyObject *_null) { - return PyUnicode_FromString(DEFAULT_FONT_NAME); + return PyUnicode_FromString(DEFAULT_FONT_FILE); } static int @@ -2320,13 +2337,13 @@ _ft_clear(PyObject *mod) * FREETYPE MODULE DECLARATION ****************************************************/ #ifndef PYPY_VERSION -struct PyModuleDef _freetypemodule = { +struct PyModuleDef _freetypemoduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, DOC_FREETYPE, sizeof(_FreeTypeState), _ft_methods, 0, _ft_traverse, _ft_clear, 0}; #else /* PYPY_VERSION */ _FreeTypeState _modstate; -struct PyModuleDef _freetypemodule = { +struct PyModuleDef _freetypemoduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, DOC_FREETYPE, @@ -2340,7 +2357,7 @@ struct PyModuleDef _freetypemodule = { MODINIT_DEFINE(_freetype) { - PyObject *module, *apiobj; + PyObject *apiobj; static void *c_api[PYGAMEAPI_FREETYPE_NUMSLOTS]; import_pygame_base(); @@ -2368,27 +2385,38 @@ MODINIT_DEFINE(_freetype) return NULL; } - module = PyModule_Create(&_freetypemodule); - - if (!module) { + /* type preparation */ + if (PyType_Ready(&pgFont_Type) < 0) { return NULL; } - FREETYPE_MOD_STATE(module)->freetype = 0; - FREETYPE_MOD_STATE(module)->cache_size = 0; - FREETYPE_MOD_STATE(module)->resolution = PGFT_DEFAULT_RESOLUTION; + _freetypemodule = PyModule_Create(&_freetypemoduledef); + + if (!_freetypemodule) { + return NULL; + } + + FREETYPE_MOD_STATE(_freetypemodule)->freetype = 0; + FREETYPE_MOD_STATE(_freetypemodule)->cache_size = 0; + FREETYPE_MOD_STATE(_freetypemodule)->resolution = PGFT_DEFAULT_RESOLUTION; - if (PyModule_AddType(module, &pgFont_Type)) { - Py_DECREF(module); + if (PyModule_AddType(_freetypemodule, &pgFont_Type)) { + Py_DECREF(_freetypemodule); return NULL; } #define DEC_CONST(x) \ - if (PyModule_AddIntConstant(module, #x, (int)FT_##x)) { \ - Py_DECREF(module); \ + if (PyModule_AddIntConstant(_freetypemodule, #x, (int)FT_##x)) { \ + Py_DECREF(_freetypemodule); \ return NULL; \ } +#define DEC_CONST(x) \ + if (PyModule_AddIntConstant(_freetypemodule, #x, (int)FT_##x)) { \ + Py_DECREF(_freetypemodule); \ + return NULL; \ + } + DEC_CONST(STYLE_NORMAL); DEC_CONST(STYLE_STRONG); DEC_CONST(STYLE_OBLIQUE); @@ -2409,11 +2437,14 @@ MODINIT_DEFINE(_freetype) c_api[1] = &pgFont_New; apiobj = encapsulate_api(c_api, "freetype"); - if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { + if (PyModule_AddObject(_freetypemodule, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { Py_XDECREF(apiobj); - Py_DECREF(module); + Py_DECREF(_freetypemodule); return NULL; } - return module; -} + _ft_autoinit(_freetypemodule, NULL); + _freetypestate = FREETYPE_MOD_STATE(_freetypemodule); + _freetypeinstance = FREETYPE_MOD_STATE(_freetypemodule)->freetype; + return _freetypemodule; +} \ No newline at end of file diff --git a/src_c/freetype/ft_wrap.h b/src_c/freetype/ft_wrap.h index 0ecff0c450..f94ee03032 100644 --- a/src_c/freetype/ft_wrap.h +++ b/src_c/freetype/ft_wrap.h @@ -232,9 +232,10 @@ typedef struct { } _FreeTypeState; #if !defined(PYPY_VERSION) -extern struct PyModuleDef _freetypemodule; +extern struct PyModuleDef _freetypemoduledef; #define FREETYPE_MOD_STATE(mod) ((_FreeTypeState *)PyModule_GetState(mod)) -#define FREETYPE_STATE FREETYPE_MOD_STATE(PyState_FindModule(&_freetypemodule)) +#define FREETYPE_STATE \ + FREETYPE_MOD_STATE(PyState_FindModule(&_freetypemoduledef)) #else /* defined(PYPY_VERSION) */ extern _FreeTypeState _modstate; #define FREETYPE_MOD_STATE(mod) (&_modstate) diff --git a/src_py/freetype.py b/src_py/freetype.py index 146e6750d2..44404ae738 100644 --- a/src_py/freetype.py +++ b/src_py/freetype.py @@ -45,7 +45,7 @@ def SysFont(name, size, bold=False, italic=False, constructor=None): - """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font + """pygame.freetype.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font Create a pygame Font from system font resources. This will search the system fonts for the given font diff --git a/src_py/sysfont.py b/src_py/sysfont.py index 7c512dbac7..4b2ee1283b 100644 --- a/src_py/sysfont.py +++ b/src_py/sysfont.py @@ -199,12 +199,43 @@ def initsysfonts_darwin(): return fonts -# read the fonts on unix +# read the fonts on posix/unix def initsysfonts_unix(path="fc-list"): - """use the fc-list from fontconfig to get a list of fonts""" + """if not embedded, use the fc-list from fontconfig to get a list of fonts""" fonts = {} - if sys.platform == "emscripten": + # these are embedded and cannot get os to list fonts a simple way. + if hasattr(sys, "getandroidapilevel") or sys.platform in ( + "emscripten", + "wasi", + "android", + ): + from pathlib import Path + + # default font + import importlib.resources + import pygame + + entry = importlib.resources.files(pygame) / pygame._freetype.get_default_font() + _parse_font_entry_unix(f"{entry}: FreeSans:style=Bold", fonts) + + # cache in search order main script folder, then /tmp + main = __import__("__main__") + + if os.environ.get("FC_CACHE", ""): + fc_cache = Path(os.environ.get("FC_CACHE")) + elif hasattr(main, "__file__"): + fc_cache = Path(main.__file__).parent / path + else: + fc_cache = Path(__import__("tempfile").gettempdir()) / path + + if fc_cache.is_file(): + with open(fc_cache, "rb") as file: + for entry in file.read().decode("utf-8").splitlines(): + _parse_font_entry_unix(entry, fonts) + else: + warnings.warn(f"no font cache (fc-list format) file found at {fc_cache}") + return fonts import subprocess