Make --retain-gids truncate empty glyphs after the last non-empty glyph.

This commit is contained in:
Garret Rieger 2019-05-15 13:33:10 -07:00
parent 80a4993edc
commit dce15980fd
4 changed files with 73 additions and 65 deletions

View File

@ -2572,17 +2572,19 @@ class Subsetter(object):
self.glyphs_retained = frozenset(self.glyphs)
self.glyphs_emptied = frozenset()
if self.options.retain_gids:
self.glyphs_emptied = realGlyphs - self.glyphs_retained
# TODO Drop empty glyphs at the end of GlyphOrder vector.
order = font.getReverseGlyphMap()
self.reverseOrigGlyphMap = {g:order[g] for g in self.glyphs_retained}
self.reverseEmptiedGlyphMap = {g:order[g] for g in self.glyphs_emptied}
self.last_retained_order = max(self.reverseOrigGlyphMap.values())
self.last_retained_glyph = font.getGlyphOrder()[self.last_retained_order]
self.glyphs_emptied = frozenset()
if self.options.retain_gids:
self.glyphs_emptied = {g for g in realGlyphs - self.glyphs_retained if order[g] <= self.last_retained_order}
self.reverseEmptiedGlyphMap = {g:order[g] for g in self.glyphs_emptied}
log.info("Retaining %d glyphs", len(self.glyphs_retained))
del self.glyphs
@ -2610,12 +2612,16 @@ class Subsetter(object):
log.warning("%s NOT subset; don't know how to subset; dropped", tag)
del font[tag]
if not self.options.retain_gids:
with timer("subset GlyphOrder"):
glyphOrder = font.getGlyphOrder()
with timer("subset GlyphOrder"):
glyphOrder = font.getGlyphOrder()
if not self.options.retain_gids:
glyphOrder = [g for g in glyphOrder if g in self.glyphs_retained]
font.setGlyphOrder(glyphOrder)
font._buildReverseGlyphOrderDict()
else:
glyphOrder = [g for g in glyphOrder if font.getGlyphID(g) <= self.last_retained_order]
font.setGlyphOrder(glyphOrder)
font._buildReverseGlyphOrderDict()
def _prune_post_subset(self, font):
for tag in font.keys():

View File

@ -113,42 +113,46 @@ def subset_glyphs(self, s):
font = cff[fontname]
cs = font.CharStrings
glyphs = s.glyphs.union(s.glyphs_emptied)
# Load all glyphs
for g in font.charset:
if g not in glyphs: continue
c, _ = cs.getItemAndSelector(g)
if cs.charStringsAreIndexed:
indices = [i for i,g in enumerate(font.charset) if g in glyphs]
csi = cs.charStringsIndex
csi.items = [csi.items[i] for i in indices]
del csi.file, csi.offsets
if hasattr(font, "FDSelect"):
sel = font.FDSelect
# XXX We want to set sel.format to None, such that the
# most compact format is selected. However, OTS was
# broken and couldn't parse a FDSelect format 0 that
# happened before CharStrings. As such, always force
# format 3 until we fix cffLib to always generate
# FDSelect after CharStrings.
# https://github.com/khaledhosny/ots/pull/31
#sel.format = None
sel.format = 3
sel.gidArray = [sel.gidArray[i] for i in indices]
cs.charStrings = {g:indices.index(v)
for g,v in cs.charStrings.items()
if g in glyphs}
else:
cs.charStrings = {g:v
for g,v in cs.charStrings.items()
if g in glyphs}
font.charset = [g for g in font.charset if g in glyphs]
font.numGlyphs = len(font.charset)
if s.options.retain_gids:
isCFF2 = cff.major > 1
for g in s.glyphs_emptied:
_empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True)
else:
# Load all glyphs
for g in font.charset:
if g not in s.glyphs: continue
c, _ = cs.getItemAndSelector(g)
if cs.charStringsAreIndexed:
indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
csi = cs.charStringsIndex
csi.items = [csi.items[i] for i in indices]
del csi.file, csi.offsets
if hasattr(font, "FDSelect"):
sel = font.FDSelect
# XXX We want to set sel.format to None, such that the
# most compact format is selected. However, OTS was
# broken and couldn't parse a FDSelect format 0 that
# happened before CharStrings. As such, always force
# format 3 until we fix cffLib to always generate
# FDSelect after CharStrings.
# https://github.com/khaledhosny/ots/pull/31
#sel.format = None
sel.format = 3
sel.gidArray = [sel.gidArray[i] for i in indices]
cs.charStrings = {g:indices.index(v)
for g,v in cs.charStrings.items()
if g in s.glyphs}
else:
cs.charStrings = {g:v
for g,v in cs.charStrings.items()
if g in s.glyphs}
font.charset = [g for g in font.charset if g in s.glyphs]
font.numGlyphs = len(font.charset)
return True # any(cff[fontname].numGlyphs for fontname in cff.keys())

View File

@ -8,7 +8,6 @@
<GlyphID id="2" name="B"/>
<GlyphID id="3" name="glyph00003"/>
<GlyphID id="4" name="D"/>
<GlyphID id="5" name="glyph00005"/>
</GlyphOrder>
<HVAR>
@ -63,7 +62,6 @@
<Map glyph="D" outer="0" inner="3"/>
<Map glyph="glyph00001" outer="0" inner="0"/>
<Map glyph="glyph00003" outer="0" inner="0"/>
<Map glyph="glyph00005" outer="0" inner="3"/>
</LsbMap>
<RsbMap>
<Map glyph=".notdef" outer="0" inner="1"/>
@ -71,7 +69,6 @@
<Map glyph="D" outer="1" inner="1"/>
<Map glyph="glyph00001" outer="0" inner="0"/>
<Map glyph="glyph00003" outer="0" inner="0"/>
<Map glyph="glyph00005" outer="1" inner="1"/>
</RsbMap>
</HVAR>
@ -132,7 +129,6 @@
<Map glyph="D" outer="1" inner="1"/>
<Map glyph="glyph00001" outer="0" inner="0"/>
<Map glyph="glyph00003" outer="0" inner="0"/>
<Map glyph="glyph00005" outer="1" inner="1"/>
</AdvHeightMap>
<VOrgMap>
<Map glyph=".notdef" outer="1" inner="2"/>
@ -140,7 +136,6 @@
<Map glyph="D" outer="1" inner="3"/>
<Map glyph="glyph00001" outer="0" inner="0"/>
<Map glyph="glyph00003" outer="0" inner="0"/>
<Map glyph="glyph00005" outer="1" inner="3"/>
</VOrgMap>
</VVAR>

View File

@ -530,20 +530,20 @@ class SubsetTest(unittest.TestCase):
"--retain-gids",
"--output-file=%s" % subsetpath,
"--glyph-names",
"A",
"B",
]
)
subsetfont = TTFont(subsetpath)
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder())
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3])
hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["A"], (500, 132))
self.assertEqual(hmtx["B"], (0, 0))
self.assertEqual(hmtx["A"], ( 0, 0))
self.assertEqual(hmtx["B"], (400, 132))
glyf = subsetfont["glyf"]
self.assertGreater(glyf["A"].numberOfContours, 0)
self.assertEqual(glyf["B"].numberOfContours, 0)
self.assertEqual(glyf["A"].numberOfContours, 0)
self.assertGreater(glyf["B"].numberOfContours, 0)
def test_retain_gids_cff(self):
_, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
@ -551,11 +551,13 @@ class SubsetTest(unittest.TestCase):
self.assertEqual(font["hmtx"]["A"], (500, 132))
self.assertEqual(font["hmtx"]["B"], (400, 132))
self.assertEqual(font["hmtx"]["C"], (500, 0))
font["CFF "].cff[0].decompileAllCharStrings()
cs = font["CFF "].cff[0].CharStrings
self.assertGreater(len(cs["A"].program), 0)
self.assertGreater(len(cs["B"].program), 0)
self.assertGreater(len(cs["C"].program), 0)
subsetpath = self.temp_path(".otf")
subset.main(
@ -564,21 +566,22 @@ class SubsetTest(unittest.TestCase):
"--retain-gids",
"--output-file=%s" % subsetpath,
"--glyph-names",
"A",
"B",
]
)
subsetfont = TTFont(subsetpath)
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder())
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3])
hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["A"], (500, 132))
self.assertEqual(hmtx["B"], (0, 0))
self.assertEqual(hmtx["A"], (0, 0))
self.assertEqual(hmtx["B"], (400, 132))
subsetfont["CFF "].cff[0].decompileAllCharStrings()
cs = subsetfont["CFF "].cff[0].CharStrings
self.assertGreater(len(cs["A"].program), 0)
self.assertEqual(cs["B"].program, ["endchar"])
self.assertEqual(cs["A"].program, ["endchar"])
self.assertGreater(len(cs["B"].program), 0)
def test_retain_gids_cff2(self):
ttx_path = self.getpath("../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx")
@ -598,21 +601,21 @@ class SubsetTest(unittest.TestCase):
fontpath,
"--retain-gids",
"--output-file=%s" % subsetpath,
"A",
"T",
]
)
subsetfont = TTFont(subsetpath)
self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()))
self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()[0:3]))
hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["A"], (600, 31))
self.assertEqual(hmtx["glyph00002"], (0, 0))
self.assertEqual(hmtx["glyph00001"], ( 0, 0))
self.assertEqual(hmtx["T"], (600, 41))
subsetfont["CFF2"].cff[0].decompileAllCharStrings()
cs = subsetfont["CFF2"].cff[0].CharStrings
self.assertGreater(len(cs["A"].program), 0)
self.assertEqual(cs["glyph00002"].program, [])
self.assertEqual(cs["glyph00001"].program, [])
self.assertGreater(len(cs["T"].program), 0)
def test_HVAR_VVAR(self):
_, fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf")