SVG and text rendering
Rendering text is a common first step for labels, captions, and watermark building. libvips renders text through Pango, so you get markup (<i> italic, <span foreground=...> colour), word wrapping, and alignment. This recipe renders three text styles — basic, wrapped, and Pango markup — each composited onto a transparent 640 × 120 RGBA canvas and exported as PNG with alpha preserved.
The input is an empty transparent SVG canvas of known dimensions; it acts as a neutral base and is not composited in this recipe.
The reference output was generated with govips v2.19.0 / libvips 8.17.3. Cross-binding pixel comparison is still pending.
Basic single-line text
Section titled “Basic single-line text”One line of text at DejaVu Sans 28, rendered to RGBA and placed at the top-left of the canvas.

txt, err := vips.Text(&vips.TextParams{ Text: "VIPS Handbook", Font: "DejaVu Sans 28", DPI: 72, RGBA: true, Wrap: vips.TextWrapWord,})await sharp({ create: { width: 640, height: 120, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }) .composite([{ input: { text: { text: 'VIPS Handbook', font: 'DejaVu Sans 28', rgba: true } }, left: 24, top: 24, blend: 'over' }]) .png() .toFile('text-basic.png');canvas = pyvips.Image.black(640, 120, bands=4).cast("uchar").copy(interpretation="srgb")text = pyvips.Image.text("VIPS Handbook", font="DejaVu Sans 28", rgba=True)result = canvas.composite2(text, "over", x=24, y=24)result.write_to_file("text-basic.png")vips black canvas.png 640 120 --bands 4vips copy canvas.png rgba.png --interpretation srgbvips text txt.png "VIPS Handbook" --font 'DejaVu Sans 28' --rgbavips composite2 rgba.png txt.png 'text-basic.png' over --x 24 --y 24Wrapped text
Section titled “Wrapped text”A longer sentence wrapped to a 320 pixel width at DejaVu Sans 20. The transparent canvas keeps the output the same 640 × 120 size across bindings.

txt, err := vips.Text(&vips.TextParams{ Text: "The libvips demand-driven pipeline keeps memory flat while processing large images in production.", Font: "DejaVu Sans 20", DPI: 72, Width: 320, RGBA: true, Wrap: vips.TextWrapWord,})await sharp({ create: { width: 640, height: 120, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }) .composite([{ input: { text: { text: 'The libvips demand-driven pipeline keeps memory flat while processing large images in production.', font: 'DejaVu Sans 20', width: 320, rgba: true } }, left: 24, top: 24, blend: 'over' }]) .png() .toFile('text-wrapped.png');canvas = pyvips.Image.black(640, 120, bands=4).cast("uchar").copy(interpretation="srgb")text = pyvips.Image.text( "The libvips demand-driven pipeline keeps memory flat while processing large images in production.", font="DejaVu Sans 20", width=320, rgba=True)result = canvas.composite2(text, "over", x=24, y=24)result.write_to_file("text-wrapped.png")vips black canvas.png 640 120 --bands 4vips copy canvas.png rgba.png --interpretation srgbvips text txt.png "The libvips demand-driven pipeline keeps memory flat while processing large images in production." --font 'DejaVu Sans 20' --rgba --width 320vips composite2 rgba.png txt.png 'text-wrapped.png' over --x 24 --y 24Pango markup
Section titled “Pango markup”Pango markup mixes font weight, colour, and italic in a single string: bold violet VIPS followed by an italic Handbook.

txt, err := vips.Text(&vips.TextParams{ Text: "<span font_desc='DejaVu Sans' font_size='30000' font_weight='bold' foreground='#5d2f86'>VIPS</span> <i>Handbook</i>", Font: "DejaVu Sans", DPI: 72, RGBA: true, Wrap: vips.TextWrapWord,})await sharp({ create: { width: 640, height: 120, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }) .composite([{ input: { text: { text: "<span font_desc='DejaVu Sans' font_size='30000' font_weight='bold' foreground='#5d2f86'>VIPS</span> <i>Handbook</i>", rgba: true } }, left: 24, top: 24, blend: 'over' }]) .png() .toFile('text-pango.png');canvas = pyvips.Image.black(640, 120, bands=4).cast("uchar").copy(interpretation="srgb")text = pyvips.Image.text( "<span font_desc='DejaVu Sans' font_size='30000' font_weight='bold' foreground='#5d2f86'>VIPS</span> <i>Handbook</i>", rgba=True)result = canvas.composite2(text, "over", x=24, y=24)result.write_to_file("text-pango.png")vips black canvas.png 640 120 --bands 4vips copy canvas.png rgba.png --interpretation srgbvips text txt.png "<span font_desc='DejaVu Sans' font_size='30000' font_weight='bold' foreground='#5d2f86'>VIPS</span> <i>Handbook</i>" --rgbavips composite2 rgba.png txt.png 'text-pango.png' over --x 24 --y 24How each binding exposes text rendering
Section titled “How each binding exposes text rendering”Text rendering is the one “drawing”-like operation with a shared portable core, but the API shape differs:
- govips has a hand-written
vips.Text(*TextParams)helper that returns a standalone*ImageRef. - sharp has no standalone text method; text is rendered as an overlay via
composite([{ input: { text: {...} } }]). - pyvips exposes
pyvips.Image.text(...)directly. - vips CLI exposes
vips text.
The wrapped and Pango variants here were intentionally composited onto a fixed-size transparent canvas so the output dimensions are identical across all four bindings (640 × 120). If you render text at its natural size instead, the glyph metrics depend on the installed font set and DPI, and the output can differ between bindings.