Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
    • Switch to GitLab Next
  • Sign in / Register
iterm2
iterm2
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 2,246
    • Issues 2,246
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
    • Iterations
  • Requirements
    • Requirements
    • List
  • Security & Compliance
    • Security & Compliance
    • Dependency List
    • License Compliance
  • Operations
    • Operations
    • Incidents
  • Analytics
    • Analytics
    • Insights
    • Issue
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Create a new issue
  • Commits
  • Issue Boards
  • George Nachman
  • iterm2iterm2
  • Issues
  • #5392

Closed
Open
Opened Dec 09, 2016 by George Nachman@gnachmanMaintainer

Parse OTF GSUB table to find ligatures

Core text is slow. With ligature fonts and ASCII text we only need it when a ligature is present. We could look at the OTF tables to find what ligatures substitutions are in use for the current font. From looking at FreeType, it's not too bad. The structure of the GSUB table is like this:

GSUB {
  4 bytes of version which must equal 0x00010000
  2 byte offset of scripts
  2 byte offset of features
  2 byte offset of lookups
  data
}

Features table: {
  2 byte count
  `count` entries {
    4 byte feature tag
    2 byte offset to feature
  }
}

Feature:
2 byte feature params
2 byte lookup count
`lookup count` entries {
  2 byte index into lookup table
}

Lookup table: {
  2 byte count
  `count` entries {
    2 byte offset to subtable
  }
}

Lookup Subtable {
  2 byte lookup type [1, typecount)
  2 byte lookup flag
  2 byte subtable count
  `subtable count` entries {
    2 byte entry offset
  }
}

Ligature sub entry:
2 byte sub format (must be 1)
If type is 1: G  2 byte coverage offset
  2 byte coverage count
  `count` entries {
    2 byte LigatureSet offset
  }
}

LigatureSet {
  2 byte count
  `count` entries {
    2 byte Ligature offset
  }
}

Ligature {
  2 byte ligature glyph [0, glyphcount]
  2 byte component count > 0
  `component count` entries {
    2 byte glyph index
  }
}

Coverage: {
  2 byte coverage format
  If coverage fromat is 1 {
    2 byte glyph count
    `glyph count` entries {
      2 byte glyph id < glyph count
    }
  } else if coverage format is 2 {
    2 byte range count
    `range count` entries {
      2 byte start
      2 byte end < start
      2 byte start cov index
    }
  }
}

Lookup types:
single-sub = 1
multi-sub = 2
alt-sub = 3
ligature sub = 4  * this is the one we care about *
context sub = 5
chain context sub = 6
extension sub = 7 
reverse chain single sub = 8

The relevant freetype files to infer the structure are are otvgsub.c, otvcommn.h, and otvcommn.c

We can get the GSUB table with core graphics APIs like this:

    CTFontRef ctfont = (CTFontRef) [NSFont fontWithName:@"FiraCode-Regular" size:12];
    CGFontRef cgfont = CTFontCopyGraphicsFont(ctfont, nil);

    CFArrayRef *tags = CGFontCopyTableTags(cgfont);
    int numTags = CFArrayGetCount(tags);
    for (int t = 0; t < numTags; t++) {
        uint32_t tag = CFArrayGetValueAtIndex(tags, t);
        if (tag == TRUETYPE_TAG('G', 'S', 'U', 'B')) {
            CFDataRef table = CGFontCopyTableForTag(cgfont, tag);
            char *p = CFDataGetBytePtr(table);
            int len = CFDataGetLength(table);
            NSLog(@"%p %d",p, len);
        }
    }
Assignee
Assign to
Feature Complete 3.1
Milestone
Feature Complete 3.1 (Past due)
Assign milestone
Time tracking
None
Due date
None
Reference: gnachman/iterm2#5392