def _Lookup_PairPosFormat2_subtables_recombine(a, b, font): """Combine two subtables that have the same general structure already.""" self = ot.PairPos() self.Format = 2 self.Coverage = ot.Coverage() self.Coverage.Format = 1 glyphs, _ = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in (a, b)]) self.Coverage.glyphs = glyphs self.Class1Count = a.Class1Count + b.Class1Count self.ClassDef1 = ot.ClassDef() classDefs = self.ClassDef1.classDefs = {} offset = a.Class1Count # First subtable overrides any possible shared glyph, so add b first. sets = _ClassDef_invert(b.ClassDef1, allGlyphs=b.Coverage.glyphs) for i, s in enumerate(sets): for g in s: classDefs[g] = i + offset sets = _ClassDef_invert(a.ClassDef1, allGlyphs=a.Coverage.glyphs) assert len(sets) <= offset for i, s in enumerate(sets): for g in s: classDefs[g] = i records = self.Class1Record = [] assert a.Class1Count == len(a.Class1Record) assert b.Class1Count == len(b.Class1Record) records.extend(a.Class1Record) records.extend(b.Class1Record) for name in ('Class2Count', 'ClassDef2', 'ValueFormat1', 'ValueFormat2'): setattr(self, name, getattr(a, name)) return self
def _Lookup_PairPosFormat2_subtables_flatten(lst, font): assert allEqual([l.ValueFormat2 == 0 for l in lst if l.Class1Record]), "Report bug against fonttools." self = ot.PairPos() self.Format = 2 self.Coverage = ot.Coverage() self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0) self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0) # Align them glyphs, _ = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in lst]) self.Coverage.glyphs = glyphs matrices = _PairPosFormat2_align_matrices(self, lst, font, transparent=True) matrix = self.Class1Record = [] for rows in zip(*matrices): row = ot.Class1Record() matrix.append(row) row.Class2Record = [] row = row.Class2Record for cols in zip(*list(r.Class2Record for r in rows)): col = next(iter(c for c in cols if c is not None)) row.append(col) return self
def makeGDEF(self): gdef = otTables.GDEF() gdef.Version = 1.0 gdef.GlyphClassDef = otTables.GlyphClassDef() inferredGlyphClass = {} for lookup in self.lookups_: inferredGlyphClass.update(lookup.inferGlyphClasses()) marks = {} # glyph --> markClass for markClass in self.parseTree.markClasses.values(): for markClassDef in markClass.definitions: for glyph in markClassDef.glyphSet(): other = marks.get(glyph) if other not in (None, markClass): name1, name2 = sorted([markClass.name, other.name]) raise FeatureLibError( 'Glyph %s cannot be both in ' 'markClass @%s and @%s' % (glyph, name1, name2), markClassDef.location) marks[glyph] = markClass inferredGlyphClass[glyph] = 3 gdef.GlyphClassDef.classDefs = inferredGlyphClass gdef.AttachList = None gdef.LigCaretList = None markAttachClass = {g: c for g, (c, _) in self.markAttach_.items()} if markAttachClass: gdef.MarkAttachClassDef = otTables.MarkAttachClassDef() gdef.MarkAttachClassDef.classDefs = markAttachClass else: gdef.MarkAttachClassDef = None if self.markFilterSets_: gdef.Version = 0x00010002 m = gdef.MarkGlyphSetsDef = otTables.MarkGlyphSetsDef() m.MarkSetTableFormat = 1 m.MarkSetCount = len(self.markFilterSets_) m.Coverage = [] filterSets = [(id, glyphs) for (glyphs, id) in self.markFilterSets_.items()] for i, glyphs in sorted(filterSets): coverage = otTables.Coverage() coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID) m.Coverage.append(coverage) if (len(gdef.GlyphClassDef.classDefs) == 0 and gdef.MarkAttachClassDef is None): return None result = getTableClass("GDEF")() result.table = gdef return result
def _Lookup_PairPosFormat1_subtables_merge_overlay(lst, font): self = ot.PairPos() self.Format = 1 self.Coverage = ot.Coverage() self.Coverage.Format = 1 self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0) self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0) # Align them glyphs, padded = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in lst], [v.PairSet for v in lst]) self.Coverage.glyphs = glyphs self.PairSet = [_PairSet_merge_overlay([v for v in values if v is not None], font) for values in zip(*padded)] self.PairSetCount = len(self.PairSet) return self
def _Lookup_PairPosFormat1_subtables_flatten(lst, font): assert allEqual([l.ValueFormat2 == 0 for l in lst if l.PairSet]), "Report bug against fonttools." self = ot.PairPos() self.Format = 1 self.Coverage = ot.Coverage() self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0) self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0) # Align them glyphs, padded = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in lst], [v.PairSet for v in lst]) self.Coverage.glyphs = glyphs self.PairSet = [_PairSet_flatten([v for v in values if v is not None], font) for values in zip(*padded)] self.PairSetCount = len(self.PairSet) return self
def _PairSet_flatten(lst, font): self = ot.PairSet() self.Coverage = ot.Coverage() # Align them glyphs, padded = _merge_GlyphOrders( font, [[v.SecondGlyph for v in vs.PairValueRecord] for vs in lst], [vs.PairValueRecord for vs in lst]) self.Coverage.glyphs = glyphs self.PairValueRecord = pvrs = [] for values in zip(*padded): for v in values: if v is not None: pvrs.append(v) break else: assert False self.PairValueCount = len(self.PairValueRecord) return self
def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format): glyphs, _ = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in lst], None) num_glyphs = len(glyphs) new = ot.SinglePos() new.Format = 2 new.ValueFormat = min_inclusive_rec_format new.Coverage = ot.Coverage() new.Coverage.glyphs = glyphs new.ValueCount = num_glyphs new.Value = [None] * num_glyphs for singlePos in lst: if singlePos.Format == 1: val_rec = singlePos.Value for gname in singlePos.Coverage.glyphs: i = glyphs.index(gname) new.Value[i] = copy.deepcopy(val_rec) elif singlePos.Format == 2: for j, gname in enumerate(singlePos.Coverage.glyphs): val_rec = singlePos.Value[j] i = glyphs.index(gname) new.Value[i] = copy.deepcopy(val_rec) return [new]
def build(self): subtables = [] # If multiple glyphs have the same ValueRecord, they can go into # the same subtable which saves space. Therefore, we first build # a reverse mapping from ValueRecord to glyph coverage. values = {} for glyph, valuerecord in self.mapping.items(): values.setdefault(valuerecord, []).append(glyph) # For compliance with the OpenType specification, # we sort the glyph coverage by glyph ID. for glyphs in values.values(): glyphs.sort(key=self.font.getGlyphID) # Make a list of (glyphs, (otBase.ValueRecord, int valueFormat)). # Glyphs with the same otBase.ValueRecord are grouped into one item. values = [(glyphs, makeOpenTypeValueRecord(valrec)) for valrec, glyphs in values.items()] # Find out which glyphs should be encoded as SinglePos format 2. # Format 2 is more compact than format 1 when multiple glyphs # have different values but share the same integer valueFormat. format2 = {} # valueFormat --> [(glyph, value), (glyph, value), ...] for glyphs, (value, valueFormat) in values: if len(glyphs) == 1: glyph = glyphs[0] format2.setdefault(valueFormat, []).append((glyph, value)) # Only use format 2 if multiple glyphs share the same valueFormat. # Otherwise, format 1 is more compact. format2 = [(valueFormat, valueList) for valueFormat, valueList in format2.items() if len(valueList) > 1] format2.sort() format2Glyphs = set() # {"A", "B", "C"} for _, valueList in format2: for (glyph, _) in valueList: format2Glyphs.add(glyph) for valueFormat, valueList in format2: valueList.sort(key=lambda x: self.font.getGlyphID(x[0])) st = otTables.SinglePos() subtables.append(st) st.Format = 2 st.ValueFormat = valueFormat st.Coverage = otTables.Coverage() st.Coverage.glyphs = [glyph for glyph, _value in valueList] st.ValueCount = len(valueList) st.Value = [value for _glyph, value in valueList] # To make the ordering of our subtables deterministic, # we sort subtables by the first glyph ID in their coverage. # Not doing this would be OK for OpenType, but testing the # compiler would be harder with non-deterministic output. values.sort(key=lambda x: self.font.getGlyphID(x[0][0])) for glyphs, (value, valueFormat) in values: if len(glyphs) == 1 and glyphs[0] in format2Glyphs: continue # already emitted as part of a format 2 subtable st = otTables.SinglePos() subtables.append(st) st.Format = 1 st.Coverage = self.buildCoverage_(glyphs) st.Value, st.ValueFormat = value, valueFormat return self.buildLookup_(subtables)
def makeCoverage(glyphs): coverage = otTables.Coverage() coverage.glyphs = glyphs return coverage
def buildCoverage(glyphs, glyphMap): if not glyphs: return None self = ot.Coverage() self.glyphs = sorted(glyphs, key=glyphMap.__getitem__) return self
def _coverage(glyphs): c = otTables.Coverage() c.glyphs = glyphs return c