Skip to content

Add support for GSUB Type 8 (Reverse Chaining Contextual Single Substitution)#370

Open
lehni wants to merge 1 commit into
foliojs:masterfrom
lineto:feature/gsub-type-8
Open

Add support for GSUB Type 8 (Reverse Chaining Contextual Single Substitution)#370
lehni wants to merge 1 commit into
foliojs:masterfrom
lineto:feature/gsub-type-8

Conversation

@lehni
Copy link
Copy Markdown

@lehni lehni commented May 20, 2026

fontkit's GSUBProcessor currently throws "GSUB lookupType 8 is not supported" when it encounters a Reverse Chaining Contextual Single Substitution lookup. This affects fonts whose default ccmp or calt features include Type 8 — for example, Noto Sans Coptic, where the failure prevents any layout call from succeeding.

This PR:

  • Fixes the GSUB Type 8 struct definition: the missing backtrackGlyphCount field meant the binary reader couldn't decode the table. Also renames substitutessubstitute (LazyArray) to match the Type 1 v2 convention in the same file.
  • Implements Type 8 application in GSUBProcessor: check coverage at the current glyph, verify backtrack + lookahead context, replace the glyph with substitute.get(coverageIndex) on match.
  • Iterates Type 8 lookups right-to-left in OTProcessor.applyLookups (per the OpenType spec) so lookahead context always sees post-substitution glyphs and backtrack sees pre-substitution ones.
  • Adds Noto Sans Coptic as a test fixture (with its SIL OFL) and a shaping assertion that matches HarfBuzz's output for a stacked-macron Coptic input. The assertion checks that the Type 8 lookup converts uni0305uni0305.cap (glyph 199) when followed by another macron in the run.

Spec reference: https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#82-reverse-chaining-contextual-single-substitution-format-1

Resolves #365.

…itution)

- Fix the GSUB Type 8 struct to include the missing `backtrackGlyphCount` field; without it the binary reader couldn't decode the table at all
- Implement Type 8 application in GSUBProcessor: check coverage at the current glyph, verify backtrack + lookahead context, replace the glyph with `substitutes[coverageIndex]` on match
- Iterate Type 8 lookups right-to-left in OTProcessor's applyLookups (per spec) so lookahead always sees post-substitution glyphs and backtrack sees the pre-substitution ones
- Add Noto Coptic as a test fixture (its ccmp feature uses Type 8; without this fix any Coptic layout throws "GSUB lookupType 8 is not supported")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fontkit not properly supporting reverse chained contexts single substitution

1 participant