What is HarfBuzz?
HarfBuzz is the industry-standard text shaping engine. It takes a sequence of Unicode codepoints and a font, and produces a sequence of glyph IDs with precise placement offsets ready for a renderer such as FreeType or Cairo. Without shaping, rendered text would be broken for any script beyond basic ASCII.
HarfBuzz handles the full complexity of international typography:
ligatures (fi fl ff ffi ffl collapse to single glyphs),
kerning (adjust spacing between specific pairs like AV),
Arabic and Hebrew right-to-left and contextual letter forms,
Devanagari conjunct consonants and vowel mark attachment,
CJK ideographic spacing and vertical text modes,
bidirectional (BiDi) mixed LTR+RTL paragraphs,
mark placement (diacritics, combining characters), and the full
OpenType GSUB/GPOS glyph substitution and positioning pipeline.
HarfBuzz is used inside Chrome, Firefox, Android, iOS (CoreText), LibreOffice, Pango, Qt, GNOME, WebKit, and Inkscape — essentially every modern application that renders text on Linux, Android, or the web.
Why Shaping Matters
The two codepoints
f + i may map to a single ligature glyph
(fi) in the font. An Arabic letter has four different glyph forms depending on whether
it appears at the start, middle, end, or in isolation within a word. Devanagari consonant clusters
require conjunct forms that are completely different glyphs from their component letters.
HarfBuzz consults the font’s OpenType lookup tables and figures out the correct glyph
sequence so the renderer only needs to paint pixels.
Typical Pipeline
HarfBuzz outputs glyph IDs and advance/offset values in font units. FreeType uses those glyph IDs to rasterize bitmaps at the requested point size. Cairo or SDL2 composites the bitmaps at the positions HarfBuzz provided.
Types
| Type | Description |
|---|---|
| THBBuffer | Opaque handle to a shaping buffer. Holds input text and, after Shape, the output glyph sequence. |
| THBFont | HarfBuzz font object — wraps a FreeType face at a given scale. |
| THBFace | Represents a font file plus face index (for TTC collections with multiple faces). |
| THBDirection | Enum: hdInvalid=0, hdLTR=4, hdRTL=5, hdTTB=6 (top-to-bottom), hdBTT=7 (bottom-to-top). |
| THBScript | Enum of OpenType script tags: hsLatin, hsArabic, hsDevanagari, hsCyrillic, hsCJK, hsHebrew, hsThai, hsGreek. |
| THBGlyphInfo | Record: GlyphIndex: Integer (glyph ID in font) + Cluster: Integer (index of first Unicode codepoint that produced this glyph). |
| THBGlyphPos | Record: XAdvance, YAdvance (pen advance) + XOffset, YOffset (placement offset). All in font units (26.6 fixed-point). |
| THBShapeResult | Record: GlyphCount: Integer + Glyphs: array of THBGlyphInfo + Positions: array of THBGlyphPos. |
| THBFeature | Record: Tag: string (4-char OpenType tag, e.g. 'liga') + Value: Integer (0=off, 1=on) + Start/Stop character range (−1 = all). |
API Reference
Face & Font
FaceIndex=0 for single-face fonts.Buffer
ClearBuffer to avoid allocation overhead.GuessProperties.GuessProperties.'en', 'ar', 'zh-Hans'). Affects language-specific OpenType features.Shaping
GetShapeResult returns the glyph sequence.ParseFeature('liga=0') to disable ligatures, 'smcp=1' for small caps, etc.Shape or ShapeEx. Returns glyph IDs and advance/offset positions.Convenience
OpenType Features
'liga=1', 'kern=0', 'smcp', or 'liga[2:5]=0' (range). Returns a THBFeature for use with ShapeEx.'liga', 'kern', 'smcp', 'onum'). Useful for building feature UIs.Info
'8.3.0'.Shape English Text
uses harfbuzz;
var
Face : THBFace;
Font : THBFont;
Buf : THBBuffer;
Res : THBShapeResult;
I : Integer;
begin
Face := NewFaceFromFile('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 0);
Font := NewFont(Face);
SetFontScale(Font, 32 * 64, 32 * 64); { 32 px in 26.6 fixed-point format }
Buf := NewBuffer;
AddText(Buf, 'Hello, world!');
GuessProperties(Buf); { auto-detect: LTR + Latin }
Shape(Font, Buf);
Res := GetShapeResult(Buf);
for I := 0 to Res.GlyphCount - 1 do
WriteLn('Glyph ', Res.Glyphs[I].GlyphIndex,
' advance ', Res.Positions[I].XAdvance);
FreeBuffer(Buf);
FreeFont(Font);
FreeFace(Face);
end.
Disable Ligatures with OpenType Features
By default HarfBuzz applies all active features in the font, including liga
(standard ligatures) and kern. Use ShapeEx to override specific features:
uses harfbuzz;
var
Face : THBFace;
Font : THBFont;
Buf : THBBuffer;
Res : THBShapeResult;
NoLiga : THBFeature;
begin
Face := NewFaceFromFile('/usr/share/fonts/opentype/eb-garamond/EBGaramond-Regular.otf', 0);
Font := NewFont(Face);
SetFontScale(Font, 18 * 64, 18 * 64);
Buf := NewBuffer;
AddText(Buf, 'difficult office affairs');
GuessProperties(Buf);
{ Disable standard ligatures: 'fi', 'fl', 'ff' will remain separate glyphs }
NoLiga := ParseFeature('liga=0');
ShapeEx(Font, Buf, [NoLiga]);
Res := GetShapeResult(Buf);
WriteLn('Glyph count: ', Res.GlyphCount);
FreeBuffer(Buf);
FreeFont(Font);
FreeFace(Face);
end.
One-shot Convenience
For simple cases where you only need to shape once and do not want to manage a buffer manually,
use ShapeText:
uses harfbuzz;
var
Face : THBFace;
Font : THBFont;
Res : THBShapeResult;
I : Integer;
begin
Face := NewFaceFromFile('/usr/share/fonts/truetype/noto/NotoSansArabic-Regular.ttf', 0);
Font := NewFont(Face);
SetFontScale(Font, 24 * 64, 24 * 64);
{ Shape Arabic text right-to-left }
Res := ShapeText(Font, 'مرحبا بالعالم', hdRTL, hsArabic, 'ar');
for I := 0 to Res.GlyphCount - 1 do
WriteLn(Res.Glyphs[I].GlyphIndex, ' @cluster ', Res.Glyphs[I].Cluster);
FreeFont(Font);
FreeFace(Face);
end.