tituli.render
Rasterise a Layout with Pillow.
The default engine is Pillow alone: un-rotated runs go straight through
ImageDraw.text (FreeType under the hood); rotated runs are drawn into a tile
and rotated about their baseline origin with bicubic resampling. That is what
lets a calligram exist with no dependency beyond Pillow. The optional
tituli.shaping tier swaps in HarfBuzz shaping + FreeType outline transforms
for scripts and ligatures Pillow’s basic layout cannot do — it is the engine=
seam of render().
Output is always a full-frame image: an RGBA overlay when the frame has no background (compose it onto video later — never into the stills, or a camera move would drag the lettering along with the picture), or an RGB composite when the frame carries a colour or a picture.
>>> from tituli.frame import Frame
>>> from tituli.layout import block
>>> from tituli.style import TITLE
>>> img = render(block("Hi", TITLE, 1080), Frame.blank((640, 360)))
>>> img.mode, img.size
('RGBA', (640, 360))
- class tituli.render.Engine(*args, **kwargs)[source]
Draws one run onto an RGBA canvas at a given opacity.
- tituli.render.draw_plate(canvas: Image, plate: Plate) None[source]
Composite one plate (scrim, box or rule) onto the canvas.
- tituli.render.frames(layout: Layout, frame: Frame, *, duration: float, fps: float = 30.0, engine: Engine | None = None, hold_after: float = 0.0) Iterator[Image][source]
Yield one image per video frame, honouring the runs’ reveal envelopes.
durationis the whole clip; the last reveal completes atlayout.duration_hintand the layout then holds. Frames are rendered lazily so a long clip never sits in memory at once.
- tituli.render.make_engine(name: str = 'pillow') Engine[source]
Resolve an engine by name:
"pillow"(default) or"harfbuzz".