harfbuzz clib 1.0.0

Unicode text shaping engine. Converts codepoints to correctly positioned glyph sequences with ligatures, kerning, Arabic/Hebrew RTL, Devanagari conjuncts, and full OpenType GSUB/GPOS support.

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

Unicode codepoints → glyphs is NOT a 1:1 mapping.

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 shapesFreeType rendersCairo / SDL2 blits to screen

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

TypeDescription
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

function NewFaceFromFile(Path: string; FaceIndex: Integer): THBFace
Load a font face from a file path (TTF, OTF, TTC). Use FaceIndex=0 for single-face fonts.
function NewFaceFromMemory(Data: string; FaceIndex: Integer): THBFace
Create a face from font data already loaded into a string/byte buffer. Useful for embedded or downloaded fonts.
procedure FreeFace(Face: THBFace)
Release the face and its associated resources.
function NewFont(Face: THBFace): THBFont
Create a HarfBuzz font from a face. The face must stay alive for the lifetime of the font.
procedure SetFontScale(Font: THBFont; Width, Height: Integer)
Set font scale in 26.6 fixed-point pixels (multiply point size by 64). E.g. 32×64 = 2048 for 32 px text.
procedure FreeFont(Font: THBFont)
Release the font object.

Buffer

function NewBuffer: THBBuffer
Allocate a shaping buffer. Reuse between calls with ClearBuffer to avoid allocation overhead.
procedure FreeBuffer(Buf: THBBuffer)
Release a buffer and all its contents.
procedure ClearBuffer(Buf: THBBuffer)
Reset the buffer for reuse. Clears text, direction, script, and any previous shaping result.
procedure AddText(Buf: THBBuffer; Text: string)
Append UTF-8 text to the buffer. May be called multiple times before shaping to build the full run.
procedure SetDirection(Buf: THBBuffer; Dir: THBDirection)
Explicitly set text direction. Skip this if you call GuessProperties.
procedure SetScript(Buf: THBBuffer; Script: THBScript)
Set the Unicode script for the run. Required for correct shaping; skip if calling GuessProperties.
procedure SetLanguage(Buf: THBBuffer; Lang: string)
Set BCP 47 language tag (e.g. 'en', 'ar', 'zh-Hans'). Affects language-specific OpenType features.
procedure GuessProperties(Buf: THBBuffer)
Auto-detect direction and script from the buffer’s Unicode content. Convenient for Latin/CJK/Arabic; for mixed-script text, set properties explicitly.

Shaping

procedure Shape(Font: THBFont; Buf: THBBuffer)
Shape the text in the buffer using the font with all default OpenType features active. After this call, GetShapeResult returns the glyph sequence.
procedure ShapeEx(Font: THBFont; Buf: THBBuffer; Features: array of THBFeature)
Shape with explicit OpenType feature overrides. Use ParseFeature('liga=0') to disable ligatures, 'smcp=1' for small caps, etc.
function GetShapeResult(Buf: THBBuffer): THBShapeResult
Extract the shaping result from the buffer after Shape or ShapeEx. Returns glyph IDs and advance/offset positions.

Convenience

function ShapeText(Font: THBFont; Text: string; Dir: THBDirection; Script: THBScript; Lang: string): THBShapeResult
One-shot helper: allocates a buffer internally, adds text, sets direction/script/language, shapes, and returns the result. No manual buffer management needed.

OpenType Features

function ParseFeature(FeatureStr: string): THBFeature
Parse a feature string like 'liga=1', 'kern=0', 'smcp', or 'liga[2:5]=0' (range). Returns a THBFeature for use with ShapeEx.
function ListFeatures(Face: THBFace): array of string
Return all OpenType feature tags available in the font (e.g. 'liga', 'kern', 'smcp', 'onum'). Useful for building feature UIs.

Info

function HarfBuzzVersion: string
Return the runtime HarfBuzz library version string, e.g. '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.