Merge pull request #1447 from anthrotype/retain-gids-cff
[subset] set emptied CFF charstrings to 'endchar' with --retain-gids
This commit is contained in:
commit
a3dd59d6ea
@ -66,6 +66,26 @@ def closure_glyphs(self, s):
|
|||||||
s.glyphs.update(components)
|
s.glyphs.update(components)
|
||||||
decompose = components
|
decompose = components
|
||||||
|
|
||||||
|
def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False):
|
||||||
|
c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName)
|
||||||
|
if isCFF2 or ignoreWidth:
|
||||||
|
# CFF2 charstrings have no widths nor 'endchar' operators
|
||||||
|
c.decompile()
|
||||||
|
c.program = [] if isCFF2 else ['endchar']
|
||||||
|
else:
|
||||||
|
if hasattr(font, 'FDArray') and font.FDArray is not None:
|
||||||
|
private = font.FDArray[fdSelectIndex].Private
|
||||||
|
else:
|
||||||
|
private = font.Private
|
||||||
|
dfltWdX = private.defaultWidthX
|
||||||
|
nmnlWdX = private.nominalWidthX
|
||||||
|
pen = NullPen()
|
||||||
|
c.draw(pen) # this will set the charstring's width
|
||||||
|
if c.width != dfltWdX:
|
||||||
|
c.program = [c.width - nmnlWdX, 'endchar']
|
||||||
|
else:
|
||||||
|
c.program = ['endchar']
|
||||||
|
|
||||||
@_add_method(ttLib.getTableClass('CFF '))
|
@_add_method(ttLib.getTableClass('CFF '))
|
||||||
def prune_pre_subset(self, font, options):
|
def prune_pre_subset(self, font, options):
|
||||||
cff = self.cff
|
cff = self.cff
|
||||||
@ -73,21 +93,10 @@ def prune_pre_subset(self, font, options):
|
|||||||
cff.fontNames = cff.fontNames[:1]
|
cff.fontNames = cff.fontNames[:1]
|
||||||
|
|
||||||
if options.notdef_glyph and not options.notdef_outline:
|
if options.notdef_glyph and not options.notdef_outline:
|
||||||
|
isCFF2 = cff.major > 1
|
||||||
for fontname in cff.keys():
|
for fontname in cff.keys():
|
||||||
font = cff[fontname]
|
font = cff[fontname]
|
||||||
c, fdSelectIndex = font.CharStrings.getItemAndSelector('.notdef')
|
_empty_charstring(font, ".notdef", isCFF2=isCFF2)
|
||||||
if hasattr(font, 'FDArray') and font.FDArray is not None:
|
|
||||||
private = font.FDArray[fdSelectIndex].Private
|
|
||||||
else:
|
|
||||||
private = font.Private
|
|
||||||
dfltWdX = private.defaultWidthX
|
|
||||||
nmnlWdX = private.nominalWidthX
|
|
||||||
pen = NullPen()
|
|
||||||
c.draw(pen) # this will set the charstring's width
|
|
||||||
if c.width != dfltWdX:
|
|
||||||
c.program = [c.width - nmnlWdX, 'endchar']
|
|
||||||
else:
|
|
||||||
c.program = ['endchar']
|
|
||||||
|
|
||||||
# Clear useless Encoding
|
# Clear useless Encoding
|
||||||
for fontname in cff.keys():
|
for fontname in cff.keys():
|
||||||
@ -104,37 +113,42 @@ def subset_glyphs(self, s):
|
|||||||
font = cff[fontname]
|
font = cff[fontname]
|
||||||
cs = font.CharStrings
|
cs = font.CharStrings
|
||||||
|
|
||||||
# Load all glyphs
|
if s.options.retain_gids:
|
||||||
for g in font.charset:
|
isCFF2 = cff.major > 1
|
||||||
if g not in s.glyphs: continue
|
for g in s.glyphs_emptied:
|
||||||
c, _ = cs.getItemAndSelector(g)
|
_empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True)
|
||||||
|
|
||||||
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:
|
else:
|
||||||
cs.charStrings = {g:v
|
# Load all glyphs
|
||||||
for g,v in cs.charStrings.items()
|
for g in font.charset:
|
||||||
if g in s.glyphs}
|
if g not in s.glyphs: continue
|
||||||
font.charset = [g for g in font.charset if g in s.glyphs]
|
c, _ = cs.getItemAndSelector(g)
|
||||||
font.numGlyphs = len(font.charset)
|
|
||||||
|
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())
|
return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
|
||||||
|
|
||||||
|
@ -475,7 +475,7 @@ class SubsetTest(unittest.TestCase):
|
|||||||
subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
|
subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
|
||||||
self.assertLess(modified, TTFont(subsetpath)['head'].modified)
|
self.assertLess(modified, TTFont(subsetpath)['head'].modified)
|
||||||
|
|
||||||
def test_retain_gids(self):
|
def test_retain_gids_ttf(self):
|
||||||
_, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
|
_, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
|
||||||
font = TTFont(fontpath)
|
font = TTFont(fontpath)
|
||||||
|
|
||||||
@ -507,6 +507,75 @@ class SubsetTest(unittest.TestCase):
|
|||||||
self.assertGreater(glyf["A"].numberOfContours, 0)
|
self.assertGreater(glyf["A"].numberOfContours, 0)
|
||||||
self.assertEqual(glyf["B"].numberOfContours, 0)
|
self.assertEqual(glyf["B"].numberOfContours, 0)
|
||||||
|
|
||||||
|
def test_retain_gids_cff(self):
|
||||||
|
_, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
|
||||||
|
font = TTFont(fontpath)
|
||||||
|
|
||||||
|
self.assertEqual(font["hmtx"]["A"], (500, 132))
|
||||||
|
self.assertEqual(font["hmtx"]["B"], (400, 132))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
subsetpath = self.temp_path(".otf")
|
||||||
|
subset.main(
|
||||||
|
[
|
||||||
|
fontpath,
|
||||||
|
"--retain-gids",
|
||||||
|
"--output-file=%s" % subsetpath,
|
||||||
|
"--glyph-names",
|
||||||
|
"A",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subsetfont = TTFont(subsetpath)
|
||||||
|
|
||||||
|
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder())
|
||||||
|
|
||||||
|
hmtx = subsetfont["hmtx"]
|
||||||
|
self.assertEqual(hmtx["A"], (500, 132))
|
||||||
|
self.assertEqual(hmtx["B"], (0, 0))
|
||||||
|
|
||||||
|
subsetfont["CFF "].cff[0].decompileAllCharStrings()
|
||||||
|
cs = subsetfont["CFF "].cff[0].CharStrings
|
||||||
|
self.assertGreater(len(cs["A"].program), 0)
|
||||||
|
self.assertEqual(cs["B"].program, ["endchar"])
|
||||||
|
|
||||||
|
def test_retain_gids_cff2(self):
|
||||||
|
fontpath = self.getpath("../../varLib/data/TestCFF2VF.otf")
|
||||||
|
font = TTFont(fontpath)
|
||||||
|
|
||||||
|
self.assertEqual(font["hmtx"]["A"], (600, 31))
|
||||||
|
self.assertEqual(font["hmtx"]["T"], (600, 41))
|
||||||
|
|
||||||
|
font["CFF2"].cff[0].decompileAllCharStrings()
|
||||||
|
cs = font["CFF2"].cff[0].CharStrings
|
||||||
|
self.assertGreater(len(cs["A"].program), 0)
|
||||||
|
self.assertGreater(len(cs["T"].program), 0)
|
||||||
|
|
||||||
|
subsetpath = self.temp_path(".otf")
|
||||||
|
subset.main(
|
||||||
|
[
|
||||||
|
fontpath,
|
||||||
|
"--retain-gids",
|
||||||
|
"--output-file=%s" % subsetpath,
|
||||||
|
"A",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subsetfont = TTFont(subsetpath)
|
||||||
|
|
||||||
|
self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()))
|
||||||
|
|
||||||
|
hmtx = subsetfont["hmtx"]
|
||||||
|
self.assertEqual(hmtx["A"], (600, 31))
|
||||||
|
self.assertEqual(hmtx["glyph00002"], (0, 0))
|
||||||
|
|
||||||
|
subsetfont["CFF2"].cff[0].decompileAllCharStrings()
|
||||||
|
cs = subsetfont["CFF2"].cff[0].CharStrings
|
||||||
|
self.assertGreater(len(cs["A"].program), 0)
|
||||||
|
self.assertEqual(cs["glyph00002"].program, [])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(unittest.main())
|
sys.exit(unittest.main())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user