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.

class tituli.render.PillowEngine[source]

The zero-extra-dependency engine.

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.

duration is the whole clip; the last reveal completes at layout.duration_hint and 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".

tituli.render.render(layout: Layout, frame: Frame, *, t: float | None = None, engine: Engine | None = None) Image[source]

Render onto the frame: RGBA overlay if it has no background, else RGB.

engine is the rasteriser seam (default Pillow; see tituli.shaping).

tituli.render.render_overlay(layout: Layout, size: tuple[int, int], *, t: float | None = None, engine: Engine | None = None) Image[source]

The layout on a transparent canvas of size (RGBA).