merge.py revision 0e235becc52d6048dde39f3bb400c617877302d7
145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod# Copyright 2013 Google, Inc. All Rights Reserved.
245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod#
347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod"""Font merger.
645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod"""
745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
81ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
9f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.misc.py23 import *
10f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools import ttLib, cffLib
11e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderfrom fontTools.ttLib.tables import otTables, _h_e_a_d
129e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbodfrom fontTools.ttLib.tables.DefaultTable import DefaultTable
13f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom functools import reduce
1445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport sys
15f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodimport time
1649028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbodimport operator
1745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
1845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
199e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef _add_method(*clazzes, **kwargs):
20c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	"""Returns a decorator function that adds a new method to one or
21c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	more classes."""
22c68c0ff12fcf38d97304540b7dc0253f4142b046Behdad Esfahbod	allowDefault = kwargs.get('allowDefaultTable', False)
23c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	def wrapper(method):
24c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod		for clazz in clazzes:
2535e3c7270d93778f51b0b16110acb2ed234f5593Behdad Esfahbod			assert allowDefault or clazz != DefaultTable, 'Oops, table class not found.'
269e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod			assert method.__name__ not in clazz.__dict__, \
27c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod				"Oops, class '%s' has method '%s'." % (clazz.__name__,
28f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod								       method.__name__)
299e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod			setattr(clazz, method.__name__, method)
30c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod		return None
31c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	return wrapper
3245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
3347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# General utility functions for merging values from different fonts
3492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
3549028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahboddef equal(lst):
36477dad1ee854c3701f8b0b6ff338fb7523ea27b6Behdad Esfahbod	lst = list(lst)
3749028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod	t = iter(lst)
3849028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod	first = next(t)
39477dad1ee854c3701f8b0b6ff338fb7523ea27b6Behdad Esfahbod	assert all(item == first for item in t), "Expected all items to be equal: %s" % lst
40e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader	return first
4147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
4247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef first(lst):
4349028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod	return next(iter(lst))
4447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
45e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef recalculate(lst):
4692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return NotImplemented
47e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader
48e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef current_time(lst):
4949028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod	return int(time.time() - _h_e_a_d.mac_epoch_diff)
50e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader
51642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef bitwise_and(lst):
52642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return reduce(operator.and_, lst)
53642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
54e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournaderdef bitwise_or(lst):
5549028b3ba7196b0389ac964f3a2db163367d24b8Behdad Esfahbod	return reduce(operator.or_, lst)
56e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader
5792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahboddef avg_int(lst):
5892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	lst = list(lst)
5992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return sum(lst) // len(lst)
6045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
610d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahboddef onlyExisting(func):
6292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	"""Returns a filter func that when called with a list,
6392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	only calls func on the non-NotImplemented items of the list,
6492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	and only so if there's at least one item remaining.
6592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	Otherwise returns NotImplemented."""
6692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
6792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	def wrapper(lst):
6892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		items = [item for item in lst if item is not NotImplemented]
6992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		return func(items) if items else NotImplemented
7092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
7192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return wrapper
7292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
739e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef sumLists(lst):
749e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	l = []
759e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	for item in lst:
769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod		l.extend(item)
779e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	return l
789e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
799e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahboddef sumDicts(lst):
809e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	d = {}
819e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	for item in lst:
829e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod		d.update(item)
839e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	return d
84f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
8512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahboddef mergeObjects(lst):
8612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	lst = [item for item in lst if item is not None and item is not NotImplemented]
8712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	if not lst:
8812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		return None # Not all can be NotImplemented
8912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
9012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	clazz = lst[0].__class__
9112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	assert all(type(item) == clazz for item in lst), lst
9212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	logic = clazz.mergeMap
9312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	returnTable = clazz()
9482c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod	returnDict = {}
9512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
9612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	allKeys = set.union(set(), *(vars(table).keys() for table in lst))
9712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	for key in allKeys:
9812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		try:
9912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			mergeLogic = logic[key]
10012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		except KeyError:
10112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			try:
10212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod				mergeLogic = logic['*']
10312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			except KeyError:
10412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod				raise Exception("Don't know how to merge key %s of class %s" %
10512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod						(key, clazz.__name__))
10612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		if mergeLogic is NotImplemented:
10712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			continue
10812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
10912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		if value is not NotImplemented:
11082c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod			returnDict[key] = value
11182c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod
11282c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod	returnTable.__dict__ = returnDict
11312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
11412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	return returnTable
11512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
116b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahboddef mergeBits(bitmap, lst):
117642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	lst = list(lst)
118642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	returnValue = 0
119b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbod	for bitNumber in range(bitmap['size']):
120642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		try:
121b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbod			mergeLogic = bitmap[bitNumber]
122642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		except KeyError:
123642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			try:
124b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbod				mergeLogic = bitmap['*']
125642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			except KeyError:
126642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader				raise Exception("Don't know how to merge bit %s" % bitNumber)
127642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		shiftedBit = 1 << bitNumber
128642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
129642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		returnValue |= mergedValue << bitNumber
130642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return returnValue
131642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
13265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
1339e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod@_add_method(DefaultTable, allowDefaultTable=True)
1343b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
1359e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	if not hasattr(self, 'mergeMap'):
1369e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod		m.log("Don't know how to merge '%s'." % self.tableTag)
13792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		return NotImplemented
1389e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
13927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	logic = self.mergeMap
14027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod
14127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	if isinstance(logic, dict):
14227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod		return m.mergeObjects(self, self.mergeMap, tables)
14327c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	else:
14427c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod		return logic(tables)
14527c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod
14671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
1479e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('maxp').mergeMap = {
1489e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': max,
1499e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
1509e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': equal,
1519e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'numGlyphs': sum,
15227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxStorage': first,
15327c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxFunctionDefs': first,
15427c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxInstructionDefs': first,
1559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# TODO When we correctly merge hinting data, update these values:
1569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
1579e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
1589e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
159b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad EsfahbodheadFlagsMergeBitMap = {
160642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'size': 16,
161642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'*': bitwise_or,
162642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	1: bitwise_and, # Baseline at y = 0
163642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	2: bitwise_and, # lsb at x = 0
164642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	3: bitwise_and, # Force ppem to integer values. FIXME?
165642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	5: bitwise_and, # Font is vertical
166642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	6: lambda bit: 0, # Always set to zero
167642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	11: bitwise_and, # Font data is 'lossless'
168642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	13: bitwise_and, # Optimized for ClearType
169642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	14: bitwise_and, # Last resort font. FIXME? equal or first may be better
170642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	15: lambda bit: 0, # Always set to zero
171642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader}
172642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
1739e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('head').mergeMap = {
1749e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
1759e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': max,
1769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fontRevision': max,
17792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'checkSumAdjustment': lambda lst: 0, # We need *something* here
1789e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'magicNumber': equal,
179b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbod	'flags': lambda lst: mergeBits(headFlagsMergeBitMap, lst),
1809e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'unitsPerEm': equal,
1819e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'created': current_time,
1829e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'modified': current_time,
1839e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMin': min,
1849e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'yMin': min,
1859e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMax': max,
1869e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'yMax': max,
1879e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'macStyle': first,
1889e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'lowestRecPPEM': max,
1899e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fontDirectionHint': lambda lst: 2,
1909e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'indexToLocFormat': recalculate,
1919e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphDataFormat': equal,
1929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
1939e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
1949e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('hhea').mergeMap = {
1959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': equal,
1969e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
1979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': max,
1989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ascent': max,
1999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'descent': min,
2009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'lineGap': max,
2019e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'advanceWidthMax': max,
2029e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minLeftSideBearing': min,
2039e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minRightSideBearing': min,
2049e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMaxExtent': max,
205642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretSlopeRise': first,
206642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretSlopeRun': first,
207642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretOffset': first,
2089e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'numberOfHMetrics': recalculate,
2099e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
2109e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
211b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbodos2FsTypeMergeBitMap = {
212642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'size': 16,
213642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'*': lambda bit: 0,
214642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	1: bitwise_or, # no embedding permitted
215642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	2: bitwise_and, # allow previewing and printing documents
216642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	3: bitwise_and, # allow editing documents
217642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	8: bitwise_or, # no subsetting permitted
218642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	9: bitwise_or, # no embedding of outlines permitted
219642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader}
220642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
221642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef mergeOs2FsType(lst):
222642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	lst = list(lst)
223642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if all(item == 0 for item in lst):
224642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		return 0
225642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
226642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	# Compute least restrictive logic for each fsType value
227642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	for i in range(len(lst)):
228642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# unset bit 1 (no embedding permitted) if either bit 2 or 3 is set
229642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		if lst[i] & 0x000C:
230642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] &= ~0x0002
231642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# set bit 2 (allow previewing) if bit 3 is set (allow editing)
232642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		elif lst[i] & 0x0008:
233642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] |= 0x0004
234642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# set bits 2 and 3 if everything is allowed
235642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		elif lst[i] == 0:
236642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] = 0x000C
237642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
238b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbod	fsType = mergeBits(os2FsTypeMergeBitMap, lst)
239642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	# unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
240642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if fsType & 0x0002:
241642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		fsType &= ~0x000C
242642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return fsType
243642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
244642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
2459e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('OS/2').mergeMap = {
2469e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': first,
2479e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2489e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'version': max,
24992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'xAvgCharWidth': avg_int, # Apparently fontTools doesn't recalc this
250642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'fsType': mergeOs2FsType, # Will be overwritten
251642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'panose': first, # FIXME: should really be the first Latin font
2529e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange1': bitwise_or,
2539e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange2': bitwise_or,
2549e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange3': bitwise_or,
2559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange4': bitwise_or,
2569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fsFirstCharIndex': min,
2579e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fsLastCharIndex': max,
2589e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoAscender': max,
2599e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoDescender': min,
2609e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoLineGap': max,
2619e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'usWinAscent': max,
2629e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'usWinDescent': max,
2630e235becc52d6048dde39f3bb400c617877302d7Behdad Esfahbod	# Version 2,3,4
2647765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'ulCodePageRange1': onlyExisting(bitwise_or),
2657765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'ulCodePageRange2': onlyExisting(bitwise_or),
2667765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'usMaxContex': onlyExisting(max),
2679e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# TODO version 5
2689e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
2699e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
270642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader@_add_method(ttLib.getTableClass('OS/2'))
271642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef merge(self, m, tables):
272642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	DefaultTable.merge(self, m, tables)
273642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if self.version < 2:
274642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# bits 8 and 9 are reserved and should be set to zero
275642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		self.fsType &= ~0x0300
276642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if self.version >= 3:
277642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# Only one of bits 1, 2, and 3 may be set. We already take
278642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# care of bit 1 implications in mergeOs2FsType. So unset
279642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# bit 2 if bit 3 is already set.
280642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		if self.fsType & 0x0008:
281642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			self.fsType &= ~0x0004
282642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return self
283642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
2849e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('post').mergeMap = {
2859e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': first,
2869e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2879e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'formatType': max,
2889e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'isFixedPitch': min,
2899e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minMemType42': max,
2909e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'maxMemType42': lambda lst: 0,
2919e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minMemType1': max,
2929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'maxMemType1': lambda lst: 0,
2930d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod	'mapping': onlyExisting(sumDicts),
294c68c0ff12fcf38d97304540b7dc0253f4142b046Behdad Esfahbod	'extraNames': lambda lst: [],
2959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
296f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
2979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('vmtx').mergeMap = ttLib.getTableClass('hmtx').mergeMap = {
2989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'metrics': sumDicts,
3009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
30165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
3027a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('gasp').mergeMap = {
3037a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'tableTag': equal,
3047a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'version': max,
3057a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'gaspRange': first, # FIXME? Appears irreconcilable
3067a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader}
3077a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader
3087a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('name').mergeMap = {
3097a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'tableTag': equal,
3107a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'names': first, # FIXME? Does mixing name records make sense?
3117a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader}
3127a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader
3139e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('loca').mergeMap = {
31492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'*': recalculate,
3159e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
3169e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
3179e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
3189e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('glyf').mergeMap = {
3199e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
3209e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphs': sumDicts,
3219e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphOrder': sumLists,
3229e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
323f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
324be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('glyf'))
3253b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
32627c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	for i,table in enumerate(tables):
327436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod		for g in table.glyphs.values():
32827c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod			if i:
32927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				# Drop hints for all but first font, since
33027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				# we don't map functions / CVT values.
33127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				g.removeHinting()
332436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# Expand composite glyphs to load their
333436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# composite glyph names.
334436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			if g.isComposite():
335436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod				g.expand(table)
33692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return DefaultTable.merge(self, m, tables)
337f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
33827c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('prep').mergeMap = lambda self, lst: first(lst)
33927c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('fpgm').mergeMap = lambda self, lst: first(lst)
34027c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('cvt ').mergeMap = lambda self, lst: first(lst)
34145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
342be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('cmap'))
3433b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
34471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Handle format=14.
3453b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahbod	cmapTables = [t for table in tables for t in table.tables
346f480c7cf21c51ef67570f1a5fa1c1653fa817bfcBehdad Esfahbod		      if t.isUnicode()]
34771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Better handle format-4 and format-12 coexisting in same font.
34871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Insert both a format-4 and format-12 if needed.
349be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod	module = ttLib.getTableModule('cmap')
35071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	assert all(t.format in [4, 12] for t in cmapTables)
35171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	format = max(t.format for t in cmapTables)
35271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable = module.cmap_classes[format](format)
35371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.cmap = {}
35471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platformID = 3
35571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platEncID = max(t.platEncID for t in cmapTables)
35671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.language = 0
35771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	for table in cmapTables:
35871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		# TODO handle duplicates.
35971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		cmapTable.cmap.update(table.cmap)
36071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tableVersion = 0
36171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tables = [cmapTable]
36271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.numSubTables = len(self.tables)
36392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return self
36471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
365c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
3662642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.ScriptList.mergeMap = {
3672642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'ScriptCount': sum,
368972af5af639cc036cddd4534c051cc42bbf81672Behdad Esfahbod	'ScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.ScriptTag),
3692642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3702642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
3712642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.FeatureList.mergeMap = {
3722642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'FeatureCount': sum,
3732642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'FeatureRecord': sumLists,
3742642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3752642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
3762642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.LookupList.mergeMap = {
3772642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'LookupCount': sum,
3782642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'Lookup': sumLists,
3792642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3802642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
38112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.Coverage.mergeMap = {
38212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'glyphs': sumLists,
38312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
38412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
38512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.ClassDef.mergeMap = {
38612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'classDefs': sumDicts,
38712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
38812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
38912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.LigCaretList.mergeMap = {
39012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': mergeObjects,
39112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'LigGlyphCount': sum,
39212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'LigGlyph': sumLists,
39312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
394c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
39512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.AttachList.mergeMap = {
39612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': mergeObjects,
39712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'GlyphCount': sum,
39812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'AttachPoint': sumLists,
39912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
40012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
40112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod# XXX Renumber MarkFilterSets of lookups
40212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.MarkGlyphSetsDef.mergeMap = {
40312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'MarkSetTableFormat': equal,
40412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'MarkSetCount': sum,
40512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': sumLists,
40612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
40712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
40812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.GDEF.mergeMap = {
40912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'*': mergeObjects,
41012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Version': max,
41112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
41212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
4132642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.GSUB.mergeMap = otTables.GPOS.mergeMap = {
4142642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'*': mergeObjects,
41512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Version': max,
4162642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
4172642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
4182642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GDEF').mergeMap = \
4192642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GSUB').mergeMap = \
4202642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GPOS').mergeMap = \
4212642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('BASE').mergeMap = \
4222642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('JSTF').mergeMap = \
4232642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('MATH').mergeMap = \
4242642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod{
4250d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod	'tableTag': onlyExisting(equal), # XXX clean me up
42612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'table': mergeObjects,
42712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
428f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
4292642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
4305080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.SingleSubst,
4315080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MultipleSubst,
4325080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.AlternateSubst,
4335080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.LigatureSubst,
4345080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ReverseChainSingleSubst,
4355080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.SinglePos,
4365080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.PairPos,
4375080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.CursivePos,
4385080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkBasePos,
4395080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkLigPos,
4405080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkMarkPos)
4415080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
4425080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  pass
4435080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4445080331251906861fbba4a9986efcd04978207beBehdad Esfahbod# Copied and trimmed down from subset.py
4455080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst,
4465080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextSubst,
4475080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ContextPos,
4485080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextPos)
4495080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef __classify_context(self):
4505080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4515080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  class ContextHelper(object):
4525080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    def __init__(self, klass, Format):
4535080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if klass.__name__.endswith('Subst'):
4545080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Typ = 'Sub'
4555080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Type = 'Subst'
4565080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      else:
4575080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Typ = 'Pos'
4585080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Type = 'Pos'
4595080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if klass.__name__.startswith('Chain'):
4605080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Chain = 'Chain'
4615080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      else:
4625080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Chain = ''
4635080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      ChainTyp = Chain+Typ
4645080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4655080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Typ = Typ
4665080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Type = Type
4675080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Chain = Chain
4685080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.ChainTyp = ChainTyp
4695080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4705080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.LookupRecord = Type+'LookupRecord'
4715080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4725080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if Format == 1:
4735080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.Rule = ChainTyp+'Rule'
4745080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.RuleSet = ChainTyp+'RuleSet'
4755080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      elif Format == 2:
4765080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.Rule = ChainTyp+'ClassRule'
4775080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.RuleSet = ChainTyp+'ClassSet'
4785080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4795080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format not in [1, 2, 3]:
4805080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    return None  # Don't shoot the messenger; let it go
4815080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if not hasattr(self.__class__, "__ContextHelpers"):
4825080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    self.__class__.__ContextHelpers = {}
4835080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format not in self.__class__.__ContextHelpers:
4845080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    helper = ContextHelper(self.__class__, self.Format)
4855080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    self.__class__.__ContextHelpers[self.Format] = helper
4865080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  return self.__class__.__ContextHelpers[self.Format]
4875080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4885080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4895080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst,
4905080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextSubst,
4915080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ContextPos,
4925080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextPos)
4935080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
4945080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  c = self.__classify_context()
4955080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4965080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format in [1, 2]:
4975080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    for rs in getattr(self, c.RuleSet):
4985080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if not rs: continue
4995080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      for r in getattr(rs, c.Rule):
5005080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        if not r: continue
5015080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        for ll in getattr(r, c.LookupRecord):
5025080331251906861fbba4a9986efcd04978207beBehdad Esfahbod          if not ll: continue
5035080331251906861fbba4a9986efcd04978207beBehdad Esfahbod          ll.LookupListIndex = lookupMap[ll.LookupListIndex]
5045080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  elif self.Format == 3:
5055080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    for ll in getattr(self, c.LookupRecord):
5065080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if not ll: continue
5075080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      ll.LookupListIndex = lookupMap[ll.LookupListIndex]
5085080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  else:
5095080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    assert 0, "unknown format: %s" % self.Format
5105080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5115080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.Lookup)
5125080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
5135080331251906861fbba4a9986efcd04978207beBehdad Esfahbod	for st in self.SubTable:
5145080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		if not st: continue
5155080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		st.mapLookups(lookupMap)
5165080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5175080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.LookupList)
5185080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
5195080331251906861fbba4a9986efcd04978207beBehdad Esfahbod	for l in self.Lookup:
5205080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		if not l: continue
5215080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		l.mapLookups(lookupMap)
5225080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5232642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Feature)
5242642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap):
5252642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex]
5262642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5272642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.FeatureList)
5282642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap):
5292642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for f in self.FeatureRecord:
5302642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not f or not f.Feature: continue
5312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		f.Feature.mapLookups(lookupMap)
5322642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5332642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.DefaultLangSys,
5342642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod             otTables.LangSys)
5352642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap):
5362642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
5372642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	if self.ReqFeatureIndex != 65535:
5382642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex]
5392642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5402642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Script)
5412642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap):
5422642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	if self.DefaultLangSys:
5432642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self.DefaultLangSys.mapFeatures(featureMap)
5442642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for l in self.LangSysRecord:
5452642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not l or not l.LangSys: continue
5462642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		l.LangSys.mapFeatures(featureMap)
5472642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5482642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.ScriptList)
549398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahboddef mapFeatures(self, featureMap):
5502642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for s in self.ScriptRecord:
5512642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not s or not s.Script: continue
5522642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		s.Script.mapFeatures(featureMap)
5532642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5542642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
555f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Options(object):
556f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
557f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  class UnknownOptionError(Exception):
558f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    pass
559f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
560f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, **kwargs):
561f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
562f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**kwargs)
563f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
564f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def set(self, **kwargs):
565f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    for k,v in kwargs.items():
566f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
567f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        raise self.UnknownOptionError("Unknown option '%s'" % k)
568f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      setattr(self, k, v)
569f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
570f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv, ignore_unknown=False):
571f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    ret = []
572f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    opts = {}
573f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for a in argv:
574f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      orig_a = a
575f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not a.startswith('--'):
576f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        ret.append(a)
577f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        continue
578f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      a = a[2:]
579f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      i = a.find('=')
580f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      op = '='
581f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if i == -1:
582f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if a.startswith("no-"):
583f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a[3:]
584f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = False
585f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
586f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a
587f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = True
588f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      else:
589f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        k = a[:i]
590f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if k[-1] in "-+":
591f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          op = k[-1]+'='  # Ops is '-=' or '+=' now.
592f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = k[:-1]
593f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = a[i+1:]
594f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      k = k.replace('-', '_')
595f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
596f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if ignore_unknown == True or k in ignore_unknown:
597f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          ret.append(orig_a)
598f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          continue
599f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
600f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          raise self.UnknownOptionError("Unknown option '%s'" % a)
601f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
602f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      ov = getattr(self, k)
603f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if isinstance(ov, bool):
604f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = bool(v)
605f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, int):
606f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = int(v)
607f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, list):
608f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = v.split(',')
609f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if vv == ['']:
610f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          vv = []
611f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
612f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if op == '=':
613f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = vv
614f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '+=':
615f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
616f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v.extend(vv)
617f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '-=':
618f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
619f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          for x in vv:
620f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod            if x in v:
621f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod              v.remove(x)
622f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
623f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          assert 0
624f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
625f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      opts[k] = v
626f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**opts)
627f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
628f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return ret
629f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
630f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
6319e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbodclass Merger(object):
63245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
633f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def __init__(self, options=None, log=None):
634f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
635f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not log:
636f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			log = Logger()
637f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not options:
638f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			options = Options()
639f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
640f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.options = options
641f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.log = log
64245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
643f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def merge(self, fontfiles):
64445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
64545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = ttLib.TTFont()
64645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
64745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
64845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Settle on a mega glyph order.
64945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
650f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
65145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		glyphOrders = [font.getGlyphOrder() for font in fonts]
65245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
65345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Reload fonts and set new glyph names on them.
65445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# TODO Is it necessary to reload font?  I think it is.  At least
65545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# it's safer, in case tables were loaded to provide glyph names.
656f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
6573235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod		for font,glyphOrder in zip(fonts, glyphOrders):
6583235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod			font.setGlyphOrder(glyphOrder)
65945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega.setGlyphOrder(megaGlyphOrder)
66045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6612642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		for font in fonts:
6622642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod			self._preMerge(font)
6632642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
664f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
66545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		allTags.remove('GlyphOrder')
66645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for tag in allTags:
667f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
66845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			clazz = ttLib.getTableClass(tag)
66945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6702642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod			tables = [font.get(tag, NotImplemented) for font in fonts]
67192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			table = clazz(tag).merge(self, tables)
67292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if table is not NotImplemented and table is not False:
67365f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod				mega[tag] = table
674b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Merged '%s'." % tag)
67565f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod			else:
6769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				self.log("Dropped '%s'." % tag)
677b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod			self.log.lapse("merge '%s'" % tag)
67845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6792642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self._postMerge(mega)
6802642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
68145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
68245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
68345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	def _mergeGlyphOrders(self, glyphOrders):
684c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		"""Modifies passed-in glyphOrders to reflect new glyph names.
685c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		Returns glyphOrder for the merged font."""
68645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Simply append font index to the glyph name for now.
687c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# TODO Even this simplistic numbering can result in conflicts.
688c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# But then again, we have to improve this soon anyway.
68945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = []
69045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for n,glyphOrder in enumerate(glyphOrders):
69145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			for i,glyphName in enumerate(glyphOrder):
692f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod				glyphName += "#" + repr(n)
69345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				glyphOrder[i] = glyphName
69445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				mega.append(glyphName)
69545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
69645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6976baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod	def mergeObjects(self, returnTable, logic, tables):
69892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		# Right now we don't use self at all.  Will use in the future
69992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		# for options and logging.
70092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
70192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		if logic is NotImplemented:
70292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			return NotImplemented
70392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
70492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		allKeys = set.union(set(), *(vars(table).keys() for table in tables if table is not NotImplemented))
70547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		for key in allKeys:
706e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader			try:
7076baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod				mergeLogic = logic[key]
708e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader			except KeyError:
7099e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				try:
7106baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod					mergeLogic = logic['*']
7119e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				except KeyError:
7126baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod					raise Exception("Don't know how to merge key %s of class %s" %
7136baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod							(key, returnTable.__class__.__name__))
71492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if mergeLogic is NotImplemented:
715e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader				continue
71692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
71792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if value is not NotImplemented:
71892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod				setattr(returnTable, key, value)
71992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
72092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		return returnTable
72147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
7222642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	def _preMerge(self, font):
7232642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7242642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GDEF = font.get('GDEF')
7252642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GSUB = font.get('GSUB')
7262642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GPOS = font.get('GPOS')
7272642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7282642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		for t in [GSUB, GPOS]:
729398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if not t: continue
730398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
7315080331251906861fbba4a9986efcd04978207beBehdad Esfahbod			if t.table.LookupList:
732b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				lookupMap = {i:id(v) for i,v in enumerate(t.table.LookupList.Lookup)}
7335080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				t.table.LookupList.mapLookups(lookupMap)
7345080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				if t.table.FeatureList:
7355080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					# XXX Handle present FeatureList but absent LookupList
7365080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					t.table.FeatureList.mapLookups(lookupMap)
7372642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
738398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if t.table.FeatureList and t.table.ScriptList:
739b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				featureMap = {i:id(v) for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
740398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod				t.table.ScriptList.mapFeatures(featureMap)
741398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
742398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO GDEF/Lookup MarkFilteringSets
7432642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		# TODO FeatureParams nameIDs
7442642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7452642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	def _postMerge(self, font):
746398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
747398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GDEF = font.get('GDEF')
748398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GSUB = font.get('GSUB')
749398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GPOS = font.get('GPOS')
750398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
751398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		for t in [GSUB, GPOS]:
752398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if not t: continue
753398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
7545080331251906861fbba4a9986efcd04978207beBehdad Esfahbod			if t.table.LookupList:
755b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				lookupMap = {id(v):i for i,v in enumerate(t.table.LookupList.Lookup)}
7565080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				t.table.LookupList.mapLookups(lookupMap)
7575080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				if t.table.FeatureList:
7585080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					# XXX Handle present FeatureList but absent LookupList
7595080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					t.table.FeatureList.mapLookups(lookupMap)
760398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
761398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if t.table.FeatureList and t.table.ScriptList:
7625080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				# XXX Handle present ScriptList but absent FeatureList
763b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				featureMap = {id(v):i for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
764398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod				t.table.ScriptList.mapFeatures(featureMap)
765398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
766398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO GDEF/Lookup MarkFilteringSets
767398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO FeatureParams nameIDs
7682642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
769f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
770f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Logger(object):
771f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
772f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, verbose=False, xml=False, timing=False):
773f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.verbose = verbose
774f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.xml = xml
775f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.timing = timing
776f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = self.start_time = time.time()
777f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
778f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv):
779f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    argv = argv[:]
780f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for v in ['verbose', 'xml', 'timing']:
781f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if "--"+v in argv:
782f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        setattr(self, v, True)
783f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        argv.remove("--"+v)
784f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return argv
785f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
786f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __call__(self, *things):
787f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.verbose:
788f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
789f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print(' '.join(str(x) for x in things))
790f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
791f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def lapse(self, *things):
792f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.timing:
793f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
794f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    new_time = time.time()
795f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print("Took %0.3fs to %s" %(new_time - self.last_time,
796f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod                                 ' '.join(str(x) for x in things)))
797f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = new_time
798f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
799f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def font(self, font, file=sys.stdout):
800f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.xml:
801f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
802f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    from fontTools.misc import xmlWriter
803f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    writer = xmlWriter.XMLWriter(file)
804f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    font.disassembleInstructions = False  # Work around ttLib bug
805f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for tag in font.keys():
806f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.begintag(tag)
807f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
808f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      font[tag].toXML(writer, font)
809f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.endtag(tag)
810f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
811f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
812f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
813f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod__all__ = [
814f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Options',
815f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Merger',
816f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Logger',
817f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'main'
818f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod]
819f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
82045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args):
821f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
822f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	log = Logger()
823f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = log.parse_opts(args)
824f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
825f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	options = Options()
826f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = options.parse_opts(args)
827f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
82845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	if len(args) < 1:
829f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		print("usage: pyftmerge font...", file=sys.stderr)
83045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		sys.exit(1)
831f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
832f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	merger = Merger(options=options, log=log)
833f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	font = merger.merge(args)
83445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	outfile = 'merged.ttf'
83545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	font.save(outfile)
836b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("compile and save font")
837b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod
838b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.last_time = log.start_time
839b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("make one with everything(TOTAL TIME)")
84045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
84145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__":
84245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	main(sys.argv[1:])
843