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 _PairPosFormat2_align_matrices(self, lst, font, transparent=False): matrices = [l.Class1Record for l in lst] # Align first classes self.ClassDef1, classes = _ClassDef_merge_classify( [l.ClassDef1 for l in lst], [l.Coverage.glyphs for l in lst]) self.Class1Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): nullRow = None coverage = set(l.Coverage.glyphs) classDef1 = l.ClassDef1.classDefs class1Records = [] for classSet in classes: exemplarGlyph = next(iter(classSet)) if exemplarGlyph not in coverage: # Follow-up to e6125b353e1f54a0280ded5434b8e40d042de69f, # Fixes https://github.com/googlei18n/fontmake/issues/470 # Again, revert 8d441779e5afc664960d848f62c7acdbfc71d7b9 # when merger becomes selfless. nullRow = None if nullRow is None: nullRow = ot.Class1Record() class2records = nullRow.Class2Record = [] # TODO: When merger becomes selfless, revert e6125b353e1f54a0280ded5434b8e40d042de69f for _ in range(l.Class2Count): if transparent: rec2 = None else: rec2 = ot.Class2Record() rec2.Value1 = otBase.ValueRecord( self.ValueFormat1 ) if self.ValueFormat1 else None rec2.Value2 = otBase.ValueRecord( self.ValueFormat2 ) if self.ValueFormat2 else None class2records.append(rec2) rec1 = nullRow else: klass = classDef1.get(exemplarGlyph, 0) rec1 = matrix[klass] # TODO handle out-of-range? class1Records.append(rec1) new_matrices.append(class1Records) matrices = new_matrices del new_matrices # Align second classes self.ClassDef2, classes = _ClassDef_merge_classify( [l.ClassDef2 for l in lst]) self.Class2Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): classDef2 = l.ClassDef2.classDefs class1Records = [] for rec1old in matrix: oldClass2Records = rec1old.Class2Record rec1new = ot.Class1Record() class2Records = rec1new.Class2Record = [] for classSet in classes: if not classSet: # class=0 rec2 = oldClass2Records[0] else: exemplarGlyph = next(iter(classSet)) klass = classDef2.get(exemplarGlyph, 0) rec2 = oldClass2Records[klass] class2Records.append(copy.deepcopy(rec2)) class1Records.append(rec1new) new_matrices.append(class1Records) matrices = new_matrices del new_matrices return matrices
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
def _PairPosFormat2_align_matrices(self, lst, font, transparent=False): matrices = [l.Class1Record for l in lst] # Align first classes self.ClassDef1, classes = _ClassDef_merge_classify( [l.ClassDef1 for l in lst], allGlyphs=set(self.Coverage.glyphs)) _ClassDef_calculate_Format(self.ClassDef1, font) self.Class1Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): nullRow = None coverage = set(l.Coverage.glyphs) classDef1 = l.ClassDef1.classDefs class1Records = [] for classSet in classes: exemplarGlyph = next(iter(classSet)) if exemplarGlyph not in coverage: if nullRow is None: nullRow = ot.Class1Record() class2records = nullRow.Class2Record = [] # TODO: When merger becomes selfless, revert e6125b353e1f54a0280ded5434b8e40d042de69f for _ in range(l.Class2Count): if transparent: rec2 = None else: rec2 = ot.Class2Record() rec2.Value1 = otBase.ValueRecord( self.ValueFormat1 ) if self.ValueFormat1 else None rec2.Value2 = otBase.ValueRecord( self.ValueFormat2 ) if self.ValueFormat2 else None class2records.append(rec2) rec1 = nullRow else: klass = classDef1.get(exemplarGlyph, 0) rec1 = matrix[klass] # TODO handle out-of-range? class1Records.append(rec1) new_matrices.append(class1Records) matrices = new_matrices del new_matrices # Align second classes self.ClassDef2, classes = _ClassDef_merge_classify( [l.ClassDef2 for l in lst]) _ClassDef_calculate_Format(self.ClassDef2, font) self.Class2Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): classDef2 = l.ClassDef2.classDefs class1Records = [] for rec1old in matrix: oldClass2Records = rec1old.Class2Record rec1new = ot.Class1Record() class2Records = rec1new.Class2Record = [] for classSet in classes: if not classSet: # class=0 rec2 = oldClass2Records[0] else: exemplarGlyph = next(iter(classSet)) klass = classDef2.get(exemplarGlyph, 0) rec2 = oldClass2Records[klass] class2Records.append(rec2) class1Records.append(rec1new) new_matrices.append(class1Records) matrices = new_matrices del new_matrices return matrices
def _PairPosFormat2_merge(self, lst, merger): merger.mergeObjects(self, lst, exclude=('Coverage', 'ClassDef1', 'Class1Count', 'ClassDef2', 'Class2Count', 'Class1Record')) # Align coverages glyphs, _ = _merge_GlyphOrders(merger.font, [v.Coverage.glyphs for v in lst]) self.Coverage.glyphs = glyphs glyphSet = set(glyphs) # Currently, if the coverage of PairPosFormat2 subtables are different, # we do NOT bother walking down the subtable list when filling in new # rows for alignment. As such, this is only correct if current subtable # is the last subtable in the lookup. Ensure that. # # Note that our canonicalization process merges some PairPosFormat2's, # so in reality this might not be common. # # TODO: Remove this requirement for l,subtables in zip(lst,merger.lookup_subtables): if l.Coverage.glyphs != glyphs: assert l == subtables[-1] matrices = [l.Class1Record for l in lst] # Align first classes self.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], allGlyphs=glyphSet) _ClassDef_calculate_Format(self.ClassDef1, merger.font) self.Class1Count = len(classes) new_matrices = [] for l,matrix in zip(lst, matrices): nullRow = None coverage = set(l.Coverage.glyphs) classDef1 = l.ClassDef1.classDefs class1Records = [] for classSet in classes: exemplarGlyph = next(iter(classSet)) if exemplarGlyph not in coverage: if nullRow is None: nullRow = ot.Class1Record() class2records = nullRow.Class2Record = [] # TODO: When merger becomes selfless, revert e6125b353e1f54a0280ded5434b8e40d042de69f for _ in range(l.Class2Count): rec2 = ot.Class2Record() rec2.Value1 = otBase.ValueRecord(l.ValueFormat1) if l.ValueFormat1 else None rec2.Value2 = otBase.ValueRecord(l.ValueFormat2) if l.ValueFormat2 else None class2records.append(rec2) rec1 = nullRow else: klass = classDef1.get(exemplarGlyph, 0) rec1 = matrix[klass] # TODO handle out-of-range? class1Records.append(rec1) new_matrices.append(class1Records) matrices = new_matrices del new_matrices # Align second classes self.ClassDef2, classes = _ClassDef_merge_classify([l.ClassDef2 for l in lst]) _ClassDef_calculate_Format(self.ClassDef2, merger.font) self.Class2Count = len(classes) new_matrices = [] for l,matrix in zip(lst, matrices): classDef2 = l.ClassDef2.classDefs class1Records = [] for rec1old in matrix: oldClass2Records = rec1old.Class2Record rec1new = ot.Class1Record() class2Records = rec1new.Class2Record = [] for classSet in classes: if not classSet: # class=0 rec2 = oldClass2Records[0] else: exemplarGlyph = next(iter(classSet)) klass = classDef2.get(exemplarGlyph, 0) rec2 = oldClass2Records[klass] class2Records.append(rec2) class1Records.append(rec1new) new_matrices.append(class1Records) matrices = new_matrices del new_matrices self.Class1Record = list(matrices[0]) # TODO move merger to be selfless merger.mergeLists(self.Class1Record, matrices)
def _PairPosFormat2_merge(self, lst, merger): merger.mergeObjects(self, lst, exclude=('Coverage', 'ClassDef1', 'Class1Count', 'ClassDef2', 'Class2Count', 'Class1Record')) # Align coverages glyphs, _ = _merge_GlyphOrders(merger.font, [v.Coverage.glyphs for v in lst]) self.Coverage.glyphs = glyphs glyphSet = set(glyphs) # Currently, if the coverage of PairPosFormat2 subtables are different, # we do NOT bother walking down the subtable list when filling in new # rows for alignment. As such, this is only correct if current subtable # is the last subtable in the lookup. Ensure that. # TODO: Remove this requirement for l, lookup in zip(lst, merger.lookups): if l.Coverage.glyphs != glyphs: assert l == lookup.SubTable[-1] matrices = [l.Class1Record for l in lst] # Align first classes self.ClassDef1, classes = _ClassDef_merge_classify( [l.ClassDef1 for l in lst], allGlyphs=glyphSet) self.Class1Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): nullRow = None coverage = set(l.Coverage.glyphs) classDef1 = l.ClassDef1.classDefs class1Records = [] for classSet in classes: exemplarGlyph = next(iter(classSet)) if exemplarGlyph not in coverage: if nullRow is None: rec2 = ot.Class2Record() rec2.Value1 = otBase.ValueRecord( l.ValueFormat1) if l.ValueFormat1 else None rec2.Value2 = otBase.ValueRecord( l.ValueFormat2) if l.ValueFormat2 else None nullRow = ot.Class1Record() nullRow.Class2Record = [rec2] * l.Class2Count rec1 = nullRow else: klass = classDef1.get(exemplarGlyph, 0) rec1 = matrix[klass] # TODO handle out-of-range? class1Records.append(rec1) new_matrices.append(class1Records) matrices = new_matrices del new_matrices # Align second classes self.ClassDef2, classes = _ClassDef_merge_classify( [l.ClassDef2 for l in lst]) self.Class2Count = len(classes) new_matrices = [] for l, matrix in zip(lst, matrices): classDef2 = l.ClassDef2.classDefs class1Records = [] for rec1old in matrix: oldClass2Records = rec1old.Class2Record rec1new = ot.Class1Record() class2Records = rec1new.Class2Record = [] for classSet in classes: if not classSet: # class=0 rec2 = oldClass2Records[0] else: exemplarGlyph = next(iter(classSet)) klass = classDef2.get(exemplarGlyph, 0) rec2 = oldClass2Records[klass] class2Records.append(rec2) class1Records.append(rec1new) new_matrices.append(class1Records) matrices = new_matrices del new_matrices self.Class1Record = list(matrices[0]) # TODO move merger to be selfless merger.mergeLists(self.Class1Record, matrices)