• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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