---
title: "Turn on OpenType features"
url: https://recursiveai.org/onefont/cookbook/opentype-features/
description: "Switch OpenType features such as small caps, figure styles, fractions and ligatures on or off in C#, and choose a language’s local forms, with ShapingOptions."
version: "1.3.0"
---

# Turn on OpenType features

Switch OpenType features such as small caps, figure styles, fractions and ligatures on or off in C#, and choose a language’s local forms, with ShapingOptions.

Fonts carry optional features as GSUB and GPOS lookups under four-letter tags. onefont applies the defaults for horizontal text on every run. `ShapingOptions` adds or removes features and picks the language whose forms the font should use.

```csharp
using OneFont;

FontFace face = FontFace.Load(File.ReadAllBytes("NotoSerif-Regular.ttf"));

Show("office", new ShapingOptions());                                     // liga is on by default
Show("office", With(FontFeature.Off("liga")));
Show("office", With(FontFeature.On("smcp")));                             // small caps run before liga: no ligature
Show("1/2", With(FontFeature.On("frac")));
Show("1110", new ShapingOptions());                                     // this font's default figures are tabular
Show("1110", With(FontFeature.On("pnum")));                               // proportional: each digit its own width
Show("1110", With(FontFeature.On("onum")));                               // old-style figures
Show("ş", new ShapingOptions());
Show("ş", new ShapingOptions { Language = "ro" });                       // Romanian locl: s with comma below
Console.WriteLine($"cmap: ş is glyph {face.GetGlyphIndex('ş')}, ș is glyph {face.GetGlyphIndex('ș')}");

static ShapingOptions With(FontFeature feature)
{
    var options = new ShapingOptions();
    options.Features.Add(feature);
    return options;
}

void Show(string text, ShapingOptions options)
{
    var clusters = TextShaping.ShapeRun(text, face, options: options);
    ShapedGlyph[] glyphs = clusters.SelectMany(c => c.Glyphs.ToArray()).ToArray();
    string settings = string.Join(" ", options.Features) + (options.Language is null ? "" : $"lang={options.Language}");
    string ids = string.Join(" ", glyphs.Select(g => g.GlyphId));
    string advances = string.Join(" ", glyphs.Select(g => g.XAdvance));
    Console.WriteLine($"{text,-7} {(settings == "" ? "defaults" : settings),-9} glyphs [{ids}] advances [{advances}]");
}
```

```text
office  defaults  glyphs [82 2132 70 72] advances [577 1002 492 535]
office  liga=0    glyphs [82 73 73 76 70 72] advances [577 369 369 320 492 535]
office  smcp=1    glyphs [2207 2170 2170 2179 2150 2160] advances [631 504 504 330 537 535]
1/2     frac=1    glyphs [2530 534 2521] advances [400 62 400]
1110    defaults  glyphs [20 20 20 19] advances [559 559 559 559]
1110    pnum=1    glyphs [2490 2490 2490 2489] advances [459 459 459 579]
1110    onum=1    glyphs [2510 2510 2510 2509] advances [579 579 579 579]
ş       defaults  glyphs [288] advances [451]
ş       lang=ro   glyphs [329] advances [451]
cmap: ş is glyph 288, ș is glyph 329
```

- **Defaults.** Every run gets `ccmp`, `locl`, `rlig`, `liga`, `clig`, `calt`, `rclt`, `kern`, `mark`, `mkmk` and `curs`, plus the script’s own features for Arabic, Indic and other complex scripts. So “office” has the ffi ligature without asking.
- **On and off.** `FontFeature.On("smcp")` and `FontFeature.Off("liga")` go into `Features`. `new FontFeature("salt", 2)` picks the second alternate. Later settings replace earlier ones for the same tag. A tag the font doesn’t have does nothing.
- **Order.** Lookups run in the font’s lookup-list order, as the OpenType specification says: `ccmp` and `locl` first, then everything else. With `smcp` the letters become small capitals before `liga` looks for “ffi”, so no ligature forms. SixLabors.Fonts ligates first.
- **Figures.** Noto Serif’s default figures are tabular, every digit 559 units wide. `pnum` gives proportional figures and `onum` old-style ones. `frac` turns “1/2” into a numerator, a fraction slash and a denominator.
- **Kerning.** `Kerning = KerningMode.None` turns pair kerning off. It is the same as `FontFeature.Off("kern")`, and an explicit `kern` setting wins over it.
- **Language.** `Language` takes a BCP 47 tag such as `ro`, `tr` or `sr`, or an OpenType language tag in capitals such as `ROM`. Here Romanian `locl` replaces ş (s with cedilla) with the font’s ș (s with comma below). A language the font has no forms for uses the script’s defaults.

A feature applies to the whole run: feature ranges inside one string aren’t supported, so shape text with different settings as separate runs. One `ShapingOptions` can be shared by any number of calls and threads as long as nobody changes it while they run. It caches the feature set it builds.

The full feature order, including the Arabic and Indic stages, is in [Shaping, bidi and segmentation](https://recursiveai.org/onefont/docs/shaping/).

What the font formats actually contain: [OpenType vs TrueType](https://recursiveai.org/onefont/articles/opentype-vs-truetype/).
