TTFontFile::_getClasses() keeps glyphs that a ClassDef explicitly lists as class 0. Otl::_getClasses() drops them. The parser and the dump therefore read class 0 differently from the shaper.
src/TTFontFile.php:
foreach (ClassDef::pairsAt($this->reader, $offset) as $pair) {
list($glyphID, $class) = $pair;
if (isset($this->glyphToChar[$glyphID][0])) {
$GlyphByClass[$class][] = GlyphString::of($this->glyphToChar[$glyphID][0]);
}
}
src/Otl.php:
if ($class > 0 && $uni) {
$GlyphByClass[$class][$uni] = 1;
}
A Format 1 ClassDef gives a class value to every glyph in its range, so glyphs in class 0 can appear in it explicitly. TTFontFile::classGlyphs() assumes that never happens. Its docblock says: "Class 0 is every glyph the ClassDef does not mention, so a ClassDef never lists it and _getClasses() never returns a key for it."
What follows
- Class 0 exclusions include class 0 glyphs. The
class0excl strings built in TTFontFile (implode('|', $subtable['InputClasses']) and the backtrack and lookahead equivalents) include glyphs that are really in class 0. So class 0 is read as "not these", where "these" wrongly includes some class 0 glyphs.
- A rule naming class 0 gets a glyph list.
classGlyphs($classes, 0) returns the listed class 0 glyphs instead of ''. That list is written into the context-rule data the parser caches and into the OTL dump.
Scope
Found by the #394 survey. Bundled fonts with Format 1 ClassDefs that list class 0 explicitly:
- FreeSansBold: 4 of 12
- FreeSerif: 1 of 52
- Garuda: all four styles
tests/data/ttf/NotoSansSinhala-Subset: 2 of 3
It has not been established whether the parser's cached data changes shaping, or only what the dump and caches say. A fix needs to answer that, and to compare shaping against hb-shape for any run that changes.
Expected
TTFontFile::_getClasses() drops class 0 as Otl::_getClasses() does, so the parser, the dump and the shaper agree, and the classGlyphs() docblock becomes true. Font caches and golden masters for the fonts above will change, and each change needs explaining.
mpdf/mpdf development's _getClasses() has no class 0 filter either, so this belongs upstream as well.
Found while doing #394.
TTFontFile::_getClasses()keeps glyphs that a ClassDef explicitly lists as class 0.Otl::_getClasses()drops them. The parser and the dump therefore read class 0 differently from the shaper.src/TTFontFile.php:src/Otl.php:A Format 1 ClassDef gives a class value to every glyph in its range, so glyphs in class 0 can appear in it explicitly.
TTFontFile::classGlyphs()assumes that never happens. Its docblock says: "Class 0 is every glyph the ClassDef does not mention, so a ClassDef never lists it and _getClasses() never returns a key for it."What follows
class0exclstrings built inTTFontFile(implode('|', $subtable['InputClasses'])and the backtrack and lookahead equivalents) include glyphs that are really in class 0. So class 0 is read as "not these", where "these" wrongly includes some class 0 glyphs.classGlyphs($classes, 0)returns the listed class 0 glyphs instead of''. That list is written into the context-rule data the parser caches and into the OTL dump.Scope
Found by the #394 survey. Bundled fonts with Format 1 ClassDefs that list class 0 explicitly:
tests/data/ttf/NotoSansSinhala-Subset: 2 of 3It has not been established whether the parser's cached data changes shaping, or only what the dump and caches say. A fix needs to answer that, and to compare shaping against
hb-shapefor any run that changes.Expected
TTFontFile::_getClasses()drops class 0 asOtl::_getClasses()does, so the parser, the dump and the shaper agree, and theclassGlyphs()docblock becomes true. Font caches and golden masters for the fonts above will change, and each change needs explaining.mpdf/mpdfdevelopment's_getClasses()has no class 0 filter either, so this belongs upstream as well.Found while doing #394.