Changelog¶
1.0¶
Architecture and breaking changes¶
ManimPango 1.0 is a hard break from the 0.6.x API: callers should migrate
rather than expect a compatibility layer. This section summarizes the design
surface for review; the API Reference is the authoritative API contract.
The redesigned implementation was first made available for testing in
1.0.0a3.
The public API separates user-facing validation and immutable result models from the native Pango and Cairo implementation. Rendering follows one layout pipeline:
render()accepts plain text and optionalTextSpanobjects. Spans are normalized into Pango attributes for one layout, preserving shaping across styled ranges.render_markup()accepts raw Pango markup. Markup is validated before it enters the same layout and SVG-rendering pipeline.Both functions return
RenderedText, an immutable object containing the SVG and layout metadata in SVG user-space units.register_font()returns a reference-countedFontRegistrationhandle. The native backend registers the font and the renderer updates its visible font map when registration state changes.
This division keeps generic text rendering in ManimPango while leaving Manim-specific text selection and object integration to Manim.
Public API.
render() accepts plain text only. It has keyword-only options and returns
an in-memory result instead of requiring an output path. Use
render_markup() for raw Pango markup; structured spans and markup cannot
be mixed in one call.
TextSpan offsets are half-open Python code-point ranges, just like string
slices. Overlapping spans compose when they set different attributes;
conflicting values for the same attribute raise ValueError rather than
depending on span order.
All public geometry is represented by float values in SVG user-space
units. RenderedText includes SVG text, overall dimensions and baseline,
ink and logical bounds, and per-line text, ranges, bounds, and baselines.
Custom fonts have explicit ownership. A registration remains active until its handle is closed; use the handle as a context manager for scoped registration. Repeated registrations of the same normalized path are reference-counted.
Breaking changes from 0.6.x.
0.6.x API or pattern |
1.0 replacement |
|---|---|
|
|
|
|
|
|
Mutable |
Immutable |
|
|
|
RenderedText.save(path) is the explicit opt-in for writing SVG. It writes
UTF-8 content to an existing parent directory and propagates filesystem errors.
Scope.
Version 1.0 intentionally does not provide:
a compatibility facade for the 0.6.x utility classes and path-based font unregistration;
rendering backends other than SVG;
a Manim
TextorMarkupTextintegration layer; orLinux binary wheels. Linux users continue to install from source with the required native build dependencies.
These boundaries keep the package focused on a small, testable SVG text rendering interface. Future work can build on that interface without changing its core contracts.
Migration¶
render() accepts plain text only. Use render_markup() for raw Pango
markup; plain and markup rendering are separate entry points. Use
TextSpan objects with render() for range styling.
# Plain text with structured styling.
result = manimpango.render(
"Hello world",
spans=(
manimpango.TextSpan(0, 5, foreground="#3366cc"),
manimpango.TextSpan(6, 11, style=manimpango.Style.ITALIC),
),
)
# Raw Pango markup uses a different entry point.
marked = manimpango.render_markup("<b>Hello</b> <i>world</i>")
TextSpan.start and TextSpan.end are half-open Python code-point
offsets. Overlapping spans compose when they affect different attributes;
conflicting values for the same attribute raise ValueError.
Both rendering functions return a frozen, slotted
RenderedText. Geometry is always expressed in SVG
user-space units, not pixels or raw Pango units. The former synthetic
line-spacing metadata value and mutable underscore-backed fields have been
removed; line_spacing= remains a Pango layout multiplier.
result = manimpango.render("one\ntwo", width=200.0)
print(result.width, result.height, result.baseline)
print(result.ink_bounds, result.logical_bounds)
for line in result.lines:
# Ranges exclude newline separators.
print(line.text, line.start, line.end, line.bounds, line.baseline)
result.lines is a tuple of LineInfo instances.
For markup, line ranges refer to parsed text rather than the markup source.
Both layout bounds and per-line bounds use the same transformed coordinate
system as the SVG.
RenderedText.save(path) writes UTF-8 SVG to path. The parent
directory must already exist; missing parents raise FileNotFoundError.
Custom-font registration has explicit ownership:
with manimpango.register_font("path/to/font.ttf") as registration:
result = manimpango.render("Example", font="My Font")
assert registration.closed
Missing files raise FontNotFoundError; other backend
registration failures raise FontRegistrationError.