from __future__ import annotations import base64 from io import BytesIO from dataclasses import dataclass from functools import lru_cache from importlib.resources import files from pathlib import Path @dataclass(frozen=True, slots=True) class FontFace: family: str weight: int italic: bool filename: str @property def path(self) -> Path: return Path(str(files("editable_image").joinpath("fonts", self.filename))) FACES = ( FontFace("Lato", 400, False, "Lato-Regular.ttf"), FontFace("Lato", 400, True, "Lato-Italic.ttf"), FontFace("Lato", 700, False, "Lato-Bold.ttf"), FontFace("Lato", 700, True, "Lato-BoldItalic.ttf"), FontFace("Liberation Sans", 400, False, "LiberationSans-Regular.ttf"), FontFace("Liberation Sans", 400, True, "LiberationSans-Italic.ttf"), FontFace("Liberation Sans", 700, False, "LiberationSans-Bold.ttf"), FontFace("Liberation Sans", 700, True, "LiberationSans-BoldItalic.ttf"), FontFace("Liberation Serif", 400, False, "LiberationSerif-Regular.ttf"), FontFace("Liberation Serif", 400, True, "LiberationSerif-Italic.ttf"), FontFace("Liberation Serif", 700, False, "LiberationSerif-Bold.ttf"), FontFace("Liberation Serif", 700, True, "LiberationSerif-BoldItalic.ttf"), FontFace("Liberation Mono", 400, False, "LiberationMono-Regular.ttf"), FontFace("Liberation Mono", 400, True, "LiberationMono-Italic.ttf"), FontFace("Liberation Mono", 700, False, "LiberationMono-Bold.ttf"), FontFace("Liberation Mono", 700, True, "LiberationMono-BoldItalic.ttf"), ) def families() -> list[str]: return list(dict.fromkeys(face.family for face in FACES if face.path.exists())) def get_face(family: str, weight: int, italic: bool) -> FontFace: candidates = [face for face in FACES if face.family == family and face.path.exists()] if not candidates: candidates = [face for face in FACES if face.path.exists()] if not candidates: raise FileNotFoundError("No bundled fonts are installed") return min( candidates, key=lambda face: abs(face.weight - weight) + (1000 if face.italic != italic else 0), ) @lru_cache(maxsize=32) def font_data_uri(filename: str) -> str: path = Path(str(files("editable_image").joinpath("fonts", filename))) from fontTools.ttLib import TTFont font = TTFont(path) font.flavor = "woff2" output = BytesIO() font.save(output) data = base64.b64encode(output.getvalue()).decode("ascii") return f"data:font/woff2;base64,{data}"