Follow-up to #150, which dropped the unreachable glyphIdArray from repackageTTF() and left the
other two builders alone.
All three write the same format 4 subtable in the same shape, and all three write idRangeOffset = 0
for every segment - which is exactly what tells a reader to resolve that segment through its
idDelta and to look no further. Each then appends a glyphIdArray holding every glyph of every
segment, plus a trailing zero. Nothing can reach it. #140 corrected the length field to count it,
#150 established that it need not be there at all, and makeSubset() and makeSubsetSIP() still
write it.
Measured
tests/data/ttf, cleared font cache, the subtable taken out of the emitted cmap and measured
against the 16 + (8 * segCount) bytes a header and four segment arrays occupy. The subset is the
63-character pangram of #151 - a space, the digits, both cases of the alphabet - or everything the
font maps, where the row says so.
| font |
builder |
subset |
subtable |
segments |
unreachable |
| NotoSans-Regular |
makeSubset |
pangram |
184 |
5 |
128 (70%) |
| Poppins-Regular |
makeSubset |
pangram |
184 |
5 |
128 (70%) |
| Manjari-Regular |
makeSubset |
pangram |
184 |
5 |
128 (70%) |
| angerthas |
makeSubset |
pangram |
212 |
10 |
116 (55%) |
| NotoSans-Regular |
makeSubset |
3,054 characters |
12,356 |
779 |
6,108 (49%) |
| Poppins-Regular |
makeSubset |
472 characters |
1,696 |
92 |
944 (56%) |
| Manjari-Regular |
makeSubset |
404 characters |
2,514 |
211 |
810 (32%) |
| NotoSans-Regular |
makeSubsetSIP |
pangram |
160 |
2 |
128 (80%) |
| angerthas |
makeSubsetSIP |
pangram |
148 |
2 |
116 (78%) |
Over all 31 fonts, makeSubset to the pangram: 2,164 bytes of subtable between them, 852 of it
unreachable. To everything each font maps, which is what the golden masters capture: 219,660 bytes,
207,004 of it unreachable - 6,678 dead bytes per embedded font, and 94% of the subtable.
So a document embedding one subsetted text font carries somewhere between a hundred bytes and six
kilobytes that no reader opens. Small beside the glyf table next to it, which is why this is an
enhancement and not a bug.
It is not the overflow
Neither builder is near the ceiling a uint16 length imposes. The widest subtable either writes over
this corpus is makeSubset on NotoSans-Regular's whole character set, 12,356 bytes, and without the
array it would be 6,248. #150 was repackageTTF() running to 133,080 bytes and the field keeping the
low sixteen bits of it; nothing here does that. The guard #150 added - measure the packed subtable and
raise a FontException rather than let pack('n') wrap - belongs in these two as well when they are
touched.
What a fix looks like
The loop and the trailing zero after $cmap[] = 0; // idRangeOffset of last Segment, removed from
each: src/Fonts/FontSubsetter.php:384 and :965. Six lines.
What it costs
Which is why #150 stopped where it did. Both builders' output is pinned twice over:
Every one of those has to be refreshed and read.
Follow-up to #150, which dropped the unreachable
glyphIdArrayfromrepackageTTF()and left theother two builders alone.
All three write the same format 4 subtable in the same shape, and all three write
idRangeOffset = 0for every segment - which is exactly what tells a reader to resolve that segment through its
idDeltaand to look no further. Each then appends aglyphIdArrayholding every glyph of everysegment, plus a trailing zero. Nothing can reach it. #140 corrected the length field to count it,
#150 established that it need not be there at all, and
makeSubset()andmakeSubsetSIP()stillwrite it.
Measured
tests/data/ttf, cleared font cache, the subtable taken out of the emittedcmapand measuredagainst the
16 + (8 * segCount)bytes a header and four segment arrays occupy. The subset is the63-character pangram of #151 - a space, the digits, both cases of the alphabet - or everything the
font maps, where the row says so.
makeSubsetmakeSubsetmakeSubsetmakeSubsetmakeSubsetmakeSubsetmakeSubsetmakeSubsetSIPmakeSubsetSIPOver all 31 fonts,
makeSubsetto the pangram: 2,164 bytes of subtable between them, 852 of itunreachable. To everything each font maps, which is what the golden masters capture: 219,660 bytes,
207,004 of it unreachable - 6,678 dead bytes per embedded font, and 94% of the subtable.
So a document embedding one subsetted text font carries somewhere between a hundred bytes and six
kilobytes that no reader opens. Small beside the
glyftable next to it, which is why this is anenhancement and not a bug.
It is not the overflow
Neither builder is near the ceiling a
uint16length imposes. The widest subtable either writes overthis corpus is
makeSubseton NotoSans-Regular's whole character set, 12,356 bytes, and without thearray it would be 6,248. #150 was
repackageTTF()running to 133,080 bytes and the field keeping thelow sixteen bits of it; nothing here does that. The guard #150 added - measure the packed subtable and
raise a
FontExceptionrather than letpack('n')wrap - belongs in these two as well when they aretouched.
What a fix looks like
The loop and the trailing zero after
$cmap[] = 0; // idRangeOffset of last Segment, removed fromeach:
src/Fonts/FontSubsetter.php:384and:965. Six lines.What it costs
Which is why #150 stopped where it did. Both builders' output is pinned twice over:
tests/data/subset/*.jsongolden masters, in themakeSubsetandmakeSubsetSIPsectionsof both
useOTLsettings - four sections per font, against the one section repackageTTF() can build a format 4 cmap subtable larger than the uint16 its length field is written as #150 moved;font programs.
Every one of those has to be refreshed and read.