The example string a context rule is shown against is built seven times, in three shapes that disagree.
#118 lifted the CONTEXT block of a contextual rule into OtlDump::reportContext(), which seven call sites now share. The half below it - the $exB / $exL pair that the nested lookup's own output is then rendered between - stayed where it was, because the seven copies are not the same code. #118 said that wants settling first. This is that.
The seven
All seven run the same shape: open with the backtrack, add the input positions before $seqIndex to $exB, add the ones after it to $exL, close with the lookahead. They differ in three ways.
Which formatter. GSUB format 1 calls formatEntity(); GSUB format 2 calls formatEntityFirst(); GSUB format 3 and all of GPOS reuse $exampleI[$ip], which reportContext() already built with formatEntityFirst().
|
src/OtlDump.php |
input positions |
| GSUB 5/1, 6/1 |
1151, 1341 |
formatEntity($inputGlyphs[$ip]) |
| GSUB 5/2, 6/2 |
1212, 1433 |
formatEntityFirst($inputGlyphs[$ip]), [*] when empty |
| GSUB 5/3, 6/3 |
1265, 1508 |
$exampleI[$ip] |
| GPOS 7/, 8/ |
2828 |
$exampleI[$ip] |
Where the joiner goes. Six of the seven append ‍ to each fragment of both $exB and $exL. GPOS prepends it on $exL:
$exL .= '‍' . $exampleI[$ip]; // GPOS, src/OtlDump.php:2838
$exL .= $this->formatEntityFirst($inputGlyphs[$ip]) . '‍'; // every GSUB copy
The joiner separates a fragment from the nested lookup's output, which sits between $exB and $exL. GPOS's placement is the consistent one: trailing on everything before the position, leading on everything after. GSUB's leaves a joiner between the last input position and the lookahead span that opens right after it.
What an unnamed position shows. GSUB format 2 prints [*] for a position whose glyph string is empty - a class 0 position. The others print the empty string, except that GPOS's $exampleI now carries [NOT ...] from reportContext() since #117, which says the same thing a different way.
Why it is a decision and not a refactor
Sharing these needs one answer to each of the three, and two of the three change the dump's output bytes for fonts that are in the corpus today. Picking GPOS's joiner placement moves every GSUB contextual report; picking [NOT ...] over [*] moves GSUB format 2. Neither is wrong today - they are two renderings of the same rule - so the choice wants making deliberately rather than falling out of whichever copy the shared method happens to be written from.
Once settled, the shared method takes $exampleB, $exampleI, $exampleL and $seqIndex and returns the pair, and all seven call sites collapse to one line each - the same shape reportContext() took.
Where
src/OtlDump.php, the seven blocks listed above, each directly below its reportContext() call.
The example string a context rule is shown against is built seven times, in three shapes that disagree.
#118lifted the CONTEXT block of a contextual rule intoOtlDump::reportContext(), which seven call sites now share. The half below it - the$exB/$exLpair that the nested lookup's own output is then rendered between - stayed where it was, because the seven copies are not the same code.#118said that wants settling first. This is that.The seven
All seven run the same shape: open with the backtrack, add the input positions before
$seqIndexto$exB, add the ones after it to$exL, close with the lookahead. They differ in three ways.Which formatter. GSUB format 1 calls
formatEntity(); GSUB format 2 callsformatEntityFirst(); GSUB format 3 and all of GPOS reuse$exampleI[$ip], whichreportContext()already built withformatEntityFirst().src/OtlDump.phpformatEntity($inputGlyphs[$ip])formatEntityFirst($inputGlyphs[$ip]),[*]when empty$exampleI[$ip]$exampleI[$ip]Where the joiner goes. Six of the seven append
‍to each fragment of both$exBand$exL. GPOS prepends it on$exL:The joiner separates a fragment from the nested lookup's output, which sits between
$exBand$exL. GPOS's placement is the consistent one: trailing on everything before the position, leading on everything after. GSUB's leaves a joiner between the last input position and the lookahead span that opens right after it.What an unnamed position shows. GSUB format 2 prints
[*]for a position whose glyph string is empty - a class 0 position. The others print the empty string, except that GPOS's$exampleInow carries[NOT ...]fromreportContext()since#117, which says the same thing a different way.Why it is a decision and not a refactor
Sharing these needs one answer to each of the three, and two of the three change the dump's output bytes for fonts that are in the corpus today. Picking GPOS's joiner placement moves every GSUB contextual report; picking
[NOT ...]over[*]moves GSUB format 2. Neither is wrong today - they are two renderings of the same rule - so the choice wants making deliberately rather than falling out of whichever copy the shared method happens to be written from.Once settled, the shared method takes
$exampleB,$exampleI,$exampleLand$seqIndexand returns the pair, and all seven call sites collapse to one line each - the same shapereportContext()took.Where
src/OtlDump.php, the seven blocks listed above, each directly below itsreportContext()call.