Letras Diferentes
Unicode research · OpenType feature engineering

Ligatures and OpenType Features: GSUB, GPOS and Text Semantics

Distinguish standard, discretionary and required ligatures from Unicode characters, and examine OpenType feature selection without sacrificing searchable text.

Technical referenceStandards-aware implementation guide

OpenType layout features are instructions for choosing and positioning glyphs. They normally do not rewrite the Unicode string. This distinction is essential for preserving text selection, search, copy/paste, accessibility and editorial behavior when a font turns sequences of letters into joined forms.

A ligature glyph is usually not a ligature character

The common fi ligature substitutes one glyph for a sequence of U+0066 LATIN SMALL LETTER F followed by U+0069 LATIN SMALL LETTER I. The characters remain separately representable and searchable. Unicode also contains legacy presentation characters such as U+FB01 LATIN SMALL LIGATURE FI, but encoding a typographic preference using U+FB01 is not generally the best way to write modern plain text.

Compatibility normalization NFKC maps U+FB01 to the sequence fi, unlike NFC, which retains the presentation character. This demonstrates why glyph presentation and character identity should be designed separately.

OpenType lookup structure and feature tags

GSUB organizes substitution lookups; GPOS organizes positioning lookups; GDEF describes classes, attachment points and related metadata. Features such as liga, clig, dlig, rlig and kern enable different behaviors and may be conditioned on script and language systems. A font listing a feature does not ensure that all scripts or glyph sequences have relevant lookups.

The shaping engine chooses lookup order and feature applicability according to font data and script rules. Disabling a feature for decoration must not break required shaping for scripts that depend on contextual substitutions or mandatory ligatures.

Standard, contextual and discretionary ligatures

liga typically controls standard ligatures such as fi and fl where provided. clig supports contextual ligatures. dlig covers discretionary forms whose stylistic appropriateness depends on design and editorial preference; they are commonly off by default. rlig identifies required ligature behavior needed for correct rendering in relevant contexts.

These tags express intent rather than universal visual outcomes. A font may have no matching lookup, and feature defaults can differ across text engines or text environments. Always test the actual font and run rather than expecting a given feature tag to produce a fixed image everywhere.

CSS font-feature-settings and higher-level properties

CSS exposes font-variant-ligatures for common controls and font-feature-settings for explicit OpenType feature tags. The former is generally easier to author and reason about for typical design needs. A rule like font-feature-settings: 'dlig' 1 requests discretionary ligatures where the font provides them; it cannot synthesize missing glyphs.

High-level typography properties and explicit low-level feature settings can interact, so inspect computed styles and the font actually loaded. Do not assume font-variant-ligatures: none is a harmless workaround for complex-script failures without testing the script's required shaping behavior.

Caret placement and cluster mapping

A single ligature glyph may correspond to several source characters. Cursor positioning requires logical-to-visual mapping or ligature caret information rather than treating each glyph as a single character. OpenType GDEF can supply ligature caret positions, but software may use its own fallback approximation.

This matters for text selection inside words, editor hit-testing, spell checking, and selection highlighting. If a user cannot place the cursor between the two letters of fi, the visible ligature alone does not explain the editor's behavior; inspect the editor's mapping between character offsets and shaped clusters.

Kerning is a positioning feature, not ligature substitution

Kerning adjusts spacing or glyph placement for combinations such as AV; it does not necessarily combine those glyphs into one. A ligature can affect glyph count, while kerning usually changes advances or offsets. OpenType can express both through independent structures and feature controls.

For reproducible rendering tests, compare glyph IDs and positions separately. If disabling kern alters spacing but preserves glyph IDs, the system is behaving as expected. If disabling liga changes glyph sequence and caret opportunities, that is a different phenomenon.

Complex scripts require more than Latin heuristics

Arabic contextual forms and several Indic orthographic behaviors involve script-specific substitutions and reordering. A tester who validates only Latin fi may miss serious shaping failures. In some scripts, mandatory shaping is required for legibility, not simply improved visual polish.

Shaping engines coordinate script-specific rules, character properties, language tags and font lookups. A raw GSUB feature list is not a substitute for applying Unicode character analysis or segmentation correctly, and testing a feature in isolation may not reproduce a real user-facing run.

Preserve semantics in fancy and decorative output

Do not substitute a presentation-form scalar merely to force a ligature when editable Latin letters are intended. Conversely, styled Unicode mathematical symbols are actual encoded characters, not just font toggles. For example, U+1D41F MATHEMATICAL BOLD SMALL F remains a distinct character from U+0066.

When experimenting with Unicode styled text, inspect the resulting code points and test copy/paste separately from visual rendering. A copied ligature created by a font is generally still the original letters; a copied compatibility character may behave differently under NFKC and in search.

Reproducible experiment: preserve the input while comparing the output

The following specimen is deliberately small enough to audit manually. For any test involving rendering, also record the exact font files, shaping configuration, user locale and platform versions. The code prints or illustrates source-level facts; it does not claim that JavaScript can identify a platform's selected font or glyph IDs without the relevant layout instrumentation.

const plain = 'fi';
const presentation = '\uFB01';
console.log([...plain].map(c => c.codePointAt(0).toString(16))); // 66,69
console.log([...presentation].map(c => c.codePointAt(0).toString(16))); // fb01
console.log(presentation.normalize('NFKC') === plain); // true
// CSS: font-variant-ligatures: common-ligatures;
// CSS: font-feature-settings: 'dlig' 1; (only if font supports it)

A useful report includes both positive and negative evidence. Record what stayed identical as well as what changed: identical scalar values with different glyphs indicate a presentation-layer divergence, whereas differing scalars reveal a transformation, selection or interchange problem. Preserve the original code points before applying normalization or conversion.

Evidence matrix for OpenType feature engineering

Layer or specimenControlled observationCorrect interpretation
fi lettersU+0066 U+0069two scalars; a font may substitute one glyph
fi characterU+FB01compatibility character; NFKC decomposes it
ligastandard ligaturesmay be toggled for Latin layout
rligrequired ligaturesmust be evaluated for script correctness
kernpair positioningadjusts spacing; need not change glyph IDs

This matrix is a diagnostic aid, not a substitute for conformance testing. Some differences are allowed by Unicode or CSS; others indicate a bug in an implementation, a font, a font-subsetting pipeline or an application that edits text. Be explicit about the relevant standard and the end-user expectation.

Validation protocol for applications and browser integrations

  1. fi letters: Reproduce the observed behavior using U+0066 U+0069 as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is two scalars; a font may substitute one glyph; do not extrapolate from a screenshot alone.
  2. fi character: Reproduce the observed behavior using U+FB01 as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is compatibility character; NFKC decomposes it; do not extrapolate from a screenshot alone.
  3. liga: Reproduce the observed behavior using standard ligatures as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is may be toggled for Latin layout; do not extrapolate from a screenshot alone.
  4. rlig: Reproduce the observed behavior using required ligatures as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is must be evaluated for script correctness; do not extrapolate from a screenshot alone.
  5. kern: Reproduce the observed behavior using pair positioning as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is adjusts spacing; need not change glyph IDs; do not extrapolate from a screenshot alone.

Keep regression fixtures in UTF-8 with explicit scalar annotations when invisible characters, combining sequences or non-BMP characters are present. Test a stable baseline and at least one deliberately adversarial input. Store observed environment data alongside results so later font or browser updates can be interpreted rather than mistaken for content edits.

Failure patterns, false positives and interpretation

  • Overinterpreting fi letters. A result associated with U+0066 U+0069 does not, by itself, establish the integrity of the whole text pipeline. two scalars; a font may substitute one glyph. Compare the input and an independently observed intermediate state before assigning the defect to a component.
  • Overinterpreting fi character. A result associated with U+FB01 does not, by itself, establish the integrity of the whole text pipeline. compatibility character; NFKC decomposes it. Compare the input and an independently observed intermediate state before assigning the defect to a component.
  • Overinterpreting liga. A result associated with standard ligatures does not, by itself, establish the integrity of the whole text pipeline. may be toggled for Latin layout. Compare the input and an independently observed intermediate state before assigning the defect to a component.
  • Overinterpreting rlig. A result associated with required ligatures does not, by itself, establish the integrity of the whole text pipeline. must be evaluated for script correctness. Compare the input and an independently observed intermediate state before assigning the defect to a component.
  • Overinterpreting kern. A result associated with pair positioning does not, by itself, establish the integrity of the whole text pipeline. adjusts spacing; need not change glyph IDs. Compare the input and an independently observed intermediate state before assigning the defect to a component.

Do not correct a visual artifact by silently rewriting the user's Unicode string, stripping default-ignorable characters without context, replacing unknown scalars with question marks, or flattening rich text without consent. Data integrity, rendering aesthetics, selection behavior and search policy have different constraints. A fix at one layer can create regressions in another.

Standards and primary implementation references

Primary references are linked below. A standard describes normative requirements or terminology, while an implementation manual describes a particular engine. Where behavior is unspecified or depends on fonts and applications, treat observations as environment-specific rather than universal guarantees.

  • OpenType Feature Registry — use this normative reference or implementation manual to verify the applicable definitions and requirements for OpenType feature engineering. Distinguish what the source specifies from what a particular font or platform happens to implement.
  • OpenType GSUB — use this normative reference or implementation manual to verify the applicable definitions and requirements for OpenType feature engineering. Distinguish what the source specifies from what a particular font or platform happens to implement.
  • OpenType GPOS — use this normative reference or implementation manual to verify the applicable definitions and requirements for OpenType feature engineering. Distinguish what the source specifies from what a particular font or platform happens to implement.
  • CSS Fonts Level 4 — use this normative reference or implementation manual to verify the applicable definitions and requirements for OpenType feature engineering. Distinguish what the source specifies from what a particular font or platform happens to implement.
  • Unicode normalization UAX #15 — use this normative reference or implementation manual to verify the applicable definitions and requirements for OpenType feature engineering. Distinguish what the source specifies from what a particular font or platform happens to implement.

This article belongs to the Fonts, Shaping & Text Rendering virtual silo. Related published technical notes explain adjacent layers so the original problem can be isolated without conflating scalar identity, shaping output, font support and editor behavior.

When reproducing a finding, cite the relevant standard, the exact Unicode sequence and the environment in which it occurred. This preserves the value of a technical observation even after browsers, fonts and rendering libraries change.