Features

OpenType Features

OpenType features control glyph substitution (GSUB) and positioning (GPOS) during shaping. Features like kern (kerning), liga (ligatures), and smcp (small caps) can be enabled, disabled, or configured per shaping call.

Default Features

When you call Shape(buf, nil), textshape applies a set of default features that match HarfBuzz’s behavior:

GSUB (substitution):

Tag Name Description
ccmp Glyph Composition/Decomposition Recomposes/decomposes characters
rlig Required Ligatures Ligatures required by the script
calt Contextual Alternates Context-dependent glyph variants
liga Standard Ligatures Common ligatures (fi, fl, …)
clig Contextual Ligatures Context-dependent ligatures
rand Random Alternates Randomly selected glyph variants

GPOS (positioning):

Tag Name Description
abvm Above-base Marks Position marks above base glyphs
blwm Below-base Marks Position marks below base glyphs
mark Mark Positioning Attach marks to base glyphs
mkmk Mark-to-Mark Attach marks to other marks
curs Cursive Positioning Connect glyphs cursively (Arabic)
dist Distances Adjust distances between glyphs
kern Kerning Adjust spacing between glyph pairs

Script-specific features (e.g., Arabic init, medi, fina, isol or Indic nukt, akhn, pref, blwf, etc.) are applied automatically based on the detected script.

You can retrieve the default feature list programmatically:

defaults := ot.DefaultFeatures()

Specifying Features

Creating Features Programmatically

// Enable a feature
smcp := ot.NewFeatureOn(ot.MakeTag('s', 'm', 'c', 'p'))

// Disable a feature
noLiga := ot.NewFeatureOff(ot.MakeTag('l', 'i', 'g', 'a'))

// Feature with a specific value (e.g., stylistic alternates)
salt3 := ot.NewFeature(ot.MakeTag('s', 'a', 'l', 't'), 3)

Pass features to Shape:

features := []ot.Feature{smcp, noLiga}
shaper.Shape(buf, features)

Parsing Feature Strings

Feature strings follow the HarfBuzz format:

// Single feature
f, ok := ot.FeatureFromString("smcp")     // smcp=1 (on)
f, ok = ot.FeatureFromString("-liga")      // liga=0 (off)
f, ok = ot.FeatureFromString("+kern")      // kern=1 (on)
f, ok = ot.FeatureFromString("aalt=2")     // aalt=2 (alternate #2)

// Comma-separated list
features := ot.ParseFeatures("smcp,-liga,kern")
shaper.Shape(buf, features)

Supported string formats:

Format Meaning
kern Enable kern
kern=1 Enable kern
kern=0 Disable kern
-kern Disable kern
+kern Enable kern
aalt=2 Select alternate #2
kern[3:5] Enable kern for clusters 3–5
kern[3:] Enable kern from cluster 3 to end
kern[:5] Enable kern from start to cluster 5
kern[3:5]=0 Disable kern for clusters 3–5

Feature Ranges

Features can be limited to a range of clusters, which allows different features for different parts of the text:

// Small caps only for the first 5 clusters
f := ot.Feature{
    Tag:   ot.MakeTag('s', 'm', 'c', 'p'),
    Value: 1,
    Start: 0,
    End:   5,
}

// Or use the string parser
f, _ = ot.FeatureFromString("smcp[0:5]")

A feature is global (applies to the entire buffer) when Start == FeatureGlobalStart and End == FeatureGlobalEnd. You can check this:

f.IsGlobal() // true if the feature applies everywhere

Default Features on the Shaper

You can set default features on the shaper itself. These are used when Shape is called with nil:

// Override defaults: only kerning, no ligatures
shaper.SetDefaultFeatures([]ot.Feature{
    ot.NewFeatureOn(ot.MakeTag('k', 'e', 'r', 'n')),
})

// Shape with these custom defaults
shaper.Shape(buf, nil)

// Get current defaults
features := shaper.GetDefaultFeatures()

When you pass a non-nil feature slice to Shape, it replaces (not merges with) the default features for that call.

Common Feature Tags

Here are some commonly used OpenType feature tags:

Ligatures

Tag Name Description
liga Standard Ligatures fi, fl, ff, ffi, ffl
dlig Discretionary Ligatures ct, st, and other optional ligatures
hlig Historical Ligatures Long s ligatures, etc.

Letter Forms

Tag Name Description
smcp Small Caps Lowercase → small capitals
c2sc Caps to Small Caps Uppercase → small capitals
swsh Swash Decorative swash forms
onum Oldstyle Figures 0123 → oldstyle numerals
lnum Lining Figures Oldstyle → lining numerals
tnum Tabular Figures Fixed-width numerals
pnum Proportional Figures Variable-width numerals

Alternates

Tag Name Description
salt Stylistic Alternates Alternate glyph forms (value selects variant)
ss01ss20 Stylistic Sets Named glyph variant sets
calt Contextual Alternates Context-dependent variants

Example: Typographic Features

shaper, _ := ot.NewShaper(font)
buf := ot.NewBuffer()
buf.AddString("Office 12345")
buf.GuessSegmentProperties()

// Enable small caps and oldstyle figures
features := ot.ParseFeatures("smcp,onum")
shaper.Shape(buf, features)