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