• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Merge OpenType Layout tables (GDEF / GPOS / GSUB).
3"""
4import copy
5from operator import ior
6from fontTools.misc import classifyTools
7from fontTools.misc.roundTools import otRound
8from fontTools.ttLib.tables import otTables as ot
9from fontTools.ttLib.tables import otBase as otBase
10from fontTools.ttLib.tables.DefaultTable import DefaultTable
11from fontTools.varLib import builder, models, varStore
12from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo
13from fontTools.varLib.varStore import VarStoreInstancer
14from functools import reduce
15from fontTools.otlLib.builder import buildSinglePos
16
17from .errors import (
18    ShouldBeConstant,
19    FoundANone,
20    MismatchedTypes,
21    LengthsDiffer,
22    KeysDiffer,
23    InconsistentGlyphOrder,
24    InconsistentExtensions,
25    UnsupportedFormat,
26    UnsupportedFormat,
27    VarLibMergeError,
28)
29
30class Merger(object):
31
32	def __init__(self, font=None):
33		self.font = font
34
35	@classmethod
36	def merger(celf, clazzes, attrs=(None,)):
37		assert celf != Merger, 'Subclass Merger instead.'
38		if 'mergers' not in celf.__dict__:
39			celf.mergers = {}
40		if type(clazzes) == type:
41			clazzes = (clazzes,)
42		if type(attrs) == str:
43			attrs = (attrs,)
44		def wrapper(method):
45			assert method.__name__ == 'merge'
46			done = []
47			for clazz in clazzes:
48				if clazz in done: continue # Support multiple names of a clazz
49				done.append(clazz)
50				mergers = celf.mergers.setdefault(clazz, {})
51				for attr in attrs:
52					assert attr not in mergers, \
53						"Oops, class '%s' has merge function for '%s' defined already." % (clazz.__name__, attr)
54					mergers[attr] = method
55			return None
56		return wrapper
57
58	@classmethod
59	def mergersFor(celf, thing, _default={}):
60		typ = type(thing)
61
62		for celf in celf.mro():
63
64			mergers = getattr(celf, 'mergers', None)
65			if mergers is None:
66				break;
67
68			m = celf.mergers.get(typ, None)
69			if m is not None:
70				return m
71
72		return _default
73
74	def mergeObjects(self, out, lst, exclude=()):
75		if hasattr(out, "ensureDecompiled"):
76			out.ensureDecompiled()
77		for item in lst:
78			if hasattr(item, "ensureDecompiled"):
79				item.ensureDecompiled()
80		keys = sorted(vars(out).keys())
81		if not all(keys == sorted(vars(v).keys()) for v in lst):
82			raise KeysDiffer(self, expected=keys,
83				got=[sorted(vars(v).keys()) for v in lst]
84			)
85		mergers = self.mergersFor(out)
86		defaultMerger = mergers.get('*', self.__class__.mergeThings)
87		try:
88			for key in keys:
89				if key in exclude: continue
90				value = getattr(out, key)
91				values = [getattr(table, key) for table in lst]
92				mergerFunc = mergers.get(key, defaultMerger)
93				mergerFunc(self, value, values)
94		except VarLibMergeError as e:
95			e.stack.append('.'+key)
96			raise
97
98	def mergeLists(self, out, lst):
99		if not allEqualTo(out, lst, len):
100			raise LengthsDiffer(self, expected=len(out), got=[len(x) for x in lst])
101		for i,(value,values) in enumerate(zip(out, zip(*lst))):
102			try:
103				self.mergeThings(value, values)
104			except VarLibMergeError as e:
105				e.stack.append('[%d]' % i)
106				raise
107
108	def mergeThings(self, out, lst):
109		if not allEqualTo(out, lst, type):
110			raise MismatchedTypes(self,
111					expected=type(out).__name__,
112					got=[type(x).__name__ for x in lst]
113			)
114		mergerFunc = self.mergersFor(out).get(None, None)
115		if mergerFunc is not None:
116			mergerFunc(self, out, lst)
117		elif hasattr(out, '__dict__'):
118			self.mergeObjects(out, lst)
119		elif isinstance(out, list):
120			self.mergeLists(out, lst)
121		else:
122			if not allEqualTo(out, lst):
123				raise ShouldBeConstant(self, expected=out, got=lst)
124
125	def mergeTables(self, font, master_ttfs, tableTags):
126		for tag in tableTags:
127			if tag not in font: continue
128			try:
129				self.ttfs = [m for m in master_ttfs if tag in m]
130				self.mergeThings(font[tag], [m[tag] if tag in m else None
131							     for m in master_ttfs])
132			except VarLibMergeError as e:
133				e.stack.append(tag)
134				raise
135
136#
137# Aligning merger
138#
139class AligningMerger(Merger):
140	pass
141
142@AligningMerger.merger(ot.GDEF, "GlyphClassDef")
143def merge(merger, self, lst):
144	if self is None:
145		if not allNone(lst):
146			raise NotANone(self, expected=None, got=lst)
147		return
148
149	lst = [l.classDefs for l in lst]
150	self.classDefs = {}
151	# We only care about the .classDefs
152	self = self.classDefs
153
154	allKeys = set()
155	allKeys.update(*[l.keys() for l in lst])
156	for k in allKeys:
157		allValues = nonNone(l.get(k) for l in lst)
158		if not allEqual(allValues):
159			raise ShouldBeConstant(self, expected=allValues[0], got=lst, stack="."+k)
160		if not allValues:
161			self[k] = None
162		else:
163			self[k] = allValues[0]
164
165def _SinglePosUpgradeToFormat2(self):
166	if self.Format == 2: return self
167
168	ret = ot.SinglePos()
169	ret.Format = 2
170	ret.Coverage = self.Coverage
171	ret.ValueFormat = self.ValueFormat
172	ret.Value = [self.Value for _ in ret.Coverage.glyphs]
173	ret.ValueCount = len(ret.Value)
174
175	return ret
176
177def _merge_GlyphOrders(font, lst, values_lst=None, default=None):
178	"""Takes font and list of glyph lists (must be sorted by glyph id), and returns
179	two things:
180	- Combined glyph list,
181	- If values_lst is None, return input glyph lists, but padded with None when a glyph
182	  was missing in a list.  Otherwise, return values_lst list-of-list, padded with None
183	  to match combined glyph lists.
184	"""
185	if values_lst is None:
186		dict_sets = [set(l) for l in lst]
187	else:
188		dict_sets = [{g:v for g,v in zip(l,vs)} for l,vs in zip(lst,values_lst)]
189	combined = set()
190	combined.update(*dict_sets)
191
192	sortKey = font.getReverseGlyphMap().__getitem__
193	order = sorted(combined, key=sortKey)
194	# Make sure all input glyphsets were in proper order
195	if not all(sorted(vs, key=sortKey) == vs for vs in lst):
196		raise InconsistentGlyphOrder(self)
197	del combined
198
199	paddedValues = None
200	if values_lst is None:
201		padded = [[glyph if glyph in dict_set else default
202			   for glyph in order]
203			  for dict_set in dict_sets]
204	else:
205		assert len(lst) == len(values_lst)
206		padded = [[dict_set[glyph] if glyph in dict_set else default
207			   for glyph in order]
208			  for dict_set in dict_sets]
209	return order, padded
210
211def _Lookup_SinglePos_get_effective_value(subtables, glyph):
212	for self in subtables:
213		if self is None or \
214		   type(self) != ot.SinglePos or \
215		   self.Coverage is None or \
216		   glyph not in self.Coverage.glyphs:
217			continue
218		if self.Format == 1:
219			return self.Value
220		elif self.Format == 2:
221			return self.Value[self.Coverage.glyphs.index(glyph)]
222		else:
223			raise UnsupportedFormat(self, subtable="single positioning lookup")
224	return None
225
226def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph):
227	for self in subtables:
228		if self is None or \
229		   type(self) != ot.PairPos or \
230		   self.Coverage is None or \
231		   firstGlyph not in self.Coverage.glyphs:
232			continue
233		if self.Format == 1:
234			ps = self.PairSet[self.Coverage.glyphs.index(firstGlyph)]
235			pvr = ps.PairValueRecord
236			for rec in pvr: # TODO Speed up
237				if rec.SecondGlyph == secondGlyph:
238					return rec
239			continue
240		elif self.Format == 2:
241			klass1 = self.ClassDef1.classDefs.get(firstGlyph, 0)
242			klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0)
243			return self.Class1Record[klass1].Class2Record[klass2]
244		else:
245			raise UnsupportedFormat(self, subtable="pair positioning lookup")
246	return None
247
248@AligningMerger.merger(ot.SinglePos)
249def merge(merger, self, lst):
250	self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst], 0)
251	if not (len(lst) == 1 or (valueFormat & ~0xF == 0)):
252		raise UnsupportedFormat(self, subtable="single positioning lookup")
253
254	# If all have same coverage table and all are format 1,
255	coverageGlyphs = self.Coverage.glyphs
256	if all(v.Format == 1 for v in lst) and all(coverageGlyphs == v.Coverage.glyphs for v in lst):
257		self.Value = otBase.ValueRecord(valueFormat)
258		merger.mergeThings(self.Value, [v.Value for v in lst])
259		self.ValueFormat = self.Value.getFormat()
260		return
261
262	# Upgrade everything to Format=2
263	self.Format = 2
264	lst = [_SinglePosUpgradeToFormat2(v) for v in lst]
265
266	# Align them
267	glyphs, padded = _merge_GlyphOrders(merger.font,
268					    [v.Coverage.glyphs for v in lst],
269					    [v.Value for v in lst])
270
271	self.Coverage.glyphs = glyphs
272	self.Value = [otBase.ValueRecord(valueFormat) for _ in glyphs]
273	self.ValueCount = len(self.Value)
274
275	for i,values in enumerate(padded):
276		for j,glyph in enumerate(glyphs):
277			if values[j] is not None: continue
278			# Fill in value from other subtables
279			# Note!!! This *might* result in behavior change if ValueFormat2-zeroedness
280			# is different between used subtable and current subtable!
281			# TODO(behdad) Check and warn if that happens?
282			v = _Lookup_SinglePos_get_effective_value(merger.lookup_subtables[i], glyph)
283			if v is None:
284				v = otBase.ValueRecord(valueFormat)
285			values[j] = v
286
287	merger.mergeLists(self.Value, padded)
288
289	# Merge everything else; though, there shouldn't be anything else. :)
290	merger.mergeObjects(self, lst,
291			    exclude=('Format', 'Coverage', 'Value', 'ValueCount'))
292	self.ValueFormat = reduce(int.__or__, [v.getFormat() for v in self.Value], 0)
293
294@AligningMerger.merger(ot.PairSet)
295def merge(merger, self, lst):
296	# Align them
297	glyphs, padded = _merge_GlyphOrders(merger.font,
298				[[v.SecondGlyph for v in vs.PairValueRecord] for vs in lst],
299				[vs.PairValueRecord for vs in lst])
300
301	self.PairValueRecord = pvrs = []
302	for glyph in glyphs:
303		pvr = ot.PairValueRecord()
304		pvr.SecondGlyph = glyph
305		pvr.Value1 = otBase.ValueRecord(merger.valueFormat1) if merger.valueFormat1 else None
306		pvr.Value2 = otBase.ValueRecord(merger.valueFormat2) if merger.valueFormat2 else None
307		pvrs.append(pvr)
308	self.PairValueCount = len(self.PairValueRecord)
309
310	for i,values in enumerate(padded):
311		for j,glyph in enumerate(glyphs):
312			# Fill in value from other subtables
313			v = ot.PairValueRecord()
314			v.SecondGlyph = glyph
315			if values[j] is not None:
316				vpair = values[j]
317			else:
318				vpair = _Lookup_PairPos_get_effective_value_pair(merger.lookup_subtables[i], self._firstGlyph, glyph)
319			if vpair is None:
320				v1, v2 = None, None
321			else:
322				v1 = getattr(vpair, "Value1", None)
323				v2 = getattr(vpair, "Value2", None)
324			v.Value1 = otBase.ValueRecord(merger.valueFormat1, src=v1) if merger.valueFormat1 else None
325			v.Value2 = otBase.ValueRecord(merger.valueFormat2, src=v2) if merger.valueFormat2 else None
326			values[j] = v
327	del self._firstGlyph
328
329	merger.mergeLists(self.PairValueRecord, padded)
330
331def _PairPosFormat1_merge(self, lst, merger):
332	assert allEqual([l.ValueFormat2 == 0 for l in lst if l.PairSet]), "Report bug against fonttools."
333
334	# Merge everything else; makes sure Format is the same.
335	merger.mergeObjects(self, lst,
336			    exclude=('Coverage',
337				     'PairSet', 'PairSetCount',
338				     'ValueFormat1', 'ValueFormat2'))
339
340	empty = ot.PairSet()
341	empty.PairValueRecord = []
342	empty.PairValueCount = 0
343
344	# Align them
345	glyphs, padded = _merge_GlyphOrders(merger.font,
346					    [v.Coverage.glyphs for v in lst],
347					    [v.PairSet for v in lst],
348					    default=empty)
349
350	self.Coverage.glyphs = glyphs
351	self.PairSet = [ot.PairSet() for _ in glyphs]
352	self.PairSetCount = len(self.PairSet)
353	for glyph, ps in zip(glyphs, self.PairSet):
354		ps._firstGlyph = glyph
355
356	merger.mergeLists(self.PairSet, padded)
357
358def _ClassDef_invert(self, allGlyphs=None):
359
360	if isinstance(self, dict):
361		classDefs = self
362	else:
363		classDefs = self.classDefs if self and self.classDefs else {}
364	m = max(classDefs.values()) if classDefs else 0
365
366	ret = []
367	for _ in range(m + 1):
368		ret.append(set())
369
370	for k,v in classDefs.items():
371		ret[v].add(k)
372
373	# Class-0 is special.  It's "everything else".
374	if allGlyphs is None:
375		ret[0] = None
376	else:
377		# Limit all classes to glyphs in allGlyphs.
378		# Collect anything without a non-zero class into class=zero.
379		ret[0] = class0 = set(allGlyphs)
380		for s in ret[1:]:
381			s.intersection_update(class0)
382			class0.difference_update(s)
383
384	return ret
385
386def _ClassDef_merge_classify(lst, allGlyphses=None):
387	self = ot.ClassDef()
388	self.classDefs = classDefs = {}
389	allGlyphsesWasNone = allGlyphses is None
390	if allGlyphsesWasNone:
391		allGlyphses = [None] * len(lst)
392
393	classifier = classifyTools.Classifier()
394	for classDef,allGlyphs in zip(lst, allGlyphses):
395		sets = _ClassDef_invert(classDef, allGlyphs)
396		if allGlyphs is None:
397			sets = sets[1:]
398		classifier.update(sets)
399	classes = classifier.getClasses()
400
401	if allGlyphsesWasNone:
402		classes.insert(0, set())
403
404	for i,classSet in enumerate(classes):
405		if i == 0:
406			continue
407		for g in classSet:
408			classDefs[g] = i
409
410	return self, classes
411
412def _PairPosFormat2_align_matrices(self, lst, font, transparent=False):
413
414	matrices = [l.Class1Record for l in lst]
415
416	# Align first classes
417	self.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], [l.Coverage.glyphs for l in lst])
418	self.Class1Count = len(classes)
419	new_matrices = []
420	for l,matrix in zip(lst, matrices):
421		nullRow = None
422		coverage = set(l.Coverage.glyphs)
423		classDef1 = l.ClassDef1.classDefs
424		class1Records = []
425		for classSet in classes:
426			exemplarGlyph = next(iter(classSet))
427			if exemplarGlyph not in coverage:
428				# Follow-up to e6125b353e1f54a0280ded5434b8e40d042de69f,
429				# Fixes https://github.com/googlei18n/fontmake/issues/470
430				# Again, revert 8d441779e5afc664960d848f62c7acdbfc71d7b9
431				# when merger becomes selfless.
432				nullRow = None
433				if nullRow is None:
434					nullRow = ot.Class1Record()
435					class2records = nullRow.Class2Record = []
436					# TODO: When merger becomes selfless, revert e6125b353e1f54a0280ded5434b8e40d042de69f
437					for _ in range(l.Class2Count):
438						if transparent:
439							rec2 = None
440						else:
441							rec2 = ot.Class2Record()
442							rec2.Value1 = otBase.ValueRecord(self.ValueFormat1) if self.ValueFormat1 else None
443							rec2.Value2 = otBase.ValueRecord(self.ValueFormat2) if self.ValueFormat2 else None
444						class2records.append(rec2)
445				rec1 = nullRow
446			else:
447				klass = classDef1.get(exemplarGlyph, 0)
448				rec1 = matrix[klass] # TODO handle out-of-range?
449			class1Records.append(rec1)
450		new_matrices.append(class1Records)
451	matrices = new_matrices
452	del new_matrices
453
454	# Align second classes
455	self.ClassDef2, classes = _ClassDef_merge_classify([l.ClassDef2 for l in lst])
456	self.Class2Count = len(classes)
457	new_matrices = []
458	for l,matrix in zip(lst, matrices):
459		classDef2 = l.ClassDef2.classDefs
460		class1Records = []
461		for rec1old in matrix:
462			oldClass2Records = rec1old.Class2Record
463			rec1new = ot.Class1Record()
464			class2Records = rec1new.Class2Record = []
465			for classSet in classes:
466				if not classSet: # class=0
467					rec2 = oldClass2Records[0]
468				else:
469					exemplarGlyph = next(iter(classSet))
470					klass = classDef2.get(exemplarGlyph, 0)
471					rec2 = oldClass2Records[klass]
472				class2Records.append(copy.deepcopy(rec2))
473			class1Records.append(rec1new)
474		new_matrices.append(class1Records)
475	matrices = new_matrices
476	del new_matrices
477
478	return matrices
479
480def _PairPosFormat2_merge(self, lst, merger):
481	assert allEqual([l.ValueFormat2 == 0 for l in lst if l.Class1Record]), "Report bug against fonttools."
482
483	merger.mergeObjects(self, lst,
484			    exclude=('Coverage',
485				     'ClassDef1', 'Class1Count',
486				     'ClassDef2', 'Class2Count',
487				     'Class1Record',
488				     'ValueFormat1', 'ValueFormat2'))
489
490	# Align coverages
491	glyphs, _ = _merge_GlyphOrders(merger.font,
492				       [v.Coverage.glyphs for v in lst])
493	self.Coverage.glyphs = glyphs
494
495	# Currently, if the coverage of PairPosFormat2 subtables are different,
496	# we do NOT bother walking down the subtable list when filling in new
497	# rows for alignment.  As such, this is only correct if current subtable
498	# is the last subtable in the lookup.  Ensure that.
499	#
500	# Note that our canonicalization process merges trailing PairPosFormat2's,
501	# so in reality this is rare.
502	for l,subtables in zip(lst,merger.lookup_subtables):
503		if l.Coverage.glyphs != glyphs:
504			assert l == subtables[-1]
505
506	matrices = _PairPosFormat2_align_matrices(self, lst, merger.font)
507
508	self.Class1Record = list(matrices[0]) # TODO move merger to be selfless
509	merger.mergeLists(self.Class1Record, matrices)
510
511@AligningMerger.merger(ot.PairPos)
512def merge(merger, self, lst):
513	merger.valueFormat1 = self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
514	merger.valueFormat2 = self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
515
516	if self.Format == 1:
517		_PairPosFormat1_merge(self, lst, merger)
518	elif self.Format == 2:
519		_PairPosFormat2_merge(self, lst, merger)
520	else:
521		raise UnsupportedFormat(self, subtable="pair positioning lookup")
522
523	del merger.valueFormat1, merger.valueFormat2
524
525	# Now examine the list of value records, and update to the union of format values,
526	# as merge might have created new values.
527	vf1 = 0
528	vf2 = 0
529	if self.Format == 1:
530		for pairSet in self.PairSet:
531			for pairValueRecord in pairSet.PairValueRecord:
532				pv1 = getattr(pairValueRecord, "Value1", None)
533				if pv1 is not None:
534					vf1 |= pv1.getFormat()
535				pv2 = getattr(pairValueRecord, "Value2", None)
536				if pv2 is not None:
537					vf2 |= pv2.getFormat()
538	elif self.Format == 2:
539		for class1Record in self.Class1Record:
540			for class2Record in class1Record.Class2Record:
541				pv1 = getattr(class2Record, "Value1", None)
542				if pv1 is not None:
543					vf1 |= pv1.getFormat()
544				pv2 = getattr(class2Record, "Value2", None)
545				if pv2 is not None:
546					vf2 |= pv2.getFormat()
547	self.ValueFormat1 = vf1
548	self.ValueFormat2 = vf2
549
550def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'):
551	self.ClassCount = max(l.ClassCount for l in lst)
552
553	MarkCoverageGlyphs, MarkRecords = \
554		_merge_GlyphOrders(merger.font,
555				   [getattr(l, Mark+'Coverage').glyphs for l in lst],
556				   [getattr(l, Mark+'Array').MarkRecord for l in lst])
557	getattr(self, Mark+'Coverage').glyphs = MarkCoverageGlyphs
558
559	BaseCoverageGlyphs, BaseRecords = \
560		_merge_GlyphOrders(merger.font,
561				   [getattr(l, Base+'Coverage').glyphs for l in lst],
562				   [getattr(getattr(l, Base+'Array'), Base+'Record') for l in lst])
563	getattr(self, Base+'Coverage').glyphs = BaseCoverageGlyphs
564
565	# MarkArray
566	records = []
567	for g,glyphRecords in zip(MarkCoverageGlyphs, zip(*MarkRecords)):
568		allClasses = [r.Class for r in glyphRecords if r is not None]
569
570		# TODO Right now we require that all marks have same class in
571		# all masters that cover them.  This is not required.
572		#
573		# We can relax that by just requiring that all marks that have
574		# the same class in a master, have the same class in every other
575		# master.  Indeed, if, say, a sparse master only covers one mark,
576		# that mark probably will get class 0, which would possibly be
577		# different from its class in other masters.
578		#
579		# We can even go further and reclassify marks to support any
580		# input.  But, since, it's unlikely that two marks being both,
581		# say, "top" in one master, and one being "top" and other being
582		# "top-right" in another master, we shouldn't do that, as any
583		# failures in that case will probably signify mistakes in the
584		# input masters.
585
586		if not allEqual(allClasses):
587			raise allClasses(self, allClasses)
588			rec = None
589		else:
590			rec = ot.MarkRecord()
591			rec.Class = allClasses[0]
592			allAnchors = [None if r is None else r.MarkAnchor for r in glyphRecords]
593			if allNone(allAnchors):
594				anchor = None
595			else:
596				anchor = ot.Anchor()
597				anchor.Format = 1
598				merger.mergeThings(anchor, allAnchors)
599			rec.MarkAnchor = anchor
600		records.append(rec)
601	array = ot.MarkArray()
602	array.MarkRecord = records
603	array.MarkCount = len(records)
604	setattr(self, Mark+"Array", array)
605
606	# BaseArray
607	records = []
608	for g,glyphRecords in zip(BaseCoverageGlyphs, zip(*BaseRecords)):
609		if allNone(glyphRecords):
610			rec = None
611		else:
612			rec = getattr(ot, Base+'Record')()
613			anchors = []
614			setattr(rec, Base+'Anchor', anchors)
615			glyphAnchors = [[] if r is None else getattr(r, Base+'Anchor')
616					for r in glyphRecords]
617			for l in glyphAnchors:
618				l.extend([None] * (self.ClassCount - len(l)))
619			for allAnchors in zip(*glyphAnchors):
620				if allNone(allAnchors):
621					anchor = None
622				else:
623					anchor = ot.Anchor()
624					anchor.Format = 1
625					merger.mergeThings(anchor, allAnchors)
626				anchors.append(anchor)
627		records.append(rec)
628	array = getattr(ot, Base+'Array')()
629	setattr(array, Base+'Record', records)
630	setattr(array, Base+'Count', len(records))
631	setattr(self, Base+'Array', array)
632
633@AligningMerger.merger(ot.MarkBasePos)
634def merge(merger, self, lst):
635	if not allEqualTo(self.Format, (l.Format for l in lst)):
636		raise InconsistentFormats(self,
637			subtable="mark-to-base positioning lookup",
638			expected=self.Format,
639			got=[l.Format for l in lst]
640		)
641	if self.Format == 1:
642		_MarkBasePosFormat1_merge(self, lst, merger)
643	else:
644		raise UnsupportedFormat(self, subtable="mark-to-base positioning lookup")
645
646@AligningMerger.merger(ot.MarkMarkPos)
647def merge(merger, self, lst):
648	if not allEqualTo(self.Format, (l.Format for l in lst)):
649		raise InconsistentFormats(self,
650			subtable="mark-to-mark positioning lookup",
651			expected=self.Format,
652			got=[l.Format for l in lst]
653		)
654	if self.Format == 1:
655		_MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2')
656	else:
657		raise UnsupportedFormat(self, subtable="mark-to-mark positioning lookup")
658
659def _PairSet_flatten(lst, font):
660	self = ot.PairSet()
661	self.Coverage = ot.Coverage()
662
663	# Align them
664	glyphs, padded = _merge_GlyphOrders(font,
665				[[v.SecondGlyph for v in vs.PairValueRecord] for vs in lst],
666				[vs.PairValueRecord for vs in lst])
667
668	self.Coverage.glyphs = glyphs
669	self.PairValueRecord = pvrs = []
670	for values in zip(*padded):
671		for v in values:
672			if v is not None:
673				pvrs.append(v)
674				break
675		else:
676			assert False
677	self.PairValueCount = len(self.PairValueRecord)
678
679	return self
680
681def _Lookup_PairPosFormat1_subtables_flatten(lst, font):
682	assert allEqual([l.ValueFormat2 == 0 for l in lst if l.PairSet]), "Report bug against fonttools."
683
684	self = ot.PairPos()
685	self.Format = 1
686	self.Coverage = ot.Coverage()
687	self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
688	self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
689
690	# Align them
691	glyphs, padded = _merge_GlyphOrders(font,
692					    [v.Coverage.glyphs for v in lst],
693					    [v.PairSet for v in lst])
694
695	self.Coverage.glyphs = glyphs
696	self.PairSet = [_PairSet_flatten([v for v in values if v is not None], font)
697		        for values in zip(*padded)]
698	self.PairSetCount = len(self.PairSet)
699	return self
700
701def _Lookup_PairPosFormat2_subtables_flatten(lst, font):
702	assert allEqual([l.ValueFormat2 == 0 for l in lst if l.Class1Record]), "Report bug against fonttools."
703
704	self = ot.PairPos()
705	self.Format = 2
706	self.Coverage = ot.Coverage()
707	self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
708	self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
709
710	# Align them
711	glyphs, _ = _merge_GlyphOrders(font,
712				       [v.Coverage.glyphs for v in lst])
713	self.Coverage.glyphs = glyphs
714
715	matrices = _PairPosFormat2_align_matrices(self, lst, font, transparent=True)
716
717	matrix = self.Class1Record = []
718	for rows in zip(*matrices):
719		row = ot.Class1Record()
720		matrix.append(row)
721		row.Class2Record = []
722		row = row.Class2Record
723		for cols in zip(*list(r.Class2Record for r in rows)):
724			col = next(iter(c for c in cols if c is not None))
725			row.append(col)
726
727	return self
728
729def _Lookup_PairPos_subtables_canonicalize(lst, font):
730	"""Merge multiple Format1 subtables at the beginning of lst,
731	and merge multiple consecutive Format2 subtables that have the same
732	Class2 (ie. were split because of offset overflows).  Returns new list."""
733	lst = list(lst)
734
735	l = len(lst)
736	i = 0
737	while i < l and lst[i].Format == 1:
738		i += 1
739	lst[:i] = [_Lookup_PairPosFormat1_subtables_flatten(lst[:i], font)]
740
741	l = len(lst)
742	i = l
743	while i > 0 and lst[i - 1].Format == 2:
744		i -= 1
745	lst[i:] = [_Lookup_PairPosFormat2_subtables_flatten(lst[i:], font)]
746
747	return lst
748
749def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format):
750	glyphs, _ = _merge_GlyphOrders(font,
751		[v.Coverage.glyphs for v in lst], None)
752	num_glyphs = len(glyphs)
753	new = ot.SinglePos()
754	new.Format = 2
755	new.ValueFormat = min_inclusive_rec_format
756	new.Coverage = ot.Coverage()
757	new.Coverage.glyphs = glyphs
758	new.ValueCount = num_glyphs
759	new.Value = [None] * num_glyphs
760	for singlePos in lst:
761		if singlePos.Format == 1:
762			val_rec = singlePos.Value
763			for gname in singlePos.Coverage.glyphs:
764				i = glyphs.index(gname)
765				new.Value[i] = copy.deepcopy(val_rec)
766		elif singlePos.Format == 2:
767			for j, gname in enumerate(singlePos.Coverage.glyphs):
768				val_rec = singlePos.Value[j]
769				i = glyphs.index(gname)
770				new.Value[i] = copy.deepcopy(val_rec)
771	return [new]
772
773@AligningMerger.merger(ot.Lookup)
774def merge(merger, self, lst):
775	subtables = merger.lookup_subtables = [l.SubTable for l in lst]
776
777	# Remove Extension subtables
778	for l,sts in list(zip(lst,subtables))+[(self,self.SubTable)]:
779		if not sts:
780			continue
781		if sts[0].__class__.__name__.startswith('Extension'):
782			if not allEqual([st.__class__ for st in sts]):
783				raise InconsistentExtensions(self,
784					expected="Extension",
785					got=[st.__class__.__name__ for st in sts]
786				)
787			if not allEqual([st.ExtensionLookupType for st in sts]):
788				raise InconsistentExtensions(self)
789			l.LookupType = sts[0].ExtensionLookupType
790			new_sts = [st.ExtSubTable for st in sts]
791			del sts[:]
792			sts.extend(new_sts)
793
794	isPairPos = self.SubTable and isinstance(self.SubTable[0], ot.PairPos)
795
796	if isPairPos:
797		# AFDKO and feaLib sometimes generate two Format1 subtables instead of one.
798		# Merge those before continuing.
799		# https://github.com/fonttools/fonttools/issues/719
800		self.SubTable = _Lookup_PairPos_subtables_canonicalize(self.SubTable, merger.font)
801		subtables = merger.lookup_subtables = [_Lookup_PairPos_subtables_canonicalize(st, merger.font) for st in subtables]
802	else:
803		isSinglePos = self.SubTable and isinstance(self.SubTable[0], ot.SinglePos)
804		if isSinglePos:
805			numSubtables = [len(st) for st in subtables]
806			if not all([nums == numSubtables[0] for nums in numSubtables]):
807				# Flatten list of SinglePos subtables to single Format 2 subtable,
808				# with all value records set to the rec format type.
809				# We use buildSinglePos() to optimize the lookup after merging.
810				valueFormatList = [t.ValueFormat for st in subtables for t in st]
811				# Find the minimum value record that can accomodate all the singlePos subtables.
812				mirf = reduce(ior, valueFormatList)
813				self.SubTable = _Lookup_SinglePos_subtables_flatten(self.SubTable, merger.font, mirf)
814				subtables = merger.lookup_subtables = [
815					_Lookup_SinglePos_subtables_flatten(st, merger.font, mirf) for st in subtables]
816				flattened = True
817			else:
818				flattened = False
819
820	merger.mergeLists(self.SubTable, subtables)
821	self.SubTableCount = len(self.SubTable)
822
823	if isPairPos:
824		# If format-1 subtable created during canonicalization is empty, remove it.
825		assert len(self.SubTable) >= 1 and self.SubTable[0].Format == 1
826		if not self.SubTable[0].Coverage.glyphs:
827			self.SubTable.pop(0)
828			self.SubTableCount -= 1
829
830		# If format-2 subtable created during canonicalization is empty, remove it.
831		assert len(self.SubTable) >= 1 and self.SubTable[-1].Format == 2
832		if not self.SubTable[-1].Coverage.glyphs:
833			self.SubTable.pop(-1)
834			self.SubTableCount -= 1
835
836	elif isSinglePos and flattened:
837		singlePosTable = self.SubTable[0]
838		glyphs = singlePosTable.Coverage.glyphs
839		# We know that singlePosTable is Format 2, as this is set
840		# in _Lookup_SinglePos_subtables_flatten.
841		singlePosMapping = {
842			gname: valRecord
843			for gname, valRecord in zip(glyphs, singlePosTable.Value)
844		}
845		self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap())
846	merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount'])
847
848	del merger.lookup_subtables
849
850
851#
852# InstancerMerger
853#
854
855class InstancerMerger(AligningMerger):
856	"""A merger that takes multiple master fonts, and instantiates
857	an instance."""
858
859	def __init__(self, font, model, location):
860		Merger.__init__(self, font)
861		self.model = model
862		self.location = location
863		self.scalars = model.getScalars(location)
864
865@InstancerMerger.merger(ot.CaretValue)
866def merge(merger, self, lst):
867	assert self.Format == 1
868	Coords = [a.Coordinate for a in lst]
869	model = merger.model
870	scalars = merger.scalars
871	self.Coordinate = otRound(model.interpolateFromMastersAndScalars(Coords, scalars))
872
873@InstancerMerger.merger(ot.Anchor)
874def merge(merger, self, lst):
875	assert self.Format == 1
876	XCoords = [a.XCoordinate for a in lst]
877	YCoords = [a.YCoordinate for a in lst]
878	model = merger.model
879	scalars = merger.scalars
880	self.XCoordinate = otRound(model.interpolateFromMastersAndScalars(XCoords, scalars))
881	self.YCoordinate = otRound(model.interpolateFromMastersAndScalars(YCoords, scalars))
882
883@InstancerMerger.merger(otBase.ValueRecord)
884def merge(merger, self, lst):
885	model = merger.model
886	scalars = merger.scalars
887	# TODO Handle differing valueformats
888	for name, tableName in [('XAdvance','XAdvDevice'),
889				('YAdvance','YAdvDevice'),
890				('XPlacement','XPlaDevice'),
891				('YPlacement','YPlaDevice')]:
892
893		assert not hasattr(self, tableName)
894
895		if hasattr(self, name):
896			values = [getattr(a, name, 0) for a in lst]
897			value = otRound(model.interpolateFromMastersAndScalars(values, scalars))
898			setattr(self, name, value)
899
900
901#
902# MutatorMerger
903#
904
905class MutatorMerger(AligningMerger):
906	"""A merger that takes a variable font, and instantiates
907	an instance.  While there's no "merging" to be done per se,
908	the operation can benefit from many operations that the
909	aligning merger does."""
910
911	def __init__(self, font, instancer, deleteVariations=True):
912		Merger.__init__(self, font)
913		self.instancer = instancer
914		self.deleteVariations = deleteVariations
915
916@MutatorMerger.merger(ot.CaretValue)
917def merge(merger, self, lst):
918
919	# Hack till we become selfless.
920	self.__dict__ = lst[0].__dict__.copy()
921
922	if self.Format != 3:
923		return
924
925	instancer = merger.instancer
926	dev = self.DeviceTable
927	if merger.deleteVariations:
928		del self.DeviceTable
929	if dev:
930		assert dev.DeltaFormat == 0x8000
931		varidx = (dev.StartSize << 16) + dev.EndSize
932		delta = otRound(instancer[varidx])
933		self.Coordinate += delta
934
935	if merger.deleteVariations:
936		self.Format = 1
937
938@MutatorMerger.merger(ot.Anchor)
939def merge(merger, self, lst):
940
941	# Hack till we become selfless.
942	self.__dict__ = lst[0].__dict__.copy()
943
944	if self.Format != 3:
945		return
946
947	instancer = merger.instancer
948	for v in "XY":
949		tableName = v+'DeviceTable'
950		if not hasattr(self, tableName):
951			continue
952		dev = getattr(self, tableName)
953		if merger.deleteVariations:
954			delattr(self, tableName)
955		if dev is None:
956			continue
957
958		assert dev.DeltaFormat == 0x8000
959		varidx = (dev.StartSize << 16) + dev.EndSize
960		delta = otRound(instancer[varidx])
961
962		attr = v+'Coordinate'
963		setattr(self, attr, getattr(self, attr) + delta)
964
965	if merger.deleteVariations:
966		self.Format = 1
967
968@MutatorMerger.merger(otBase.ValueRecord)
969def merge(merger, self, lst):
970
971	# Hack till we become selfless.
972	self.__dict__ = lst[0].__dict__.copy()
973
974	instancer = merger.instancer
975	for name, tableName in [('XAdvance','XAdvDevice'),
976				('YAdvance','YAdvDevice'),
977				('XPlacement','XPlaDevice'),
978				('YPlacement','YPlaDevice')]:
979
980		if not hasattr(self, tableName):
981			continue
982		dev = getattr(self, tableName)
983		if merger.deleteVariations:
984			delattr(self, tableName)
985		if dev is None:
986			continue
987
988		assert dev.DeltaFormat == 0x8000
989		varidx = (dev.StartSize << 16) + dev.EndSize
990		delta = otRound(instancer[varidx])
991
992		setattr(self, name, getattr(self, name) + delta)
993
994
995#
996# VariationMerger
997#
998
999class VariationMerger(AligningMerger):
1000	"""A merger that takes multiple master fonts, and builds a
1001	variable font."""
1002
1003	def __init__(self, model, axisTags, font):
1004		Merger.__init__(self, font)
1005		self.store_builder = varStore.OnlineVarStoreBuilder(axisTags)
1006		self.setModel(model)
1007
1008	def setModel(self, model):
1009		self.model = model
1010		self.store_builder.setModel(model)
1011
1012	def mergeThings(self, out, lst):
1013		masterModel = None
1014		if None in lst:
1015			if allNone(lst):
1016				if out is not None:
1017					raise FoundANone(self, got=lst)
1018				return
1019			masterModel = self.model
1020			model, lst = masterModel.getSubModel(lst)
1021			self.setModel(model)
1022
1023		super(VariationMerger, self).mergeThings(out, lst)
1024
1025		if masterModel:
1026			self.setModel(masterModel)
1027
1028
1029def buildVarDevTable(store_builder, master_values):
1030	if allEqual(master_values):
1031		return master_values[0], None
1032	base, varIdx = store_builder.storeMasters(master_values)
1033	return base, builder.buildVarDevTable(varIdx)
1034
1035@VariationMerger.merger(ot.BaseCoord)
1036def merge(merger, self, lst):
1037	if self.Format != 1:
1038		raise UnsupportedFormat(self, subtable="a baseline coordinate")
1039	self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
1040	if DeviceTable:
1041		self.Format = 3
1042		self.DeviceTable = DeviceTable
1043
1044@VariationMerger.merger(ot.CaretValue)
1045def merge(merger, self, lst):
1046	if self.Format != 1:
1047		raise UnsupportedFormat(self, subtable="a caret")
1048	self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
1049	if DeviceTable:
1050		self.Format = 3
1051		self.DeviceTable = DeviceTable
1052
1053@VariationMerger.merger(ot.Anchor)
1054def merge(merger, self, lst):
1055	if self.Format != 1:
1056		raise UnsupportedFormat(self, subtable="an anchor")
1057	self.XCoordinate, XDeviceTable = buildVarDevTable(merger.store_builder, [a.XCoordinate for a in lst])
1058	self.YCoordinate, YDeviceTable = buildVarDevTable(merger.store_builder, [a.YCoordinate for a in lst])
1059	if XDeviceTable or YDeviceTable:
1060		self.Format = 3
1061		self.XDeviceTable = XDeviceTable
1062		self.YDeviceTable = YDeviceTable
1063
1064@VariationMerger.merger(otBase.ValueRecord)
1065def merge(merger, self, lst):
1066	for name, tableName in [('XAdvance','XAdvDevice'),
1067				('YAdvance','YAdvDevice'),
1068				('XPlacement','XPlaDevice'),
1069				('YPlacement','YPlaDevice')]:
1070
1071		if hasattr(self, name):
1072			value, deviceTable = buildVarDevTable(merger.store_builder,
1073							      [getattr(a, name, 0) for a in lst])
1074			setattr(self, name, value)
1075			if deviceTable:
1076				setattr(self, tableName, deviceTable)
1077