"""The font module for text rendering"""
import sdl2, sdl2.sdlttf, sdl2.ext, sdl2.sdlgfx
from importlib.resources import files
from . import Defaults, Color
[docs]class Font:
"""
This is the font object that is used to render text.
Args:
options: A Font config. Defaults to the :ref:`Font defaults <fontdef>`.
"""
def __init__(self, options: dict = {}):
param = Defaults.font_defaults | options
self._size = param["size"]
self._styles = param["styles"]
self._color = param["color"] if isinstance(param["color"], Color) else Color(*param["color"])
if param["font"] in Defaults.text_fonts:
self._font_path = str(files("rubato.static.fonts").joinpath(Defaults.text_fonts[param["font"]]))
else:
self._font_path = param["font"]
try:
self._font = sdl2.ext.FontTTF(self._font_path, self._size, self._color.to_tuple())
except ValueError as e:
raise FileNotFoundError("Font " + param["font"] + " cannot be found.") from e
self.apply_styles()
@property
def size(self) -> int:
"""The size of the text in points."""
return self._size
@size.setter
def size(self, size: int):
self._size = size
sdl2.sdlttf.TTF_SetFontSize(self._font, size)
@property
def color(self) -> Color:
"""The color of the text."""
return self._color
@color.setter
def color(self, color: Color):
self._color = color
self._font = sdl2.ext.FontTTF(self._font_path, self._size, self._color.to_tuple())
[docs] def generate_surface(
self,
text: str,
align: str = Defaults.text_defaults["anchor"],
width: int = Defaults.text_defaults["width"],
rot: int = 0
) -> sdl2.SDL_Surface:
"""
Generate the surface containing the text.
Args:
text: The text to render.
align: The alignment to use. Defaults to Vector(0, 0).
width: The maximum width to use. Defaults to -1.
rot: The rotation of the text in degrees. Defaults to 0.
Raises:
ValueError: The width is too small for the text.
ValueError: The size of the text is too large for the font.
Returns:
sdl2.SDL_Surface: The surface containing the text.
"""
try:
return sdl2.sdlgfx.rotozoomSurface(
self._font.render_text(text, width=None if width < 0 else width, align=align), rot, 1, 1
)
except RuntimeError as e:
raise ValueError(f"The width {width} is too small for the text.") from e
except OSError as e:
raise ValueError(f"The size {self._size} is too big for the text.") from e
[docs] def add_style(self, style: str):
"""
Adds a style to the font.
Args:
style: The style to add. Can be one of the following: bold, italic, underline, strikethrough.
"""
if style in Defaults.text_styles and style not in self._styles:
self._styles.append(style)
self.apply_styles()
else:
raise ValueError(f"Style {style} is not valid or is already applied.")
[docs] def remove_style(self, style: str):
"""
Removes a style from the font.
Args:
style: The style to remove. Can be one of the following: bold, italic, underline, strikethrough.
"""
if style in self._styles:
self._styles.remove(style)
self.apply_styles()
else:
raise ValueError(f"Style {style} is not currently applied.")
[docs] def apply_styles(self):
"""Applies the styles to the font."""
s = Defaults.text_styles["normal"]
for style in self._styles:
s |= Defaults.text_styles[style]
sdl2.sdlttf.TTF_SetFontStyle(self._font.get_ttf_font(), s)