Source code for neoscore.core.font

from __future__ import annotations

from typing import Optional, Union

from neoscore.core.rect import Rect
from neoscore.core.units import Unit
from neoscore.interface.font_interface import FontInterface

_BOUNDING_RECT_CACHE: Dict[Tuple[Font, str], Rect] = {}


[docs]class Font: """A text font. All fonts are immutable. To get a modified version of a font, use :obj:`.Font.modified` """
[docs] def __init__( self, family_name: str, size: Union[Unit, float], weight: Optional[int] = None, italic: bool = False, ): """ Args: family_name: The font family name size: The size (height) of the font weight: The font weight on a 0-100 scale, where 50 is normal, lower numbers are lighter, and higher are darker. If ``None`` (the default), a normal weight will be used. italic: Whether the font is italicized """ self._family_name = family_name self._size = size if isinstance(size, Unit) else Unit(size) self._weight = weight self._italic = italic self._interface = FontInterface( self.family_name, self.size, self.weight, self.italic )
@property def family_name(self) -> str: return self._family_name @property def size(self) -> Unit: return self._size @property def weight(self) -> Optional[int]: return self._weight @property def italic(self) -> bool: return self._italic @property def ascent(self) -> Unit: """The ascent of the font. The ascent is the vertical distance between the font baseline and the highest any font characters reach. """ return self._interface.ascent @property def descent(self) -> Unit: """The descent of the font. The ascent is the vertical distance between the font baseline and the lowest any font characters reach. """ return self._interface.descent @property def x_height(self) -> Unit: """The x-height for the font.""" return self._interface.x_height @property def interface(self) -> FontInterface: """The backing low-level font interface""" return self._interface def __str__(self): return f"Font('{self.family_name}', {self.size}, {self.weight}, {self.italic})" def __eq__(self, other): return ( isinstance(other, Font) and self.family_name == other.family_name and self.size == other.size and self.weight == other.weight and self.italic == other.italic ) def __hash__(self): return hash( (self.family_name, self.size.rounded_base_value, self.weight, self.italic) )
[docs] def modified( self, family_name: Optional[str] = None, size: Optional[Union[Unit, float]] = None, weight: Optional[int] = None, italic: Optional[bool] = None, ) -> Font: """Derive a font from this one. All properties not specified will be taken from the existing font. """ return Font( family_name if family_name is not None else self.family_name, size if size is not None else self.size, weight if weight is not None else self.weight, italic if italic is not None else self.italic, )
[docs] def bounding_rect_of(self, string: str) -> Rect: """Approximate the bounding rect of a string in this font.""" key = (self, string) cached_rect = _BOUNDING_RECT_CACHE.get(key) if cached_rect: return cached_rect rect = self._interface.bounding_rect_of(string) _BOUNDING_RECT_CACHE[key] = rect return rect