from __future__ import annotations
from threading import RLock
from scistag.common import get_edp
from scistag.filestag import FileStag
from scistag.imagestag.font import Font
[docs]class RegisteredFont:
"""
A registered font contains information about a single available font and
it's style variations.
Upon request it can be used to create a real font handle with the specified
properties such as weight and style.
"""
def __init__(self, font_face: str, base_path: str,
variations: list[tuple[str, set[str]]]):
"""
:param font_face: The font's face name, e.g. Roboto
:param base_path: Base file name without extension,
e.g. /home/user/myProject/fonts/Roboto
:param variations: The single font variations. The flags (e.g. "Bold")
as string and the file name extension,
e.g. "-Bold")
"""
self.font_face = font_face
"The font face's name"
self.base_path = base_path
"The filename base path (excluding variations)"
self.extension = ".ttf"
"The file extension"
self.variable_weight = 'wght' in variations[0][0]
"Has the font a totally flexible weight?"
self.variations = variations
"Different main variations, e.g. Italic"
[docs] def get_handle(self, size: int, flags: set[str] | None = None) -> \
Font | None:
"""
Tries to create a font handle for given font
:param size: The font's size
:param flags: The flags such as {'Bold'} or {'Bold', 'Italic'}
:return: On success the handle of the font
"""
if flags is None:
flags = set()
from scistag.imagestag.font import Font, ImsFramework
for variation in self.variations:
if flags == variation[1]:
full_name = self.base_path + variation[0] + self.extension
data = FileStag.load(full_name)
font = Font(source=data, size=size, framework=ImsFramework.PIL)
return font
return None
[docs]class FontRegistry:
"""
Manages all available fonts which can be used from (especially) ImageStag
and SlideStag.
"""
access_lock = RLock()
"Multi-thread access lock"
_base_fonts_registered = False
"Defines if the base fonts were configured already"
fonts = {}
"Dictionary of registered fonts"
[docs] @classmethod
def register_font(cls, font_face: str, base_path: str,
variations: list[tuple[str, set[str]]]):
"""
Registers a single font. See _register_base_fonts for examples.
:param font_face: The font's face name, e.g. Roboto
:param base_path: Base file name without extension,
e.g. /home/user/myProject/fonts/Roboto
:param variations: The single font variations. The flags (e.g. "Bold")
as string and the file name extension,
e.g. "-Bold")
"""
if not cls._base_fonts_registered:
cls._ensure_setup()
with cls.access_lock:
if font_face in cls.fonts:
raise ValueError("Font was already registered")
cls.fonts[font_face] = RegisteredFont(font_face=font_face,
base_path=base_path,
variations=variations)
[docs] @classmethod
def get_font(cls, font_face: str, size: int,
flags: set[str] | None = None) -> Font | None:
"""
Tries to create a font handle for given font
:param font_face: The font's face
:param size: The font's size
:param flags: The flags such as {'Bold'} or {'Bold', 'Italic'}
:return: On success the handle of the font
"""
if not cls._base_fonts_registered:
cls._ensure_setup()
reg_font = None
with cls.access_lock:
if font_face in cls.fonts:
reg_font = cls.fonts[font_face]
if reg_font is None:
return None
return reg_font.get_handle(size, flags)
[docs] @classmethod
def get_fonts(cls):
"""
Returns a list of all fonts
:return: A list of all registered fonts and their variations
"""
with cls.access_lock:
import copy
return copy.deepcopy(cls.fonts)
[docs] @classmethod
def _ensure_setup(cls):
"""
Ensures the standard fonts were set up
"""
with cls.access_lock:
if not cls._base_fonts_registered:
cls._base_fonts_registered = True
cls._register_base_fonts()
[docs] @classmethod
def _register_base_fonts(cls):
"""
Registers the standard fonts which shipped with SciStag
"""
edp = get_edp()
FontRegistry.register_font(font_face="Roboto",
base_path=edp + "fonts/Roboto/Roboto",
variations=[('-Regular', set()),
('-Italic', {'Italic'}),
('-Black', {'Black'}),
('-Bold', {'Bold'}),
('-Medium', {'Medium'}),
('-Light', {'Light'}),
('-Thin', {'Thin'}),
('-BlackItalic',
{'Black', 'Italic'}),
('-BoldItalic',
{'Bold', 'Italic'}),
('-MediumItalic',
{'Medium', 'Italic'}),
('-LightItalic',
{'Light', 'Italic'}),
('-ThinItalic',
{'Thin', 'Italic'})])
FontRegistry.register_font(font_face="Roboto Flex",
base_path=edp + \
"fonts/RobotoFlex/RobotoFlex[GRAD,XOPQ,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght].ttf",
variations=[('', set()),
('-Italic', {'Italic'})])
FontRegistry.register_font(font_face="JetBrains Mono",
base_path=edp + "fonts/JetBrains Mono/JetBrainsMono",
variations=[('[wght]', set()),
('-Italic[wght]', {'Italic'})])