1from fontTools.misc.testTools import FakeFont, getXML, parseXML 2from fontTools.misc.textTools import deHexStr, hexStr 3from fontTools.ttLib import newTable 4import unittest 5 6 7# Glyph Metamorphosis Table Examples 8# Example 1: Non-contextual Glyph Substitution 9# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html 10# The example given by Apple's 'mort' specification is suboptimally 11# encoded: it uses AAT lookup format 6 even though format 8 would be 12# more compact. Because our encoder always uses the most compact 13# encoding, this breaks our round-trip testing. Therefore, we changed 14# the example to use GlyphID 13 instead of 12 for the 'parenright' 15# character; the non-contiguous glyph range for the AAT lookup makes 16# format 6 to be most compact. 17MORT_NONCONTEXTUAL_DATA = deHexStr( 18 "0001 0000 " # 0: Version=1.0 19 "0000 0001 " # 4: MorphChainCount=1 20 "0000 0001 " # 8: DefaultFlags=1 21 "0000 0050 " # 12: StructLength=80 22 "0003 0001 " # 16: MorphFeatureCount=3, MorphSubtableCount=1 23 "0004 0000 " # 20: Feature[0].FeatureType=4/VertSubst, .FeatureSetting=on 24 "0000 0001 " # 24: Feature[0].EnableFlags=0x00000001 25 "FFFF FFFF " # 28: Feature[0].DisableFlags=0xFFFFFFFF 26 "0004 0001 " # 32: Feature[1].FeatureType=4/VertSubst, .FeatureSetting=off 27 "0000 0000 " # 36: Feature[1].EnableFlags=0x00000000 28 "FFFF FFFE " # 40: Feature[1].DisableFlags=0xFFFFFFFE 29 "0000 0001 " # 44: Feature[2].FeatureType=0/GlyphEffects, .FeatSetting=off 30 "0000 0000 " # 48: Feature[2].EnableFlags=0 (required for last feature) 31 "0000 0000 " # 52: Feature[2].EnableFlags=0 (required for last feature) 32 "0020 " # 56: Subtable[0].StructLength=32 33 "80 " # 58: Subtable[0].CoverageFlags=0x80 34 "04 " # 59: Subtable[0].MorphType=4/NoncontextualMorph 35 "0000 0001 " # 60: Subtable[0].SubFeatureFlags=0x1 36 "0006 0004 " # 64: LookupFormat=6, UnitSize=4 37 "0002 0008 " # 68: NUnits=2, SearchRange=8 38 "0001 0000 " # 72: EntrySelector=1, RangeShift=0 39 "000B 0087 " # 76: Glyph=11 (parenleft); Value=135 (parenleft.vertical) 40 "000D 0088 " # 80: Glyph=13 (parenright); Value=136 (parenright.vertical) 41 "FFFF 0000 " # 84: Glyph=<end>; Value=0 42) # 88: <end> 43assert len(MORT_NONCONTEXTUAL_DATA) == 88 44 45 46MORT_NONCONTEXTUAL_XML = [ 47 '<Version value="0x00010000"/>', 48 "<!-- MorphChainCount=1 -->", 49 '<MorphChain index="0">', 50 ' <DefaultFlags value="0x00000001"/>', 51 " <!-- StructLength=80 -->", 52 " <!-- MorphFeatureCount=3 -->", 53 " <!-- MorphSubtableCount=1 -->", 54 ' <MorphFeature index="0">', 55 ' <FeatureType value="4"/>', 56 ' <FeatureSetting value="0"/>', 57 ' <EnableFlags value="0x00000001"/>', 58 ' <DisableFlags value="0xFFFFFFFF"/>', 59 " </MorphFeature>", 60 ' <MorphFeature index="1">', 61 ' <FeatureType value="4"/>', 62 ' <FeatureSetting value="1"/>', 63 ' <EnableFlags value="0x00000000"/>', 64 ' <DisableFlags value="0xFFFFFFFE"/>', 65 " </MorphFeature>", 66 ' <MorphFeature index="2">', 67 ' <FeatureType value="0"/>', 68 ' <FeatureSetting value="1"/>', 69 ' <EnableFlags value="0x00000000"/>', 70 ' <DisableFlags value="0x00000000"/>', 71 " </MorphFeature>", 72 ' <MorphSubtable index="0">', 73 " <!-- StructLength=32 -->", 74 ' <CoverageFlags value="128"/>', 75 " <!-- MorphType=4 -->", 76 ' <SubFeatureFlags value="0x00000001"/>', 77 " <NoncontextualMorph>", 78 " <Substitution>", 79 ' <Lookup glyph="parenleft" value="parenleft.vertical"/>', 80 ' <Lookup glyph="parenright" value="parenright.vertical"/>', 81 " </Substitution>", 82 " </NoncontextualMorph>", 83 " </MorphSubtable>", 84 "</MorphChain>", 85] 86 87 88class MORTNoncontextualGlyphSubstitutionTest(unittest.TestCase): 89 @classmethod 90 def setUpClass(cls): 91 cls.maxDiff = None 92 glyphs = [".notdef"] + ["g.%d" % i for i in range(1, 140)] 93 glyphs[11], glyphs[13] = "parenleft", "parenright" 94 glyphs[135], glyphs[136] = "parenleft.vertical", "parenright.vertical" 95 cls.font = FakeFont(glyphs) 96 97 def test_decompile_toXML(self): 98 table = newTable("mort") 99 table.decompile(MORT_NONCONTEXTUAL_DATA, self.font) 100 self.assertEqual(getXML(table.toXML), MORT_NONCONTEXTUAL_XML) 101 102 def test_compile_fromXML(self): 103 table = newTable("mort") 104 for name, attrs, content in parseXML(MORT_NONCONTEXTUAL_XML): 105 table.fromXML(name, attrs, content, font=self.font) 106 self.assertEqual( 107 hexStr(table.compile(self.font)), hexStr(MORT_NONCONTEXTUAL_DATA) 108 ) 109 110 111if __name__ == "__main__": 112 import sys 113 114 sys.exit(unittest.main()) 115