Files
Templated-PPT-Master/scripts/component_typography.py
T

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)