def create_lookup(table, font, flag=0): """Create a Lookup based on mapping table.""" cmap = font_data.get_cmap(font) ligatures = {} for output, (ch1, ch2) in table.iteritems(): output = cmap[output] ch1 = get_glyph_name_or_create(ch1, font) ch2 = get_glyph_name_or_create(ch2, font) ligature = otTables.Ligature() ligature.CompCount = 2 ligature.Component = [ch2] ligature.LigGlyph = output try: ligatures[ch1].append(ligature) except KeyError: ligatures[ch1] = [ligature] ligature_subst = otTables.LigatureSubst() ligature_subst.ligatures = ligatures lookup = otTables.Lookup() lookup.LookupType = 4 lookup.LookupFlag = flag lookup.SubTableCount = 1 lookup.SubTable = [ligature_subst] return lookup
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 add_ligature(font, codes): if 'GSUB' not in font: ligature_subst = otTables.LigatureSubst() ligature_subst.ligatures = {} lookup = otTables.Lookup() lookup.LookupType = 4 lookup.LookupFlag = 0 lookup.SubTableCount = 1 lookup.SubTable = [ligature_subst] font['GSUB'] = add_emoji_gsub.create_simple_gsub([lookup]) else: lookup = font['GSUB'].table.LookupList.Lookup[0] assert lookup.LookupType == 4 assert lookup.LookupFlag == 0 ligatures = lookup.SubTable[0].ligatures lig = otTables.Ligature() lig.CompCount = len(codes) lig.Component = [glyph_name([code]) for code in codes[1:]] lig.LigGlyph = glyph_name(codes) first = "%04X" % int(codes[0], 16) try: ligatures[first].append(lig) except KeyError: ligatures[first] = [lig]
def buildLookup(subtables, flags=0, markFilterSet=None): if subtables is None: return None subtables = [st for st in subtables if st is not None] if not subtables: return None assert all(t.LookupType == subtables[0].LookupType for t in subtables), \ ("all subtables must have the same LookupType; got %s" % repr([t.LookupType for t in subtables])) self = ot.Lookup() self.LookupType = subtables[0].LookupType self.LookupFlag = flags self.SubTable = subtables self.SubTableCount = len(self.SubTable) if markFilterSet is not None: assert self.LookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET, \ ("if markFilterSet is not None, flags must set " "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x%04x" % flags) assert isinstance(markFilterSet, int), markFilterSet self.MarkFilteringSet = markFilterSet else: assert (self.LookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET) == 0, \ ("if markFilterSet is None, flags must not set " "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x%04x" % flags) return self
def get_gsub_ligature_lookup(font): """If the font does not have a GSUB table, create one with a ligature substitution lookup. If it does, ensure the first lookup is a properly initialized ligature substitution lookup. Return the lookup.""" # The template might include more lookups after lookup 0, if it has a # GSUB table. if 'GSUB' not in font: ligature_subst = otTables.LigatureSubst() ligature_subst.ligatures = {} lookup = otTables.Lookup() lookup.LookupType = 4 lookup.LookupFlag = 0 lookup.SubTableCount = 1 lookup.SubTable = [ligature_subst] font['GSUB'] = add_emoji_gsub.create_simple_gsub([lookup]) else: lookup = font['GSUB'].table.LookupList.Lookup[0] assert lookup.LookupFlag == 0 # importXML doesn't fully init GSUB structures, so help it out st = lookup.SubTable[0] if not hasattr(lookup, 'LookupType'): assert st.LookupType == 4 setattr(lookup, 'LookupType', 4) if not hasattr(st, 'ligatures'): setattr(st, 'ligatures', {}) return lookup
def add_ligature(font, seq, name): if 'GSUB' not in font: ligature_subst = otTables.LigatureSubst() ligature_subst.ligatures = {} lookup = otTables.Lookup() lookup.LookupType = 4 lookup.LookupFlag = 0 lookup.SubTableCount = 1 lookup.SubTable = [ligature_subst] font['GSUB'] = add_emoji_gsub.create_simple_gsub([lookup]) else: lookup = font['GSUB'].table.LookupList.Lookup[0] assert lookup.LookupType == 4 assert lookup.LookupFlag == 0 ligatures = lookup.SubTable[0].ligatures lig = otTables.Ligature() lig.CompCount = len(seq) lig.Component = seq[1:] lig.LigGlyph = name first = seq[0] try: ligatures[first].append(lig) except KeyError: ligatures[first] = [lig]
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" % (i + 1) lookupMap = dict((id(v), v) for v in table.table.LookupList.Lookup) featureMap = dict( (id(v), v) for v in table.table.FeatureList.FeatureRecord) 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 ]: feature = [ featureMap[v] for v in langsys.FeatureIndex if featureMap[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(id(synthFeature)) featureMap[id(synthFeature)] = synthFeature langsys.FeatureIndex.sort( key=lambda v: featureMap[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] table.table.LookupList.Lookup.append(synthLookup) table.table.LookupList.LookupCount += 1 feature.Feature.LookupListIndex[:0] = [id(synthLookup)] feature.Feature.LookupCount += 1 DefaultTable.merge(self, m, tables) return self
def buildLookup_(self, subtables): lookup = otTables.Lookup() lookup.LookupFlag = self.lookupflag lookup.LookupType = self.lookup_type lookup.SubTable = subtables lookup.SubTableCount = len(subtables) if self.markFilterSet is not None: lookup.MarkFilteringSet = self.markFilterSet return lookup
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 if table is None or table is NotImplemented: # Checks whether the dups are equivalent or not. # Discard gid if its shape is not equal to that of oldgid. for oldgid, gid in dups.items(): if not isGlyphSame(m.fonts, oldgid, gid): oldgname, oldidx = getGlyphNameAndFontIndex(oldgid) gname, idx = getGlyphNameAndFontIndex(gid) log.warn("%s:<%s> is dropped and replaced by %s:<%s>" % (m.fontfiles[idx], gname, m.fontfiles[oldidx], oldgname)) continue lookupMap = {id(v):v for v in table.table.LookupList.Lookup} featureMap = {id(v):v for v in table.table.FeatureList.FeatureRecord} 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 = [featureMap[v] for v in langsys.FeatureIndex if featureMap[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(id(synthFeature)) featureMap[id(synthFeature)] = synthFeature langsys.FeatureIndex.sort(key=lambda v: featureMap[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] table.table.LookupList.Lookup.append(synthLookup) table.table.LookupList.LookupCount += 1 feature.Feature.LookupListIndex[:0] = [id(synthLookup)] feature.Feature.LookupCount += 1 DefaultTable.merge(self, m, tables) return self
def parseLookup(lines, tableTag, font, lookupMap=None): line = lines.expect('lookup') _, name, typ = line log.debug("Parsing lookup type %s %s", typ, name) lookup = ot.Lookup() with lines.until('lookup end'): lookup.LookupFlag, filterset = parseLookupFlags(lines) if filterset is not None: lookup.MarkFilteringSet = filterset lookup.LookupType, parseLookupSubTable = { 'GSUB': { 'single': (0, parseSingleSubst), 'multiple': (0, parseMultiple), 'alternate': (0, parseAlternate), 'ligature': (0, parseLigature), 'context': (5, parseContextSubst), 'chained': (6, parseChainedSubst), 'reversechained': (8, parseReverseChainedSubst), }, 'GPOS': { 'single': (0, parseSinglePos), 'pair': (2, parsePair), 'kernset': (2, parseKernset), 'cursive': (0, parseCursive), 'mark to base': (4, parseMarkToBase), 'mark to ligature': (5, parseMarkToLigature), 'mark to mark': (6, parseMarkToMark), 'context': (7, parseContextPos), 'chained': (8, parseChainedPos), }, }[tableTag][typ] subtables = [] while lines.peek(): with lines.until(('% subtable', 'subtable end')): while lines.peek(): if lookup.LookupType is 0: subtable = parseLookupSubTable(lines, font, lookupMap) lookup.LookupType = subtable.LookupType else: subtable = ot.lookupTypes[tableTag][ lookup.LookupType]() parseLookupSubTable(subtable, lines, font, lookupMap) subtables.append(subtable) if lines.peek() and lines.peek()[0] in ('% subtable', 'subtable end'): next(lines) lines.expect('lookup end') lookup.SubTable = subtables lookup.SubTableCount = len(lookup.SubTable) if lookup.SubTableCount is 0: return None return lookup
def build(self): lookup = otTables.Lookup() lookup.SubTable = [] st = otTables.SingleSubst() st.mapping = self.mapping lookup.SubTable.append(st) lookup.LookupFlag = self.lookup_flag lookup.LookupType = self.lookup_type lookup.SubTableCount = len(lookup.SubTable) return lookup
def build(self): lookup = otTables.Lookup() lookup.SubTable = [] st = otTables.AlternateSubst() st.Format = 1 st.alternates = self.alternates lookup.SubTable.append(st) lookup.LookupFlag = self.lookup_flag lookup.LookupType = self.lookup_type lookup.SubTableCount = len(lookup.SubTable) return lookup
def parseLookup(lines, tableTag, font, lookupMap=None): line = lines.expect('lookup') _, name, typ = line log.debug("Parsing lookup type %s %s", typ, name) lookup = ot.Lookup() lookup.LookupFlag,filterset = parseLookupFlags(lines) if filterset is not None: lookup.MarkFilteringSet = filterset lookup.LookupType, parseLookupSubTable = { 'GSUB': { 'single': (1, parseSingleSubst), 'multiple': (2, parseMultiple), 'alternate': (3, parseAlternate), 'ligature': (4, parseLigature), 'context': (5, parseContextSubst), 'chained': (6, parseChainedSubst), 'reversechained':(8, parseReverseChainedSubst), }, 'GPOS': { 'single': (1, parseSinglePos), 'pair': (2, parsePair), 'kernset': (2, parseKernset), 'cursive': (3, parseCursive), 'mark to base': (4, parseMarkToBase), 'mark to ligature':(5, parseMarkToLigature), 'mark to mark': (6, parseMarkToMark), 'context': (7, parseContextPos), 'chained': (8, parseChainedPos), }, }[tableTag][typ] with lines.until('lookup end'): subtables = [] while lines.peek(): with lines.until(('% subtable', 'subtable end')): while lines.peek(): subtable = parseLookupSubTable(lines, font, lookupMap) assert lookup.LookupType == subtable.LookupType subtables.append(subtable) if lines.peeks()[0] in ('% subtable', 'subtable end'): next(lines) lines.expect('lookup end') lookup.SubTable = subtables lookup.SubTableCount = len(lookup.SubTable) if lookup.SubTableCount == 0: # Remove this return when following is fixed: # https://github.com/fonttools/fonttools/issues/789 return None return lookup
def build(self): lookup = otTables.Lookup() lookup.SubTable = [] st = otTables.LigatureSubst() st.Format = 1 st.ligatures = {} for components in sorted(self.ligatures.keys(), key=self.make_key): lig = otTables.Ligature() lig.Component = components lig.LigGlyph = self.ligatures[components] st.ligatures.setdefault(components[0], []).append(lig) lookup.SubTable.append(st) lookup.LookupFlag = self.lookup_flag lookup.LookupType = self.lookup_type lookup.SubTableCount = len(lookup.SubTable) return lookup
def add_watermark(self, ttf): cmap = ttf.getBestCmap() gsub = ttf['GSUB'].table # Obtain Version string m = re.search('^Version (\d*)\.(\d*)', font_data.font_version(ttf)) if not m: raise ValueError('The font does not have proper version string.') major = m.group(1) minor = m.group(2) # Replace the dot with space since NotoColorEmoji does not have glyph for dot. glyphs = [cmap[ord(x)] for x in '%s %s' % (major, minor)] # Update Glyph metrics ttf.getGlyphOrder().append(WATERMARK_NEW_GLYPH_ID) refGlyphId = cmap[WATERMARK_REF_CODE_POINT] ttf['hmtx'].metrics[WATERMARK_NEW_GLYPH_ID] = ttf['hmtx'].metrics[refGlyphId] ttf['vmtx'].metrics[WATERMARK_NEW_GLYPH_ID] = ttf['vmtx'].metrics[refGlyphId] # Add new Glyph to cmap font_data.add_to_cmap(ttf, { WATERMARK_NEW_CODE_POINT : WATERMARK_NEW_GLYPH_ID }) # Add lookup table for the version string. lookups = gsub.LookupList.Lookup new_lookup = otTables.Lookup() new_lookup.LookupType = 2 # Multiple Substitution Subtable. new_lookup.LookupFlag = 0 new_subtable = otTables.MultipleSubst() new_subtable.mapping = { WATERMARK_NEW_GLYPH_ID : tuple(glyphs) } new_lookup.SubTable = [ new_subtable ] new_lookup_index = len(lookups) lookups.append(new_lookup) # Add feature feature = next(x for x in gsub.FeatureList.FeatureRecord if x.FeatureTag == 'ccmp') if not feature: raise ValueError("Font doesn't contain ccmp feature.") feature.Feature.LookupListIndex.append(new_lookup_index)
def init_gsub(self): """Call this if you are going to add ligatures to the font. Creates a GSUB table if there isn't one already.""" if hasattr(self, 'ligatures'): return font = self.font if 'GSUB' not in font: ligature_subst = otTables.LigatureSubst() ligature_subst.ligatures = {} lookup = otTables.Lookup() lookup.LookupType = 4 lookup.LookupFlag = 0 lookup.SubTableCount = 1 lookup.SubTable = [ligature_subst] font['GSUB'] = add_emoji_gsub.create_simple_gsub([lookup]) else: lookup = font['GSUB'].table.LookupList.Lookup[0] assert lookup.LookupType == 4 assert lookup.LookupFlag == 0 self.ligatures = lookup.SubTable[0].ligatures