From c211100921de4c5c8425fab3dc9f13874ecd03ad Mon Sep 17 00:00:00 2001 From: Austin Hurst Date: Wed, 26 Jan 2022 21:05:26 -0400 Subject: [PATCH] Update bindings to support SDL_ttf 2.0.18 (#216) * Automatically add TTF functions to __all__ * Add raw bindings for SDL_ttf 2.0.18 * Document new TTF functions, add HB helpers * Add unit tests for new TTF functions * Update news.rst --- doc/modules/sdl2_sdlttf.rst | 122 ++++++- doc/news.rst | 1 + sdl2/sdlttf.py | 701 ++++++++++++++++++++++++++++++++++-- sdl2/test/sdlttf_test.py | 331 +++++++++++++++-- 4 files changed, 1088 insertions(+), 67 deletions(-) diff --git a/doc/modules/sdl2_sdlttf.rst b/doc/modules/sdl2_sdlttf.rst index a55b0791..cc49285e 100644 --- a/doc/modules/sdl2_sdlttf.rst +++ b/doc/modules/sdl2_sdlttf.rst @@ -67,6 +67,9 @@ Initialization functions .. autofunction:: TTF_Linked_Version +.. autofunction:: TTF_GetFreeTypeVersion + +.. autofunction:: TTF_GetHarfBuzzVersion Font loading functions @@ -80,17 +83,26 @@ Font loading functions .. autofunction:: TTF_OpenFontIndexRW +.. autofunction:: TTF_OpenFontDPI + +.. autofunction:: TTF_OpenFontIndexDPI + +.. autofunction:: TTF_OpenFontDPIRW + +.. autofunction:: TTF_OpenFontIndexDPIRW + .. autofunction:: TTF_CloseFont Font attribute functions ------------------------ -.. autofunction:: TTF_SetFontStyle +Sizing functions +^^^^^^^^^^^^^^^^ -.. autofunction:: TTF_GetFontStyle +.. autofunction:: TTF_SetFontSize -.. autofunction:: TTF_SetFontHinting +.. autofunction:: TTF_SetFontSizeDPI .. autofunction:: TTF_FontHeight @@ -100,9 +112,12 @@ Font attribute functions .. autofunction:: TTF_FontLineSkip -.. autofunction:: TTF_GetFontKerning +Style functions +^^^^^^^^^^^^^^^ -.. autofunction:: TTF_SetFontKerning +.. autofunction:: TTF_SetFontStyle + +.. autofunction:: TTF_GetFontStyle .. autofunction:: TTF_FontFaceIsFixedWidth @@ -110,12 +125,39 @@ Font attribute functions .. autofunction:: TTF_FontFaceStyleName + +Glyph information functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: TTF_GlyphIsProvided +.. autofunction:: TTF_GlyphIsProvided32 + .. autofunction:: TTF_GlyphMetrics +.. autofunction:: TTF_GlyphMetrics32 + + +Kerning functions +^^^^^^^^^^^^^^^^^ +.. autofunction:: TTF_GetFontKerning + +.. autofunction:: TTF_SetFontKerning + .. autofunction:: TTF_GetFontKerningSizeGlyphs +.. autofunction:: TTF_GetFontKerningSizeGlyphs32 + + +Render settings functions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: TTF_SetFontHinting + +.. autofunction:: TTF_SetFontSDF + +.. autofunction:: TTF_GetFontSDF + Text rendering functions ------------------------ @@ -123,6 +165,12 @@ Text rendering functions Size calculation functions ^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: TTF_MeasureText + +.. autofunction:: TTF_MeasureUTF8 + +.. autofunction:: TTF_MeasureUNICODE + .. autofunction:: TTF_SizeText .. autofunction:: TTF_SizeUTF8 @@ -141,6 +189,14 @@ Solid rendering functions .. autofunction:: TTF_RenderGlyph_Solid +.. autofunction:: TTF_RenderGlyph32_Solid + +.. autofunction:: TTF_RenderText_Solid_Wrapped + +.. autofunction:: TTF_RenderUTF8_Solid_Wrapped + +.. autofunction:: TTF_RenderUNICODE_Solid_Wrapped + Shaded rendering functions ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,6 +209,14 @@ Shaded rendering functions .. autofunction:: TTF_RenderGlyph_Shaded +.. autofunction:: TTF_RenderGlyph32_Shaded + +.. autofunction:: TTF_RenderText_Shaded_Wrapped + +.. autofunction:: TTF_RenderUTF8_Shaded_Wrapped + +.. autofunction:: TTF_RenderUNICODE_Shaded_Wrapped + Blended rendering functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,6 +229,8 @@ Blended rendering functions .. autofunction:: TTF_RenderGlyph_Blended +.. autofunction:: TTF_RenderGlyph32_Blended + .. autofunction:: TTF_RenderText_Blended_Wrapped .. autofunction:: TTF_RenderUTF8_Blended_Wrapped @@ -172,6 +238,14 @@ Blended rendering functions .. autofunction:: TTF_RenderUNICODE_Blended_Wrapped +Renderer configuration functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: TTF_SetDirection + +.. autofunction:: TTF_SetScript + + Data types ---------- @@ -257,3 +331,41 @@ Module constants Used to indicate set hinting type to none. No hinting is used, so the font may become very blurry or messy at smaller sizes. + +.. data:: TTF_HINTING_LIGHT_SUBPIXEL + + Used to indicate set hinting type to light subpixel. + This produces better results for small text sizes: glyph are rendered at + subpixel positions, so they look blurrier but are uniformly positioned. + This mode is slower than others since glyphs are rendered on the fly. + + +HarfBuzz functions and constants +-------------------------------- + +As of version 2.0.18, SDL2_ttf makes use of the HarfBuzz library for advanced +text rendering and shaping unless explicitly compiled without it. As a +consequence, some specific SDL2_ttf functions require HarfBuzz constants and +macros for input. + +To make these easier to use, the ``sdlttf`` module defines and implements the +constants and macro functions necessary to make full use of the SDL2_ttf +library. + +.. autofunction:: HB_TAG + +.. data:: HB_DIRECTION_LTR + + A constant indicating left-to-right text rendering. + +.. data:: HB_DIRECTION_RTL + + A constant indicating right-to-left text rendering. + +.. data:: HB_DIRECTION_TTB + + A constant indicating top-to-bottom text rendering. + +.. data:: HB_DIRECTION_BTT + + A constant indicating bottom-to-top text rendering. diff --git a/doc/news.rst b/doc/news.rst index 5f9e8cd4..2d4f47d1 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -10,6 +10,7 @@ Released on XXXX-XX-XX. New Features: * Updated to wrap new functions and constants in SDL2 2.0.20 (PR #214) +* Updated to wrap new functions and constants in SDL2_ttf 2.0.18 (PR #216) 0.9.10 diff --git a/sdl2/sdlttf.py b/sdl2/sdlttf.py index 907ea1cf..91374154 100644 --- a/sdl2/sdlttf.py +++ b/sdl2/sdlttf.py @@ -1,10 +1,10 @@ import os -from ctypes import Structure, POINTER, c_int, c_long, c_char_p, c_void_p +from ctypes import c_int, c_uint, c_long, c_char_p, c_void_p from ctypes import POINTER as _P from .dll import DLL, SDLFunc from .version import SDL_version, SDL_VERSIONNUM from .rwops import SDL_RWops -from .stdinc import Uint16, Uint32 +from .stdinc import Uint16, Uint32, SDL_bool from .pixels import SDL_Color from .surface import SDL_Surface from .error import SDL_GetError, SDL_SetError @@ -13,6 +13,11 @@ # Opaque Types "TTF_Font", + # Enums + "hb_direction_t", + "HB_DIRECTION_INVALID", "HB_DIRECTION_LTR", "HB_DIRECTION_RTL", + "HB_DIRECTION_TTB", "HB_DIRECTION_BTT", + # Defines "SDL_TTF_MAJOR_VERSION", "SDL_TTF_MINOR_VERSION", "SDL_TTF_PATCHLEVEL", "TTF_MAJOR_VERSION", "TTF_MINOR_VERSION", "TTF_PATCHLEVEL", @@ -20,33 +25,14 @@ "TTF_STYLE_NORMAL", "TTF_STYLE_BOLD", "TTF_STYLE_ITALIC", "TTF_STYLE_UNDERLINE", "TTF_STYLE_STRIKETHROUGH", "TTF_HINTING_NORMAL", "TTF_HINTING_LIGHT", "TTF_HINTING_MONO", - "TTF_HINTING_NONE", + "TTF_HINTING_NONE", "TTF_HINTING_LIGHT_SUBPIXEL", # Macro Functions "SDL_TTF_VERSION", "TTF_VERSION", "SDL_TTF_COMPILEDVERSION", - "SDL_TTF_VERSION_ATLEAST", - - # Functions - "TTF_Linked_Version", "TTF_ByteSwappedUNICODE", "TTF_Init", - "TTF_OpenFont", "TTF_OpenFontIndex", "TTF_OpenFontRW", - "TTF_OpenFontIndexRW", "TTF_GetFontStyle", "TTF_SetFontStyle", - "TTF_GetFontOutline", "TTF_SetFontOutline", - "TTF_GetFontHinting", "TTF_SetFontHinting", - "TTF_FontHeight", "TTF_FontAscent", "TTF_FontDescent", - "TTF_FontLineSkip", "TTF_GetFontKerning", "TTF_SetFontKerning", - "TTF_FontFaces", "TTF_FontFaceIsFixedWidth", "TTF_FontFaceFamilyName", - "TTF_FontFaceStyleName", "TTF_GlyphIsProvided", "TTF_GlyphMetrics", - "TTF_SizeText", "TTF_SizeUTF8", "TTF_SizeUNICODE", - "TTF_RenderText_Solid", "TTF_RenderUTF8_Solid", - "TTF_RenderUNICODE_Solid", "TTF_RenderGlyph_Solid", - "TTF_RenderText_Shaded", "TTF_RenderUTF8_Shaded", - "TTF_RenderUNICODE_Shaded", "TTF_RenderGlyph_Shaded", - "TTF_RenderText_Blended", "TTF_RenderUTF8_Blended", - "TTF_RenderUNICODE_Blended", "TTF_RenderText_Blended_Wrapped", - "TTF_RenderUTF8_Blended_Wrapped", "TTF_RenderUNICODE_Blended_Wrapped", - "TTF_RenderGlyph_Blended", "TTF_RenderText", "TTF_RenderUTF", - "TTF_RenderUNICODE", "TTF_CloseFont", "TTF_Quit", "TTF_WasInit", - "TTF_GetFontKerningSize", "TTF_GetFontKerningSizeGlyphs", + "SDL_TTF_VERSION_ATLEAST", "HB_TAG", + + # Functions not contained in _funcdefs + "TTF_RenderText", "TTF_RenderUTF8", "TTF_RenderUNICODE", "TTF_SetError", "TTF_GetError", # Python Functions @@ -71,7 +57,7 @@ def get_dll_file(): SDL_TTF_MAJOR_VERSION = 2 SDL_TTF_MINOR_VERSION = 0 -SDL_TTF_PATCHLEVEL = 15 +SDL_TTF_PATCHLEVEL = 18 def SDL_TTF_VERSION(x): x.major = SDL_TTF_MAJOR_VERSION @@ -99,6 +85,7 @@ def SDL_TTF_VERSION(x): TTF_HINTING_LIGHT = 1 TTF_HINTING_MONO = 2 TTF_HINTING_NONE = 3 +TTF_HINTING_LIGHT_SUBPIXEL = 4 class TTF_Font(c_void_p): """The opaque data type for fonts opened using the TTF library. @@ -110,16 +97,53 @@ class TTF_Font(c_void_p): pass +# Some additional definitions from HarfBuzz for SetDirection/SetScript + +hb_direction_t = c_int +HB_DIRECTION_INVALID = 0 +HB_DIRECTION_LTR = 4 +HB_DIRECTION_RTL = 5 +HB_DIRECTION_TTB = 6 +HB_DIRECTION_BTT = 7 + +def HB_TAG(c1, c2, c3, c4): + """Converts a 4-character ISO 15924 code into a HarfBuzz script constant. + + A full list of possible 4-character script codes can be found + here: https://unicode.org/iso15924/iso15924-codes.html + + Args: + c1 (str): The first character of the code. + c2 (str): The second character of the code. + c3 (str): The third character of the code. + c4 (str): The fourth character of the code. + + Returns: + int: The HarfBuzz contstant corresponding to the given script. + + """ + c1, c2, c3, c4 = [ord(c) & 0xFF for c in (c1, c2, c3, c4)] + return (c1 << 24 | c2 << 16 | c3 << 8 | c4) + + # Raw ctypes function definitions _funcdefs = [ SDLFunc("TTF_Linked_Version", None, _P(SDL_version)), + SDLFunc("TTF_GetFreeTypeVersion", [_P(c_int), _P(c_int), _P(c_int)], added='2.0.18'), + SDLFunc("TTF_GetHarfBuzzVersion", [_P(c_int), _P(c_int), _P(c_int)], added='2.0.18'), SDLFunc("TTF_ByteSwappedUNICODE", [c_int], None), SDLFunc("TTF_Init", None, c_int), SDLFunc("TTF_OpenFont", [c_char_p, c_int], _P(TTF_Font)), SDLFunc("TTF_OpenFontIndex", [c_char_p, c_int, c_long], _P(TTF_Font)), SDLFunc("TTF_OpenFontRW", [_P(SDL_RWops), c_int, c_int], _P(TTF_Font)), SDLFunc("TTF_OpenFontIndexRW", [_P(SDL_RWops), c_int, c_int, c_long], _P(TTF_Font)), + SDLFunc("TTF_OpenFontDPI", [c_char_p, c_int, c_uint, c_uint], _P(TTF_Font), added='2.0.18'), + SDLFunc("TTF_OpenFontIndexDPI", [c_char_p, c_int, c_long, c_uint, c_uint], _P(TTF_Font), added='2.0.18'), + SDLFunc("TTF_OpenFontDPIRW", [_P(SDL_RWops), c_int, c_int, c_uint, c_uint], _P(TTF_Font), added='2.0.18'), + SDLFunc("TTF_OpenFontIndexDPIRW", [_P(SDL_RWops), c_int, c_int, c_long, c_uint, c_uint], _P(TTF_Font), added='2.0.18'), + SDLFunc("TTF_SetFontSize", [_P(TTF_Font), c_int], c_int, added='2.0.18'), + SDLFunc("TTF_SetFontSizeDPI", [_P(TTF_Font), c_int, c_uint, c_uint], c_int, added='2.0.18'), SDLFunc("TTF_GetFontStyle", [_P(TTF_Font)], c_int), SDLFunc("TTF_SetFontStyle", [_P(TTF_Font), c_int], None), SDLFunc("TTF_GetFontOutline", [_P(TTF_Font)], c_int), @@ -137,18 +161,31 @@ class TTF_Font(c_void_p): SDLFunc("TTF_FontFaceFamilyName", [_P(TTF_Font)], c_char_p), SDLFunc("TTF_FontFaceStyleName", [_P(TTF_Font)], c_char_p), SDLFunc("TTF_GlyphIsProvided", [_P(TTF_Font), Uint16], c_int), + SDLFunc("TTF_GlyphIsProvided32", [_P(TTF_Font), Uint32], c_int, added='2.0.18'), SDLFunc("TTF_GlyphMetrics", [_P(TTF_Font), Uint16, _P(c_int), _P(c_int), _P(c_int), _P(c_int), _P(c_int)], c_int), + SDLFunc("TTF_GlyphMetrics32", [_P(TTF_Font), Uint32, _P(c_int), _P(c_int), _P(c_int), _P(c_int), _P(c_int)], c_int, added='2.0.18'), SDLFunc("TTF_SizeText", [_P(TTF_Font), c_char_p, _P(c_int), _P(c_int)], c_int), SDLFunc("TTF_SizeUTF8", [_P(TTF_Font), c_char_p, _P(c_int), _P(c_int)], c_int), SDLFunc("TTF_SizeUNICODE", [_P(TTF_Font), _P(Uint16), _P(c_int), _P(c_int)], c_int), + SDLFunc("TTF_MeasureText", [_P(TTF_Font), c_char_p, c_int, _P(c_int), _P(c_int)], c_int, added='2.0.18'), + SDLFunc("TTF_MeasureUTF8", [_P(TTF_Font), c_char_p, c_int, _P(c_int), _P(c_int)], c_int, added='2.0.18'), + SDLFunc("TTF_MeasureUNICODE", [_P(TTF_Font), _P(Uint16), c_int, _P(c_int), _P(c_int)], c_int, added='2.0.18'), SDLFunc("TTF_RenderText_Solid", [_P(TTF_Font), c_char_p, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUTF8_Solid", [_P(TTF_Font), c_char_p, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUNICODE_Solid", [_P(TTF_Font), _P(Uint16), SDL_Color], _P(SDL_Surface)), + SDLFunc("TTF_RenderText_Solid_Wrapped", [_P(TTF_Font), c_char_p, SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), + SDLFunc("TTF_RenderUTF8_Solid_Wrapped", [_P(TTF_Font), c_char_p, SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), + SDLFunc("TTF_RenderUNICODE_Solid_Wrapped", [_P(TTF_Font), _P(Uint16), SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), SDLFunc("TTF_RenderGlyph_Solid", [_P(TTF_Font), Uint16, SDL_Color], _P(SDL_Surface)), + SDLFunc("TTF_RenderGlyph32_Solid", [_P(TTF_Font), Uint32, SDL_Color], _P(SDL_Surface), added='2.0.18'), SDLFunc("TTF_RenderText_Shaded", [_P(TTF_Font), c_char_p, SDL_Color, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUTF8_Shaded", [_P(TTF_Font), c_char_p, SDL_Color, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUNICODE_Shaded", [_P(TTF_Font), _P(Uint16), SDL_Color, SDL_Color], _P(SDL_Surface)), + SDLFunc("TTF_RenderText_Shaded_Wrapped", [_P(TTF_Font), c_char_p, SDL_Color, SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), + SDLFunc("TTF_RenderUTF8_Shaded_Wrapped", [_P(TTF_Font), c_char_p, SDL_Color, SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), + SDLFunc("TTF_RenderUNICODE_Shaded_Wrapped", [_P(TTF_Font), _P(Uint16), SDL_Color, SDL_Color, Uint32], _P(SDL_Surface), added='2.0.18'), SDLFunc("TTF_RenderGlyph_Shaded", [_P(TTF_Font), Uint16, SDL_Color, SDL_Color], _P(SDL_Surface)), + SDLFunc("TTF_RenderGlyph32_Shaded", [_P(TTF_Font), Uint32, SDL_Color, SDL_Color], _P(SDL_Surface), added='2.0.18'), SDLFunc("TTF_RenderText_Blended", [_P(TTF_Font), c_char_p, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUTF8_Blended", [_P(TTF_Font), c_char_p, SDL_Color], _P(SDL_Surface)), SDLFunc("TTF_RenderUNICODE_Blended", [_P(TTF_Font), _P(Uint16), SDL_Color], _P(SDL_Surface)), @@ -156,15 +193,22 @@ class TTF_Font(c_void_p): SDLFunc("TTF_RenderUTF8_Blended_Wrapped", [_P(TTF_Font), c_char_p, SDL_Color, Uint32], _P(SDL_Surface)), SDLFunc("TTF_RenderUNICODE_Blended_Wrapped", [_P(TTF_Font), _P(Uint16), SDL_Color, Uint32], _P(SDL_Surface)), SDLFunc("TTF_RenderGlyph_Blended", [_P(TTF_Font), Uint16, SDL_Color], _P(SDL_Surface)), + SDLFunc("TTF_RenderGlyph32_Blended", [_P(TTF_Font), Uint32, SDL_Color], _P(SDL_Surface), added='2.0.18'), + SDLFunc("TTF_SetDirection", [c_int], c_int, added='2.0.18'), + SDLFunc("TTF_SetScript", [c_int], c_int, added='2.0.18'), SDLFunc("TTF_CloseFont", [_P(TTF_Font)]), SDLFunc("TTF_Quit"), SDLFunc("TTF_WasInit", None, c_int), SDLFunc("TTF_GetFontKerningSize", [_P(TTF_Font), c_int, c_int], c_int), SDLFunc("TTF_GetFontKerningSizeGlyphs", [_P(TTF_Font), Uint16, Uint16], c_int, added='2.0.14'), + SDLFunc("TTF_GetFontKerningSizeGlyphs32", [_P(TTF_Font), Uint32, Uint32], c_int, added='2.0.18'), + SDLFunc("TTF_SetFontSDF", [_P(TTF_Font), SDL_bool], c_int, added='2.0.18'), + SDLFunc("TTF_GetFontSDF", [_P(TTF_Font)], SDL_bool, added='2.0.18'), ] _funcs = {} for f in _funcdefs: _funcs[f.name] = _bind(f.name, f.args, f.returns, f.added) + __all__.append(f.name) # Python wrapper functions @@ -179,6 +223,46 @@ def TTF_Linked_Version(): """ return _funcs["TTF_Linked_Version"]() +def TTF_GetFreeTypeVersion(major, minor, patch): + """Gets the version of the FreeType library currently linked by SDL2_ttf. + + This function returns the version numbers by reference, meaning that + it needs to be called using pre-allocated ctypes variables (see + :func:`TTF_GlyphMetrics` for an example). + + `Note: Added in SDL_ttf 2.0.18` + + Args: + major (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the major version number of the linked FreeType library. + minor (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the minor version number of the linked FreeType library. + patch (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the patch level of the linked FreeType library. + + """ + return _funcs["TTF_GetFreeTypeVersion"](major, minor, patch) + +def TTF_GetHarfBuzzVersion(major, minor, patch): + """Gets the version of the HarfBuzz library currently linked by SDL2_ttf. + + This function returns the version numbers by reference, meaning that + it needs to be called using pre-allocated ctypes variables (see + :func:`TTF_GlyphMetrics` for an example). + + `Note: Added in SDL_ttf 2.0.18` + + Args: + major (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the major version number of the linked HarfBuzz library. + minor (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the minor version number of the linked HarfBuzz library. + patch (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the patch level of the linked HarfBuzz library. + + """ + return _funcs["TTF_GetHarfBuzzVersion"](major, minor, patch) + def TTF_ByteSwappedUNICODE(swapped): """Tells the library whether UCS-2 unicode text is generally byteswapped. @@ -220,7 +304,6 @@ def TTF_OpenFont(file, ptsize): file (bytes): A UTF8-encoded bytestring containing the path of the font file to load. ptsize (int): The size (in points) at which to open the font. - index (int): The index (from 0 to 15) of the font face to open. For Returns: POINTER(:obj:`TTF_Font`): A pointer to the opened font object, or a null @@ -258,7 +341,7 @@ def TTF_OpenFontRW(src, freesrc, ptsize): .. note:: The file object used to create the font (``src``) must be kept in memory until you are done with the font. Once the ``src`` has been freed, - performing any addotinoal operations with the returned :obj:`TTF_Font` + performing any additional operations with the returned :obj:`TTF_Font` will result in a hard Python crash (segmentation fault). Args: @@ -297,6 +380,142 @@ def TTF_OpenFontIndexRW(src, freesrc, ptsize, index): """ return _funcs["TTF_OpenFontIndexRW"](src, freesrc, ptsize, index) +def TTF_OpenFontDPI(file, ptsize, hdpi, vdpi): + """Opens a font file at a given size and DPI. + + The font will be opened with the given horizontal and vertical target + resolutions (in DPI). DPI scaling only applies to scalable fonts (e.g. + TrueType). Use the :func:`TTF_GetError` function to check for any errors + opening the font. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + file (bytes): A UTF8-encoded bytestring containing the path of the font + file to load. + ptsize (int): The size (in points) at which to open the font. + hdpi (int): The horizontal resolution (in DPI) at which to open the font. + vdpi (int): The vertical resolution (in DPI) at which to open the font. + + Returns: + POINTER(:obj:`TTF_Font`): A pointer to the opened font object, or a null + pointer if there was an error. + + """ + return _funcs["TTF_OpenFontDPI"](file, ptsize, hdpi, vdpi) + +def TTF_OpenFontIndexDPI(file, ptsize, index, hdpi, vdpi): + """Opens a specific font face by index from a file at a given size and DPI. + + See :func:`TTF_OpenFontDPI` and `:func:`TTF_OpenFontIndex` for more + information. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + file (bytes): A UTF8-encoded bytestring containing the path of the font + file to load. + ptsize (int): The size (in points) at which to open the font. + index (int): The index (from 0 to 15) of the font face to open. For + font files with only one face, this should always be 0. + hdpi (int): The horizontal resolution (in DPI) at which to open the font. + vdpi (int): The vertical resolution (in DPI) at which to open the font. + + Returns: + POINTER(:obj:`TTF_Font`): A pointer to the opened font object, or a null + pointer if there was an error. + + """ + return _funcs["TTF_OpenFontIndexDPI"](file, ptsize, index, hdpi, vdpi) + +def TTF_OpenFontDPIRW(src, freesrc, ptsize, hdpi, vdpi): + """Opens a font from a file object at a given size and DPI. + + See :func:`TTF_OpenFontDPI` and `:func:`TTF_OpenFontRW` for more + information. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + src (:obj:`SDL_RWops`): A file object containing a valid font. + freesrc (int): If non-zero, the provided file object will be closed and + freed automatically when the resulting :obj:`TTF_Font` is closed (or + if an error is encountered opening the font). + ptsize (int): The size (in points) at which to open the font. + hdpi (int): The horizontal resolution (in DPI) at which to open the font. + vdpi (int): The vertical resolution (in DPI) at which to open the font. + + Returns: + POINTER(:obj:`TTF_Font`): A pointer to the opened font object, or a null + pointer if there was an error. + + """ + return _funcs["TTF_OpenFontDPIRW"](src, freesrc, ptsize, hdpi, vdpi) + +def TTF_OpenFontIndexDPIRW(src, freesrc, ptsize, index, hdpi, vdpi): + """Opens a font face by index from a file object at a given size and DPI. + + See :func:`TTF_OpenFontDPI` and `:func:`TTF_OpenFontIndexRW` for more + information. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + src (:obj:`SDL_RWops`): A file object containing a valid font. + freesrc (int): If non-zero, the provided file object will be closed and + freed automatically when the resulting :obj:`TTF_Font` is closed (or + if an error is encountered opening the font). + ptsize (int): The size (in points) at which to open the font. + index (int): The index (from 0 to 15) of the font face to open. For + font files with only one face, this should always be 0. + hdpi (int): The horizontal resolution (in DPI) at which to open the font. + vdpi (int): The vertical resolution (in DPI) at which to open the font. + + Returns: + POINTER(:obj:`TTF_Font`): A pointer to the opened font object, or a null + pointer if there was an error. + + """ + return _funcs["TTF_OpenFontIndexDPIRW"](src, freesrc, ptsize, index, hdpi, vdpi) + +def TTF_SetFontSize(font, ptsize): + """Changes the size of a TTF font object dynamically. + + Use :func:`TTF_GetError` to check for errors. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to update. + ptsize (int): The new size (in points) to use for the font. + + Returns: + int: 0 on success, or -1 on error. + + """ + return _funcs["TTF_SetFontSize"](font, ptsize) + +def TTF_SetFontSizeDPI(font, ptsize, hdpi, vdpi): + """Changes the size and DPI of a TTF font object dynamically. + + Use :func:`TTF_GetError` to check for errors. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to update. + ptsize (int): The new size (in points) to use for the font. + hdpi (int): The new horizontal resolution (in DPI) at which to render + the font. + vdpi (int): The vertical resolution (in DPI) at which to render the + font. + + Returns: + int: 0 on success, or -1 on error. + + """ + return _funcs["TTF_SetFontSizeDPI"](font, ptsize, hdpi, vdpi) + def TTF_GetFontStyle(font): """Retrieves the current rendering style of a given font. @@ -399,17 +618,19 @@ def TTF_SetFontHinting(font, hinting): The hinting mode can be specified using one of the following constants: - ============= ======================= - Hinting type Constant - ============= ======================= - Normal ``TTF_HINTING_NORMAL`` - Light ``TTF_HINTING_LIGHT`` - Mono ``TTF_HINTING_MONO`` - None ``TTF_HINTING_NONE`` - ============= ======================= - - If no hinting mode is is explicitly set, "normal" hinting is used for - rendering. + ================ ============================== + Hinting type Constant + ================ ============================== + Normal ``TTF_HINTING_NORMAL`` + Light ``TTF_HINTING_LIGHT`` + Mono ``TTF_HINTING_MONO`` + None ``TTF_HINTING_NONE`` + Light (Subpixel) ``TTF_HINTING_LIGHT_SUBPIXEL`` + ================ ============================== + + Note that the ``TTF_HINTING_LIGHT_SUBPIXEL`` hinting mode requires SDL_ttf + 2.0.18 or newer. If no hinting mode is is explicitly set, "normal" hinting + is used for rendering. Args: font (:obj:`TTF_Font`): The font object for which the hinting style @@ -595,6 +816,17 @@ def TTF_GlyphIsProvided(font, ch): """ return _funcs["TTF_GlyphIsProvided"](font, ch) +def TTF_GlyphIsProvided32(font, ch): + """Checks whether a character is provided by a given font. + + Functionally identical to :func:`TTF_GlyphIsProvided`, except it supports + 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_GlyphIsProvided32"](font, ch) + def TTF_GlyphMetrics(font, ch, minx, maxx, miny, maxy, advance): """Gets the glyph metrics for a character with a given font. @@ -634,6 +866,17 @@ def TTF_GlyphMetrics(font, ch, minx, maxx, miny, maxy, advance): """ return _funcs["TTF_GlyphMetrics"](font, ch, minx, maxx, miny, maxy, advance) +def TTF_GlyphMetrics32(font, ch, minx, maxx, miny, maxy, advance): + """Gets the glyph metrics for a character with a given font. + + Functionally identical to :func:`TTF_GlyphMetrics`, except it supports + 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_GlyphMetrics32"](font, ch, minx, maxx, miny, maxy, advance) + def TTF_SizeText(font, text, w, h): """Calculates the size of an ASCII string rendered with a given font. @@ -710,6 +953,93 @@ def TTF_SizeUNICODE(font, text, w, h): """ return _funcs["TTF_SizeUNICODE"](font, text, w, h) +def TTF_MeasureText(font, text, measure_width, extent, count): + """Gets the number of characters that can fit within a given width. + + This function determines how many rendered characters from a given string of + ASCII text can fit within a given width. It additionally returns the + rendered width of the fitting characters. + + Use the :func:`TTF_GetError` function to check for any errors. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): An ASCII-encoded bytestring of text. + measure_width (int): The maximum width (in pixels) of the rendered + output surface. + extent (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the calculated rendered width of the first ``count`` characters of + the string. + count (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the returned number of characters from the string that can fit + within the given width. + + Returns: + int: 0 on success, or -1 on error (e.g. if a glyph is not found in + the font). + + """ + return _funcs["TTF_MeasureText"](font, text, measure_width, extent, count) + +def TTF_MeasureUTF8(font, text, measure_width, extent, count): + """Gets the number of characters that can fit within a given width. + + Identical to :func:`TTF_MeasureText`, except that this function is used with + UTF8-encoded bytestrings instead of ASCII-encoded ones. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): A UTF8-encoded bytestring of text. + measure_width (int): The maximum width (in pixels) of the rendered + output surface. + extent (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the calculated rendered width of the first ``count`` characters of + the string. + count (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the returned number of characters from the string that can fit + within the given width. + + Returns: + int: 0 on success, or -1 on error (e.g. if a glyph is not found in + the font). + + """ + return _funcs["TTF_MeasureUTF8"](font, text, measure_width, extent, count) + +def TTF_MeasureUNICODE(font, text, measure_width, extent, count): + """Gets the number of characters that can fit within a given width. + + Identical to :func:`TTF_MeasureText`, except that this function is used with + UCS-2 byte arrays instead of ASCII-encoded bytestrings. See the + :func:`TTF_RenderUNICODE_Solid` documentation for more information on + converting text to UCS-2 in Python. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (byref(:obj:`~ctypes.c_uint16`)): A ctypes array containing a UCS-2 + encoded string of text. + measure_width (int): The maximum width (in pixels) of the rendered + output surface. + extent (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the calculated rendered width of the first ``count`` characters of + the string. + count (byref(:obj:`~ctypes.c_int`)): A pointer to an integer containing + the returned number of characters from the string that can fit + within the given width. + + Returns: + int: 0 on success, or -1 on error (e.g. if a glyph is not found in + the font). + + """ + return _funcs["TTF_MeasureUNICODE"](font, text, measure_width, extent, count) + def TTF_RenderText_Solid(font, text, fg): """Renders an ASCII-encoded string to a non-antialiased 8-bit surface. @@ -807,6 +1137,73 @@ def TTF_RenderUNICODE_Solid(font, text, fg): """ return _funcs["TTF_RenderUNICODE_Solid"](font, text, fg) +def TTF_RenderText_Solid_Wrapped(font, text, fg, wrapLength): + """Renders an ASCII-encoded string to a non-antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderText_Solid`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): An ASCII-encoded bytestring of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderText_Solid_Wrapped"](font, text, fg, wrapLength) + +def TTF_RenderUTF8_Solid_Wrapped(font, text, fg, wrapLength): + """Renders a UTF8-encoded string to a non-antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderUTF8_Solid`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): A UTF8-encoded bytestring of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderUTF8_Solid_Wrapped"](font, text, fg, wrapLength) + +def TTF_RenderUNICODE_Solid_Wrapped(font, text, fg, wrapLength): + """Renders a UCS-2 encoded string to a non-antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderUNICODE_Solid`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (byref(:obj:`~ctypes.c_uint16`)): A ctypes array containing a UCS-2 + encoded string of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderUNICODE_Solid_Wrapped"](font, text, fg, wrapLength) + def TTF_RenderGlyph_Solid(font, ch, fg): """Renders a unicode character to a non-antialiased 8-bit surface. @@ -830,6 +1227,18 @@ def TTF_RenderGlyph_Solid(font, ch, fg): """ return _funcs["TTF_RenderGlyph_Solid"](font, ch, fg) +def TTF_RenderGlyph32_Solid(font, ch, fg): + """Renders a unicode character to a non-antialiased 8-bit surface. + + Functionally identical to :func:`TTF_RenderGlyph_Solid`, except it supports + 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_RenderGlyph32_Solid"](font, ch, fg) + + def TTF_RenderText_Shaded(font, text, fg, bg): """Renders an ASCII-encoded string to a solid antialiased 8-bit surface. @@ -906,6 +1315,82 @@ def TTF_RenderUNICODE_Shaded(font, text, fg, bg): """ return _funcs["TTF_RenderUNICODE_Shaded"](font, text, fg, bg) +def TTF_RenderText_Shaded_Wrapped(font, text, fg, bg, wrapLength): + """Renders an ASCII-encoded string to a solid antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderText_Shaded`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): An ASCII-encoded bytestring of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. This + becomes colormap index 1. + bg (:obj:`SDL_Color`): The background fill color for the text. This + becomes colormap index 0. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderText_Shaded_Wrapped"](font, text, fg, bg, wrapLength) + +def TTF_RenderUTF8_Shaded_Wrapped(font, text, fg, bg, wrapLength): + """Renders a UTF8-encoded string to a solid antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderUTF8_Shaded`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (byref(:obj:`~ctypes.c_uint16`)): A ctypes array containing a UCS-2 + encoded string of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. This + becomes colormap index 1. + bg (:obj:`SDL_Color`): The background fill color for the text. This + becomes colormap index 0. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderUTF8_Shaded_Wrapped"](font, text, fg, bg, wrapLength) + +def TTF_RenderUNICODE_Shaded_Wrapped(font, text, fg, bg, wrapLength): + """Renders a UCS-2 encoded string to a solid antialiased 8-bit surface. + + This function is identical to :func:`TTF_RenderUNICODE_Shaded`, except that + any lines exceeding the specified wrap length will be wrapped to fit within + the given width. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object to use. + text (bytes): A UTF8-encoded bytestring of text to render. + fg (:obj:`SDL_Color`): The color to use for rendering the text. This + becomes colormap index 1. + bg (:obj:`SDL_Color`): The background fill color for the text. This + becomes colormap index 0. + wrapLength (int): The maximum width of the output surface (in pixels) + + Returns: + POINTER(:obj:`SDL_Surface`): A pointer to the new surface containing the + rendered text, or a null pointer if there was an error. + + """ + return _funcs["TTF_RenderUNICODE_Shaded_Wrapped"](font, text, fg, bg, wrapLength) + def TTF_RenderGlyph_Shaded(font, ch, fg, bg): """Renders a unicode character to an 8-bit surface using a given font. @@ -927,6 +1412,18 @@ def TTF_RenderGlyph_Shaded(font, ch, fg, bg): """ return _funcs["TTF_RenderGlyph_Shaded"](font, ch, fg, bg) +def TTF_RenderGlyph32_Shaded(font, ch, fg, bg): + """Renders a unicode character to an 8-bit surface using a given font. + + Functionally identical to :func:`TTF_RenderGlyph_Shaded`, except it supports + 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_RenderGlyph32_Shaded"](font, ch, fg, bg) + + def TTF_RenderText_Blended(font, text, fg): """Renders an ASCII-encoded string to an antialiased 32-bit surface. @@ -1070,11 +1567,83 @@ def TTF_RenderGlyph_Blended(font, ch, fg): """ return _funcs["TTF_RenderGlyph_Blended"](font, ch, fg) +def TTF_RenderGlyph32_Blended(font, ch, fg): + """Renders a unicode character to an antialiased 32-bit surface. + + Functionally identical to :func:`TTF_RenderGlyph_Blended`, except it + supports 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_RenderGlyph32_Blended"](font, ch, fg) + TTF_RenderText = TTF_RenderText_Shaded TTF_RenderUTF8 = TTF_RenderUTF8_Shaded TTF_RenderUNICODE = TTF_RenderUNICODE_Shaded +def TTF_SetDirection(direction): + """Sets the global text direction to use when rendering. + + This function passes the direction to the underlying HarfBuzz library, + meaning that the direction must be one of the following HarfBuzz constants: + + =============== ==================== + Text Direction Constant + =============== ==================== + Left-to-right ``HB_DIRECTION_LTR`` + Right-to-left ``HB_DIRECTION_RTL`` + Top-to-bottom ``HB_DIRECTION_TTB`` + Bottom-to-top ``HB_DIRECTION_BTT`` + =============== ==================== + + If not specified, the TTF library defaults to left-to-right text + rendering. For convenience, these constants are provided by the + ``sdl2.sdlttf`` module. The direction can be set at any time. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + direction (int): A constant specifying the direction to use for text + rendering with the TTF library. + + Returns: + int: 0 on success, or -1 if HarfBuzz not available. + + """ + return _funcs["TTF_SetDirection"](direction) + +def TTF_SetScript(script): + """Sets the global script style (e.g. arabic) to use for rendering text. + + Setting the script style gives extra information to the text rendering + library about how to best shape words and characters. This can produce + better results when rendering with non-Latin fonts. + + The script is passed to the underlying HarfBuzz library, meaning that the + it needs to be specified in HarfBuzz ``hb_script_t`` format. To make this + convenient, the ``sdl2.sdlttf`` module implements HarfBuzz's :func:`HB_TAG` + macro for converting ISO 15924 character codes to HarfBuzz script:: + + arabic_script = HB_TAG('A', 'r', 'a', 'b') + TTF_SetScript(arabic_script) + + If no script has been set, the TTF library defaults to unknown ('Zzzz') + script. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + script (int): An integer specifying the script style to use for text + shaping. + + Returns: + int: 0 on success, or -1 if HarfBuzz not available. + + """ + return _funcs["TTF_SetScript"](script) + def TTF_CloseFont(font): """Closes and frees the memory associated with a given font. @@ -1126,6 +1695,8 @@ def TTF_GetFontKerningSizeGlyphs(font, previous_ch, ch): The units of kerning size returned by this function differ between fonts depending on their format and how they were designed. + `Note: Added in SDL_ttf 2.0.14` + Args: font (:obj:`TTF_Font`): The font object for which the kerning size should be retrieved. @@ -1138,5 +1709,55 @@ def TTF_GetFontKerningSizeGlyphs(font, previous_ch, ch): """ return _funcs["TTF_GetFontKerningSizeGlyphs"](font, previous_ch, ch) +def TTF_GetFontKerningSizeGlyphs32(font, previous_ch, ch): + """Gets the kerning size of two glyphs (by FreeType index) for a given font. + + Functionally identical to :func:`TTF_GetFontKerningSizeGlyphs`, except it + supports 32-bit character codes instead of just 16-bit ones. + + `Note: Added in SDL_ttf 2.0.18` + + """ + return _funcs["TTF_GetFontKerningSizeGlyphs32"](font, previous_ch, ch) + +def TTF_SetFontSDF(font, on_off): + """Enables or disables Signed Distance Field rendering for a given font. + + Requires a version of FreeType that supports SDF rendering (2.11.0 or + newer). As of SDL2_ttf 2.0.18, the FreeType version bundled with the + official binaries is too old to support SDF. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object for which SDF should be + enabled or disabled. + on_off (int): Whether to enable (``SDL_TRUE``) or disable + (``SDL_FALSE``) SDF rendering for the given font. + + Returns: + int: 0 on success, or -1 if SDF support not available. + + """ + return _funcs["TTF_SetFontSDF"](font, on_off) + +def TTF_GetFontSDF(font): + """Checks if Signed Distance Field rendering is enabled for a given font. + + If using a version of FreeType without SDF support, this will always + return 0. + + `Note: Added in SDL_ttf 2.0.18` + + Args: + font (:obj:`TTF_Font`): The font object for which SDF usage should + be checked. + + Returns: + int: 1 if the font is using SDF, otherwise 0. + + """ + return _funcs["TTF_GetFontSDF"](font) + TTF_SetError = SDL_SetError TTF_GetError = SDL_GetError diff --git a/sdl2/test/sdlttf_test.py b/sdl2/test/sdlttf_test.py index 9fa0f21c..73745da5 100644 --- a/sdl2/test/sdlttf_test.py +++ b/sdl2/test/sdlttf_test.py @@ -5,7 +5,8 @@ from struct import unpack from ctypes import byref, c_int, c_uint16 import sdl2 -from sdl2 import SDL_Init, SDL_Quit, SDL_Color, surface, version, rwops +from sdl2 import SDL_TRUE, SDL_FALSE, SDL_Color, surface, version, rwops +from sdl2.ext.compat import utf8 sdlttf = pytest.importorskip("sdl2.sdlttf") @@ -13,6 +14,13 @@ fontfile = os.path.join(parent_dir, "resources", "tuffy.ttf").encode("utf-8") font_test_sizes = [6, 16, 26] +def to_utf16(x): + # Converts a unicode Python string to a ctypes UTF-16 array + strlen = len(x) + 1 # +1 for byte-order mark + intstr = unpack('H' * strlen, utf8(x).encode('utf-16')) + intstr = intstr + (0, ) # Add null byte at end to terminate string + return (c_uint16 * (strlen + 1))(*intstr) + @pytest.fixture(scope="module") def with_sdl_ttf(with_sdl): ret = sdlttf.TTF_Init() @@ -30,7 +38,6 @@ def with_font(with_sdl_ttf): assert font yield font sdlttf.TTF_CloseFont(font) - gc.collect() def test_TTF_Font(): @@ -62,6 +69,18 @@ def test_TTF_Linked_Version(with_sdl_ttf): assert v.contents.minor == 0 assert v.contents.patch >= 12 +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GetFreeTypeVersion(with_sdl_ttf): + major, minor, patch = c_int(0), c_int(0), c_int(0) + sdlttf.TTF_GetFreeTypeVersion(byref(major), byref(minor), byref(patch)) + assert major.value > 0 # Only one guaranteed to be non-zero + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GetHarfBuzzVersion(with_sdl_ttf): + major, minor, patch = c_int(0), c_int(0), c_int(0) + sdlttf.TTF_GetHarfBuzzVersion(byref(major), byref(minor), byref(patch)) + assert major.value > 0 # Only one guaranteed to be non-zero + def test_TTF_ByteSwappedUNICODE(with_sdl_ttf): sdlttf.TTF_ByteSwappedUNICODE(0) sdlttf.TTF_ByteSwappedUNICODE(1) @@ -102,6 +121,95 @@ def test_TTF_OpenFontIndexRW(with_sdl_ttf): sdlttf.TTF_CloseFont(font) fp.close() +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_OpenFontDPI(with_sdl_ttf): + # This actually tests for DPI having an effect on the text, the other + # OpenFontDPI tests just test simple loading + w1, h1, w2, h2 = c_int(0), c_int(0), c_int(0), c_int(0) + font = sdlttf.TTF_OpenFontDPI(fontfile, 30, 80, 80) + assert sdlttf.TTF_GetError() == b"" + assert isinstance(font.contents, sdlttf.TTF_Font) + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w1), byref(h1)) + sdlttf.TTF_CloseFont(font) + font = sdlttf.TTF_OpenFontDPI(fontfile, 30, 100, 60) + assert sdlttf.TTF_GetError() == b"" + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + sdlttf.TTF_CloseFont(font) + # Make sure text size differs between DPIs + assert w2.value > w1.value + assert h2.value < h1.value + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_OpenFontIndexDPI(with_sdl_ttf): + test_dpi_sizes = [(50, 50), (80, 40), (100, 100)] + for hdpi, vdpi in test_dpi_sizes: + font = sdlttf.TTF_OpenFontIndexDPI(fontfile, 30, 0, hdpi, vdpi) + assert sdlttf.TTF_GetError() == b"" + assert isinstance(font.contents, sdlttf.TTF_Font) + sdlttf.TTF_CloseFont(font) + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_OpenFontDPIRW(with_sdl_ttf): + test_dpi_sizes = [(50, 50), (80, 40), (100, 100)] + fp = open(fontfile, "rb") + fontrw = rwops.rw_from_object(fp) + for hdpi, vdpi in test_dpi_sizes: + font = sdlttf.TTF_OpenFontDPIRW(fontrw, 0, 30, hdpi, vdpi) + assert sdlttf.TTF_GetError() == b"" + assert isinstance(font.contents, sdlttf.TTF_Font) + sdlttf.TTF_CloseFont(font) + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_OpenFontIndexDPIRW(with_sdl_ttf): + test_dpi_sizes = [(50, 50), (80, 40), (100, 100)] + fp = open(fontfile, "rb") + fontrw = rwops.rw_from_object(fp) + for hdpi, vdpi in test_dpi_sizes: + font = sdlttf.TTF_OpenFontIndexDPIRW(fontrw, 0, 30, 0, hdpi, vdpi) + assert sdlttf.TTF_GetError() == b"" + assert isinstance(font.contents, sdlttf.TTF_Font) + sdlttf.TTF_CloseFont(font) + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_SetFontSize(with_font): + font = with_font + w1, h1, w2, h2 = c_int(0), c_int(0), c_int(0), c_int(0) + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w1), byref(h1)) + # Increase font size and make sure it works (original size is 20pt) + ret = sdlttf.TTF_SetFontSize(font, 30) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + assert w2.value > w1.value + assert h2.value > h1.value + # Decrease font size and make sure it works + ret = sdlttf.TTF_SetFontSize(font, 10) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + assert w2.value < w1.value + assert h2.value < h1.value + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_SetFontSizeDPI(with_font): + font = with_font + w1, h1, w2, h2 = c_int(0), c_int(0), c_int(0), c_int(0) + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w1), byref(h1)) + # Adjust and make sure it works (original DPI is 72) + ret = sdlttf.TTF_SetFontSizeDPI(font, 20, 100, 50) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + assert w2.value > w1.value + assert h2.value < h1.value + # Resize the text with a different DPI and see if it works + ret = sdlttf.TTF_SetFontSizeDPI(font, 20, 50, 100) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + assert w2.value < w1.value + assert h2.value > h1.value + def test_TTF_GetSetFontStyle(with_font): normal = sdlttf.TTF_STYLE_NORMAL bold = sdlttf.TTF_STYLE_BOLD @@ -130,6 +238,8 @@ def test_TTF_GetSetFontHinting(with_font): sdlttf.TTF_HINTING_NORMAL, sdlttf.TTF_HINTING_LIGHT, sdlttf.TTF_HINTING_MONO, sdlttf.TTF_HINTING_NONE ] + if sdlttf.dll.version >= 2018: + hints.append(sdlttf.TTF_HINTING_LIGHT_SUBPIXEL) assert sdlttf.TTF_GetFontHinting(font) == sdlttf.TTF_HINTING_NORMAL for hint in hints: sdlttf.TTF_SetFontHinting(font, hint) @@ -205,6 +315,15 @@ def test_TTF_GlyphIsProvided(with_font): assert not sdlttf.TTF_GlyphIsProvided(font, 0) assert not sdlttf.TTF_GlyphIsProvided(font, 0x0ff9) +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GlyphIsProvided32(with_font): + font = with_font + assert isinstance(font.contents, sdlttf.TTF_Font) + for ch in range(32, 127): + assert sdlttf.TTF_GlyphIsProvided32(font, ch) + assert not sdlttf.TTF_GlyphIsProvided32(font, 0) + assert not sdlttf.TTF_GlyphIsProvided32(font, 0x0ff9) + def test_TTF_GlyphMetrics(with_sdl_ttf): expected = { 'A': [1, 25, 0, 29, 25], @@ -225,6 +344,27 @@ def test_TTF_GlyphMetrics(with_sdl_ttf): assert results == expected[char] sdlttf.TTF_CloseFont(font) +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GlyphMetrics32(with_sdl_ttf): + expected = { + 'A': [1, 25, 0, 29, 25], + 'j': [-3, 7, -9, 28, 9], + '.': [2, 7, -1, 4, 8] + } + font = sdlttf.TTF_OpenFont(fontfile, 40) + minX, maxX, minY, maxY = c_int(0), c_int(0), c_int(0), c_int(0) + adv = c_int(0) + for char in expected.keys(): + ret = sdlttf.TTF_GlyphMetrics32( + font, ord(char), + byref(minX), byref(maxX), byref(minY), byref(maxY), byref(adv) + ) + results = [x.value for x in (minX, maxX, minY, maxY, adv)] + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + assert results == expected[char] + sdlttf.TTF_CloseFont(font) + def test_TTF_SizeText(with_font): font = with_font min_expected_w = 69 # SDL2_ttf 2.0.18 @@ -258,10 +398,7 @@ def test_TTF_SizeUNICODE(with_font): min_expected_h = 21 # SDL2_ttf 2.0.15 with FreeType 2.10.1 max_expected_h = 25 # SDL2_ttf < 2.0.15 w, h = c_int(0), c_int(0) - teststr = u"Hi there!" - strlen = len(teststr) + 1 # +1 for byte-order mark - intstr = unpack('H' * strlen, teststr.encode('utf-16')) + (0, ) - strarr = (c_uint16 * (strlen + 1))(*intstr) + strarr = to_utf16(u"Hi there!") sdlttf.TTF_SizeUNICODE(font, strarr, byref(w), byref(h)) # For debug purposes #print(list(strarr)) @@ -271,6 +408,36 @@ def test_TTF_SizeUNICODE(with_font): assert h.value >= min_expected_h assert h.value <= max_expected_h +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_Measure(with_font): + font = with_font + extent, count = c_int(0), c_int(0) + w, h = c_int(0), c_int(0) + tst = b"This is a long line that should be wrapped!" + # With TTF_MeasureText + ret = sdlttf.TTF_MeasureText(font, tst, 200, byref(extent), byref(count)) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + assert all([x.value > 0 for x in (extent, count)]) + sdlttf.TTF_SizeText(font, tst[:count.value], byref(w), byref(h)) + assert extent.value == w.value + # With TTF_MeasureUTF8 + ret = sdlttf.TTF_MeasureUTF8(font, tst, 180, byref(extent), byref(count)) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + assert all([x.value > 0 for x in (extent, count)]) + sdlttf.TTF_SizeUTF8(font, tst[:count.value], byref(w), byref(h)) + assert extent.value == w.value + # With TTF_MeasureUNICODE + strarr = to_utf16(tst) + ret = sdlttf.TTF_MeasureUNICODE(font, strarr, 220, byref(extent), byref(count)) + assert sdlttf.TTF_GetError() == b"" + assert ret == 0 + assert all([x.value > 0 for x in (extent, count)]) + strarr = to_utf16(tst[:count.value]) + sdlttf.TTF_SizeUNICODE(font, strarr, byref(w), byref(h)) + assert extent.value == w.value + def test_TTF_Render_Solid(with_font): font = with_font color = SDL_Color(0, 0, 0) @@ -283,16 +450,34 @@ def test_TTF_Render_Solid(with_font): assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderUNICODE_Solid # NOTE: no unicode chars because number -> glyph lookup is os-dependent - teststr = u"Hi there!" - strlen = len(teststr) + 1 # +1 for byte-order mark - intstr = unpack('H' * strlen, teststr.encode('utf-16')) + (0, ) - strarr = (c_uint16 * (strlen + 1))(*intstr) + strarr = to_utf16(u"Hi there!") sf = sdlttf.TTF_RenderUNICODE_Solid(font, strarr, color) assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderGlyph_Solid sf = sdlttf.TTF_RenderGlyph_Solid(font, ord("A"), color) assert isinstance(sf.contents, surface.SDL_Surface) +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_Render_Solid_Wrapped(with_font): + font = with_font + color = SDL_Color(0, 0, 0, 255) + # Test TTF_RenderText_Solid_Wrapped + teststr = b"Hi there, this is a long line!" + sf = sdlttf.TTF_RenderText_Solid_Wrapped(font, teststr, color, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + # Test TTF_RenderUTF8_Solid_Wrapped + teststr = u"Hï thère, this is a long line!".encode('utf-8') + sf = sdlttf.TTF_RenderUTF8_Solid_Wrapped(font, teststr, color, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + # Test TTF_RenderUNICODE_Solid_Wrapped + # NOTE: no unicode chars because number -> glyph lookup is os-dependent + strarr = to_utf16(u"Hi there, this is a long line!") + sf = sdlttf.TTF_RenderUNICODE_Solid_Wrapped(font, strarr, color, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + def test_TTF_Render_Shaded(with_font): font = with_font color = SDL_Color(0, 0, 0) @@ -306,16 +491,35 @@ def test_TTF_Render_Shaded(with_font): assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderUNICODE_Shaded # NOTE: no unicode chars because number -> glyph lookup is os-dependent - teststr = u"Hi there!" - strlen = len(teststr) + 1 # +1 for byte-order mark - intstr = unpack('H' * strlen, teststr.encode('utf-16')) + (0, ) - strarr = (c_uint16 * (strlen + 1))(*intstr) + strarr = to_utf16(u"Hi there!") sf = sdlttf.TTF_RenderUNICODE_Shaded(font, strarr, color, bgcolor) assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderGlyph_Shaded sf = sdlttf.TTF_RenderGlyph_Shaded(font, ord("A"), color, bgcolor) assert isinstance(sf.contents, surface.SDL_Surface) +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_Render_Shaded_Wrapped(with_font): + font = with_font + color = SDL_Color(0, 0, 0, 255) + bgcolor = SDL_Color(255, 255, 255) + # Test TTF_RenderText_Shaded_Wrapped + teststr = b"Hi there, this is a long line!" + sf = sdlttf.TTF_RenderText_Shaded_Wrapped(font, teststr, color, bgcolor, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + # Test TTF_RenderUTF8_Shaded_Wrapped + teststr = u"Hï thère, this is a long line!".encode('utf-8') + sf = sdlttf.TTF_RenderUTF8_Shaded_Wrapped(font, teststr, color, bgcolor, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + # Test TTF_RenderUNICODE_Shaded_Wrapped + # NOTE: no unicode chars because number -> glyph lookup is os-dependent + strarr = to_utf16(u"Hi there, this is a long line!") + sf = sdlttf.TTF_RenderUNICODE_Shaded_Wrapped(font, strarr, color, bgcolor, 100) + assert isinstance(sf.contents, surface.SDL_Surface) + assert sf.contents.h > 30 + def test_TTF_Render_Blended(with_font): font = with_font color = SDL_Color(0, 0, 0, 255) @@ -328,10 +532,7 @@ def test_TTF_Render_Blended(with_font): assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderUNICODE_Blended # NOTE: no unicode chars because number -> glyph lookup is os-dependent - teststr = u"Hi there!" - strlen = len(teststr) + 1 # +1 for byte-order mark - intstr = unpack('H' * strlen, teststr.encode('utf-16')) + (0, ) - strarr = (c_uint16 * (strlen + 1))(*intstr) + strarr = to_utf16(u"Hi there!") sf = sdlttf.TTF_RenderUNICODE_Blended(font, strarr, color) assert isinstance(sf.contents, surface.SDL_Surface) # Test TTF_RenderGlyph_Blended @@ -353,17 +554,103 @@ def test_TTF_Render_Blended_Wrapped(with_font): assert sf.contents.h > 30 # Test TTF_RenderUNICODE_Blended_Wrapped # NOTE: no unicode chars because number -> glyph lookup is os-dependent - teststr = u"Hi there, this is a long line!" - strlen = len(teststr) + 1 # +1 for byte-order mark - intstr = unpack('H' * strlen, teststr.encode('utf-16')) + (0, ) - strarr = (c_uint16 * (strlen + 1))(*intstr) + strarr = to_utf16(u"Hi there, this is a long line!") sf = sdlttf.TTF_RenderUNICODE_Blended_Wrapped(font, strarr, color, 100) assert isinstance(sf.contents, surface.SDL_Surface) assert sf.contents.h > 30 +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_RenderGlyph32(with_font): + font = with_font + color = SDL_Color(0, 0, 0) + bgcolor = SDL_Color(255, 255, 255) + # Test TTF_RenderGlyph32_Solid + sf = sdlttf.TTF_RenderGlyph32_Solid(font, ord("A"), color) + assert isinstance(sf.contents, surface.SDL_Surface) + # Test TTF_RenderGlyph32_Shaded + sf = sdlttf.TTF_RenderGlyph32_Shaded(font, ord("A"), color, bgcolor) + assert isinstance(sf.contents, surface.SDL_Surface) + # Test TTF_RenderGlyph32_Blended + sf = sdlttf.TTF_RenderGlyph32_Blended(font, ord("A"), color) + assert isinstance(sf.contents, surface.SDL_Surface) + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_SetDirection(with_font): + font = with_font + # Define HarfBuzz direction constants + HB_DIRECTION_LTR = 4 + HB_DIRECTION_TTB = 6 + # Only available if compiled with HarfBuzz + major, minor, patch = c_int(0), c_int(0), c_int(0) + sdlttf.TTF_GetHarfBuzzVersion(byref(major), byref(minor), byref(patch)) + if major.value == 0: + pytest.skip("No HarfBuzz") + # Try setting the script direction + ret = sdlttf.TTF_SetDirection(HB_DIRECTION_LTR) + assert ret == 0 + # Try changing the script direction to see if it has any effect + w1, h1, w2, h2 = c_int(0), c_int(0), c_int(0), c_int(0) + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w1), byref(h1)) + ret = sdlttf.TTF_SetDirection(HB_DIRECTION_TTB) + assert ret == 0 + sdlttf.TTF_SizeText(font, b"Hi there!", byref(w2), byref(h2)) + sdlttf.TTF_SetDirection(HB_DIRECTION_LTR) # Reset direction + assert w1.value > w2.value + assert h1.value < h2.value + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_SetScript(with_font): + # Only available if compiled with HarfBuzz + major, minor, patch = c_int(0), c_int(0), c_int(0) + sdlttf.TTF_GetHarfBuzzVersion(byref(major), byref(minor), byref(patch)) + if major.value == 0: + pytest.skip("No HarfBuzz") + # Try setting the script language to Arabic and back + ret = sdlttf.TTF_SetScript(sdlttf.HB_TAG("A", "r", "a", "b")) + assert ret == 0 + # NOTE: I have no clue how to write a proper test to see if this worked + ret = sdlttf.TTF_SetScript(sdlttf.HB_TAG("Z", "y", "y", "y")) + assert ret == 0 + @pytest.mark.skipif(sdlttf.dll.version < 2014, reason="not available") def test_TTF_GetFontKerningSizeGlyphs(with_font): font = with_font # NOTE: Test font (tuffy) has no kerning info, so retval is always 0 sz = sdlttf.TTF_GetFontKerningSizeGlyphs(font, ord("A"), ord("B")) assert sz == 0 + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GetFontKerningSizeGlyphs32(with_font): + font = with_font + # NOTE: Test font (tuffy) has no kerning info, so retval is always 0 + sz = sdlttf.TTF_GetFontKerningSizeGlyphs32(font, ord("A"), ord("B")) + assert sz == 0 + +@pytest.mark.skipif(sdlttf.dll.version < 2018, reason="not available") +def test_TTF_GetSetFontSDF(with_font): + font = with_font + # Only available with FreeType >= 2.11.0 + major, minor, patch = c_int(0), c_int(0), c_int(0) + sdlttf.TTF_GetFreeTypeVersion(byref(major), byref(minor), byref(patch)) + if minor.value < 11: + pytest.skip("SDF not available") + # Try enabling SDF rendering on a given font + ret = sdlttf.TTF_SetFontSDF(font, SDL_TRUE) + assert ret == 0 + assert sdlttf.TTF_GetFontSDF(font) == SDL_TRUE + # Try disabling SDF rendering on a given font + ret = sdlttf.TTF_SetFontSDF(font, SDL_FALSE) + assert ret == 0 + assert sdlttf.TTF_GetFontSDF(font) == SDL_FALSE + + +def test_HB_TAG(): + test_scripts = { + "Arab": 1098015074, + "Mong": 1299148391, + "Zyyy": 1517910393, # HB_SCRIPT_COMMON + "Zzzz": 1517976186, # HB_SCRIPT_UNKNOWN + } + for script, expected in test_scripts.items(): + c1, c2, c3, c4 = script + assert sdlttf.HB_TAG(c1, c2, c3, c4) == expected