def count_pairpos_bytes(font: TTFont) -> int: bytes = 0 gpos = font["GPOS"] for lookup in font["GPOS"].table.LookupList.Lookup: if lookup.LookupType == 2: w = OTTableWriter(tableTag=gpos.tableTag) lookup.compile(w, font) bytes += len(w.getAllData()) elif lookup.LookupType == 9: if any(subtable.ExtensionLookupType == 2 for subtable in lookup.SubTable): w = OTTableWriter(tableTag=gpos.tableTag) lookup.compile(w, font) bytes += len(w.getAllData()) return bytes
def test_onlineVarStoreBuilder(locations, masterValues): axisTags = sorted({k for loc in locations for k in loc}) model = VariationModel(locations) builder = OnlineVarStoreBuilder(axisTags) builder.setModel(model) varIdxs = [] for masters in masterValues: _, varIdx = builder.storeMasters(masters) varIdxs.append(varIdx) varStore = builder.finish() mapping = varStore.optimize() varIdxs = [mapping[varIdx] for varIdx in varIdxs] dummyFont = TTFont() writer = OTTableWriter() varStore.compile(writer, dummyFont) data = writer.getAllData() reader = OTTableReader(data) varStore = VarStore() varStore.decompile(reader, dummyFont) fvarAxes = [buildAxis(axisTag) for axisTag in axisTags] instancer = VarStoreInstancer(varStore, fvarAxes) for masters, varIdx in zip(masterValues, varIdxs): base, *rest = masters for expectedValue, loc in zip(masters, locations): instancer.setLocation(loc) value = base + instancer[varIdx] assert expectedValue == value
def testCompile(self): a = otTables.ContextualMorphAction() a.NewState = 0x1234 a.SetMark, a.DontAdvance, a.ReservedFlags = True, True, 0x3117 a.MarkIndex, a.CurrentIndex = 0xDEAD, 0xBEEF writer = OTTableWriter() a.compile(writer, self.font, actionIndex=None) self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef")
def testCompile(self): r = otTables.RearrangementMorphAction() r.NewState = 0x1234 r.MarkFirst = r.DontAdvance = r.MarkLast = True r.ReservedFlags, r.Verb = 0x1FF0, 0xD writer = OTTableWriter() r.compile(writer, self.font, actionIndex=None) self.assertEqual(hexStr(writer.getAllData()), "1234fffd")
def testCompileFromXML(self): a = otTables.InsertionMorphAction() for name, attrs, content in parseXML(self.MORPH_ACTION_XML): a.fromXML(name, attrs, content, self.font) writer = OTTableWriter() a.compile(writer, self.font, actionIndex={('B', 'C'): 9, ('B', 'A', 'D'): 7}) self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007")
def testCompile(self): a = otTables.ContextualMorphAction() a.NewState = 0x1234 a.SetMark, a.DontAdvance, a.ReservedFlags = True, True, 0x3117 a.MarkIndex, a.CurrentIndex = 0xDEAD, 0xBEEF writer = OTTableWriter() a.compile(writer, self.font, actionIndex=None) self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef")
def testCompile(self): r = otTables.RearrangementMorphAction() r.NewState = 0x1234 r.MarkFirst = r.DontAdvance = r.MarkLast = True r.ReservedFlags, r.Verb = 0x1FF0, 0xD writer = OTTableWriter() r.compile(writer, self.font, actionIndex=None) self.assertEqual(hexStr(writer.getAllData()), "1234fffd")
def testCompileFromXML(self): a = otTables.InsertionMorphAction() for name, attrs, content in parseXML(self.MORPH_ACTION_XML): a.fromXML(name, attrs, content, self.font) writer = OTTableWriter() a.compile(writer, self.font, actionIndex={ ('B', 'C'): 9, ('B', 'A', 'D'): 7 }) self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007")
def main(args=None): """Optimize a font's GDEF variation store""" from argparse import ArgumentParser from fontTools import configLogger from fontTools.ttLib import TTFont from fontTools.ttLib.tables.otBase import OTTableWriter parser = ArgumentParser(prog='varLib.varStore', description=main.__doc__) parser.add_argument('fontfile') parser.add_argument('outfile', nargs='?') options = parser.parse_args(args) # TODO: allow user to configure logging via command-line options configLogger(level="INFO") fontfile = options.fontfile outfile = options.outfile font = TTFont(fontfile) gdef = font['GDEF'] store = gdef.table.VarStore writer = OTTableWriter() store.compile(writer, font) size = len(writer.getAllData()) print("Before: %7d bytes" % size) varidx_map = store.optimize() gdef.table.remap_device_varidxes(varidx_map) if 'GPOS' in font: font['GPOS'].table.remap_device_varidxes(varidx_map) writer = OTTableWriter() store.compile(writer, font) size = len(writer.getAllData()) print("After: %7d bytes" % size) if outfile is not None: font.save(outfile)
def main(args=None): from argparse import ArgumentParser from fontTools import configLogger from fontTools.ttLib import TTFont from fontTools.ttLib.tables.otBase import OTTableWriter parser = ArgumentParser(prog='varLib.varStore') parser.add_argument('fontfile') parser.add_argument('outfile', nargs='?') options = parser.parse_args(args) # TODO: allow user to configure logging via command-line options configLogger(level="INFO") fontfile = options.fontfile outfile = options.outfile font = TTFont(fontfile) gdef = font['GDEF'] store = gdef.table.VarStore writer = OTTableWriter() store.compile(writer, font) size = len(writer.getAllData()) print("Before: %7d bytes" % size) varidx_map = store.optimize() gdef.table.remap_device_varidxes(varidx_map) if 'GPOS' in font: font['GPOS'].table.remap_device_varidxes(varidx_map) writer = OTTableWriter() store.compile(writer, font) size = len(writer.getAllData()) print("After: %7d bytes" % size) if outfile is not None: font.save(outfile)
def _get_advance_metrics(font, masterModel, master_ttfs, axisTags, glyphOrder, advMetricses, vOrigMetricses=None): vhAdvanceDeltasAndSupports = {} vOrigDeltasAndSupports = {} for glyph in glyphOrder: vhAdvances = [ metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses ] vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports( vhAdvances, round=round) singleModel = models.allEqual( id(v[1]) for v in vhAdvanceDeltasAndSupports.values()) if vOrigMetricses: singleModel = False for glyph in glyphOrder: # We need to supply a vOrigs tuple with non-None default values # for each glyph. vOrigMetricses contains values only for those # glyphs which have a non-default vOrig. vOrigs = [ metrics[glyph] if glyph in metrics else defaultVOrig for metrics, defaultVOrig in vOrigMetricses ] vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports( vOrigs, round=round) directStore = None if singleModel: # Build direct mapping supports = next(iter(vhAdvanceDeltasAndSupports.values()))[1][1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0], round=noRound) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) # Build optimized indirect mapping storeBuilder = varStore.OnlineVarStoreBuilder(axisTags) advMapping = {} for glyphName in glyphOrder: deltas, supports = vhAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound) if vOrigMetricses: vOrigMap = {} for glyphName in glyphOrder: deltas, supports = vOrigDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound) indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() advMapping = [mapping2[advMapping[g]] for g in glyphOrder] advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder) if vOrigMetricses: vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder] useDirect = False vOrigMapping = None if directStore: # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) useDirect = directSize < indirectSize if useDirect: metricsStore = directStore advanceMapping = None else: metricsStore = indirectStore if vOrigMetricses: vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder) return metricsStore, advanceMapping, vOrigMapping
def _add_HVAR(font, masterModel, master_ttfs, axisTags): log.info("Generating HVAR") glyphOrder = font.getGlyphOrder() hAdvanceDeltasAndSupports = {} metricses = [m["hmtx"].metrics for m in master_ttfs] for glyph in glyphOrder: hAdvances = [ metrics[glyph][0] if glyph in metrics else None for metrics in metricses ] hAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports( hAdvances) singleModel = models.allEqual( id(v[1]) for v in hAdvanceDeltasAndSupports.values()) directStore = None if singleModel: # Build direct mapping supports = next(iter(hAdvanceDeltasAndSupports.values()))[1][1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: varData.addItem(hAdvanceDeltasAndSupports[glyphName][0]) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) # Build optimized indirect mapping storeBuilder = varStore.OnlineVarStoreBuilder(axisTags) mapping = {} for glyphName in glyphOrder: deltas, supports = hAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) mapping[glyphName] = storeBuilder.storeDeltas(deltas) indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() mapping = [mapping2[mapping[g]] for g in glyphOrder] advanceMapping = builder.buildVarIdxMap(mapping, glyphOrder) use_direct = False if directStore: # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) use_direct = directSize < indirectSize # Done; put it all together. assert "HVAR" not in font HVAR = font["HVAR"] = newTable('HVAR') hvar = HVAR.table = ot.HVAR() hvar.Version = 0x00010000 hvar.LsbMap = hvar.RsbMap = None if use_direct: hvar.VarStore = directStore hvar.AdvWidthMap = None else: hvar.VarStore = indirectStore hvar.AdvWidthMap = advanceMapping
def _add_HVAR(font, model, master_ttfs, axisTags): log.info("Generating HVAR") hAdvanceDeltas = {} metricses = [m["hmtx"].metrics for m in master_ttfs] for glyph in font.getGlyphOrder(): hAdvances = [metrics[glyph][0] for metrics in metricses] # TODO move round somewhere else? hAdvanceDeltas[glyph] = tuple( round(d) for d in model.getDeltas(hAdvances)[1:]) # Direct mapping supports = model.supports[1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) n = len(supports) items = [] for glyphName in font.getGlyphOrder(): items.append(hAdvanceDeltas[glyphName]) # Build indirect mapping to save on duplicates, compare both sizes uniq = list(set(items)) mapper = {v: i for i, v in enumerate(uniq)} mapping = [mapper[item] for item in items] advanceMapping = builder.buildVarIdxMap(mapping, font.getGlyphOrder()) # Direct varData = builder.buildVarData(varTupleIndexes, items) directStore = builder.buildVarStore(varTupleList, [varData]) # Indirect varData = builder.buildVarData(varTupleIndexes, uniq) indirectStore = builder.buildVarStore(varTupleList, [varData]) mapping = indirectStore.optimize() advanceMapping.mapping = { k: mapping[v] for k, v in advanceMapping.mapping.items() } # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) use_direct = directSize < indirectSize # Done; put it all together. assert "HVAR" not in font HVAR = font["HVAR"] = newTable('HVAR') hvar = HVAR.table = ot.HVAR() hvar.Version = 0x00010000 hvar.LsbMap = hvar.RsbMap = None if use_direct: hvar.VarStore = directStore hvar.AdvWidthMap = None else: hvar.VarStore = indirectStore hvar.AdvWidthMap = advanceMapping
def _add_HVAR(font, model, master_ttfs, axisTags): log.info("Generating HVAR") hAdvanceDeltas = {} metricses = [m["hmtx"].metrics for m in master_ttfs] for glyph in font.getGlyphOrder(): hAdvances = [metrics[glyph][0] for metrics in metricses] # TODO move round somewhere else? hAdvanceDeltas[glyph] = tuple(otRound(d) for d in model.getDeltas(hAdvances)[1:]) # Direct mapping supports = model.supports[1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) n = len(supports) items = [] for glyphName in font.getGlyphOrder(): items.append(hAdvanceDeltas[glyphName]) # Build indirect mapping to save on duplicates, compare both sizes uniq = list(set(items)) mapper = {v:i for i,v in enumerate(uniq)} mapping = [mapper[item] for item in items] advanceMapping = builder.buildVarIdxMap(mapping, font.getGlyphOrder()) # Direct varData = builder.buildVarData(varTupleIndexes, items) directStore = builder.buildVarStore(varTupleList, [varData]) # Indirect varData = builder.buildVarData(varTupleIndexes, uniq) indirectStore = builder.buildVarStore(varTupleList, [varData]) mapping = indirectStore.optimize() advanceMapping.mapping = {k:mapping[v] for k,v in advanceMapping.mapping.items()} # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) use_direct = directSize < indirectSize # Done; put it all together. assert "HVAR" not in font HVAR = font["HVAR"] = newTable('HVAR') hvar = HVAR.table = ot.HVAR() hvar.Version = 0x00010000 hvar.LsbMap = hvar.RsbMap = None if use_direct: hvar.VarStore = directStore hvar.AdvWidthMap = None else: hvar.VarStore = indirectStore hvar.AdvWidthMap = advanceMapping
def _add_HVAR(font, masterModel, master_ttfs, axisTags): log.info("Generating HVAR") glyphOrder = font.getGlyphOrder() hAdvanceDeltasAndSupports = {} metricses = [m["hmtx"].metrics for m in master_ttfs] for glyph in glyphOrder: hAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in metricses] hAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(hAdvances) singleModel = models.allEqual(id(v[1]) for v in hAdvanceDeltasAndSupports.values()) directStore = None if singleModel: # Build direct mapping supports = next(iter(hAdvanceDeltasAndSupports.values()))[1][1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: varData.addItem(hAdvanceDeltasAndSupports[glyphName][0]) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) # Build optimized indirect mapping storeBuilder = varStore.OnlineVarStoreBuilder(axisTags) mapping = {} for glyphName in glyphOrder: deltas,supports = hAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) mapping[glyphName] = storeBuilder.storeDeltas(deltas) indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() mapping = [mapping2[mapping[g]] for g in glyphOrder] advanceMapping = builder.buildVarIdxMap(mapping, glyphOrder) use_direct = False if directStore: # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) use_direct = directSize < indirectSize # Done; put it all together. assert "HVAR" not in font HVAR = font["HVAR"] = newTable('HVAR') hvar = HVAR.table = ot.HVAR() hvar.Version = 0x00010000 hvar.LsbMap = hvar.RsbMap = None if use_direct: hvar.VarStore = directStore hvar.AdvWidthMap = None else: hvar.VarStore = indirectStore hvar.AdvWidthMap = advanceMapping
def _get_advance_metrics(font, masterModel, master_ttfs, axisTags, glyphOrder, advMetricses, vOrigMetricses=None): vhAdvanceDeltasAndSupports = {} vOrigDeltasAndSupports = {} for glyph in glyphOrder: vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses] vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances) singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values()) if vOrigMetricses: singleModel = False for glyph in glyphOrder: # We need to supply a vOrigs tuple with non-None default values # for each glyph. vOrigMetricses contains values only for those # glyphs which have a non-default vOrig. vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig for metrics, defaultVOrig in vOrigMetricses] vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs) directStore = None if singleModel: # Build direct mapping supports = next(iter(vhAdvanceDeltasAndSupports.values()))[1][1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0]) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) # Build optimized indirect mapping storeBuilder = varStore.OnlineVarStoreBuilder(axisTags) advMapping = {} for glyphName in glyphOrder: deltas, supports = vhAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) advMapping[glyphName] = storeBuilder.storeDeltas(deltas) if vOrigMetricses: vOrigMap = {} for glyphName in glyphOrder: deltas, supports = vOrigDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas) indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() advMapping = [mapping2[advMapping[g]] for g in glyphOrder] advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder) if vOrigMetricses: vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder] useDirect = False vOrigMapping = None if directStore: # Compile both, see which is more compact writer = OTTableWriter() directStore.compile(writer, font) directSize = len(writer.getAllData()) writer = OTTableWriter() indirectStore.compile(writer, font) advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) useDirect = directSize < indirectSize if useDirect: metricsStore = directStore advanceMapping = None else: metricsStore = indirectStore if vOrigMetricses: vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder) return metricsStore, advanceMapping, vOrigMapping