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) |
ss01–ss20 |
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)