otTables.py revision d41386e7f7b950d2429fbf6d1964ba129d2bc2a6
1d4d151390d1288f8d2df30f6dfa26a309c7334dajvr"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various
2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrOpenType subtables.
3d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
464b5c80e80444a124da335e8d4d208bffcf2737bjvrMost are constructed upon import from data in otData.py, all are populated with
564b5c80e80444a124da335e8d4d208bffcf2737bjvrconverter objects from otConverters.py.
6d4d151390d1288f8d2df30f6dfa26a309c7334dajvr"""
7d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
8d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom otBase import BaseTable, FormatSwitchingBaseTable
9d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
10d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
11d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass LookupOrder(BaseTable):
12d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
14d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
15d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FeatureParams(BaseTable):
16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
17d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	# XXX The above is no longer true; the 'size' feature uses FeatureParams now.
18d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
19d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
20d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvrclass Coverage(FormatSwitchingBaseTable):
21d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
22d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	# manual implementation to get rid of glyphID dependencies
23d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
24d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def postRead(self, table, font):
25d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		if self.Format == 1:
26d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			self.glyphs = table["GlyphArray"]
27d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		elif self.Format == 2:
28d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			glyphs = self.glyphs = []
29d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			ranges = table["RangeRecord"]
30d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			for r in ranges:
31d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				assert r.StartCoverageIndex == len(glyphs), \
32d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					(r.StartCoverageIndex, len(glyphs))
33d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				start = r.Start
34d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				end = r.End
35d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				startID = font.getGlyphID(start)
36d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				endID = font.getGlyphID(end)
37d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				glyphs.append(start)
38d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				for glyphID in range(startID + 1, endID):
39d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					glyphs.append(font.getGlyphName(glyphID))
40d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				if start != end:
41d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					glyphs.append(end)
42d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		else:
43d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			assert 0
44d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
45d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def preWrite(self, font):
46d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		format = 1
47d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		table = {"GlyphArray": self.glyphs}
48d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		if self.glyphs:
49d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			glyphIDs = []
50d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			for glyphName in self.glyphs:
51d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				glyphIDs.append(font.getGlyphID(glyphName))
52d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
53d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			last = glyphIDs[0]
54d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			ranges = [[last]]
55d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			for glyphID in glyphIDs[1:]:
56d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				if glyphID == last + 1:
57d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					pass
58d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				else:
59d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges[-1].append(last)
60d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges.append([glyphID])
61d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				last = glyphID
62d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			ranges[-1].append(last)
63d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
64d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			if len(ranges) * 3 < len(self.glyphs):  # 3 words vs. 1 word
65d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				# Format 2 is more compact
66d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				index = 0
67d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				for i in range(len(ranges)):
68d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					start, end = ranges[i]
69d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r = RangeRecord()
70d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.Start = font.getGlyphName(start)
71d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.End = font.getGlyphName(end)
72d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.StartCoverageIndex = index
73d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges[i] = r
74d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					index = index + end - start + 1
75d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				format = 2
76d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				table = {"RangeRecord": ranges}
77d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			#else:
78d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			#	fallthrough; Format 1 is more compact
79d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		self.Format = format
80d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		return table
81d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
82d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def toXML2(self, xmlWriter, font):
83d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		for glyphName in self.glyphs:
84d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			xmlWriter.simpletag("Glyph", value=glyphName)
85d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			xmlWriter.newline()
86d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
87d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def fromXML(self, (name, attrs, content), font):
88d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		glyphs = getattr(self, "glyphs", None)
89d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		if glyphs is None:
90d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			glyphs = []
91d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			self.glyphs = glyphs
92d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		glyphs.append(attrs["value"])
93d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
9564b5c80e80444a124da335e8d4d208bffcf2737bjvr#
9664b5c80e80444a124da335e8d4d208bffcf2737bjvr# For each subtable format there is a class. However, we don't really distinguish
9764b5c80e80444a124da335e8d4d208bffcf2737bjvr# between "field name" and "format name": often these are the same. Yet there's
9864b5c80e80444a124da335e8d4d208bffcf2737bjvr# a whole bunch of fields with different names. The following dict is a mapping
9964b5c80e80444a124da335e8d4d208bffcf2737bjvr# from "format name" to "field name". _buildClasses() uses this to create a
10064b5c80e80444a124da335e8d4d208bffcf2737bjvr# subclass for each alternate field name.
10164b5c80e80444a124da335e8d4d208bffcf2737bjvr#
10264b5c80e80444a124da335e8d4d208bffcf2737bjvr_equivalents = {
10364b5c80e80444a124da335e8d4d208bffcf2737bjvr	'MarkArray': ("Mark1Array",),
10464b5c80e80444a124da335e8d4d208bffcf2737bjvr	'LangSys': ('DefaultLangSys',),
10564b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
106d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
10764b5c80e80444a124da335e8d4d208bffcf2737bjvr			'LookaheadCoverage'),
10864b5c80e80444a124da335e8d4d208bffcf2737bjvr	'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
10964b5c80e80444a124da335e8d4d208bffcf2737bjvr			'LookaheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
11064b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
11164b5c80e80444a124da335e8d4d208bffcf2737bjvr			'Mark2Anchor', 'MarkAnchor'),
11264b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
11364b5c80e80444a124da335e8d4d208bffcf2737bjvr			'XDeviceTable', 'YDeviceTable', 'DeviceTable'),
11464b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Axis': ('HorizAxis', 'VertAxis',),
11564b5c80e80444a124da335e8d4d208bffcf2737bjvr	'MinMax': ('DefaultMinMax',),
11664b5c80e80444a124da335e8d4d208bffcf2737bjvr	'BaseCoord': ('MinCoord', 'MaxCoord',),
11764b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfLangSys': ('DefJstfLangSys',),
11864b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
11964b5c80e80444a124da335e8d4d208bffcf2737bjvr			'ExtensionDisableGSUB',),
12064b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
12164b5c80e80444a124da335e8d4d208bffcf2737bjvr			'ExtensionDisableGPOS',),
12264b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
12364b5c80e80444a124da335e8d4d208bffcf2737bjvr}
124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
126d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildClasses():
127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	import new, re
128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	from otData import otData
129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	namespace = globals()
132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
133d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# populate module with classes
134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for name, table in otData:
135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		baseClass = BaseTable
136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		m = formatPat.match(name)
137d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if m:
138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# XxxFormatN subtable, we only add the "base" table
139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name = m.group(1)
140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			baseClass = FormatSwitchingBaseTable
141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if not namespace.has_key(name):
14264b5c80e80444a124da335e8d4d208bffcf2737bjvr			# the class doesn't exist yet, so the base implementation is used.
143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls = new.classobj(name, (baseClass,), {})
144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			namespace[name] = cls
145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
14664b5c80e80444a124da335e8d4d208bffcf2737bjvr	for base, alts in _equivalents.items():
147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		base = namespace[base]
148d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for alt in alts:
149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			namespace[alt] = new.classobj(alt, (base,), {})
150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	global lookupTypes
152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	lookupTypes = {
153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		'GSUB': {
154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			1: SingleSubst,
155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			2: MultipleSubst,
156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			3: AlternateSubst,
157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			4: LigatureSubst,
158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			5: ContextSubst,
159d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			6: ChainContextSubst,
160d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			7: ExtensionSubst,
161d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		},
162d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		'GPOS': {
163d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			1: SinglePos,
164d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			2: PairPos,
165d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			3: CursivePos,
166d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			4: MarkBasePos,
167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			5: MarkLigPos,
168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			6: MarkMarkPos,
169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			7: ContextPos,
170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			8: ChainContextPos,
171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			9: ExtensionPos,
172d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		},
173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	}
174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
17564b5c80e80444a124da335e8d4d208bffcf2737bjvr	for lookupEnum in lookupTypes.values():
17664b5c80e80444a124da335e8d4d208bffcf2737bjvr		for enum, cls in lookupEnum.items():
17764b5c80e80444a124da335e8d4d208bffcf2737bjvr			cls.LookupType = enum
178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# add converters to classes
18064b5c80e80444a124da335e8d4d208bffcf2737bjvr	from otConverters import buildConverters
181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for name, table in otData:
182d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		m = formatPat.match(name)
183d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if m:
184d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# XxxFormatN subtable, add converter to "base" table
185d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, format = m.groups()
186d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = int(format)
187d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls = namespace[name]
188d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if not hasattr(cls, "converters"):
189d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				cls.converters = {}
190d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				cls.convertersByName = {}
19164b5c80e80444a124da335e8d4d208bffcf2737bjvr			converters, convertersByName = buildConverters(table[1:], namespace)
192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls.converters[format] = converters
193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls.convertersByName[format] = convertersByName
194d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
195d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls = namespace[name]
19664b5c80e80444a124da335e8d4d208bffcf2737bjvr			cls.converters, cls.convertersByName = buildConverters(table, namespace)
197d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
198d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
199d4d151390d1288f8d2df30f6dfa26a309c7334dajvr_buildClasses()
200