6 Getting Started
dxtr edited this page 2026-10-18 15:49:23 +02:00

Getting Started

Requirements

cl-textrender calls three C libraries. Only the first two are needed for layout:

Library Version Used by Debian/Ubuntu package
HarfBuzz (libharfbuzz) 7.0 or newer cl-textrender, through cl-harfbuzz libharfbuzz0b
ICU (libicuuc) 52 or newer cl-textrender, through cl-icu libicu-dev, or a versioned runtime such as libicu74
FreeType (libfreetype) 2.13.1 or newer cl-textrender/raster and cl-textrender/atlas, through cl-ft2 libfreetype6

You also need fonts: any TrueType (.ttf), OpenType (.otf) or collection (.ttc, .otc) file. WOFF and WOFF2 web fonts must be converted first. The examples in this wiki use DejaVu Sans (fonts-dejavu-core).

cl-textrender runs on SBCL. See Platforms and Testing for the details.

Getting the code

cl-textrender and the libraries under it are not in Quicklisp. Fetch them from their git repositories. With stift, require cl-textrender in your project's stiftfile:

stift get https://fakehacker.cc/dxtr/cl-textrender.git

cl-textrender's own stiftfile requires the libraries under it, those of the optional systems included, so they come along as indirect entries. Start Lisp with stift exec sbcl.

Without stift, clone each repository where ASDF finds it, such as ~/common-lisp/: cl-textrender, cl-harfbuzz, cl-icu and cl-ffi, and for cl-textrender/raster, /atlas and /sdf, cl-ft2, cl-atlas and cl-sdf, all under https://fakehacker.cc/dxtr/.

Loading

(asdf:load-system :cl-textrender)          ; layout, hit testing, outlines
(asdf:load-system :cl-textrender/raster)   ; optional: CPU rasterizer
(asdf:load-system :cl-textrender/atlas)    ; optional: glyph texture atlas
(asdf:load-system :cl-textrender/sdf)      ; optional: distance field atlas

ql:quickload works the same, if Quicklisp is loaded.

The packages have no global nicknames. The examples in this wiki run in a package of their own that gives cl-textrender the local nickname tr:

(defpackage #:example
  (:use #:cl)
  (:local-nicknames (#:tr #:cl-textrender)))
(in-package #:example)

A local nickname needs its package to exist, so define the package after loading the systems it names.

A first program

Make a font, lay out a string, and walk its glyphs:

(tr:with-font (font "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
                    :size 16)
  (let ((layout (tr:layout-text "Hi there" font)))
    (tr:do-glyphs (g layout)
      (format t "glyph ~3D for ~S at ~6,2F ~6,2F~%"
              (tr:glyph-id g)
              (char (tr:layout-string layout) (tr:glyph-text-index g))
              (tr:glyph-x g)
              (tr:glyph-y g)))))
glyph  43 for #\H at   0.00  14.85
glyph  76 for #\i at  12.03  14.85
glyph   3 for #\  at  16.48  14.85
glyph  87 for #\t at  21.56  14.85
glyph  75 for #\h at  27.84  14.85
glyph  72 for #\e at  37.98  14.85
glyph  85 for #\r at  47.82  14.85
glyph  72 for #\e at  54.05  14.85

What happened:

  • with-font opened DejaVu Sans at 16 layout units per em, and destroys it when the body is left. Layout units are whatever you want them to be: pixels for UI text, world units for text in a scene.
  • layout-text shaped the string into glyphs. Without :width the text stays on one line.
  • Each glyph has the ID of the glyph to draw, the index of the character it came from (glyph-text-index), and the position of its origin on the baseline. The first line's top is at y 0 and y grows downwards, so the baseline is at the font's ascent, 14.85.

A renderer draws each glyph from its font, ID and position. Which way depends on the renderer:

You want to... Read
draw from a texture of rasterized glyphs Glyph Atlas
draw from distance fields, with outlines, glows and shadows Distance Field Atlas
draw from outlines (Slug, SVG, PDF, meshes) Glyph Outlines
make an image on the CPU Raster Backend

Next