# Source code for fontTools.mtiLib

    #!/usr/bin/python

    # FontDame-to-FontTools for OpenType Layout tables
    #
    # Source language spec is available at:
    # http://monotype.github.io/OpenType_Table_Source/otl_source.html
    # https://github.com/Monotype/OpenType_Table_Source/

    from fontTools import ttLib
    from fontTools.ttLib.tables._c_m_a_p import cmap_classes
    from fontTools.ttLib.tables import otTables as ot
    from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
    from fontTools.otlLib import builder as otl
    from contextlib import contextmanager
    from operator import setitem
    import logging

    class MtiLibError(Exception): pass
    class ReferenceNotFoundError(MtiLibError): pass
    class FeatureNotFoundError(ReferenceNotFoundError): pass
    class LookupNotFoundError(ReferenceNotFoundError): pass

    log = logging.getLogger("fontTools.mtiLib")

    def makeGlyph(s):
    	if s[:2] in ['U ', 'u ']:
    		return ttLib.TTFont._makeGlyphName(int(s[2:], 16))
    	elif s[:2] == '# ':
    		return "glyph%.5d" % int(s[2:])
    	assert s.find(' ') < 0, "Space found in glyph name: %s" % s
    	assert s, "Glyph name is empty"
    	return s

    def makeGlyphs(l):
    	return [makeGlyph(g) for g in l]

    def mapLookup(sym, mapping):
    	# Lookups are addressed by name.  So resolved them using a map if available.
    	# Fallback to parsing as lookup index if a map isn't provided.
    	if mapping is not None:
    		try:
    			idx = mapping[sym]
    		except KeyError:
    			raise LookupNotFoundError(sym)
    	else:
    		idx = int(sym)
    	return idx

    def mapFeature(sym, mapping):
    	# Features are referenced by index according the spec.  So, if symbol is an
    	# integer, use it directly.  Otherwise look up in the map if provided.
    	try:
    		idx = int(sym)
    	except ValueError:
    		try:
    			idx = mapping[sym]
    		except KeyError:
    			raise FeatureNotFoundError(sym)
    	return idx

    def setReference(mapper, mapping, sym, setter, collection, key):
    	try:
    		mapped = mapper(sym, mapping)
    	except ReferenceNotFoundError as e:
    		try:
    			if mapping is not None:
    				mapping.addDeferredMapping(lambda ref: setter(collection, key, ref), sym, e)
    				return
    		except AttributeError:
    			pass
    		raise
    	setter(collection, key, mapped)

    class DeferredMapping(dict):

    	def __init__(self):
    		self._deferredMappings = []

    	def addDeferredMapping(self, setter, sym, e):
    		log.debug("Adding deferred mapping for symbol '%s' %s", sym, type(e).__name__)
    		self._deferredMappings.append((setter,sym, e))

    	def applyDeferredMappings(self):
    		for setter,sym,e in self._deferredMappings:
    			log.debug("Applying deferred mapping for symbol '%s' %s", sym, type(e).__name__)
    			try:
    				mapped = self[sym]
    			except KeyError:
    				raise e
    			setter(mapped)
    			log.debug("Set to %s", mapped)
    		self._deferredMappings = []

    def parseScriptList(lines, featureMap=None):
    	self = ot.ScriptList()
    	records = []
    	with lines.between('script table'):
    		for line in lines:
    			while len(line) < 4:
    				line.append('')
    			scriptTag, langSysTag, defaultFeature, features = line
    			log.debug("Adding script %s language-system %s", scriptTag, langSysTag)

    			langSys = ot.LangSys()
    			langSys.LookupOrder = None
    			if defaultFeature:
    				setReference(mapFeature, featureMap, defaultFeature, setattr, langSys, 'ReqFeatureIndex')
    			else:
    				langSys.ReqFeatureIndex = 0xFFFF
    			syms = stripSplitComma(features)
    			langSys.FeatureIndex = theList = [3] * len(syms)
    			for i,sym in enumerate(syms):
    				setReference(mapFeature, featureMap, sym, setitem, theList, i)
    			langSys.FeatureCount = len(langSys.FeatureIndex)

    			script = [s for s in records if s.ScriptTag == scriptTag]
    			if script:
    				script = script[0].Script
    			else:
    				scriptRec = ot.ScriptRecord()
    				scriptRec.ScriptTag = scriptTag + ' '*(4 - len(scriptTag))
    				scriptRec.Script = ot.Script()
    				records.append(scriptRec)
    				script = scriptRec.Script
    				script.DefaultLangSys = None
    				script.LangSysRecord = []
    				script.LangSysCount = 0

    			if langSysTag == 'default':
    				script.DefaultLangSys = langSys
    			else:
    				langSysRec = ot.LangSysRecord()
    				langSysRec.LangSysTag = langSysTag + ' '*(4 - len(langSysTag))
    				langSysRec.LangSys = langSys
    				script.LangSysRecord.append(langSysRec)
    				script.LangSysCount = len(script.LangSysRecord)

    	for script in records:
    		script.Script.LangSysRecord = sorted(script.Script.LangSysRecord, key=lambda rec: rec.LangSysTag)
    	self.ScriptRecord = sorted(records, key=lambda rec: rec.ScriptTag)
    	self.ScriptCount = len(self.ScriptRecord)
    	return self

    def parseFeatureList(lines, lookupMap=None, featureMap=None):
    	self = ot.FeatureList()
    	self.FeatureRecord = []
    	with lines.between('feature table'):
    		for line in lines:
    			name, featureTag, lookups = line
    			if featureMap is not None:
    				assert name not in featureMap, "Duplicate feature name: %s" % name
    				featureMap[name] = len(self.FeatureRecord)
    			# If feature name is integer, make sure it matches its index.
    			try:
    				assert int(name) == len(self.FeatureRecord), "%d %d" % (name, len(self.FeatureRecord))
    			except ValueError:
    				pass
    			featureRec = ot.FeatureRecord()
    			featureRec.FeatureTag = featureTag
    			featureRec.Feature = ot.Feature()
    			self.FeatureRecord.append(featureRec)
    			feature = featureRec.Feature
    			feature.FeatureParams = None
    			syms = stripSplitComma(lookups)
    			feature.LookupListIndex = theList = [None] * len(syms)
    			for i,sym in enumerate(syms):
    				setReference(mapLookup, lookupMap, sym, setitem, theList, i)
    			feature.LookupCount = len(feature.LookupListIndex)

    	self.FeatureCount = len(self.FeatureRecord)
    	return self

    def parseLookupFlags(lines):
    	flags = 0
    	filterset = None
    	allFlags = [
    		'righttoleft',
    		'ignorebaseglyphs',
    		'ignoreligatures',
    		'ignoremarks',
    		'markattachmenttype',
    		'markfiltertype',
    	]
    	while lines.peeks()[0].lower() in allFlags:
    		line = next(lines)
    		flag = {
    			'righttoleft':		0x0001,
    			'ignorebaseglyphs':	0x0002,
    			'ignoreligatures':	0x0004,
    			'ignoremarks':		0x0008,
    			}.get(line[0].lower())
    		if flag:
    			assert line[1].lower() in ['yes', 'no'], line[1]
    			if line[1].lower() == 'yes':
    				flags |= flag
    			continue
    		if line[0].lower() == 'markattachmenttype':
    			flags |= int(line[1]) << 8
    			continue
    		if line[0].lower() == 'markfiltertype':
    			flags |= 0x10
    			filterset = int(line[1])
    	return flags, filterset

    def parseSingleSubst(lines, font, _lookupMap=None):
    	mapping = {}
    	for line in lines:
    		assert len(line) == 2, line
    		line = makeGlyphs(line)
    		mapping[line[0]] = line[1]
    	return otl.buildSingleSubstSubtable(mapping)

    def parseMultiple(lines, font, _lookupMap=None):
    	mapping = {}
    	for line in lines:
    		line = makeGlyphs(line)
    		mapping[line[0]] = line[1:]
    	return otl.buildMultipleSubstSubtable(mapping)

    def parseAlternate(lines, font, _lookupMap=None):
    	mapping = {}
    	for line in lines:
    		line = makeGlyphs(line)
    		mapping[line[0]] = line[1:]
    	return otl.buildAlternateSubstSubtable(mapping)

    def parseLigature(lines, font, _lookupMap=None):
    	mapping = {}
    	for line in lines:
    		assert len(line) >= 2, line
    		line = makeGlyphs(line)
    		mapping[tuple(line[1:])] = line[0]
    	return otl.buildLigatureSubstSubtable(mapping)

    def parseSinglePos(lines, font, _lookupMap=None):
    	values = {}
    	for line in lines:
    		assert len(line) == 3, line
    		w = line[0].title().replace(' ', '')
    		assert w in valueRecordFormatDict
    		g = makeGlyph(line[1])
    		v = int(line[2])
    		if g not in values:
    			values[g] = ValueRecord()
    		assert not hasattr(values[g], w), (g, w)
    		setattr(values[g], w, v)
    	return otl.buildSinglePosSubtable(values, font.getReverseGlyphMap())

    def parsePair(lines, font, _lookupMap=None):
    	self = ot.PairPos()
    	self.ValueFormat1 = self.ValueFormat2 = 0
    	typ = lines.peeks()[0].split()[0].lower()
    	if typ in ('left', 'right'):
    		self.Format = 1
    		values = {}
    		for line in lines:
    			assert len(line) == 4, line
    			side = line[0].split()[0].lower()
    			assert side in ('left', 'right'), side
    			what = line[0][len(side):].title().replace(' ', '')
    			mask = valueRecordFormatDict[what][0]
    			glyph1, glyph2 = makeGlyphs(line[1:3])
    			value = int(line[3])
    			if not glyph1 in values: values[glyph1] = {}
    			if not glyph2 in values[glyph1]: values[glyph1][glyph2] = (ValueRecord(),ValueRecord())
    			rec2 = values[glyph1][glyph2]
    			if side == 'left':
    				self.ValueFormat1 |= mask
    				vr = rec2[0]
    			else:
    				self.ValueFormat2 |= mask
    				vr = rec2[1]
    			assert not hasattr(vr, what), (vr, what)
    			setattr(vr, what, value)
    		self.Coverage = makeCoverage(set(values.keys()), font)
    		self.PairSet = []
    		for glyph1 in self.Coverage.glyphs:
    			values1 = values[glyph1]
    			pairset = ot.PairSet()
    			records = pairset.PairValueRecord = []
    			for glyph2 in sorted(values1.keys(), key=font.getGlyphID):
    				values2 = values1[glyph2]
    				pair = ot.PairValueRecord()
    				pair.SecondGlyph = glyph2
    				pair.Value1 = values2[0]
    				pair.Value2 = values2[1] if self.ValueFormat2 else None
    				records.append(pair)
    			pairset.PairValueCount = len(pairset.PairValueRecord)
    			self.PairSet.append(pairset)
    		self.PairSetCount = len(self.PairSet)
    	elif typ.endswith('class'):
    		self.Format = 2
    		classDefs = [None, None]
    		while lines.peeks()[0].endswith("class definition begin"):
    			typ = lines.peek()[0][:-len("class definition begin")].lower()
    			idx,klass = {
    				'first':	(0,ot.ClassDef1),
    				'second':	(1,ot.ClassDef2),
    			}[typ]
    			assert classDefs[idx] is None
    			classDefs[idx] = parseClassDef(lines, font, klass=klass)
    		self.ClassDef1, self.ClassDef2 = classDefs
    		self.Class1Count, self.Class2Count = (1+max(c.classDefs.values()) for c in classDefs)
    		self.Class1Record = [ot.Class1Record() for i in range(self.Class1Count)]
    		for rec1 in self.Class1Record:
    			rec1.Class2Record = [ot.Class2Record() for j in range(self.Class2Count)]
    			for rec2 in rec1.Class2Record:
    				rec2.Value1 = ValueRecord()
    				rec2.Value2 = ValueRecord()
    		for line in lines:
    			assert len(line) == 4, line
    			side = line[0].split()[0].lower()
    			assert side in ('left', 'right'), side
    			what = line[0][len(side):].title().replace(' ', '')
    			mask = valueRecordFormatDict[what][0]
    			class1, class2, value = (int(x) for x in line[1:4])
    			rec2 = self.Class1Record[class1].Class2Record[class2]
    			if side == 'left':
    				self.ValueFormat1 |= mask
    				vr = rec2.Value1
    			else:
    				self.ValueFormat2 |= mask
    				vr = rec2.Value2
    			assert not hasattr(vr, what), (vr, what)
    			setattr(vr, what, value)
    		for rec1 in self.Class1Record:
    			for rec2 in rec1.Class2Record:
    				rec2.Value1 = ValueRecord(self.ValueFormat1, rec2.Value1)
    				rec2.Value2 = ValueRecord(self.ValueFormat2, rec2.Value2) \
    						if self.ValueFormat2 else None

    		self.Coverage = makeCoverage(set(self.ClassDef1.classDefs.keys()), font)
    	else:
    		assert 0, typ
    	return self

    def parseKernset(lines, font, _lookupMap=None):
    	typ = lines.peeks()[0].split()[0].lower()
    	if typ in ('left', 'right'):
    		with lines.until(("firstclass definition begin", "secondclass definition begin")):
    			return parsePair(lines, font)
    	return parsePair(lines, font)

    def makeAnchor(data, klass=ot.Anchor):
    	assert len(data) <= 2
    	anchor = klass()
    	anchor.Format = 1
    	anchor.XCoordinate,anchor.YCoordinate = intSplitComma(data[0])
    	if len(data) > 1 and data[1] != '':
    		anchor.Format = 2
    		anchor.AnchorPoint = int(data[1])
    	return anchor

    def parseCursive(lines, font, _lookupMap=None):
    	records = {}
    	for line in lines:
    		assert len(line) in [3,4], line
    		idx,klass = {
    			'entry':	(0,ot.EntryAnchor),
    			'exit':		(1,ot.ExitAnchor),
    		}[line[0]]
    		glyph = makeGlyph(line[1])
    		if glyph not in records:
    			records[glyph] = [None,None]
    		assert records[glyph][idx] is None, (glyph, idx)
    		records[glyph][idx] = makeAnchor(line[2:], klass)
    	return otl.buildCursivePosSubtable(records, font.getReverseGlyphMap())

    def makeMarkRecords(data, coverage, c):
    	records = []
    	for glyph in coverage.glyphs:
    		klass, anchor = data[glyph]
    		record = c.MarkRecordClass()
    		record.Class = klass
    		setattr(record, c.MarkAnchor, anchor)
    		records.append(record)
    	return records

    def makeBaseRecords(data, coverage, c, classCount):
    	records = []
    	idx = {}
    	for glyph in coverage.glyphs:
    		idx[glyph] = len(records)
    		record = c.BaseRecordClass()
    		anchors = [None] * classCount
    		setattr(record, c.BaseAnchor, anchors)
    		records.append(record)
    	for (glyph,klass),anchor in data.items():
    		record = records[idx[glyph]]
    		anchors = getattr(record, c.BaseAnchor)
    		assert anchors[klass] is None, (glyph, klass)
    		anchors[klass] = anchor
    	return records

    def makeLigatureRecords(data, coverage, c, classCount):
    	records = [None] * len(coverage.glyphs)
    	idx = {g:i for i,g in enumerate(coverage.glyphs)}

    	for (glyph,klass,compIdx,compCount),anchor in data.items():
    		record = records[idx[glyph]]
    		if record is None:
    			record = records[idx[glyph]] = ot.LigatureAttach()
    			record.ComponentCount = compCount
    			record.ComponentRecord = [ot.ComponentRecord() for i in range(compCount)]
    			for compRec in record.ComponentRecord:
    				compRec.LigatureAnchor = [None] * classCount
    		assert record.ComponentCount == compCount, (glyph, record.ComponentCount, compCount)

    		anchors = record.ComponentRecord[compIdx - 1].LigatureAnchor
    		assert anchors[klass] is None, (glyph, compIdx, klass)
    		anchors[klass] = anchor
    	return records

    def parseMarkToSomething(lines, font, c):
    	self = c.Type()
    	self.Format = 1
    	markData = {}
    	baseData = {}
    	Data = {
    		'mark':		(markData, c.MarkAnchorClass),
    		'base':		(baseData, c.BaseAnchorClass),
    		'ligature':	(baseData, c.BaseAnchorClass),
    	}
    	maxKlass = 0
    	for line in lines:
    		typ = line[0]
    		assert typ in ('mark', 'base', 'ligature')
    		glyph = makeGlyph(line[1])
    		data, anchorClass = Data[typ]
    		extraItems = 2 if typ == 'ligature' else 0
    		extras = tuple(int(i) for i in line[2:2+extraItems])
    		klass = int(line[2+extraItems])
    		anchor = makeAnchor(line[3+extraItems:], anchorClass)
    		if typ == 'mark':
    			key,value = glyph,(klass,anchor)
    		else:
    			key,value = ((glyph,klass)+extras),anchor
    		assert key not in data, key
    		data[key] = value
    		maxKlass = max(maxKlass, klass)

    	# Mark
    	markCoverage = makeCoverage(set(markData.keys()), font, c.MarkCoverageClass)
    	markArray = c.MarkArrayClass()
    	markRecords = makeMarkRecords(markData, markCoverage, c)
    	setattr(markArray, c.MarkRecord, markRecords)
    	setattr(markArray, c.MarkCount, len(markRecords))
    	setattr(self, c.MarkCoverage, markCoverage)
    	setattr(self, c.MarkArray, markArray)
    	self.ClassCount = maxKlass + 1

    	# Base
    	self.classCount = 0 if not baseData else 1+max(k[1] for k,v in baseData.items())
    	baseCoverage = makeCoverage(set([k[0] for k in baseData.keys()]), font, c.BaseCoverageClass)
    	baseArray = c.BaseArrayClass()
    	if c.Base == 'Ligature':
    		baseRecords = makeLigatureRecords(baseData, baseCoverage, c, self.classCount)
    	else:
    		baseRecords = makeBaseRecords(baseData, baseCoverage, c, self.classCount)
    	setattr(baseArray, c.BaseRecord, baseRecords)
    	setattr(baseArray, c.BaseCount, len(baseRecords))
    	setattr(self, c.BaseCoverage, baseCoverage)
    	setattr(self, c.BaseArray, baseArray)

    	return self

    class MarkHelper(object):
    	def __init__(self):
    		for Which in ('Mark', 'Base'):
    			for What in ('Coverage', 'Array', 'Count', 'Record', 'Anchor'):
    				key = Which + What
    				if Which == 'Mark' and What in ('Count', 'Record', 'Anchor'):
    					value = key
    				else:
    					value = getattr(self, Which) + What
    				if value == 'LigatureRecord':
    					value = 'LigatureAttach'
    				setattr(self, key, value)
    				if What != 'Count':
    					klass = getattr(ot, value)
    					setattr(self, key+'Class', klass)

    class MarkToBaseHelper(MarkHelper):
    	Mark = 'Mark'
    	Base = 'Base'
    	Type = ot.MarkBasePos
    class MarkToMarkHelper(MarkHelper):
    	Mark = 'Mark1'
    	Base = 'Mark2'
    	Type = ot.MarkMarkPos
    class MarkToLigatureHelper(MarkHelper):
    	Mark = 'Mark'
    	Base = 'Ligature'
    	Type = ot.MarkLigPos

    def parseMarkToBase(lines, font, _lookupMap=None):
    	return parseMarkToSomething(lines, font, MarkToBaseHelper())
    def parseMarkToMark(lines, font, _lookupMap=None):
    	return parseMarkToSomething(lines, font, MarkToMarkHelper())
    def parseMarkToLigature(lines, font, _lookupMap=None):
    	return parseMarkToSomething(lines, font, MarkToLigatureHelper())

    def stripSplitComma(line):
    	return [s.strip() for s in line.split(',')] if line else []

    def intSplitComma(line):
    	return [int(i) for i in line.split(',')] if line else []

    # Copied from fontTools.subset
    class ContextHelper(object):
    	def __init__(self, klassName, Format):
    		if klassName.endswith('Subst'):
    			Typ = 'Sub'
    			Type = 'Subst'
    		else:
    			Typ = 'Pos'
    			Type = 'Pos'
    		if klassName.startswith('Chain'):
    			Chain = 'Chain'
    			InputIdx = 1
    			DataLen = 3
    		else:
    			Chain = ''
    			InputIdx = 0
    			DataLen = 1
    		ChainTyp = Chain+Typ

    		self.Typ = Typ
    		self.Type = Type
    		self.Chain = Chain
    		self.ChainTyp = ChainTyp
    		self.InputIdx = InputIdx
    		self.DataLen = DataLen

    		self.LookupRecord = Type+'LookupRecord'

    		if Format == 1:
    			Coverage = lambda r: r.Coverage
    			ChainCoverage = lambda r: r.Coverage
    			ContextData = lambda r:(None,)
    			ChainContextData = lambda r:(None, None, None)
    			SetContextData = None
    			SetChainContextData = None
    			RuleData = lambda r:(r.Input,)
    			ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
    			def SetRuleData(r, d):
    				(r.Input,) = d
    				(r.GlyphCount,) = (len(x)+1 for x in d)
    			def ChainSetRuleData(r, d):
    				(r.Backtrack, r.Input, r.LookAhead) = d
    				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
    		elif Format == 2:
    			Coverage = lambda r: r.Coverage
    			ChainCoverage = lambda r: r.Coverage
    			ContextData = lambda r:(r.ClassDef,)
    			ChainContextData = lambda r:(r.BacktrackClassDef,
    						     r.InputClassDef,
    						     r.LookAheadClassDef)
    			def SetContextData(r, d):
    				(r.ClassDef,) = d
    			def SetChainContextData(r, d):
    				(r.BacktrackClassDef,
    				 r.InputClassDef,
    				 r.LookAheadClassDef) = d
    			RuleData = lambda r:(r.Class,)
    			ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
    			def SetRuleData(r, d):
    				(r.Class,) = d
    				(r.GlyphCount,) = (len(x)+1 for x in d)
    			def ChainSetRuleData(r, d):
    				(r.Backtrack, r.Input, r.LookAhead) = d
    				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
    		elif Format == 3:
    			Coverage = lambda r: r.Coverage[0]
    			ChainCoverage = lambda r: r.InputCoverage[0]
    			ContextData = None
    			ChainContextData = None
    			SetContextData = None
    			SetChainContextData = None
    			RuleData = lambda r: r.Coverage
    			ChainRuleData = lambda r:(r.BacktrackCoverage +
    						  r.InputCoverage +
    						  r.LookAheadCoverage)
    			def SetRuleData(r, d):
    				(r.Coverage,) = d
    				(r.GlyphCount,) = (len(x) for x in d)
    			def ChainSetRuleData(r, d):
    				(r.BacktrackCoverage, r.InputCoverage, r.LookAheadCoverage) = d
    				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(x) for x in d)
    		else:
    			assert 0, "unknown format: %s" % Format

    		if Chain:
    			self.Coverage = ChainCoverage
    			self.ContextData = ChainContextData
    			self.SetContextData = SetChainContextData
    			self.RuleData = ChainRuleData
    			self.SetRuleData = ChainSetRuleData
    		else:
    			self.Coverage = Coverage
    			self.ContextData = ContextData
    			self.SetContextData = SetContextData
    			self.RuleData = RuleData
    			self.SetRuleData = SetRuleData

    		if Format == 1:
    			self.Rule = ChainTyp+'Rule'
    			self.RuleCount = ChainTyp+'RuleCount'
    			self.RuleSet = ChainTyp+'RuleSet'
    			self.RuleSetCount = ChainTyp+'RuleSetCount'
    			self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
    		elif Format == 2:
    			self.Rule = ChainTyp+'ClassRule'
    			self.RuleCount = ChainTyp+'ClassRuleCount'
    			self.RuleSet = ChainTyp+'ClassSet'
    			self.RuleSetCount = ChainTyp+'ClassSetCount'
    			self.Intersect = lambda glyphs, c, r: (c.intersect_class(glyphs, r) if c
    							       else (set(glyphs) if r == 0 else set()))

    			self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
    			self.ClassDefIndex = 1 if Chain else 0
    			self.Input = 'Input' if Chain else 'Class'

    def parseLookupRecords(items, klassName, lookupMap=None):
    	klass = getattr(ot, klassName)
    	lst = []
    	for item in items:
    		rec = klass()
    		item = stripSplitComma(item)
    		assert len(item) == 2, item
    		idx = int(item[0])
    		assert idx > 0, idx
    		rec.SequenceIndex = idx - 1
    		setReference(mapLookup, lookupMap, item[1], setattr, rec, 'LookupListIndex')
    		lst.append(rec)
    	return lst

    def makeClassDef(classDefs, font, klass=ot.Coverage):
    	if not classDefs: return None
    	self = klass()
    	self.classDefs = dict(classDefs)
    	return self

    def parseClassDef(lines, font, klass=ot.ClassDef):
    	classDefs = {}
    	with lines.between('class definition'):
    		for line in lines:
    			glyph = makeGlyph(line[0])
    			assert glyph not in classDefs, glyph
    			classDefs[glyph] = int(line[1])
    	return makeClassDef(classDefs, font, klass)

    def makeCoverage(glyphs, font, klass=ot.Coverage):
    	if not glyphs: return None
    	if isinstance(glyphs, set):
    		glyphs = sorted(glyphs)
    	coverage = klass()
    	coverage.glyphs = sorted(set(glyphs), key=font.getGlyphID)
    	return coverage

    def parseCoverage(lines, font, klass=ot.Coverage):
    	glyphs = []
    	with lines.between('coverage definition'):
    		for line in lines:
    			glyphs.append(makeGlyph(line[0]))
    	return makeCoverage(glyphs, font, klass)

    def bucketizeRules(self, c, rules, bucketKeys):
    	buckets = {}
    	for seq,recs in rules:
    		buckets.setdefault(seq[c.InputIdx][0], []).append((tuple(s[1 if i==c.InputIdx else 0:] for i,s in enumerate(seq)), recs))

    	rulesets = []
    	for firstGlyph in bucketKeys:
    		if firstGlyph not in buckets:
    			rulesets.append(None)
    			continue
    		thisRules = []
    		for seq,recs in buckets[firstGlyph]:
    			rule = getattr(ot, c.Rule)()
    			c.SetRuleData(rule, seq)
    			setattr(rule, c.Type+'Count', len(recs))
    			setattr(rule, c.LookupRecord, recs)
    			thisRules.append(rule)

    		ruleset = getattr(ot, c.RuleSet)()
    		setattr(ruleset, c.Rule, thisRules)
    		setattr(ruleset, c.RuleCount, len(thisRules))
    		rulesets.append(ruleset)

    	setattr(self, c.RuleSet, rulesets)
    	setattr(self, c.RuleSetCount, len(rulesets))

    def parseContext(lines, font, Type, lookupMap=None):
    	self = getattr(ot, Type)()
    	typ = lines.peeks()[0].split()[0].lower()
    	if typ == 'glyph':
    		self.Format = 1
    		log.debug("Parsing %s format %s", Type, self.Format)
    		c = ContextHelper(Type, self.Format)
    		rules = []
    		for line in lines:
    			assert line[0].lower() == 'glyph', line[0]
    			while len(line) < 1+c.DataLen: line.append('')
    			seq = tuple(makeGlyphs(stripSplitComma(i)) for i in line[1:1+c.DataLen])
    			recs = parseLookupRecords(line[1+c.DataLen:], c.LookupRecord, lookupMap)
    			rules.append((seq, recs))

    		firstGlyphs = set(seq[c.InputIdx][0] for seq,recs in rules)
    		self.Coverage = makeCoverage(firstGlyphs, font)
    		bucketizeRules(self, c, rules, self.Coverage.glyphs)
    	elif typ.endswith('class'):
    		self.Format = 2
    		log.debug("Parsing %s format %s", Type, self.Format)
    		c = ContextHelper(Type, self.Format)
    		classDefs = [None] * c.DataLen
    		while lines.peeks()[0].endswith("class definition begin"):
    			typ = lines.peek()[0][:-len("class definition begin")].lower()
    			idx,klass = {
    			1: {
    				'':		(0,ot.ClassDef),
    			},
    			3: {
    				'backtrack':	(0,ot.BacktrackClassDef),
    				'':		(1,ot.InputClassDef),
    				'lookahead':	(2,ot.LookAheadClassDef),
    			},
    			}[c.DataLen][typ]
    			assert classDefs[idx] is None, idx
    			classDefs[idx] = parseClassDef(lines, font, klass=klass)
    		c.SetContextData(self, classDefs)
    		rules = []
    		for line in lines:
    			assert line[0].lower().startswith('class'), line[0]
    			while len(line) < 1+c.DataLen: line.append('')
    			seq = tuple(intSplitComma(i) for i in line[1:1+c.DataLen])
    			recs = parseLookupRecords(line[1+c.DataLen:], c.LookupRecord, lookupMap)
    			rules.append((seq, recs))
    		firstClasses = set(seq[c.InputIdx][0] for seq,recs in rules)
    		firstGlyphs = set(g for g,c in classDefs[c.InputIdx].classDefs.items() if c in firstClasses)
    		self.Coverage = makeCoverage(firstGlyphs, font)
    		bucketizeRules(self, c, rules, range(max(firstClasses) + 1))
    	elif typ.endswith('coverage'):
    		self.Format = 3
    		log.debug("Parsing %s format %s", Type, self.Format)
    		c = ContextHelper(Type, self.Format)
    		coverages = tuple([] for i in range(c.DataLen))
    		while lines.peeks()[0].endswith("coverage definition begin"):
    			typ = lines.peek()[0][:-len("coverage definition begin")].lower()
    			idx,klass = {
    			1: {
    				'':		(0,ot.Coverage),
    			},
    			3: {
    				'backtrack':	(0,ot.BacktrackCoverage),
    				'input':	(1,ot.InputCoverage),
    				'lookahead':	(2,ot.LookAheadCoverage),
    			},
    			}[c.DataLen][typ]
    			coverages[idx].append(parseCoverage(lines, font, klass=klass))
    		c.SetRuleData(self, coverages)
    		lines = list(lines)
    		assert len(lines) == 1
    		line = lines[0]
    		assert line[0].lower() == 'coverage', line[0]
    		recs = parseLookupRecords(line[1:], c.LookupRecord, lookupMap)
    		setattr(self, c.Type+'Count', len(recs))
    		setattr(self, c.LookupRecord, recs)
    	else:
    		assert 0, typ
    	return self

    def parseContextSubst(lines, font, lookupMap=None):
    	return parseContext(lines, font, "ContextSubst", lookupMap=lookupMap)
    def parseContextPos(lines, font, lookupMap=None):
    	return parseContext(lines, font, "ContextPos", lookupMap=lookupMap)
    def parseChainedSubst(lines, font, lookupMap=None):
    	return parseContext(lines, font, "ChainContextSubst", lookupMap=lookupMap)
    def parseChainedPos(lines, font, lookupMap=None):
    	return parseContext(lines, font, "ChainContextPos", lookupMap=lookupMap)

    def parseReverseChainedSubst(lines, font, _lookupMap=None):
    	self = ot.ReverseChainSingleSubst()
    	self.Format = 1
    	coverages = ([], [])
    	while lines.peeks()[0].endswith("coverage definition begin"):
    		typ = lines.peek()[0][:-len("coverage definition begin")].lower()
    		idx,klass = {
    			'backtrack':	(0,ot.BacktrackCoverage),
    			'lookahead':	(1,ot.LookAheadCoverage),
    		}[typ]
    		coverages[idx].append(parseCoverage(lines, font, klass=klass))
    	self.BacktrackCoverage = coverages[0]
    	self.BacktrackGlyphCount = len(self.BacktrackCoverage)
    	self.LookAheadCoverage = coverages[1]
    	self.LookAheadGlyphCount = len(self.LookAheadCoverage)
    	mapping = {}
    	for line in lines:
    		assert len(line) == 2, line
    		line = makeGlyphs(line)
    		mapping[line[0]] = line[1]
    	self.Coverage = makeCoverage(set(mapping.keys()), font)
    	self.Substitute = [mapping[k] for k in self.Coverage.glyphs]
    	self.GlyphCount = len(self.Substitute)
    	return self

    def parseLookup(lines, tableTag, font, lookupMap=None):
    	line = lines.expect('lookup')
    	_, name, typ = line
    	log.debug("Parsing lookup type %s %s", typ, name)
    	lookup = ot.Lookup()
    	lookup.LookupFlag,filterset = parseLookupFlags(lines)
    	if filterset is not None:
    		lookup.MarkFilteringSet = filterset
    	lookup.LookupType, parseLookupSubTable = {
    		'GSUB': {
    			'single':	(1,	parseSingleSubst),
    			'multiple':	(2,	parseMultiple),
    			'alternate':	(3,	parseAlternate),
    			'ligature':	(4,	parseLigature),
    			'context':	(5,	parseContextSubst),
    			'chained':	(6,	parseChainedSubst),
    			'reversechained':(8,	parseReverseChainedSubst),
    		},
    		'GPOS': {
    			'single':	(1,	parseSinglePos),
    			'pair':		(2,	parsePair),
    			'kernset':	(2,	parseKernset),
    			'cursive':	(3,	parseCursive),
    			'mark to base':	(4,	parseMarkToBase),
    			'mark to ligature':(5,	parseMarkToLigature),
    			'mark to mark':	(6,	parseMarkToMark),
    			'context':	(7,	parseContextPos),
    			'chained':	(8,	parseChainedPos),
    		},
    	}[tableTag][typ]

    	with lines.until('lookup end'):
    		subtables = []

    		while lines.peek():
    			with lines.until(('% subtable', 'subtable end')):
    				while lines.peek():
    					subtable = parseLookupSubTable(lines, font, lookupMap)
    					assert lookup.LookupType == subtable.LookupType
    					subtables.append(subtable)
    			if lines.peeks()[0] in ('% subtable', 'subtable end'):
    				next(lines)
    	lines.expect('lookup end')

    	lookup.SubTable = subtables
    	lookup.SubTableCount = len(lookup.SubTable)
    	if lookup.SubTableCount == 0:
    		# Remove this return when following is fixed:
    		# https://github.com/fonttools/fonttools/issues/789
    		return None
    	return lookup

    def parseGSUBGPOS(lines, font, tableTag):
    	container = ttLib.getTableClass(tableTag)()
    	lookupMap = DeferredMapping()
    	featureMap = DeferredMapping()
    	assert tableTag in ('GSUB', 'GPOS')
    	log.debug("Parsing %s", tableTag)
    	self = getattr(ot, tableTag)()
    	self.Version = 0x00010000
    	fields = {
    		'script table begin':
    		('ScriptList',
    		 lambda lines: parseScriptList (lines, featureMap)),
    		'feature table begin':
    		('FeatureList',
    		 lambda lines: parseFeatureList (lines, lookupMap, featureMap)),
    		'lookup':
    		('LookupList',
    		 None),
    	}
    	for attr,parser in fields.values():
    		setattr(self, attr, None)
    	while lines.peek() is not None:
    		typ = lines.peek()[0].lower()
    		if typ not in fields:
    			log.debug('Skipping %s', lines.peek())
    			next(lines)
    			continue
    		attr,parser = fields[typ]
    		if typ == 'lookup':
    			if self.LookupList is None:
    				self.LookupList = ot.LookupList()
    				self.LookupList.Lookup = []
    			_, name, _ = lines.peek()
    			lookup = parseLookup(lines, tableTag, font, lookupMap)
    			if lookupMap is not None:
    				assert name not in lookupMap, "Duplicate lookup name: %s" % name
    				lookupMap[name] = len(self.LookupList.Lookup)
    			else:
    				assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup))
    			self.LookupList.Lookup.append(lookup)
    		else:
    			assert getattr(self, attr) is None, attr
    			setattr(self, attr, parser(lines))
    	if self.LookupList:
    		self.LookupList.LookupCount = len(self.LookupList.Lookup)
    	if lookupMap is not None:
    		lookupMap.applyDeferredMappings()
    	if featureMap is not None:
    		featureMap.applyDeferredMappings()
    	container.table = self
    	return container

    def parseGSUB(lines, font):
    	return parseGSUBGPOS(lines, font, 'GSUB')
    def parseGPOS(lines, font):
    	return parseGSUBGPOS(lines, font, 'GPOS')

    def parseAttachList(lines, font):
    	points = {}
    	with lines.between('attachment list'):
    		for line in lines:
    			glyph = makeGlyph(line[0])
    			assert glyph not in points, glyph
    			points[glyph] = [int(i) for i in line[1:]]
    	return otl.buildAttachList(points, font.getReverseGlyphMap())

    def parseCaretList(lines, font):
    	carets = {}
    	with lines.between('carets'):
    		for line in lines:
    			glyph = makeGlyph(line[0])
    			assert glyph not in carets, glyph
    			num = int(line[1])
    			thisCarets = [int(i) for i in line[2:]]
    			assert num == len(thisCarets), line
    			carets[glyph] = thisCarets
    	return otl.buildLigCaretList(carets, {}, font.getReverseGlyphMap())

    def makeMarkFilteringSets(sets, font):
    	self = ot.MarkGlyphSetsDef()
    	self.MarkSetTableFormat = 1
    	self.MarkSetCount = 1 + max(sets.keys())
    	self.Coverage = [None] * self.MarkSetCount
    	for k,v in sorted(sets.items()):
    		self.Coverage[k] = makeCoverage(set(v), font)
    	return self

    def parseMarkFilteringSets(lines, font):
    	sets = {}
    	with lines.between('set definition'):
    		for line in lines:
    			assert len(line) == 2, line
    			glyph = makeGlyph(line[0])
    			# TODO accept set names
    			st = int(line[1])
    			if st not in sets:
    				sets[st] = []
    			sets[st].append(glyph)
    	return makeMarkFilteringSets(sets, font)

    def parseGDEF(lines, font):
    	container = ttLib.getTableClass('GDEF')()
    	log.debug("Parsing GDEF")
    	self = ot.GDEF()
    	fields = {
    		'class definition begin':
    			('GlyphClassDef',
    			 lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef)),
    		'attachment list begin':
    			('AttachList', parseAttachList),
    		'carets begin':
    			('LigCaretList', parseCaretList),
    		'mark attachment class definition begin':
    			('MarkAttachClassDef',
    			 lambda lines, font: parseClassDef(lines, font, klass=ot.MarkAttachClassDef)),
    		'markfilter set definition begin':
    			('MarkGlyphSetsDef', parseMarkFilteringSets),
    	}
    	for attr,parser in fields.values():
    		setattr(self, attr, None)
    	while lines.peek() is not None:
    		typ = lines.peek()[0].lower()
    		if typ not in fields:
    			log.debug('Skipping %s', typ)
    			next(lines)
    			continue
    		attr,parser = fields[typ]
    		assert getattr(self, attr) is None, attr
    		setattr(self, attr, parser(lines, font))
    	self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002
    	container.table = self
    	return container

    def parseCmap(lines, font):
    	container = ttLib.getTableClass('cmap')()
    	log.debug("Parsing cmap")
    	tables = []
    	while lines.peek() is not None:
    		lines.expect('cmap subtable %d' % len(tables))
    		platId, encId, fmt, lang = [
    			parseCmapId(lines, field)
    			for field in ('platformID', 'encodingID', 'format', 'language')]
    		table = cmap_classes[fmt](fmt)
    		table.platformID = platId
    		table.platEncID = encId
    		table.language = lang
    		table.cmap = {}
    		line = next(lines)
    		while line[0] != 'end subtable':
    			table.cmap[int(line[0], 16)] = line[1]
    			line = next(lines)
    		tables.append(table)
    	container.tableVersion = 0
    	container.tables = tables
    	return container

    def parseCmapId(lines, field):
    	line = next(lines)
    	assert field == line[0]
    	return int(line[1])

    def parseTable(lines, font, tableTag=None):
    	log.debug("Parsing table")
    	line = lines.peeks()
    	tag = None
    	if line[0].split()[0] == 'FontDame':
    		tag = line[0].split()[1]
    	elif ' '.join(line[0].split()[:3]) == 'Font Chef Table':
    		tag = line[0].split()[3]
    	if tag is not None:
    		next(lines)
    		tag = tag.ljust(4)
    		if tableTag is None:
    			tableTag = tag
    		else:
    			assert tableTag == tag, (tableTag, tag)

    	assert tableTag is not None, "Don't know what table to parse and data doesn't specify"

    	return {
    		'GSUB': parseGSUB,
    		'GPOS': parseGPOS,
    		'GDEF': parseGDEF,
    		'cmap': parseCmap,
    		}[tableTag](lines, font)

    class Tokenizer(object):

    	def __init__(self, f):
    		# TODO BytesIO / StringIO as needed?  also, figure out whether we work on bytes or unicode
    		lines = iter(f)
    		try:
    			self.filename = f.name
    		except:
    			self.filename = None
    		self.lines = iter(lines)
    		self.line = ''
    		self.lineno = 0
    		self.stoppers = []
    		self.buffer = None

    	def __iter__(self):
    		return self

    	def _next_line(self):
    		self.lineno += 1
    		line = self.line = next(self.lines)
    		line = [s.strip() for s in line.split('\t')]
    		if len(line) == 1 and not line[0]:
    			del line[0]
    		if line and not line[-1]:
    			log.warning('trailing tab found on line %d: %s' % (self.lineno, self.line))
    			while line and not line[-1]:
    				del line[-1]
    		return line

    	def _next_nonempty(self):
    		while True:
    			line = self._next_line()
    			# Skip comments and empty lines
    			if line and line[0] and (line[0][0] != '%' or line[0] == '% subtable'):
    				return line

    	def _next_buffered(self):
    		if self.buffer:
    			ret = self.buffer
    			self.buffer = None
    			return ret
    		else:
    			return self._next_nonempty()

    	def __next__(self):
    		line = self._next_buffered()
    		if line[0].lower() in self.stoppers:
    			self.buffer = line
    			raise StopIteration
    		return line

    	def next(self):
    		return self.__next__()

    	def peek(self):
    		if not self.buffer:
    			try:
    				self.buffer = self._next_nonempty()
    			except StopIteration:
    				return None
    		if self.buffer[0].lower() in self.stoppers:
    			return None
    		return self.buffer

    	def peeks(self):
    		ret = self.peek()
    		return ret if ret is not None else ('',)

    	@contextmanager
    	def between(self, tag):
    		start = tag + ' begin'
    		end = tag + ' end'
    		self.expectendswith(start)
    		self.stoppers.append(end)
    		yield
    		del self.stoppers[-1]
    		self.expect(tag + ' end')

    	@contextmanager
    	def until(self, tags):
    		if type(tags) is not tuple:
    			tags = (tags,)
    		self.stoppers.extend(tags)
    		yield
    		del self.stoppers[-len(tags):]

    	def expect(self, s):
    		line = next(self)
    		tag = line[0].lower()
    		assert tag == s, "Expected '%s', got '%s'" % (s, tag)
    		return line

    	def expectendswith(self, s):
    		line = next(self)
    		tag = line[0].lower()
    		assert tag.endswith(s), "Expected '*%s', got '%s'" % (s, tag)
    		return line

    def build(f, font, tableTag=None):
    	"""Convert a Monotype font layout file to an OpenType layout object

    	A font object must be passed, but this may be a "dummy" font; it is only
    	used for sorting glyph sets when making coverage tables and to hold the
    	OpenType layout table while it is being built.

    	Args:
    		f: A file object.
    		font (TTFont): A font object.
    		tableTag (string): If provided, asserts that the file contains data for the
    			given OpenType table.

    	Returns:
    		An object representing the table. (e.g. ``table_G_S_U_B_``)
    	"""
    	lines = Tokenizer(f)
    	return parseTable(lines, font, tableTag=tableTag)

    def main(args=None, font=None):
    	"""Convert a FontDame OTL file to TTX XML

    	Writes XML output to stdout.

    	Args:
    		args: Command line arguments (``--font``, ``--table``, input files).
    	"""
    	import sys
    	from fontTools import configLogger
    	from fontTools.misc.testTools import MockFont

    	if args is None:
    		args = sys.argv[1:]

    	# configure the library logger (for >= WARNING)
    	configLogger()
    	# comment this out to enable debug messages from mtiLib's logger
    	# log.setLevel(logging.DEBUG)

    	import argparse
    	parser = argparse.ArgumentParser(
    		"fonttools mtiLib",
    		description=main.__doc__,
    	)

    	parser.add_argument('--font', '-f', metavar='FILE', dest="font",
    		help="Input TTF files (used for glyph classes and sorting coverage tables)")
    	parser.add_argument('--table', '-t', metavar='TABLE', dest="tableTag",
    		help="Table to fill (sniffed from input file if not provided)")
    	parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
    		help="Input FontDame .txt files")

    	args = parser.parse_args(args)

    	if font is None:
    		if args.font:
    			font = ttLib.TTFont(args.font)
    		else:
    			font = MockFont()

    	for f in args.inputs:
    		log.debug("Processing %s", f)
    		with open(f, 'rt', encoding="utf-8") as f:
    			table = build(f, font, tableTag=args.tableTag)
    		blob = table.compile(font) # Make sure it compiles
    		decompiled = table.__class__()
    		decompiled.decompile(blob, font) # Make sure it decompiles!

    		#continue
    		from fontTools.misc import xmlWriter
    		tag = table.tableTag
    		writer = xmlWriter.XMLWriter(sys.stdout)
    		writer.begintag(tag)
    		writer.newline()
    		#table.toXML(writer, font)
    		decompiled.toXML(writer, font)
    		writer.endtag(tag)
    		writer.newline()

    if __name__ == '__main__':
    	import sys
    	sys.exit(main())