[AAT] Support mort
and morx
tables with non-contextual substitutions
Other metamorphosis types are not yet supported and will raise an error upon decompilation. The TTX tool catches the error and continues to emit a hexdump of the table contents, just as before this change.
This commit is contained in:
parent
7bb171ed4a
commit
90f257cc60
@ -71,6 +71,8 @@ def _moduleFinderHint():
|
|||||||
from . import _l_t_a_g
|
from . import _l_t_a_g
|
||||||
from . import _m_a_x_p
|
from . import _m_a_x_p
|
||||||
from . import _m_e_t_a
|
from . import _m_e_t_a
|
||||||
|
from . import _m_o_r_t
|
||||||
|
from . import _m_o_r_x
|
||||||
from . import _n_a_m_e
|
from . import _n_a_m_e
|
||||||
from . import _o_p_b_d
|
from . import _o_p_b_d
|
||||||
from . import _p_o_s_t
|
from . import _p_o_s_t
|
||||||
|
8
Lib/fontTools/ttLib/tables/_m_o_r_t.py
Normal file
8
Lib/fontTools/ttLib/tables/_m_o_r_t.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import
|
||||||
|
from fontTools.misc.py23 import *
|
||||||
|
from .otBase import BaseTTXConverter
|
||||||
|
|
||||||
|
|
||||||
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
|
||||||
|
class table__m_o_r_t(BaseTTXConverter):
|
||||||
|
pass
|
8
Lib/fontTools/ttLib/tables/_m_o_r_x.py
Normal file
8
Lib/fontTools/ttLib/tables/_m_o_r_x.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import
|
||||||
|
from fontTools.misc.py23 import *
|
||||||
|
from .otBase import BaseTTXConverter
|
||||||
|
|
||||||
|
|
||||||
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
|
||||||
|
class table__m_o_r_x(BaseTTXConverter):
|
||||||
|
pass
|
@ -45,7 +45,11 @@ def buildConverters(tableSpec, tableNamespace):
|
|||||||
converterClass = Struct
|
converterClass = Struct
|
||||||
else:
|
else:
|
||||||
converterClass = eval(tp, tableNamespace, converterMapping)
|
converterClass = eval(tp, tableNamespace, converterMapping)
|
||||||
tableClass = tableNamespace.get(tableName)
|
if tp in ('MortChain', 'MortSubtable',
|
||||||
|
'MorxChain', 'MorxSubtable'):
|
||||||
|
tableClass = tableNamespace.get(tp)
|
||||||
|
else:
|
||||||
|
tableClass = tableNamespace.get(tableName)
|
||||||
if tableClass is not None:
|
if tableClass is not None:
|
||||||
conv = converterClass(name, repeat, aux, tableClass=tableClass)
|
conv = converterClass(name, repeat, aux, tableClass=tableClass)
|
||||||
else:
|
else:
|
||||||
@ -967,8 +971,10 @@ converterMapping = {
|
|||||||
"VarIdxMapValue": VarIdxMapValue,
|
"VarIdxMapValue": VarIdxMapValue,
|
||||||
"VarDataValue": VarDataValue,
|
"VarDataValue": VarDataValue,
|
||||||
# AAT
|
# AAT
|
||||||
"MorphChain": StructWithLength,
|
"MortChain": StructWithLength,
|
||||||
"MorphSubtable":StructWithLength,
|
"MortSubtable": StructWithLength,
|
||||||
|
"MorxChain": StructWithLength,
|
||||||
|
"MorxSubtable": StructWithLength,
|
||||||
# "Template" types
|
# "Template" types
|
||||||
"AATLookup": lambda C: partial(AATLookup, tableClass=C),
|
"AATLookup": lambda C: partial(AATLookup, tableClass=C),
|
||||||
"OffsetTo": lambda C: partial(Table, tableClass=C),
|
"OffsetTo": lambda C: partial(Table, tableClass=C),
|
||||||
|
@ -1289,25 +1289,50 @@ otData = [
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# morx
|
# mort
|
||||||
#
|
#
|
||||||
|
|
||||||
# TODO: use 'struct' when field.type == field.name
|
('mort', [
|
||||||
|
('Version', 'Version', None, None, 'Version of the mort table.'),
|
||||||
|
('uint32', 'MorphChainCount', None, None, 'Number of metamorphosis chains.'),
|
||||||
|
('MortChain', 'MorphChain', 'MorphChainCount', 0, 'Array of metamorphosis chains.'),
|
||||||
|
]),
|
||||||
|
|
||||||
|
('MortChain', [
|
||||||
|
('Flags32', 'DefaultFlags', None, None, 'The default specification for subtables.'),
|
||||||
|
('uint32', 'StructLength', None, None, 'Total byte count, including this header; must be a multiple of 4.'),
|
||||||
|
('uint16', 'MorphFeatureCount', None, None, 'Number of metamorphosis feature entries.'),
|
||||||
|
('uint16', 'MorphSubtableCount', None, None, 'The number of subtables in the chain.'),
|
||||||
|
('struct', 'MorphFeature', 'MorphFeatureCount', 0, 'Array of metamorphosis features.'),
|
||||||
|
('MortSubtable', 'MorphSubtable', 'MorphSubtableCount', 0, 'Array of metamorphosis subtables.'),
|
||||||
|
]),
|
||||||
|
|
||||||
|
('MortSubtable', [
|
||||||
|
('uint16', 'StructLength', None, None, 'Total subtable length, including this header.'),
|
||||||
|
('uint8', 'CoverageFlags', None, None, 'Most significant byte of coverage flags.'),
|
||||||
|
('uint8', 'MorphType', None, None, 'Subtable type.'),
|
||||||
|
('Flags32', 'SubFeatureFlags', None, None, 'The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero).'),
|
||||||
|
('SubStruct', 'SubStruct', None, None, 'SubTable.'),
|
||||||
|
]),
|
||||||
|
|
||||||
|
#
|
||||||
|
# morx
|
||||||
|
#
|
||||||
|
|
||||||
('morx', [
|
('morx', [
|
||||||
('uint16', 'Version', None, None, 'Version of the morx table.'),
|
('uint16', 'Version', None, None, 'Version of the morx table.'),
|
||||||
('uint16', 'Reserved', None, None, 'Reserved (set to zero).'),
|
('uint16', 'Reserved', None, None, 'Reserved (set to zero).'),
|
||||||
('uint32', 'ChainCount', None, None, 'Number of MorphChains.'),
|
('uint32', 'MorphChainCount', None, None, 'Number of extended metamorphosis chains.'),
|
||||||
('MorphChain', 'MorphChain', 'ChainCount', 0, 'Array of MorphChains.'),
|
('MorxChain', 'MorphChain', 'MorphChainCount', 0, 'Array of extended metamorphosis chains.'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
('MorphChain', [
|
('MorxChain', [
|
||||||
('Flags32', 'DefaultFlags', None, None, 'The default specification for subtables.'),
|
('Flags32', 'DefaultFlags', None, None, 'The default specification for subtables.'),
|
||||||
('uint32', 'StructLength', None, None, 'Total byte count, including this header; must be a multiple of 4.'),
|
('uint32', 'StructLength', None, None, 'Total byte count, including this header; must be a multiple of 4.'),
|
||||||
('uint32', 'MorphFeatureCount', None, None, 'Number of feature subtable entries.'),
|
('uint32', 'MorphFeatureCount', None, None, 'Number of feature subtable entries.'),
|
||||||
('uint32', 'MorphSubtableCount', None, None, 'The number of subtables in the chain.'),
|
('uint32', 'MorphSubtableCount', None, None, 'The number of subtables in the chain.'),
|
||||||
('MorphFeature', 'MorphFeature', 'MorphFeatureCount', 0, 'Array of MorphFeatures.'),
|
('MorphFeature', 'MorphFeature', 'MorphFeatureCount', 0, 'Array of metamorphosis features.'),
|
||||||
('MorphSubtable', 'MorphSubtable', 'MorphSubtableCount', 0, 'Array of MorphSubtables.'),
|
('MorxSubtable', 'MorphSubtable', 'MorphSubtableCount', 0, 'Array of extended metamorphosis subtables.'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
('MorphFeature', [
|
('MorphFeature', [
|
||||||
@ -1320,7 +1345,7 @@ otData = [
|
|||||||
# Apple TrueType Reference Manual, chapter “The ‘morx’ table”,
|
# Apple TrueType Reference Manual, chapter “The ‘morx’ table”,
|
||||||
# section “Metamorphosis Subtables”.
|
# section “Metamorphosis Subtables”.
|
||||||
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
|
||||||
('MorphSubtable', [
|
('MorxSubtable', [
|
||||||
('uint32', 'StructLength', None, None, 'Total subtable length, including this header.'),
|
('uint32', 'StructLength', None, None, 'Total subtable length, including this header.'),
|
||||||
('uint8', 'CoverageFlags', None, None, 'Most significant byte of coverage flags.'),
|
('uint8', 'CoverageFlags', None, None, 'Most significant byte of coverage flags.'),
|
||||||
('uint16', 'Reserved', None, None, 'Unused.'),
|
('uint16', 'Reserved', None, None, 'Unused.'),
|
||||||
@ -1353,7 +1378,7 @@ otData = [
|
|||||||
]),
|
]),
|
||||||
|
|
||||||
('NoncontextualMorph', [
|
('NoncontextualMorph', [
|
||||||
('AATLookup(GlyphID)', 'mapping', None, None, 'The noncontextual glyph substitution table.'),
|
('AATLookup(GlyphID)', 'Substitution', None, None, 'The noncontextual glyph substitution table.'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
('InsertionMorph', [
|
('InsertionMorph', [
|
||||||
|
@ -991,13 +991,16 @@ def _buildClasses():
|
|||||||
8: ChainContextPos,
|
8: ChainContextPos,
|
||||||
9: ExtensionPos,
|
9: ExtensionPos,
|
||||||
},
|
},
|
||||||
|
'mort': {
|
||||||
|
4: NoncontextualMorph,
|
||||||
|
},
|
||||||
'morx': {
|
'morx': {
|
||||||
0: RearrangementMorph,
|
# 0: RearrangementMorph,
|
||||||
1: ContextualMorph,
|
# 1: ContextualMorph,
|
||||||
2: LigatureMorph,
|
# 2: LigatureMorph,
|
||||||
# 3: Reserved,
|
# 3: Reserved,
|
||||||
4: NoncontextualMorph,
|
4: NoncontextualMorph,
|
||||||
5: InsertionMorph,
|
# 5: InsertionMorph,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS
|
lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS
|
||||||
|
@ -102,8 +102,8 @@ The following tables are currently supported:
|
|||||||
OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID,
|
OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID,
|
||||||
TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, avar, bsln, cmap,
|
TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, avar, bsln, cmap,
|
||||||
cvar, cvt, feat, fpgm, fvar, gasp, glyf, gvar, hdmx, head, hhea,
|
cvar, cvt, feat, fpgm, fvar, gasp, glyf, gvar, hdmx, head, hhea,
|
||||||
hmtx, kern, lcar, loca, ltag, maxp, meta, name, opbd, post, prep,
|
hmtx, kern, lcar, loca, ltag, maxp, meta, mort, morx, name, opbd,
|
||||||
prop, sbix, trak, vhea and vmtx
|
post, prep, prop, sbix, trak, vhea and vmtx
|
||||||
.. end table list
|
.. end table list
|
||||||
|
|
||||||
Other tables are dumped as hexadecimal data.
|
Other tables are dumped as hexadecimal data.
|
||||||
|
115
Tests/ttLib/tables/_m_o_r_t_test.py
Normal file
115
Tests/ttLib/tables/_m_o_r_t_test.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import, unicode_literals
|
||||||
|
from fontTools.misc.py23 import *
|
||||||
|
from fontTools.misc.testTools import FakeFont, getXML, parseXML
|
||||||
|
from fontTools.misc.textTools import deHexStr, hexStr
|
||||||
|
from fontTools.ttLib import newTable
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
# Glyph Metamorphosis Table Examples
|
||||||
|
# Example 1: Non-contextual Glyph Substitution
|
||||||
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
|
||||||
|
# The example given by Apple's 'mort' specification is suboptimally
|
||||||
|
# encoded: it uses AAT lookup format 6 even though format 8 would be
|
||||||
|
# more compact. Because our encoder always uses the most compact
|
||||||
|
# encoding, this breaks our round-trip testing. Therefore, we changed
|
||||||
|
# the example to use GlyphID 13 instead of 12 for the 'parenright'
|
||||||
|
# character; the non-contiguous glyph range for the AAT lookup makes
|
||||||
|
# format 6 to be most compact.
|
||||||
|
MORT_NONCONTEXTUAL_DATA = deHexStr(
|
||||||
|
'0001 0000 ' # 0: Version=1.0
|
||||||
|
'0000 0001 ' # 4: MorphChainCount=1
|
||||||
|
'0000 0001 ' # 8: DefaultFlags=1
|
||||||
|
'0000 0050 ' # 12: StructLength=80
|
||||||
|
'0003 0001 ' # 16: MorphFeatureCount=3, MorphSubtableCount=1
|
||||||
|
'0004 0000 ' # 20: Feature[0].FeatureType=4/VertSubst, .FeatureSetting=on
|
||||||
|
'0000 0001 ' # 24: Feature[0].EnableFlags=0x00000001
|
||||||
|
'FFFF FFFF ' # 28: Feature[0].DisableFlags=0xFFFFFFFF
|
||||||
|
'0004 0001 ' # 32: Feature[1].FeatureType=4/VertSubst, .FeatureSetting=off
|
||||||
|
'0000 0000 ' # 36: Feature[1].EnableFlags=0x00000000
|
||||||
|
'FFFF FFFE ' # 40: Feature[1].DisableFlags=0xFFFFFFFE
|
||||||
|
'0000 0001 ' # 44: Feature[2].FeatureType=0/GlyphEffects, .FeatSetting=off
|
||||||
|
'0000 0000 ' # 48: Feature[2].EnableFlags=0 (required for last feature)
|
||||||
|
'0000 0000 ' # 52: Feature[2].EnableFlags=0 (required for last feature)
|
||||||
|
'0020 ' # 56: Subtable[0].StructLength=32
|
||||||
|
'80 ' # 58: Subtable[0].CoverageFlags=0x80
|
||||||
|
'04 ' # 59: Subtable[0].MorphType=4/NoncontextualMorph
|
||||||
|
'0000 0001 ' # 60: Subtable[0].SubFeatureFlags=0x1
|
||||||
|
'0006 0004 ' # 64: LookupFormat=6, UnitSize=4
|
||||||
|
'0002 0008 ' # 68: NUnits=2, SearchRange=8
|
||||||
|
'0001 0000 ' # 72: EntrySelector=1, RangeShift=0
|
||||||
|
'000B 0087 ' # 76: Glyph=11 (parenleft); Value=135 (parenleft.vertical)
|
||||||
|
'000D 0088 ' # 80: Glyph=13 (parenright); Value=136 (parenright.vertical)
|
||||||
|
'FFFF 0000 ' # 84: Glyph=<end>; Value=0
|
||||||
|
) # 88: <end>
|
||||||
|
assert len(MORT_NONCONTEXTUAL_DATA) == 88
|
||||||
|
|
||||||
|
|
||||||
|
MORT_NONCONTEXTUAL_XML = [
|
||||||
|
'<Version value="0x00010000"/>',
|
||||||
|
'<!-- MorphChainCount=1 -->',
|
||||||
|
'<MorphChain index="0">',
|
||||||
|
' <DefaultFlags value="0x00000001"/>',
|
||||||
|
' <!-- StructLength=80 -->',
|
||||||
|
' <!-- MorphFeatureCount=3 -->',
|
||||||
|
' <!-- MorphSubtableCount=1 -->',
|
||||||
|
' <MorphFeature index="0">',
|
||||||
|
' <FeatureType value="4"/>',
|
||||||
|
' <FeatureSetting value="0"/>',
|
||||||
|
' <EnableFlags value="0x00000001"/>',
|
||||||
|
' <DisableFlags value="0xFFFFFFFF"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphFeature index="1">',
|
||||||
|
' <FeatureType value="4"/>',
|
||||||
|
' <FeatureSetting value="1"/>',
|
||||||
|
' <EnableFlags value="0x00000000"/>',
|
||||||
|
' <DisableFlags value="0xFFFFFFFE"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphFeature index="2">',
|
||||||
|
' <FeatureType value="0"/>',
|
||||||
|
' <FeatureSetting value="1"/>',
|
||||||
|
' <EnableFlags value="0x00000000"/>',
|
||||||
|
' <DisableFlags value="0x00000000"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphSubtable index="0">',
|
||||||
|
' <!-- StructLength=32 -->',
|
||||||
|
' <CoverageFlags value="128"/>',
|
||||||
|
' <!-- MorphType=4 -->',
|
||||||
|
' <SubFeatureFlags value="0x00000001"/>',
|
||||||
|
' <NoncontextualMorph>',
|
||||||
|
' <Substitution>',
|
||||||
|
' <Lookup glyph="parenleft" value="parenleft.vertical"/>',
|
||||||
|
' <Lookup glyph="parenright" value="parenright.vertical"/>',
|
||||||
|
' </Substitution>',
|
||||||
|
' </NoncontextualMorph>',
|
||||||
|
' </MorphSubtable>',
|
||||||
|
'</MorphChain>',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MORTNoncontextualGlyphSubstitutionTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.maxDiff = None
|
||||||
|
glyphs = ['.notdef'] + ['g.%d' % i for i in range (1, 140)]
|
||||||
|
glyphs[11], glyphs[13] = 'parenleft', 'parenright'
|
||||||
|
glyphs[135], glyphs[136] = 'parenleft.vertical', 'parenright.vertical'
|
||||||
|
cls.font = FakeFont(glyphs)
|
||||||
|
|
||||||
|
def test_decompile_toXML(self):
|
||||||
|
table = newTable('mort')
|
||||||
|
table.decompile(MORT_NONCONTEXTUAL_DATA, self.font)
|
||||||
|
self.assertEqual(getXML(table.toXML), MORT_NONCONTEXTUAL_XML)
|
||||||
|
|
||||||
|
def test_compile_fromXML(self):
|
||||||
|
table = newTable('mort')
|
||||||
|
for name, attrs, content in parseXML(MORT_NONCONTEXTUAL_XML):
|
||||||
|
table.fromXML(name, attrs, content, font=self.font)
|
||||||
|
self.assertEqual(hexStr(table.compile(self.font)),
|
||||||
|
hexStr(MORT_NONCONTEXTUAL_DATA))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
sys.exit(unittest.main())
|
114
Tests/ttLib/tables/_m_o_r_x_test.py
Normal file
114
Tests/ttLib/tables/_m_o_r_x_test.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import, unicode_literals
|
||||||
|
from fontTools.misc.py23 import *
|
||||||
|
from fontTools.misc.testTools import FakeFont, getXML, parseXML
|
||||||
|
from fontTools.misc.textTools import deHexStr, hexStr
|
||||||
|
from fontTools.ttLib import newTable
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
# A simple 'morx' table with non-contextual glyph substitution.
|
||||||
|
# Unfortunately, the Apple spec for 'morx' does not contain a complete example.
|
||||||
|
# The test case has therefore been adapted from the example 'mort' table in
|
||||||
|
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
|
||||||
|
MORX_NONCONTEXTUAL_DATA = deHexStr(
|
||||||
|
'0002 0000 ' # 0: Version=2, Reserved=0
|
||||||
|
'0000 0001 ' # 4: MorphChainCount=1
|
||||||
|
'0000 0001 ' # 8: DefaultFlags=1
|
||||||
|
'0000 0058 ' # 12: StructLength=88
|
||||||
|
'0000 0003 ' # 16: MorphFeatureCount=3
|
||||||
|
'0000 0001 ' # 20: MorphSubtableCount=1
|
||||||
|
'0004 0000 ' # 24: Feature[0].FeatureType=4/VertSubst, .FeatureSetting=on
|
||||||
|
'0000 0001 ' # 28: Feature[0].EnableFlags=0x00000001
|
||||||
|
'FFFF FFFF ' # 32: Feature[0].DisableFlags=0xFFFFFFFF
|
||||||
|
'0004 0001 ' # 36: Feature[1].FeatureType=4/VertSubst, .FeatureSetting=off
|
||||||
|
'0000 0000 ' # 40: Feature[1].EnableFlags=0x00000000
|
||||||
|
'FFFF FFFE ' # 44: Feature[1].DisableFlags=0xFFFFFFFE
|
||||||
|
'0000 0001 ' # 48: Feature[2].FeatureType=0/GlyphEffects, .FeatSetting=off
|
||||||
|
'0000 0000 ' # 52: Feature[2].EnableFlags=0 (required for last feature)
|
||||||
|
'0000 0000 ' # 56: Feature[2].EnableFlags=0 (required for last feature)
|
||||||
|
'0000 0024 ' # 60: Subtable[0].StructLength=36
|
||||||
|
'80 ' # 64: Subtable[0].CoverageFlags=0x80
|
||||||
|
'00 00 ' # 65: Subtable[0].Reserved=0
|
||||||
|
'04 ' # 67: Subtable[0].MorphType=4/NoncontextualMorph
|
||||||
|
'0000 0001 ' # 68: Subtable[0].SubFeatureFlags=0x1
|
||||||
|
'0006 0004 ' # 72: LookupFormat=6, UnitSize=4
|
||||||
|
'0002 0008 ' # 76: NUnits=2, SearchRange=8
|
||||||
|
'0001 0000 ' # 80: EntrySelector=1, RangeShift=0
|
||||||
|
'000B 0087 ' # 84: Glyph=11 (parenleft); Value=135 (parenleft.vertical)
|
||||||
|
'000D 0088 ' # 88: Glyph=13 (parenright); Value=136 (parenright.vertical)
|
||||||
|
'FFFF 0000 ' # 92: Glyph=<end>; Value=0
|
||||||
|
) # 96: <end>
|
||||||
|
assert len(MORX_NONCONTEXTUAL_DATA) == 96
|
||||||
|
|
||||||
|
|
||||||
|
MORX_NONCONTEXTUAL_XML = [
|
||||||
|
'<Version value="2"/>',
|
||||||
|
'<Reserved value="0"/>',
|
||||||
|
'<!-- MorphChainCount=1 -->',
|
||||||
|
'<MorphChain index="0">',
|
||||||
|
' <DefaultFlags value="0x00000001"/>',
|
||||||
|
' <!-- StructLength=88 -->',
|
||||||
|
' <!-- MorphFeatureCount=3 -->',
|
||||||
|
' <!-- MorphSubtableCount=1 -->',
|
||||||
|
' <MorphFeature index="0">',
|
||||||
|
' <FeatureType value="4"/>',
|
||||||
|
' <FeatureSetting value="0"/>',
|
||||||
|
' <EnableFlags value="0x00000001"/>',
|
||||||
|
' <DisableFlags value="0xFFFFFFFF"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphFeature index="1">',
|
||||||
|
' <FeatureType value="4"/>',
|
||||||
|
' <FeatureSetting value="1"/>',
|
||||||
|
' <EnableFlags value="0x00000000"/>',
|
||||||
|
' <DisableFlags value="0xFFFFFFFE"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphFeature index="2">',
|
||||||
|
' <FeatureType value="0"/>',
|
||||||
|
' <FeatureSetting value="1"/>',
|
||||||
|
' <EnableFlags value="0x00000000"/>',
|
||||||
|
' <DisableFlags value="0x00000000"/>',
|
||||||
|
' </MorphFeature>',
|
||||||
|
' <MorphSubtable index="0">',
|
||||||
|
' <!-- StructLength=36 -->',
|
||||||
|
' <CoverageFlags value="128"/>',
|
||||||
|
' <Reserved value="0"/>',
|
||||||
|
' <!-- MorphType=4 -->',
|
||||||
|
' <SubFeatureFlags value="0x00000001"/>',
|
||||||
|
' <NoncontextualMorph>',
|
||||||
|
' <Substitution>',
|
||||||
|
' <Lookup glyph="parenleft" value="parenleft.vertical"/>',
|
||||||
|
' <Lookup glyph="parenright" value="parenright.vertical"/>',
|
||||||
|
' </Substitution>',
|
||||||
|
' </NoncontextualMorph>',
|
||||||
|
' </MorphSubtable>',
|
||||||
|
'</MorphChain>',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MORXNoncontextualGlyphSubstitutionTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.maxDiff = None
|
||||||
|
glyphs = ['.notdef'] + ['g.%d' % i for i in range (1, 140)]
|
||||||
|
glyphs[11], glyphs[13] = 'parenleft', 'parenright'
|
||||||
|
glyphs[135], glyphs[136] = 'parenleft.vertical', 'parenright.vertical'
|
||||||
|
cls.font = FakeFont(glyphs)
|
||||||
|
|
||||||
|
def test_decompile_toXML(self):
|
||||||
|
table = newTable('morx')
|
||||||
|
table.decompile(MORX_NONCONTEXTUAL_DATA, self.font)
|
||||||
|
self.assertEqual(getXML(table.toXML), MORX_NONCONTEXTUAL_XML)
|
||||||
|
|
||||||
|
def test_compile_fromXML(self):
|
||||||
|
table = newTable('morx')
|
||||||
|
for name, attrs, content in parseXML(MORX_NONCONTEXTUAL_XML):
|
||||||
|
table.fromXML(name, attrs, content, font=self.font)
|
||||||
|
self.assertEqual(hexStr(table.compile(self.font)),
|
||||||
|
hexStr(MORX_NONCONTEXTUAL_DATA))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
sys.exit(unittest.main())
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user