def buildGSUB(): """Build a GSUB table from scratch.""" fontTable = newTable("GSUB") gsub = fontTable.table = ot.GSUB() gsub.Version = 0x00010001 # allow gsub.FeatureVariations gsub.ScriptList = ot.ScriptList() gsub.ScriptList.ScriptRecord = [] gsub.FeatureList = ot.FeatureList() gsub.FeatureList.FeatureRecord = [] gsub.LookupList = ot.LookupList() gsub.LookupList.Lookup = [] srec = ot.ScriptRecord() srec.ScriptTag = 'DFLT' srec.Script = ot.Script() srec.Script.DefaultLangSys = None srec.Script.LangSysRecord = [] langrec = ot.LangSysRecord() langrec.LangSys = ot.LangSys() langrec.LangSys.ReqFeatureIndex = 0xFFFF langrec.LangSys.FeatureIndex = [0] srec.Script.DefaultLangSys = langrec.LangSys gsub.ScriptList.ScriptRecord.append(srec) gsub.FeatureVariations = None return fontTable
def create_lookup_list(lookups): """Create a LookupList for the GSUB table.""" lookup_list = otTables.LookupList() lookup_list.LookupCount = len(lookups) lookup_list.Lookup = lookups return lookup_list
def add_gsub_to_font(fontfile): """Adds an empty GSUB table to a font.""" font = ttLib.TTFont(fontfile) gsub_table = ttLib.getTableClass('GSUB')('GSUB') gsub_table.table = otTables.GSUB() gsub_table.table.Version = 1.0 gsub_table.table.ScriptList = otTables.ScriptList() gsub_table.table.ScriptCount = 1 gsub_table.table.LookupList = otTables.LookupList() gsub_table.table.LookupList.LookupCount = 0 gsub_table.table.LookupList.Lookup = [] gsub_table.table.FeatureList = otTables.FeatureList() gsub_table.table.FeatureList.FeatureCount = 0 gsub_table.table.LookupList.FeatureRecord = [] script_record = otTables.ScriptRecord() script_record.ScriptTag = get_opentype_script_tag(fontfile) script_record.Script = otTables.Script() script_record.Script.LangSysCount = 0 script_record.Script.LangSysRecord = [] default_lang_sys = otTables.DefaultLangSys() default_lang_sys.FeatureIndex = [] default_lang_sys.FeatureCount = 0 default_lang_sys.LookupOrder = None default_lang_sys.ReqFeatureIndex = 65535 script_record.Script.DefaultLangSys = default_lang_sys gsub_table.table.ScriptList.ScriptRecord = [script_record] font['GSUB'] = gsub_table target_file = tempfile.gettempdir() + '/' + os.path.basename(fontfile) font.save(target_file) return target_file
def merge(self, m, tables): assert len(tables) == len(m.duplicateGlyphsPerFont) for i, (table, dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)): if not dups: continue assert (table is not None and table is not NotImplemented ), "Have duplicates to resolve for font %d but no GSUB: %s" % ( i + 1, dups) synthFeature = None synthLookup = None for script in table.table.ScriptList.ScriptRecord: if script.ScriptTag == 'DFLT': continue # XXX for langsys in [script.Script.DefaultLangSys] + [ l.LangSys for l in script.Script.LangSysRecord ]: if langsys is None: continue # XXX Create! feature = [ v for v in langsys.FeatureIndex if v.FeatureTag == 'locl' ] assert len(feature) <= 1 if feature: feature = feature[0] else: if not synthFeature: synthFeature = otTables.FeatureRecord() synthFeature.FeatureTag = 'locl' f = synthFeature.Feature = otTables.Feature() f.FeatureParams = None f.LookupCount = 0 f.LookupListIndex = [] langsys.FeatureIndex.append(synthFeature) langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag) table.table.FeatureList.FeatureRecord.append( synthFeature) table.table.FeatureList.FeatureCount += 1 feature = synthFeature if not synthLookup: subtable = otTables.SingleSubst() subtable.mapping = dups synthLookup = otTables.Lookup() synthLookup.LookupFlag = 0 synthLookup.LookupType = 1 synthLookup.SubTableCount = 1 synthLookup.SubTable = [subtable] if table.table.LookupList is None: # mtiLib uses None as default value for LookupList, # while feaLib points to an empty array with count 0 # TODO: make them do the same table.table.LookupList = otTables.LookupList() table.table.LookupList.Lookup = [] table.table.LookupList.LookupCount = 0 table.table.LookupList.Lookup.append(synthLookup) table.table.LookupList.LookupCount += 1 feature.Feature.LookupListIndex[:0] = [synthLookup] feature.Feature.LookupCount += 1 DefaultTable.merge(self, m, tables) return self
def doit(args): font = args.ifont args.type = args.type.upper() for tag in ('GSUB', 'GPOS'): if tag == args.type or args.type == 'BOTH': table = ttLib.getTableClass(tag)() t = getattr(otTables, tag, None)() t.Version = 1.0 t.ScriptList = otTables.ScriptList() t.ScriptList.ScriptRecord = [] t.FeatureList = otTables.FeatureList() t.FeatureList.FeatureRecord = [] t.LookupList = otTables.LookupList() t.LookupList.Lookup = [] srec = otTables.ScriptRecord() srec.ScriptTag = args.script srec.Script = otTables.Script() srec.Script.DefaultLangSys = None srec.Script.LangSysRecord = [] t.ScriptList.ScriptRecord.append(srec) t.ScriptList.ScriptCount = 1 t.FeatureList.FeatureCount = 0 t.LookupList.LookupCount = 0 table.table = t font[tag] = table return font
def parseGSUBGPOS(lines, font, tableTag): container = ttLib.getTableClass(tableTag)() lookupMap = DeferredMapping() featureMap = DeferredMapping() assert tableTag in ('GSUB', 'GPOS') log.debug("Parsing %s", tableTag) self = getattr(ot, tableTag)() self.Version = 0x00010000 fields = { 'script table begin': ('ScriptList', lambda lines: parseScriptList (lines, featureMap)), 'feature table begin': ('FeatureList', lambda lines: parseFeatureList (lines, lookupMap, featureMap)), 'lookup': ('LookupList', None), } for attr,parser in fields.values(): setattr(self, attr, None) while lines.peek() is not None: typ = lines.peek()[0].lower() if typ not in fields: log.debug('Skipping %s', lines.peek()) next(lines) continue attr,parser = fields[typ] if typ == 'lookup': if self.LookupList is None: self.LookupList = ot.LookupList() self.LookupList.Lookup = [] _, name, _ = lines.peek() lookup = parseLookup(lines, tableTag, font, lookupMap) if lookupMap is not None: assert name not in lookupMap, "Duplicate lookup name: %s" % name lookupMap[name] = len(self.LookupList.Lookup) else: assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup)) self.LookupList.Lookup.append(lookup) else: assert getattr(self, attr) is None, attr setattr(self, attr, parser(lines)) if self.LookupList: self.LookupList.LookupCount = len(self.LookupList.Lookup) if lookupMap is not None: lookupMap.applyDeferredMappings() if featureMap is not None: featureMap.applyDeferredMappings() container.table = self return container
def _add_gpos_table(self): logger.info('Adding GPOS table to "%s"', self) ttfont = self.ttfont assert ttfont.get('GPOS') is None table = otTables.GPOS() table.Version = 0x00010000 table.ScriptList = otTables.ScriptList() table.ScriptList.ScriptRecord = [self.create_script_record()] table.FeatureList = otTables.FeatureList() table.FeatureList.FeatureRecord = [] table.LookupList = otTables.LookupList() table.LookupList.Lookup = [] gpos = ttfont['GPOS'] = newTable('GPOS') gpos.table = table return gpos
def parseGSUBGPOS(lines, font, tableTag): lookupMap = None#{} Until we support forward references... featureMap = {} assert tableTag in ('GSUB', 'GPOS') debug("Parsing", tableTag) self = getattr(ot, tableTag)() self.Version = 1.0 fields = { 'script table begin': ('ScriptList', lambda lines: parseScriptList (lines, featureMap)), 'feature table begin': ('FeaturetList', lambda lines: parseFeatureList (lines, lookupMap, featureMap)), 'lookup': ('LookupList', None), } for attr,parser in fields.values(): setattr(self, attr, None) while lines.peek() is not None: typ = lines.peek()[0].lower() if typ not in fields: debug ('Skipping', lines.peek()) next(lines) continue attr,parser = fields[typ] if typ == 'lookup': if self.LookupList is None: self.LookupList = ot.LookupList() self.LookupList.Lookup = [] _, name, _ = lines.peek() lookup = parseLookup(lines, tableTag, font, lookupMap) if lookupMap: assert name not in lookupMap, "Duplicate lookup name: %s" % name lookupMap[name] = len(self.LookupList.Lookup) else: assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup)) self.LookupList.Lookup.append(lookup) else: assert getattr(self, attr) is None, attr setattr(self, attr, parser(lines)) if self.LookupList: self.LookupList.LookupCount = len(self.LookupList.Lookup) return self
def makeTable(self, tag): table = getattr(otTables, tag, None)() table.Version = 1.0 table.ScriptList = otTables.ScriptList() table.ScriptList.ScriptRecord = [] table.FeatureList = otTables.FeatureList() table.FeatureList.FeatureRecord = [] table.LookupList = otTables.LookupList() table.LookupList.Lookup = [] for lookup in self.lookups_: lookup.lookup_index = None for i, lookup_builder in enumerate(self.lookups_): if lookup_builder.table != tag: continue # If multiple lookup builders would build equivalent lookups, # emit them only once. This is quadratic in the number of lookups, # but the checks are cheap. If performance ever becomes an issue, # we could hash the lookup content and only compare those with # the same hash value. equivalent_builder = None for other_builder in self.lookups_[:i]: if lookup_builder.equals(other_builder): equivalent_builder = other_builder if equivalent_builder is not None: lookup_builder.lookup_index = equivalent_builder.lookup_index continue lookup_builder.lookup_index = len(table.LookupList.Lookup) table.LookupList.Lookup.append(lookup_builder.build()) # Build a table for mapping (tag, lookup_indices) to feature_index. # For example, ('liga', (2,3,7)) --> 23. feature_indices = {} required_feature_indices = {} # ('latn', 'DEU') --> 23 scripts = {} # 'latn' --> {'DEU': [23, 24]} for feature #23,24 for key, lookups in sorted(self.features_.items()): script, lang, feature_tag = key # l.lookup_index will be None when a lookup is not needed # for the table under construction. For example, substitution # rules will have no lookup_index while building GPOS tables. lookup_indices = tuple([ l.lookup_index for l in lookups if l.lookup_index is not None ]) if len(lookup_indices) == 0: continue feature_key = (feature_tag, lookup_indices) feature_index = feature_indices.get(feature_key) if feature_index is None: feature_index = len(table.FeatureList.FeatureRecord) frec = otTables.FeatureRecord() frec.FeatureTag = feature_tag frec.Feature = otTables.Feature() frec.Feature.FeatureParams = None frec.Feature.LookupListIndex = lookup_indices frec.Feature.LookupCount = len(lookup_indices) table.FeatureList.FeatureRecord.append(frec) feature_indices[feature_key] = feature_index scripts.setdefault(script, {}).setdefault(lang, []).append(feature_index) if self.required_features_.get((script, lang)) == feature_tag: required_feature_indices[(script, lang)] = feature_index # Build ScriptList. for script, lang_features in sorted(scripts.items()): srec = otTables.ScriptRecord() srec.ScriptTag = script srec.Script = otTables.Script() srec.Script.DefaultLangSys = None srec.Script.LangSysRecord = [] for lang, feature_indices in sorted(lang_features.items()): langrec = otTables.LangSysRecord() langrec.LangSys = otTables.LangSys() langrec.LangSys.LookupOrder = None req_feature_index = \ required_feature_indices.get((script, lang)) if req_feature_index is None: langrec.LangSys.ReqFeatureIndex = 0xFFFF else: langrec.LangSys.ReqFeatureIndex = req_feature_index langrec.LangSys.FeatureIndex = [ i for i in feature_indices if i != req_feature_index ] langrec.LangSys.FeatureCount = \ len(langrec.LangSys.FeatureIndex) if lang == "dflt": srec.Script.DefaultLangSys = langrec.LangSys else: langrec.LangSysTag = lang srec.Script.LangSysRecord.append(langrec) srec.Script.LangSysCount = len(srec.Script.LangSysRecord) table.ScriptList.ScriptRecord.append(srec) table.ScriptList.ScriptCount = len(table.ScriptList.ScriptRecord) table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord) table.LookupList.LookupCount = len(table.LookupList.Lookup) return table
parser.add_argument('infont', help='Input font file') parser.add_argument('outfont', help='Output font file') parser.add_argument('-s','--script',default='DFLT', help='Script tag to generate [DFLT]') parser.add_argument('-t','--type',default='both', help='Table to create: gpos, gsub, [both]') args = parser.parse_args() inf = ttLib.TTFont(args.infont) for tag in ('GSUB', 'GPOS') : if tag.lower() == args.type or args.type == 'both' : table = ttLib.getTableClass(tag)() t = getattr(otTables, tag, None)() t.Version = 1.0 t.ScriptList = otTables.ScriptList() t.ScriptList.ScriptRecord = [] t.FeatureList = otTables.FeatureList() t.FeatureList.FeatureRecord = [] t.LookupList = otTables.LookupList() t.LookupList.Lookup = [] srec = otTables.ScriptRecord() srec.ScriptTag = args.script srec.Script = otTables.Script() srec.Script.DefaultLangSys = None srec.Script.LangSysRecord = [] t.ScriptList.ScriptRecord.append(srec) t.ScriptList.ScriptCount = 1 t.FeatureList.FeatureCount = 0 t.LookupList.LookupCount = 0 table.table = t inf[tag] = table inf.save(args.outfont)
def makeTable(self, tag): table = getattr(otTables, tag, None)() table.Version = 1.0 table.ScriptList = otTables.ScriptList() table.ScriptList.ScriptRecord = [] table.FeatureList = otTables.FeatureList() table.FeatureList.FeatureRecord = [] table.LookupList = otTables.LookupList() table.LookupList.Lookup = self.buildLookups_(tag) # Build a table for mapping (tag, lookup_indices) to feature_index. # For example, ('liga', (2,3,7)) --> 23. feature_indices = {} required_feature_indices = {} # ('latn', 'DEU') --> 23 scripts = {} # 'latn' --> {'DEU': [23, 24]} for feature #23,24 for key, lookups in sorted(self.features_.items()): script, lang, feature_tag = key # l.lookup_index will be None when a lookup is not needed # for the table under construction. For example, substitution # rules will have no lookup_index while building GPOS tables. lookup_indices = tuple([l.lookup_index for l in lookups if l.lookup_index is not None]) if len(lookup_indices) == 0: continue feature_key = (feature_tag, lookup_indices) feature_index = feature_indices.get(feature_key) if feature_index is None: feature_index = len(table.FeatureList.FeatureRecord) frec = otTables.FeatureRecord() frec.FeatureTag = feature_tag frec.Feature = otTables.Feature() frec.Feature.FeatureParams = None frec.Feature.LookupListIndex = lookup_indices frec.Feature.LookupCount = len(lookup_indices) table.FeatureList.FeatureRecord.append(frec) feature_indices[feature_key] = feature_index scripts.setdefault(script, {}).setdefault(lang, []).append( feature_index) if self.required_features_.get((script, lang)) == feature_tag: required_feature_indices[(script, lang)] = feature_index # Build ScriptList. for script, lang_features in sorted(scripts.items()): srec = otTables.ScriptRecord() srec.ScriptTag = script srec.Script = otTables.Script() srec.Script.DefaultLangSys = None srec.Script.LangSysRecord = [] for lang, feature_indices in sorted(lang_features.items()): langrec = otTables.LangSysRecord() langrec.LangSys = otTables.LangSys() langrec.LangSys.LookupOrder = None req_feature_index = \ required_feature_indices.get((script, lang)) if req_feature_index is None: langrec.LangSys.ReqFeatureIndex = 0xFFFF else: langrec.LangSys.ReqFeatureIndex = req_feature_index langrec.LangSys.FeatureIndex = [i for i in feature_indices if i != req_feature_index] langrec.LangSys.FeatureCount = \ len(langrec.LangSys.FeatureIndex) if lang == "dflt": srec.Script.DefaultLangSys = langrec.LangSys else: langrec.LangSysTag = lang srec.Script.LangSysRecord.append(langrec) srec.Script.LangSysCount = len(srec.Script.LangSysRecord) table.ScriptList.ScriptRecord.append(srec) table.ScriptList.ScriptCount = len(table.ScriptList.ScriptRecord) table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord) table.LookupList.LookupCount = len(table.LookupList.Lookup) return table