53 lines
2.0 KiB
Python
53 lines
2.0 KiB
Python
"""Resolve template text roles once for reuse within a component render."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import math
|
|
|
|
import template_ref as ref
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TextRole:
|
|
family: str
|
|
size: float
|
|
weight: int
|
|
line_height: float
|
|
|
|
@property
|
|
def svg_weight(self):
|
|
return "normal" if self.weight == 400 else "bold" if self.weight == 700 else str(self.weight)
|
|
|
|
@property
|
|
def bold(self):
|
|
return self.weight >= 600
|
|
|
|
@property
|
|
def line_size(self):
|
|
return self.size * self.line_height
|
|
|
|
|
|
def resolve_role(style: dict, name: str) -> TextRole:
|
|
typography = style["typography"]
|
|
ref.require(name in typography["roles"], f"unknown typography role: {name}")
|
|
role = typography["roles"][name]
|
|
family = typography["families"][role["family_role"]]
|
|
face = family["family"]
|
|
ref.require(isinstance(face, str) and bool(face.strip()), f"{name}: font family required")
|
|
ratio, points = style["canvas"]["pt_to_px"], role["size_pt"]
|
|
line_height = role.get("line_height", 1.25)
|
|
for value, label in ((ratio, "pt_to_px"), (points, "size_pt"), (line_height, "line_height")):
|
|
ref.require(type(value) in (int, float) and math.isfinite(value) and value > 0,
|
|
f"{name}: positive finite {label} required")
|
|
# A role can refine its family weight without losing the family's defaults.
|
|
raw = role.get("weight_number", role.get("weight", family.get("weight_number", family.get("weight", "Regular"))))
|
|
if isinstance(raw, str):
|
|
weights = {"thin": 100, "extralight": 200, "light": 300, "regular": 400,
|
|
"normal": 400, "medium": 500, "semibold": 600, "bold": 700,
|
|
"extrabold": 800, "black": 900}
|
|
raw = weights.get(raw.lower().replace(" ", "").replace("-", ""), raw)
|
|
if isinstance(raw, str) and raw.isdigit():
|
|
raw = int(raw)
|
|
ref.require(type(raw) is int and 1 <= raw <= 1000, f"{name}: invalid font weight")
|
|
return TextRole(face, points * ratio, raw, line_height)
|