def compact_class_pairs(font: TTFont, mode: str, subtable: otTables.PairPos) -> List[otTables.PairPos]: from fontTools.otlLib.builder import buildPairPosClassesSubtable subtables = [] classes1: DefaultDict[int, List[str]] = defaultdict(list) for g in subtable.Coverage.glyphs: classes1[subtable.ClassDef1.classDefs.get(g, 0)].append(g) classes2: DefaultDict[int, List[str]] = defaultdict(list) for g, i in subtable.ClassDef2.classDefs.items(): classes2[i].append(g) all_pairs = {} for i, class1 in enumerate(subtable.Class1Record): for j, class2 in enumerate(class1.Class2Record): if is_really_zero(class2): continue all_pairs[(tuple(sorted(classes1[i])), tuple(sorted(classes2[j])))] = ( getattr(class2, "Value1", None), getattr(class2, "Value2", None), ) if len(mode) == 1 and mode in "123456789": grouped_pairs = cluster_pairs_by_class2_coverage_custom_cost( font, all_pairs, int(mode)) for pairs in grouped_pairs: subtables.append( buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap())) else: raise ValueError(f"Bad {GPOS_COMPACT_MODE_ENV_KEY}={mode}") return subtables
def compact_class_pairs( font: TTFont, level: int, subtable: otTables.PairPos ) -> List[otTables.PairPos]: from fontTools.otlLib.builder import buildPairPosClassesSubtable subtables = [] classes1: DefaultDict[int, List[str]] = defaultdict(list) for g in subtable.Coverage.glyphs: classes1[subtable.ClassDef1.classDefs.get(g, 0)].append(g) classes2: DefaultDict[int, List[str]] = defaultdict(list) for g, i in subtable.ClassDef2.classDefs.items(): classes2[i].append(g) all_pairs = {} for i, class1 in enumerate(subtable.Class1Record): for j, class2 in enumerate(class1.Class2Record): if is_really_zero(class2): continue all_pairs[(tuple(sorted(classes1[i])), tuple(sorted(classes2[j])))] = ( getattr(class2, "Value1", None), getattr(class2, "Value2", None), ) grouped_pairs = cluster_pairs_by_class2_coverage_custom_cost(font, all_pairs, level) for pairs in grouped_pairs: subtables.append(buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap())) return subtables
def test_buildPairPosClassesSubtable(self): d20 = builder.buildValue({"XPlacement": -20}) d50 = builder.buildValue({"XPlacement": -50}) d0 = builder.buildValue({}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) subtable = builder.buildPairPosClassesSubtable( { (tuple("A"), tuple(["zero"])): (d0, d50), (tuple("A"), tuple(["one", "two"])): (None, d20), (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50), }, self.GLYPHMAP, ) assert getXML(subtable.toXML) == [ '<PairPos Format="2">', " <Coverage>", ' <Glyph value="A"/>', ' <Glyph value="B"/>', ' <Glyph value="C"/>', " </Coverage>", ' <ValueFormat1 value="3"/>', ' <ValueFormat2 value="1"/>', " <ClassDef1>", ' <ClassDef glyph="A" class="1"/>', " </ClassDef1>", " <ClassDef2>", ' <ClassDef glyph="one" class="1"/>', ' <ClassDef glyph="two" class="1"/>', ' <ClassDef glyph="zero" class="2"/>', " </ClassDef2>", " <!-- Class1Count=2 -->", " <!-- Class2Count=3 -->", ' <Class1Record index="0">', ' <Class2Record index="0">', " </Class2Record>", ' <Class2Record index="1">', " </Class2Record>", ' <Class2Record index="2">', ' <Value1 XPlacement="-80" YPlacement="-20"/>', ' <Value2 XPlacement="-50"/>', " </Class2Record>", " </Class1Record>", ' <Class1Record index="1">', ' <Class2Record index="0">', " </Class2Record>", ' <Class2Record index="1">', ' <Value2 XPlacement="-20"/>', " </Class2Record>", ' <Class2Record index="2">', " <Value1/>", ' <Value2 XPlacement="-50"/>', " </Class2Record>", " </Class1Record>", "</PairPos>", ]
def test_buildPairPosClassesSubtable(self): d20 = builder.buildValue({"XPlacement": -20}) d50 = builder.buildValue({"XPlacement": -50}) d0 = builder.buildValue({}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) subtable = builder.buildPairPosClassesSubtable( { (frozenset("A"), frozenset(["zero"])): (d0, d50), (frozenset("A"), frozenset(["one", "two"])): (None, d20), (frozenset(["B", "C"]), frozenset(["zero"])): (d8020, d50), }, self.GLYPHMAP) self.maxDiff = None self.assertEqual( getXML(subtable.toXML), '<PairPos Format="2">' ' <Coverage>' ' <Glyph value="A"/>' ' <Glyph value="B"/>' ' <Glyph value="C"/>' ' </Coverage>' ' <ValueFormat1 value="3"/>' ' <ValueFormat2 value="1"/>' ' <ClassDef1>' ' <ClassDef glyph="A" class="1"/>' ' </ClassDef1>' ' <ClassDef2>' ' <ClassDef glyph="one" class="1"/>' ' <ClassDef glyph="two" class="1"/>' ' <ClassDef glyph="zero" class="2"/>' ' </ClassDef2>' ' <!-- Class1Count=2 -->' ' <!-- Class2Count=3 -->' ' <Class1Record index="0">' ' <Class2Record index="0">' ' </Class2Record>' ' <Class2Record index="1">' ' </Class2Record>' ' <Class2Record index="2">' ' <Value1 XPlacement="-80" YPlacement="-20"/>' ' <Value2 XPlacement="-50"/>' ' </Class2Record>' ' </Class1Record>' ' <Class1Record index="1">' ' <Class2Record index="0">' ' </Class2Record>' ' <Class2Record index="1">' ' <Value2 XPlacement="-20"/>' ' </Class2Record>' ' <Class2Record index="2">' ' <Value1/>' ' <Value2 XPlacement="-50"/>' ' </Class2Record>' ' </Class1Record>' '</PairPos>')
def test_buildPairPosClassesSubtable(self): d20 = builder.buildValue({"XPlacement": -20}) d50 = builder.buildValue({"XPlacement": -50}) d0 = builder.buildValue({}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) subtable = builder.buildPairPosClassesSubtable({ (tuple("A",), tuple(["zero"])): (d0, d50), (tuple("A",), tuple(["one", "two"])): (None, d20), (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50), }, self.GLYPHMAP) self.maxDiff = None self.assertEqual(getXML(subtable.toXML), '<PairPos Format="2">' ' <Coverage>' ' <Glyph value="A"/>' ' <Glyph value="B"/>' ' <Glyph value="C"/>' ' </Coverage>' ' <ValueFormat1 value="3"/>' ' <ValueFormat2 value="1"/>' ' <ClassDef1>' ' <ClassDef glyph="A" class="1"/>' ' </ClassDef1>' ' <ClassDef2>' ' <ClassDef glyph="one" class="1"/>' ' <ClassDef glyph="two" class="1"/>' ' <ClassDef glyph="zero" class="2"/>' ' </ClassDef2>' ' <!-- Class1Count=2 -->' ' <!-- Class2Count=3 -->' ' <Class1Record index="0">' ' <Class2Record index="0">' ' </Class2Record>' ' <Class2Record index="1">' ' </Class2Record>' ' <Class2Record index="2">' ' <Value1 XPlacement="-80" YPlacement="-20"/>' ' <Value2 XPlacement="-50"/>' ' </Class2Record>' ' </Class1Record>' ' <Class1Record index="1">' ' <Class2Record index="0">' ' </Class2Record>' ' <Class2Record index="1">' ' <Value2 XPlacement="-20"/>' ' </Class2Record>' ' <Class2Record index="2">' ' <Value1/>' ' <Value2 XPlacement="-50"/>' ' </Class2Record>' ' </Class1Record>' '</PairPos>')
def flush_(self): if self.classDef1_ is None or self.classDef2_ is None: return st = otl.buildPairPosClassesSubtable(self.values_, self.builder_.glyphMap) self.subtables_.append(st)