Getting started
This tour uses DejaVu Sans (fonts-dejavu-core on Debian and Ubuntu). Every result shown is real output from FreeType 2.14.3.
(asdf:load-system :cl-ft2)
(defpackage #:example
(:use #:cl)
(:local-nicknames (#:ft2 #:cl-ft2)))
(in-package #:example)
(defparameter *font* "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf")
The examples in this wiki call cl-ft2 by the local nickname ft2, given in a package's :local-nicknames as here. cl-ft2 defines no global nickname.
1. A library and a face
All FreeType work happens inside a library. A face is one typeface from a font file:
(defparameter *lib* (ft2:make-library))
(defparameter *face* (ft2:open-face *lib* *font*))
*face* ; => #<FACE "DejaVu Sans" "Book" {…}>
(ft2:face-num-glyphs *face*) ; => 6253
(ft2:face-units-per-em *face*) ; => 2048
(ft2:face-flags *face*)
;; => (:SCALABLE :SFNT :HORIZONTAL :KERNING :GLYPH-NAMES :HINTER)
In real code, prefer with-library and with-face. They release the objects when the body exits, even through a non-local exit:
(ft2:with-library (lib)
(ft2:with-face (face lib *font*)
…))
2. Choose a size
A face has no size until you give it one. Set it in pixels per em:
(setf (ft2:face-size *face*) 32)
(ft2:face-size-metrics *face*)
;; => #S(SIZE-METRICS :X-PPEM 32 :Y-PPEM 32 :X-SCALE 1/64 :Y-SCALE 1/64
;; :ASCENDER 30 :DESCENDER -8 :HEIGHT 37 :MAX-ADVANCE 60)
For a size in points, convert: 12 pt at 96 dpi is (setf (ft2:face-size *face*) (* 12 96/72)), 16 pixels per em. Sizes may be fractional.
3. Render a character
char-bitmap loads, renders and copies the glyph in one call:
(setf (ft2:face-size *face*) 8)
(ft2:char-bitmap *face* #\A)
;; => #S(BITMAP :WIDTH 6 :HEIGHT 6 :PIXEL-MODE :GRAY :NUM-GRAYS 256 :LEFT 0 :TOP 6
;; :DATA #2A((0 1 223 94 0 0)
;; (0 67 148 183 0 0)
;; (0 163 45 177 30 0)
;; (12 204 0 92 122 0)
;; (97 255 255 255 217 0)
;; (189 42 0 0 172 56)))
data is indexed (row column), with the top row first. left and top place the bitmap relative to the pen position on the baseline. Here the bitmap starts 0 pixels right of the pen, and its top row is 6 pixels above the baseline. For a 1-bit bitmap, pass :mode :mono:
(ft2:bitmap-data (ft2:char-bitmap *face* #\A :mode :mono))
;; => #2A((0 0 1 0 0) (0 0 1 0 0) (0 1 0 1 0) (0 1 0 1 0) (0 1 1 1 0) (1 0 0 0 1))
4. Load a glyph and inspect it
Each face has a single glyph slot. load-char and load-glyph load a glyph into it, and the glyph-… readers report on the glyph in the slot:
(setf (ft2:face-size *face*) 32)
(ft2:load-char *face* #\A) ; => 36, the glyph index
(ft2:glyph-format *face*) ; => :OUTLINE
(ft2:glyph-metrics *face*)
;; => #S(GLYPH-METRICS :WIDTH 22 :HEIGHT 23 :HORI-BEARING-X 0 :HORI-BEARING-Y 23
;; :HORI-ADVANCE 22 :VERT-BEARING-X -11 :VERT-BEARING-Y 4
;; :VERT-ADVANCE 32)
(ft2:glyph-advance *face*) ; => 22, 0 (hinted)
(ft2:glyph-linear-advance *face*) ; => 1401/64, 32 (unhinted: 21.890625 px)
Distances are exact rationals in pixels. FreeType's 26.6 fixed point (1/64 pixel) becomes 1401/64 rather than a float. Whole-pixel values come back as integers.
5. Outlines
Before it is rendered, a glyph from a scalable font is a vector outline:
(ft2:load-char *face* #\l)
(ft2:glyph-outline *face*)
;; => (((:MOVE-TO 193/64 24) (:LINE-TO 377/64 24) (:LINE-TO 377/64 0)
;; (:LINE-TO 193/64 0) (:LINE-TO 193/64 24)))
The result is a list of contours, and each contour is a list of segments. Curved glyphs use :conic-to (quadratic) and :cubic-to segments. See Outlines.
6. Kerning
(ft2:kerning *face* (ft2:char-index *face* #\A) (ft2:char-index *face* #\V))
;; => -2, 0
This reads only the legacy TrueType kern table. Real text layout, with GPOS kerning, ligatures and complex scripts, needs a shaper such as HarfBuzz. See Scope and limitations.
7. Clean up
(ft2:destroy-library *lib*) ; also closes *face* and anything else made in *lib*
After that, using *face* signals a freetype-error named :invalid-face-handle. It never touches freed memory:
(ft2:face-family-name *face*)
;; error: The face has been destroyed, by DESTROY-FACE or by DESTROY-LIBRARY on its library.
;; Open the font again with OPEN-FACE, and keep the face open while
;; you use it.
Where next
- Core concepts explains units, coordinate directions and lifetimes in detail.
- Cookbook shows how to draw a whole line of text into a buffer, build a glyph atlas, and draw outlined text.