def build(self): subtables = [] # (valueFormat1, valueFormat2) --> [(glyph1, glyph2, value1, value2)*] format1 = {} for (gc1, gc2), (location, value1, value2) in self.pairs.items(): if len(gc1) == 1 and len(gc2) == 1: val1, valFormat1 = makeOpenTypeValueRecord(value1) val2, valFormat2 = makeOpenTypeValueRecord(value2) format1.setdefault(((valFormat1, valFormat2)), []).append( (gc1[0], gc2[0], val1, val2)) for (vf1, vf2), pairs in sorted(format1.items()): p = {} for glyph1, glyph2, val1, val2 in pairs: p.setdefault(glyph1, []).append((glyph2, val1, val2)) st = otTables.PairPos() subtables.append(st) st.Format = 1 st.ValueFormat1, st.ValueFormat2 = vf1, vf2 st.Coverage = self.buildCoverage_(p) st.PairSet = [] for glyph in st.Coverage.glyphs: ps = otTables.PairSet() ps.PairValueRecord = [] st.PairSet.append(ps) for glyph2, val1, val2 in sorted( p[glyph], key=lambda x: self.font.getGlyphID(x[0])): pvr = otTables.PairValueRecord() pvr.SecondGlyph = glyph2 pvr.Value1, pvr.Value2 = val1, val2 ps.PairValueRecord.append(pvr) ps.PairValueCount = len(ps.PairValueRecord) st.PairSetCount = len(st.PairSet) return self.buildLookup_(subtables)
def buildPairPosGlyphsSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None): self = ot.PairPos() self.Format = 1 self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0) self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1) p = {} for (glyphA, glyphB), (valA, valB) in pairs.items(): p.setdefault(glyphA, []).append((glyphB, valA, valB)) self.Coverage = buildCoverage({g for g, _ in pairs.keys()}, glyphMap) self.PairSet = [] for glyph in self.Coverage.glyphs: ps = ot.PairSet() ps.PairValueRecord = [] self.PairSet.append(ps) for glyph2, val1, val2 in \ sorted(p[glyph], key=lambda x: glyphMap[x[0]]): pvr = ot.PairValueRecord() pvr.SecondGlyph = glyph2 pvr.Value1 = val1 if val1 and val1.getFormat() != 0 else None pvr.Value2 = val2 if val2 and val2.getFormat() != 0 else None ps.PairValueRecord.append(pvr) ps.PairValueCount = len(ps.PairValueRecord) self.PairSetCount = len(self.PairSet) 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 buildPairPosClassesSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None): coverage = set() classDef1 = ClassDefBuilder(useClass0=True) classDef2 = ClassDefBuilder(useClass0=False) for gc1, gc2 in sorted(pairs): coverage.update(gc1) classDef1.add(gc1) classDef2.add(gc2) self = ot.PairPos() self.Format = 2 self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0) self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1) self.Coverage = buildCoverage(coverage, glyphMap) self.ClassDef1 = classDef1.build() self.ClassDef2 = classDef2.build() classes1 = classDef1.classes() classes2 = classDef2.classes() self.Class1Record = [] for c1 in classes1: rec1 = ot.Class1Record() rec1.Class2Record = [] self.Class1Record.append(rec1) for c2 in classes2: rec2 = ot.Class2Record() rec2.Value1, rec2.Value2 = pairs.get((c1, c2), (None, None)) rec1.Class2Record.append(rec2) self.Class1Count = len(self.Class1Record) self.Class2Count = len(classes2) return self
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_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 parsePair(lines, font, _lookupMap=None): self = ot.PairPos() self.ValueFormat1 = self.ValueFormat2 = 0 typ = lines.peeks()[0].split()[0].lower() if typ in ('left', 'right'): self.Format = 1 values = {} for line in lines: assert len(line) == 4, line side = line[0].split()[0].lower() assert side in ('left', 'right'), side what = line[0][len(side):].title().replace(' ', '') mask = valueRecordFormatDict[what][0] glyph1, glyph2 = makeGlyphs(line[1:3]) value = int(line[3]) if not glyph1 in values: values[glyph1] = {} if not glyph2 in values[glyph1]: values[glyph1][glyph2] = (ValueRecord(),ValueRecord()) rec2 = values[glyph1][glyph2] if side == 'left': self.ValueFormat1 |= mask vr = rec2[0] else: self.ValueFormat2 |= mask vr = rec2[1] assert not hasattr(vr, what), (vr, what) setattr(vr, what, value) self.Coverage = makeCoverage(set(values.keys()), font) self.PairSet = [] for glyph1 in self.Coverage.glyphs: values1 = values[glyph1] pairset = ot.PairSet() records = pairset.PairValueRecord = [] for glyph2 in sorted(values1.keys(), key=font.getGlyphID): values2 = values1[glyph2] pair = ot.PairValueRecord() pair.SecondGlyph = glyph2 pair.Value1 = values2[0] pair.Value2 = values2[1] if self.ValueFormat2 else None records.append(pair) pairset.PairValueCount = len(pairset.PairValueRecord) self.PairSet.append(pairset) self.PairSetCount = len(self.PairSet) elif typ.endswith('class'): self.Format = 2 classDefs = [None, None] while lines.peeks()[0].endswith("class definition begin"): typ = lines.peek()[0][:-len("class definition begin")].lower() idx,klass = { 'first': (0,ot.ClassDef1), 'second': (1,ot.ClassDef2), }[typ] assert classDefs[idx] is None classDefs[idx] = parseClassDef(lines, font, klass=klass) self.ClassDef1, self.ClassDef2 = classDefs self.Class1Count, self.Class2Count = (1+max(c.classDefs.values()) for c in classDefs) self.Class1Record = [ot.Class1Record() for i in range(self.Class1Count)] for rec1 in self.Class1Record: rec1.Class2Record = [ot.Class2Record() for j in range(self.Class2Count)] for rec2 in rec1.Class2Record: rec2.Value1 = ValueRecord() rec2.Value2 = ValueRecord() for line in lines: assert len(line) == 4, line side = line[0].split()[0].lower() assert side in ('left', 'right'), side what = line[0][len(side):].title().replace(' ', '') mask = valueRecordFormatDict[what][0] class1, class2, value = (int(x) for x in line[1:4]) rec2 = self.Class1Record[class1].Class2Record[class2] if side == 'left': self.ValueFormat1 |= mask vr = rec2.Value1 else: self.ValueFormat2 |= mask vr = rec2.Value2 assert not hasattr(vr, what), (vr, what) setattr(vr, what, value) for rec1 in self.Class1Record: for rec2 in rec1.Class2Record: rec2.Value1 = ValueRecord(self.ValueFormat1, rec2.Value1) rec2.Value2 = ValueRecord(self.ValueFormat2, rec2.Value2) \ if self.ValueFormat2 else None self.Coverage = makeCoverage(set(self.ClassDef1.classDefs.keys()), font) else: assert 0, typ return self