You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
10 KiB

from fontTools.misc.xmlWriter import XMLWriter
from fontTools.ttLib import TTFont
from fontTools import mtiLib
import difflib
from io import StringIO
import os
import sys
import unittest
class MtiTest(unittest.TestCase):
GLYPH_ORDER = ['.notdef',
'a', 'b', 'pakannada', 'phakannada', 'vakannada', 'pevowelkannada',
'phevowelkannada', 'vevowelkannada', 'uvowelsignkannada', 'uuvowelsignkannada',
'uvowelsignaltkannada', 'uuvowelsignaltkannada', 'uuvowelsignsinh',
'uvowelsignsinh', 'rakarsinh', 'zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'slash', 'fraction', 'A', 'B', 'C', 'fi',
'fl', 'breve', 'acute', 'uniFB01', 'ffi', 'grave', 'commaacent', 'dotbelow',
'dotabove', 'cedilla', 'commaaccent', 'Acircumflex', 'V', 'T', 'acircumflex',
'Aacute', 'Agrave', 'O', 'Oacute', 'Ograve', 'Ocircumflex', 'aacute', 'agrave',
'aimatrabindigurmukhi', 'aimatragurmukhi', 'aimatratippigurmukhi',
'aumatrabindigurmukhi', 'aumatragurmukhi', 'bindigurmukhi',
'eematrabindigurmukhi', 'eematragurmukhi', 'eematratippigurmukhi',
'oomatrabindigurmukhi', 'oomatragurmukhi', 'oomatratippigurmukhi',
'lagurmukhi', 'lanuktagurmukhi', 'nagurmukhi', 'nanuktagurmukhi',
'ngagurmukhi', 'nganuktagurmukhi', 'nnagurmukhi', 'nnanuktagurmukhi',
'tthagurmukhi', 'tthanuktagurmukhi', 'bsuperior', 'isuperior', 'vsuperior',
'wsuperior', 'periodsuperior', 'osuperior', 'tsuperior', 'dollarsuperior',
'fsuperior', 'gsuperior', 'zsuperior', 'dsuperior', 'psuperior', 'hsuperior',
'oesuperior', 'aesuperior', 'centsuperior', 'esuperior', 'lsuperior',
'qsuperior', 'csuperior', 'asuperior', 'commasuperior', 'xsuperior',
'egravesuperior', 'usuperior', 'rsuperior', 'nsuperior', 'ssuperior',
'msuperior', 'jsuperior', 'ysuperior', 'ksuperior', 'guilsinglright',
'guilsinglleft', 'uniF737', 'uniE11C', 'uniE11D', 'uniE11A', 'uni2077',
'uni2087', 'uniE11B', 'uniE119', 'uniE0DD', 'uniE0DE', 'uniF736', 'uniE121',
'uniE122', 'uniE11F', 'uni2076', 'uni2086', 'uniE120', 'uniE11E', 'uniE0DB',
'uniE0DC', 'uniF733', 'uniE12B', 'uniE12C', 'uniE129', 'uni00B3', 'uni2083',
'uniE12A', 'uniE128', 'uniF732', 'uniE133', 'uniE134', 'uniE131', 'uni00B2',
'uni2082', 'uniE132', 'uniE130', 'uniE0F9', 'uniF734', 'uniE0D4', 'uniE0D5',
'uniE0D2', 'uni2074', 'uni2084', 'uniE0D3', 'uniE0D1', 'uniF730', 'uniE13D',
'uniE13E', 'uniE13A', 'uni2070', 'uni2080', 'uniE13B', 'uniE139', 'uniE13C',
'uniF739', 'uniE0EC', 'uniE0ED', 'uniE0EA', 'uni2079', 'uni2089', 'uniE0EB',
'uniE0E9', 'uniF735', 'uniE0CD', 'uniE0CE', 'uniE0CB', 'uni2075', 'uni2085',
'uniE0CC', 'uniE0CA', 'uniF731', 'uniE0F3', 'uniE0F4', 'uniE0F1', 'uni00B9',
'uni2081', 'uniE0F2', 'uniE0F0', 'uniE0F8', 'uniF738', 'uniE0C0', 'uniE0C1',
'uniE0BE', 'uni2078', 'uni2088', 'uniE0BF', 'uniE0BD', 'I', 'Ismall', 't', 'i',
'f', 'IJ', 'J', 'IJsmall', 'Jsmall', 'tt', 'ij', 'j', 'ffb', 'ffh', 'h', 'ffk',
'k', 'ffl', 'l', 'fft', 'fb', 'ff', 'fh', 'fj', 'fk', 'ft', 'janyevoweltelugu',
'kassevoweltelugu', 'jaivoweltelugu', 'nyasubscripttelugu', 'kaivoweltelugu',
'ssasubscripttelugu', 'bayi1', 'jeemi1', 'kafi1', 'ghafi1', 'laami1', 'kafm1',
'ghafm1', 'laamm1', 'rayf2', 'reyf2', 'yayf2', 'zayf2', 'fayi1', 'ayehf2',
'hamzayeharabf2', 'hamzayehf2', 'yehf2', 'ray', 'rey', 'zay', 'yay', 'dal',
'del', 'zal', 'rayf1', 'reyf1', 'yayf1', 'zayf1', 'ayehf1', 'hamzayeharabf1',
'hamzayehf1', 'yehf1', 'dal1', 'del1', 'zal1', 'onehalf', 'onehalf.alt',
'onequarter', 'onequarter.alt', 'threequarters', 'threequarters.alt',
'AlefSuperiorNS', 'DammaNS', 'DammaRflxNS', 'DammatanNS', 'Fatha2dotsNS',
'FathaNS', 'FathatanNS', 'FourDotsAboveNS', 'HamzaAboveNS', 'MaddaNS',
'OneDotAbove2NS', 'OneDotAboveNS', 'ShaddaAlefNS', 'ShaddaDammaNS',
'ShaddaDammatanNS', 'ShaddaFathatanNS', 'ShaddaKasraNS', 'ShaddaKasratanNS',
'ShaddaNS', 'SharetKafNS', 'SukunNS', 'ThreeDotsDownAboveNS',
'ThreeDotsUpAboveNS', 'TwoDotsAboveNS', 'TwoDotsVerticalAboveNS', 'UltapeshNS',
'WaslaNS', 'AinIni.12m_MeemFin.02', 'AinIni_YehBarreeFin',
'AinMed_YehBarreeFin', 'BehxIni_MeemFin', 'BehxIni_NoonGhunnaFin',
'BehxIni_RehFin', 'BehxIni_RehFin.b', 'BehxMed_MeemFin.py',
'BehxMed_NoonGhunnaFin', 'BehxMed_NoonGhunnaFin.cup', 'BehxMed_RehFin',
'BehxMed_RehFin.cup', 'BehxMed_YehxFin', 'FehxMed_YehBarreeFin',
'HahIni_YehBarreeFin', 'KafIni_YehBarreeFin', 'KafMed.12_YehxFin.01',
'KafMed_MeemFin', 'KafMed_YehBarreeFin', 'LamAlefFin', 'LamAlefFin.cup',
'LamAlefFin.cut', 'LamAlefFin.short', 'LamAlefSep', 'LamIni_MeemFin',
'LamIni_YehBarreeFin', 'LamMed_MeemFin', 'LamMed_MeemFin.b', 'LamMed_YehxFin',
'LamMed_YehxFin.cup', 'TahIni_YehBarreeFin', 'null', 'CR', 'space',
'exclam', 'quotedbl', 'numbersign',
]
# Feature files in data/*.txt; output gets compared to data/*.ttx.
TESTS = {
None: (
'mti/cmap',
),
'cmap': (
'mti/cmap',
),
'GSUB': (
'featurename-backward',
'featurename-forward',
'lookupnames-backward',
'lookupnames-forward',
'mixed-toplevels',
'mti/scripttable',
'mti/chainedclass',
'mti/chainedcoverage',
'mti/chained-glyph',
'mti/gsubalternate',
'mti/gsubligature',
'mti/gsubmultiple',
'mti/gsubreversechanined',
'mti/gsubsingle',
),
'GPOS': (
'mti/scripttable',
'mti/chained-glyph',
'mti/gposcursive',
'mti/gposkernset',
'mti/gposmarktobase',
'mti/gpospairclass',
'mti/gpospairglyph',
'mti/gpossingle',
'mti/mark-to-ligature',
),
'GDEF': (
'mti/gdefattach',
'mti/gdefclasses',
'mti/gdefligcaret',
'mti/gdefmarkattach',
'mti/gdefmarkfilter',
),
}
# TODO:
# https://github.com/Monotype/OpenType_Table_Source/issues/12
#
# 'mti/featuretable'
# 'mti/contextclass'
# 'mti/contextcoverage'
# 'mti/context-glyph'
def __init__(self, methodName):
unittest.TestCase.__init__(self, methodName)
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
# and fires deprecation warnings if a program uses the old name.
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def setUp(self):
pass
def tearDown(self):
pass
@staticmethod
def getpath(testfile):
path, _ = os.path.split(__file__)
return os.path.join(path, "data", testfile)
def expect_ttx(self, expected_ttx, actual_ttx, fromfile=None, tofile=None):
expected = [l+'\n' for l in expected_ttx.split('\n')]
actual = [l+'\n' for l in actual_ttx.split('\n')]
if actual != expected:
sys.stderr.write('\n')
for line in difflib.unified_diff(
expected, actual, fromfile=fromfile, tofile=tofile):
sys.stderr.write(line)
self.fail("TTX output is different from expected")
@classmethod
def create_font(celf):
font = TTFont()
font.setGlyphOrder(celf.GLYPH_ORDER)
return font
def check_mti_file(self, name, tableTag=None):
xml_expected_path = self.getpath("%s.ttx" % name + ('.'+tableTag if tableTag is not None else ''))
with open(xml_expected_path, 'rt', encoding="utf-8") as xml_expected_file:
xml_expected = xml_expected_file.read()
font = self.create_font()
with open(self.getpath("%s.txt" % name), 'rt', encoding="utf-8") as f:
table = mtiLib.build(f, font, tableTag=tableTag)
if tableTag is not None:
self.assertEqual(tableTag, table.tableTag)
tableTag = table.tableTag
# Make sure it compiles.
blob = table.compile(font)
# Make sure it decompiles.
decompiled = table.__class__()
decompiled.decompile(blob, font)
# XML from built object.
writer = XMLWriter(StringIO(), newlinestr='\n')
writer.begintag(tableTag); writer.newline()
table.toXML(writer, font)
writer.endtag(tableTag); writer.newline()
xml_built = writer.file.getvalue()
# XML from decompiled object.
writer = XMLWriter(StringIO(), newlinestr='\n')
writer.begintag(tableTag); writer.newline()
decompiled.toXML(writer, font)
writer.endtag(tableTag); writer.newline()
xml_binary = writer.file.getvalue()
self.expect_ttx(xml_binary, xml_built, fromfile='decompiled', tofile='built')
self.expect_ttx(xml_expected, xml_built, fromfile=xml_expected_path, tofile='built')
from fontTools.misc import xmlReader
f = StringIO()
f.write(xml_expected)
f.seek(0)
font2 = TTFont()
font2.setGlyphOrder(font.getGlyphOrder())
reader = xmlReader.XMLReader(f, font2)
reader.read(rootless=True)
# XML from object read from XML.
writer = XMLWriter(StringIO(), newlinestr='\n')
writer.begintag(tableTag); writer.newline()
font2[tableTag].toXML(writer, font)
writer.endtag(tableTag); writer.newline()
xml_fromxml = writer.file.getvalue()
self.expect_ttx(xml_expected, xml_fromxml, fromfile=xml_expected_path, tofile='fromxml')
def generate_mti_file_test(name, tableTag=None):
return lambda self: self.check_mti_file(os.path.join(*name.split('/')), tableTag=tableTag)
for tableTag,tests in MtiTest.TESTS.items():
for name in tests:
setattr(MtiTest, "test_MtiFile_%s%s" % (name, '_'+tableTag if tableTag else ''),
generate_mti_file_test(name, tableTag=tableTag))
if __name__ == "__main__":
if len(sys.argv) > 1:
from fontTools.mtiLib import main
font = MtiTest.create_font()
sys.exit(main(sys.argv[1:], font))
sys.exit(unittest.main())