Cairo's Text Rendering

Today I want to describe how Cairo Vector Graphics renders text, which under the hood is primarily done via the cairo_scaled_font_t class.

Which itself is created by calling .get_implementation().scaled_font_create() on the passed cairo_font_face_t, surrounded by plenty of caching and error handling. Including caching of the most recently used font, because of how frequently callers like to reuse it.

One cairo_font_face_t implementation you could use is cairo_ft_font_face_t, which is backed by the FontConfig (for lookup, patterns may be converted from a cairo_toy_font_face_t) and FreeType (for reading font files).

.get_implementation(), with some caching, has FontConfig lookup & construct a FreeType font for it, which’ll then wrap in a ScaledFont and in turn a cairo_ft_font_face_t.

.scaled_font_create() creates a ScaledFont with that UnscaledFont, and copies over other properties.

On Mac OS X Cairo can wrap native “Quartz”/”Core Graphics” fonts, though it has to augment the ScaledFont implementation to do so.

cairo_toy_font_face_t basically just dispatches to a compile-time configured default.

And cairo_user_font_face_t instantiates it’s own cairo_scaled_font_t subclass both wrapping cairo_recording_surface_t to allow the caller to draw their own font to render with.

Recording Surface queues up method calls from GState in a Bounding Box Tree.

Back to cairo_ft_font_face, it’s underlying UnscaledFont class is specific to that implementation and serves as an extra layer of abstraction before making it fit in with Cairo’s generic traits*.

cairo_ft_font_face also provides it’s own cairo_scaled_font_t subclass that renders vector fonts using Cairo’s normal routines and converts raster fonts into a supported format.

That pretty much covers how glyphs are looked up from fonts in various different cases (which the cairo_scaled_font_t superclass wraps with a cache), but we need to render/process multiple glyphs to have anything meaningful.

Computing the extents involves summing widths of all the glyphs and selecting out the min/max x/y. Or more there’s approximate implementation.

There’s another method to look up and extract font a glyph’s extents.

Failing to dispatch to the subclass cairo_scaled_font_t will allocate both a glyph and clusters array before deciding whether to use a simplistic hashmap cache in the process.

The subclass is then asked to convert each individual unicode character into a “glyph”, the results of which is verified to actually be in the font. This operation will then typically be passed on directly to FreeType or CoreGraphics.

Outside of the fonts subsystem, an ImageSurface will render it by loading all the glyphs into a PixMan GlyphCache to pass to pixman_composite_glyphs[_no_mask].

And as described yesterday GState (which stores all the configurable properties used for rendering) provides it’s own APIs wrapping what I’ve just described and exposing it as public methods.