Typography
The pydreamplet.typography module resolves installed font files and measures
text with HarfBuzz shaping plus fontTools line metrics. Use it when generated
layout depends on text dimensions.
Typography utilities are imported from pydreamplet.typography.
from pydreamplet.typography import TypographyMeasurer, get_system_font_path
Visual Example
This example measures a Text element and draws the measured box behind it. Use
an explicit font_path when reproducible dimensions matter, because system font
lookup depends on the machine where the code runs.
import pydreamplet as dp
from pydreamplet.typography import TypographyMeasurer, get_system_font_path
font_path = get_system_font_path("Arial", 400)
if font_path is None:
raise RuntimeError("Arial is not available on this system.")
label = dp.Text(
"pyDreamplet",
x=32,
y=78,
font_family="Arial",
font_size=32,
font_weight=400,
fill="currentColor",
)
measurer = TypographyMeasurer(font_path=font_path)
width, height = measurer.measure_text(label)
svg = dp.SVG(300, 140)
svg.append(
dp.Rect(
x=label.x,
y=label.y - height * 0.78,
width=width,
height=height,
fill="none",
stroke="#14b8a6",
stroke_width=2,
stroke_dasharray="5 4",
),
dp.Line(label.x, label.y, label.x + width, label.y, stroke="#f83898", stroke_width=2),
label,
dp.Text(f"{width:.1f} x {height:.1f}", x=32, y=116, font_size=12, fill="currentColor"),
)
get_system_font_path
get_system_font_path(
font_family: str,
weight: int = 400,
weight_tolerance: int = 100,
) -> str | None
Searches common system font directories for a .ttf or .otf file whose name
records contain font_family. If the font has an OS/2 table, its
usWeightClass must be within weight_tolerance of weight.
font_path = get_system_font_path("Arial", 700)
if font_path is None:
print("Font not found")
else:
print(font_path)
The lookup is intentionally environment-dependent. For stable output in a
project or CI job, pass a known font file path to TypographyMeasurer.
TypographyMeasurer
TypographyMeasurer(dpi: float = 72.0, font_path: str | None = None)
dpi controls point-to-pixel conversion. At the default 72.0, one point maps
to one pixel. font_path can be set once so later measurements do not need a
system font lookup.
measurer = TypographyMeasurer(font_path="assets/fonts/Inter-Regular.ttf")
measure_text
measure_text(
text: str | TextElementLike,
*,
font_family: str | None = None,
weight: int | None = None,
font_size: Real | str | None = None,
) -> tuple[float, float]
Measures shaped text width and line-metric height in pixels. text can be a
plain string or an element with a string content property, such as Text.
For plain strings, provide font_family and weight unless the measurer already
has font_path.
measurer = TypographyMeasurer()
width, height = measurer.measure_text(
"Hello\nWorld",
font_family="Arial",
weight=400,
font_size=16,
)
For Text elements, measure_text() reads content, font_family,
font_weight, and font_size from the element when those values are not passed
explicitly. font_size may be numeric or a string with a leading number, such as
"16px" or "10.5pt".
label = dp.Text("Measured label")
label.font_family = "Arial"
label.font_size = "16px"
label.font_weight = 400
width, height = TypographyMeasurer().measure_text(label)
Multiline text uses the widest shaped line and one line height per line.
single_width, single_height = measurer.measure_text(
"Line",
font_family="Arial",
weight=400,
font_size=16,
)
multi_width, multi_height = measurer.measure_text(
"Line\nLine",
font_family="Arial",
weight=400,
font_size=16,
)
assert multi_width == single_width
assert multi_height == single_height * 2
measure_text() raises ValueError when no font path can be resolved and
raises TypeError when the input is neither a string nor a text-like element.