Base glyph is incorrect when parsing ligature components with PHP php-font-lib fork [closed]

I’m trying to parse ligature components to Unicode mapping in PHP using my own fork of php-font-lib which implements gsub table parsing. The official library doesn’t support this yet.

I’m using it with the Material Icons Font.
According to Fontdrop, the font contains all necessary ligature and GSUB data:
Fontdrop ligature parse image

The output I am trying to achieve should follow this pattern:

[
"fire_extinguisher": "uniF1D8",
]

However with my current script (You can find the code below), I get an output like this:

2ire_extinguisher -> 

The base glyph is wrong or sometimes missing entirely:

oom_in -> 
2ont_download_off -> 

Steps to reproduce:

1.) Clone my fork:

git clone https://github.com/NLion74/php-font-lib
cd php-font-lib
touch reproduction_example.php

1.) Add this code to reproduction_example.php

<?php
require __DIR__ . '/vendor/autoload.php';
use FontLibFont;

$fontPath = "../MaterialIcons-Regular.ttf";
if (!file_exists($fontPath)) die("Font not found");

$font = Font::load($fontPath);
$font->parse();

$cmap = $font->getData("cmap")['subtables'][0]['glyphIndexArray'] ?? [];
$glyphIDtoChar = [];
foreach ($cmap as $unicode => $gid) {
    if ($gid !== 0) $glyphIDtoChar[$gid] = mb_chr($unicode, 'UTF-8');
}

$gsub = $font->getData("GSUB");
$ligatureMap = [];

foreach ($gsub['lookupList']['lookups'] as $lookup) {
    if ($lookup['lookupType'] !== 4) continue;
    foreach ($lookup['subtables'] as $subtable) {
        if (!isset($subtable['ligSets'])) continue;

        $leadingChars = [];
        if (!empty($subtable['coverage']['rangeRecords'])) {
            foreach ($subtable['coverage']['rangeRecords'] as $range) {
                for ($gid = $range['start']; $gid <= $range['end']; $gid++) {
                    $leadingChars[] = $glyphIDtoChar[$gid];
                }
            }
        }
        if (!empty($subtable['coverageGlyphs'])) {
            foreach ($subtable['coverageGlyphs'] as $gid) {
                $leadingChars[] = $glyphIDtoChar[$gid];
            }
        }

        foreach ($subtable['ligSets'] as $index => $ligSet) {
            $baseGlyph = $leadingChars[$index];
            foreach ($ligSet['ligatures'] as $lig) {
                $components = array_map(fn($gid) => $glyphIDtoChar[$gid], $lig['components']);
                array_unshift($components, $baseGlyph);
                $seqStr = implode('', $components);
                $ligatureGlyph = $glyphIDtoChar[$lig['ligatureGlyph']];
                $ligatureMap[$seqStr] = $ligatureGlyph;
            }
        }
    }
}

foreach ($ligatureMap as $seq => $lig) {
    echo "$seq -> $lign";
}

3.) Run:

composer install
php reproduction_example.php

Expected behavior:

  • The base glyph plus components map correctly to the ligature glyph.

Actual behavior:

  • The base glyph seems to be entirely wrong

Question:

  • How can I correctly extract the base glyph for each ligature set when parsing the GSUB table in PHP?