merge.py revision 2772d8496e6979f6b7992d35233e2b973d7b86b5
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):
860884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod	lst = [item for item in lst if item is not NotImplemented]
8712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	if not lst:
880884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod		return NotImplemented
890884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod	lst = [item for item in lst if item is not None]
900884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod	if not lst:
910884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod		return None
9212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
9312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	clazz = lst[0].__class__
9412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	assert all(type(item) == clazz for item in lst), lst
950884507c0eedb210bbe691393064083dfc1aa291Behdad Esfahbod
9612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	logic = clazz.mergeMap
9712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	returnTable = clazz()
9882c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod	returnDict = {}
9912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
10012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	allKeys = set.union(set(), *(vars(table).keys() for table in lst))
10112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	for key in allKeys:
10212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		try:
10312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			mergeLogic = logic[key]
10412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		except KeyError:
10512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			try:
10612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod				mergeLogic = logic['*']
10712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			except KeyError:
10812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod				raise Exception("Don't know how to merge key %s of class %s" %
10912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod						(key, clazz.__name__))
11012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		if mergeLogic is NotImplemented:
11112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod			continue
11212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
11312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod		if value is not NotImplemented:
11482c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod			returnDict[key] = value
11582c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod
11682c54638e32a5b7c0f7ad3ac3dafacf7fa27dad4Behdad Esfahbod	returnTable.__dict__ = returnDict
11712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
11812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	return returnTable
11912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
120201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahboddef mergeBits(bitmap):
121201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod
122201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod	def wrapper(lst):
123201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod		lst = list(lst)
124201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod		returnValue = 0
125201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod		for bitNumber in range(bitmap['size']):
126642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			try:
127201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod				mergeLogic = bitmap[bitNumber]
128642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			except KeyError:
129201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod				try:
130201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod					mergeLogic = bitmap['*']
131201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod				except KeyError:
132201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod					raise Exception("Don't know how to merge bit %s" % bitNumber)
133201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod			shiftedBit = 1 << bitNumber
134201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod			mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
135201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod			returnValue |= mergedValue << bitNumber
136201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod		return returnValue
137201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod
138201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod	return wrapper
139642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
14065f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
1419e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod@_add_method(DefaultTable, allowDefaultTable=True)
1423b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
1439e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	if not hasattr(self, 'mergeMap'):
1449e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod		m.log("Don't know how to merge '%s'." % self.tableTag)
14592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		return NotImplemented
1469e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
14727c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	logic = self.mergeMap
14827c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod
14927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	if isinstance(logic, dict):
15027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod		return m.mergeObjects(self, self.mergeMap, tables)
15127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	else:
15227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod		return logic(tables)
15327c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod
15471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
1559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('maxp').mergeMap = {
1569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': max,
1579e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
1589e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': equal,
1599e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'numGlyphs': sum,
16027c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxStorage': first,
16127c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxFunctionDefs': first,
16227c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	'maxInstructionDefs': first,
1639e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# TODO When we correctly merge hinting data, update these values:
1649e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
1659e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
1669e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
167b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad EsfahbodheadFlagsMergeBitMap = {
168642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'size': 16,
169642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'*': bitwise_or,
170642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	1: bitwise_and, # Baseline at y = 0
171642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	2: bitwise_and, # lsb at x = 0
172642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	3: bitwise_and, # Force ppem to integer values. FIXME?
173642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	5: bitwise_and, # Font is vertical
174642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	6: lambda bit: 0, # Always set to zero
175642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	11: bitwise_and, # Font data is 'lossless'
176642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	13: bitwise_and, # Optimized for ClearType
177642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	14: bitwise_and, # Last resort font. FIXME? equal or first may be better
178642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	15: lambda bit: 0, # Always set to zero
179642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader}
180642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
1819e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('head').mergeMap = {
1829e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
1839e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': max,
1849e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fontRevision': max,
18592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'checkSumAdjustment': lambda lst: 0, # We need *something* here
1869e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'magicNumber': equal,
187201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod	'flags': mergeBits(headFlagsMergeBitMap),
1889e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'unitsPerEm': equal,
1899e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'created': current_time,
1909e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'modified': current_time,
1919e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMin': min,
1929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'yMin': min,
1939e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMax': max,
1949e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'yMax': max,
1959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'macStyle': first,
1969e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'lowestRecPPEM': max,
1979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fontDirectionHint': lambda lst: 2,
1989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'indexToLocFormat': recalculate,
1999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphDataFormat': equal,
2009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
2019e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
2029e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('hhea').mergeMap = {
2039e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': equal,
2049e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2059e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableVersion': max,
2069e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ascent': max,
2079e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'descent': min,
2089e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'lineGap': max,
2099e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'advanceWidthMax': max,
2109e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minLeftSideBearing': min,
2119e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minRightSideBearing': min,
2129e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'xMaxExtent': max,
213642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretSlopeRise': first,
214642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretSlopeRun': first,
215642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'caretOffset': first,
2169e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'numberOfHMetrics': recalculate,
2179e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
2189e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
219b8039e26530e2a76c3c15c3bd79b31137e3719f2Behdad Esfahbodos2FsTypeMergeBitMap = {
220642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'size': 16,
221642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'*': lambda bit: 0,
222642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	1: bitwise_or, # no embedding permitted
223642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	2: bitwise_and, # allow previewing and printing documents
224642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	3: bitwise_and, # allow editing documents
225642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	8: bitwise_or, # no subsetting permitted
226642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	9: bitwise_or, # no embedding of outlines permitted
227642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader}
228642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
229642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef mergeOs2FsType(lst):
230642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	lst = list(lst)
231642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if all(item == 0 for item in lst):
232642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		return 0
233642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
234642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	# Compute least restrictive logic for each fsType value
235642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	for i in range(len(lst)):
236642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# unset bit 1 (no embedding permitted) if either bit 2 or 3 is set
237642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		if lst[i] & 0x000C:
238642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] &= ~0x0002
239642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# set bit 2 (allow previewing) if bit 3 is set (allow editing)
240642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		elif lst[i] & 0x0008:
241642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] |= 0x0004
242642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# set bits 2 and 3 if everything is allowed
243642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		elif lst[i] == 0:
244642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			lst[i] = 0x000C
245642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
246201a68182159fdf58cf54472ef5f4ea4260984e4Behdad Esfahbod	fsType = mergeBits(os2FsTypeMergeBitMap)(lst)
247642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	# unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
248642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if fsType & 0x0002:
249642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		fsType &= ~0x000C
250642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return fsType
251642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
252642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
2539e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('OS/2').mergeMap = {
2549e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': first,
2559e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2569e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'version': max,
25792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'xAvgCharWidth': avg_int, # Apparently fontTools doesn't recalc this
258642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'fsType': mergeOs2FsType, # Will be overwritten
259642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	'panose': first, # FIXME: should really be the first Latin font
2609e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange1': bitwise_or,
2619e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange2': bitwise_or,
2629e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange3': bitwise_or,
2639e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'ulUnicodeRange4': bitwise_or,
2649e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fsFirstCharIndex': min,
2659e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'fsLastCharIndex': max,
2669e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoAscender': max,
2679e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoDescender': min,
2689e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'sTypoLineGap': max,
2699e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'usWinAscent': max,
2709e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'usWinDescent': max,
2710e235becc52d6048dde39f3bb400c617877302d7Behdad Esfahbod	# Version 2,3,4
2727765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'ulCodePageRange1': onlyExisting(bitwise_or),
2737765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'ulCodePageRange2': onlyExisting(bitwise_or),
2747765421ab76c79a38a565d7c8b631b2d1a13781cBehdad Esfahbod	'usMaxContex': onlyExisting(max),
2759e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	# TODO version 5
2769e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
2779e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
278642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader@_add_method(ttLib.getTableClass('OS/2'))
279642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournaderdef merge(self, m, tables):
280642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	DefaultTable.merge(self, m, tables)
281642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if self.version < 2:
282642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# bits 8 and 9 are reserved and should be set to zero
283642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		self.fsType &= ~0x0300
284642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	if self.version >= 3:
285642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# Only one of bits 1, 2, and 3 may be set. We already take
286642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# care of bit 1 implications in mergeOs2FsType. So unset
287642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		# bit 2 if bit 3 is already set.
288642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader		if self.fsType & 0x0008:
289642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader			self.fsType &= ~0x0004
290642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader	return self
291642eaf135d7803894c7cf56bdfd4649da9031adeRoozbeh Pournader
2929e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('post').mergeMap = {
2939e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'*': first,
2949e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
2959e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'formatType': max,
2969e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'isFixedPitch': min,
2979e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minMemType42': max,
2989e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'maxMemType42': lambda lst: 0,
2999e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'minMemType1': max,
3009e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'maxMemType1': lambda lst: 0,
3010d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod	'mapping': onlyExisting(sumDicts),
302c68c0ff12fcf38d97304540b7dc0253f4142b046Behdad Esfahbod	'extraNames': lambda lst: [],
3039e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
304f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
3059e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('vmtx').mergeMap = ttLib.getTableClass('hmtx').mergeMap = {
3069e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
3079e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'metrics': sumDicts,
3089e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
30965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
3107a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('gasp').mergeMap = {
3117a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'tableTag': equal,
3127a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'version': max,
3137a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'gaspRange': first, # FIXME? Appears irreconcilable
3147a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader}
3157a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader
3167a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh PournaderttLib.getTableClass('name').mergeMap = {
3177a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'tableTag': equal,
3187a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader	'names': first, # FIXME? Does mixing name records make sense?
3197a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader}
3207a27214fcb96457a071c8a55b4ff2b59f5a43e58Roozbeh Pournader
3219e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('loca').mergeMap = {
32292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	'*': recalculate,
3239e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
3249e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
3259e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod
3269e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad EsfahbodttLib.getTableClass('glyf').mergeMap = {
3279e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'tableTag': equal,
3289e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphs': sumDicts,
3299e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod	'glyphOrder': sumLists,
3309e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod}
331f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
332be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('glyf'))
3333b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
33427c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod	for i,table in enumerate(tables):
335436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod		for g in table.glyphs.values():
33627c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod			if i:
33727c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				# Drop hints for all but first font, since
33827c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				# we don't map functions / CVT values.
33927c71f9f6093fed7668c73b13943f46dac19eecfBehdad Esfahbod				g.removeHinting()
340436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# Expand composite glyphs to load their
341436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# composite glyph names.
342436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			if g.isComposite():
343436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod				g.expand(table)
34492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return DefaultTable.merge(self, m, tables)
345f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
34627c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('prep').mergeMap = lambda self, lst: first(lst)
34727c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('fpgm').mergeMap = lambda self, lst: first(lst)
34827c71f9f6093fed7668c73b13943f46dac19eecfBehdad EsfahbodttLib.getTableClass('cvt ').mergeMap = lambda self, lst: first(lst)
34945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
350be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('cmap'))
3513b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahboddef merge(self, m, tables):
35271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Handle format=14.
3533b36f55adf2dacf187e60191b32a92703ef77abcBehdad Esfahbod	cmapTables = [t for table in tables for t in table.tables
354f480c7cf21c51ef67570f1a5fa1c1653fa817bfcBehdad Esfahbod		      if t.isUnicode()]
35571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Better handle format-4 and format-12 coexisting in same font.
35671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Insert both a format-4 and format-12 if needed.
357be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod	module = ttLib.getTableModule('cmap')
35871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	assert all(t.format in [4, 12] for t in cmapTables)
35971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	format = max(t.format for t in cmapTables)
36071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable = module.cmap_classes[format](format)
36171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.cmap = {}
36271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platformID = 3
36371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platEncID = max(t.platEncID for t in cmapTables)
36471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.language = 0
36571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	for table in cmapTables:
36671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		# TODO handle duplicates.
36771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		cmapTable.cmap.update(table.cmap)
36871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tableVersion = 0
36971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tables = [cmapTable]
37071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.numSubTables = len(self.tables)
37192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod	return self
37271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
373c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
3742642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.ScriptList.mergeMap = {
3752642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'ScriptCount': sum,
376972af5af639cc036cddd4534c051cc42bbf81672Behdad Esfahbod	'ScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.ScriptTag),
3772642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3782642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
3792642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.FeatureList.mergeMap = {
3802642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'FeatureCount': sum,
3812642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'FeatureRecord': sumLists,
3822642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3832642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
3842642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.LookupList.mergeMap = {
3852642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'LookupCount': sum,
3862642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'Lookup': sumLists,
3872642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
3882642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
38912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.Coverage.mergeMap = {
39012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'glyphs': sumLists,
39112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
39212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
39312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.ClassDef.mergeMap = {
39412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'classDefs': sumDicts,
39512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
39612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
39712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.LigCaretList.mergeMap = {
39812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': mergeObjects,
39912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'LigGlyphCount': sum,
40012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'LigGlyph': sumLists,
40112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
402c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
40312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.AttachList.mergeMap = {
40412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': mergeObjects,
40512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'GlyphCount': sum,
40612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'AttachPoint': sumLists,
40712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
40812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
40912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod# XXX Renumber MarkFilterSets of lookups
41012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.MarkGlyphSetsDef.mergeMap = {
41112dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'MarkSetTableFormat': equal,
41212dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'MarkSetCount': sum,
41312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Coverage': sumLists,
41412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
41512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
41612dd547c012d4e1295a6c9c2eb48835cd57851caBehdad EsfahbodotTables.GDEF.mergeMap = {
41712dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'*': mergeObjects,
41812dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Version': max,
41912dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
42012dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod
4212642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodotTables.GSUB.mergeMap = otTables.GPOS.mergeMap = {
4222642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	'*': mergeObjects,
42312dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'Version': max,
4242642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod}
4252642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
4262642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GDEF').mergeMap = \
4272642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GSUB').mergeMap = \
4282642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('GPOS').mergeMap = \
4292642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('BASE').mergeMap = \
4302642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('JSTF').mergeMap = \
4312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad EsfahbodttLib.getTableClass('MATH').mergeMap = \
4322642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod{
4330d5fcf498c27bc77ff6203469cc7d622a41dcebbBehdad Esfahbod	'tableTag': onlyExisting(equal), # XXX clean me up
43412dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod	'table': mergeObjects,
43512dd547c012d4e1295a6c9c2eb48835cd57851caBehdad Esfahbod}
436f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
4372642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
4385080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.SingleSubst,
4395080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MultipleSubst,
4405080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.AlternateSubst,
4415080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.LigatureSubst,
4425080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ReverseChainSingleSubst,
4435080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.SinglePos,
4445080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.PairPos,
4455080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.CursivePos,
4465080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkBasePos,
4475080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkLigPos,
4485080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.MarkMarkPos)
4495080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
4505080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  pass
4515080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4525080331251906861fbba4a9986efcd04978207beBehdad Esfahbod# Copied and trimmed down from subset.py
4535080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst,
4545080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextSubst,
4555080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ContextPos,
4565080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextPos)
4575080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef __classify_context(self):
4585080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4595080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  class ContextHelper(object):
4605080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    def __init__(self, klass, Format):
4615080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if klass.__name__.endswith('Subst'):
4625080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Typ = 'Sub'
4635080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Type = 'Subst'
4645080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      else:
4655080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Typ = 'Pos'
4665080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Type = 'Pos'
4675080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if klass.__name__.startswith('Chain'):
4685080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Chain = 'Chain'
4695080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      else:
4705080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        Chain = ''
4715080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      ChainTyp = Chain+Typ
4725080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4735080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Typ = Typ
4745080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Type = Type
4755080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.Chain = Chain
4765080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.ChainTyp = ChainTyp
4775080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4785080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      self.LookupRecord = Type+'LookupRecord'
4795080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4805080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if Format == 1:
4815080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.Rule = ChainTyp+'Rule'
4825080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.RuleSet = ChainTyp+'RuleSet'
4835080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      elif Format == 2:
4845080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.Rule = ChainTyp+'ClassRule'
4855080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        self.RuleSet = ChainTyp+'ClassSet'
4865080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4875080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format not in [1, 2, 3]:
4885080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    return None  # Don't shoot the messenger; let it go
4895080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if not hasattr(self.__class__, "__ContextHelpers"):
4905080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    self.__class__.__ContextHelpers = {}
4915080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format not in self.__class__.__ContextHelpers:
4925080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    helper = ContextHelper(self.__class__, self.Format)
4935080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    self.__class__.__ContextHelpers[self.Format] = helper
4945080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  return self.__class__.__ContextHelpers[self.Format]
4955080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4965080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
4975080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.ContextSubst,
4985080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextSubst,
4995080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ContextPos,
5005080331251906861fbba4a9986efcd04978207beBehdad Esfahbod             otTables.ChainContextPos)
5015080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
5025080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  c = self.__classify_context()
5035080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5045080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  if self.Format in [1, 2]:
5055080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    for rs in getattr(self, c.RuleSet):
5065080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if not rs: continue
5075080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      for r in getattr(rs, c.Rule):
5085080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        if not r: continue
5095080331251906861fbba4a9986efcd04978207beBehdad Esfahbod        for ll in getattr(r, c.LookupRecord):
5105080331251906861fbba4a9986efcd04978207beBehdad Esfahbod          if not ll: continue
5115080331251906861fbba4a9986efcd04978207beBehdad Esfahbod          ll.LookupListIndex = lookupMap[ll.LookupListIndex]
5125080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  elif self.Format == 3:
5135080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    for ll in getattr(self, c.LookupRecord):
5145080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      if not ll: continue
5155080331251906861fbba4a9986efcd04978207beBehdad Esfahbod      ll.LookupListIndex = lookupMap[ll.LookupListIndex]
5165080331251906861fbba4a9986efcd04978207beBehdad Esfahbod  else:
5175080331251906861fbba4a9986efcd04978207beBehdad Esfahbod    assert 0, "unknown format: %s" % self.Format
5185080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5195080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.Lookup)
5205080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
5215080331251906861fbba4a9986efcd04978207beBehdad Esfahbod	for st in self.SubTable:
5225080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		if not st: continue
5235080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		st.mapLookups(lookupMap)
5245080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5255080331251906861fbba4a9986efcd04978207beBehdad Esfahbod@_add_method(otTables.LookupList)
5265080331251906861fbba4a9986efcd04978207beBehdad Esfahboddef mapLookups(self, lookupMap):
5275080331251906861fbba4a9986efcd04978207beBehdad Esfahbod	for l in self.Lookup:
5285080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		if not l: continue
5295080331251906861fbba4a9986efcd04978207beBehdad Esfahbod		l.mapLookups(lookupMap)
5305080331251906861fbba4a9986efcd04978207beBehdad Esfahbod
5312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Feature)
5322642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap):
5332642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex]
5342642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5352642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.FeatureList)
5362642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapLookups(self, lookupMap):
5372642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for f in self.FeatureRecord:
5382642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not f or not f.Feature: continue
5392642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		f.Feature.mapLookups(lookupMap)
5402642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5412642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.DefaultLangSys,
5422642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod             otTables.LangSys)
5432642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap):
5442642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
5452642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	if self.ReqFeatureIndex != 65535:
5462642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex]
5472642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5482642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.Script)
5492642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahboddef mapFeatures(self, featureMap):
5502642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	if self.DefaultLangSys:
5512642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self.DefaultLangSys.mapFeatures(featureMap)
5522642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for l in self.LangSysRecord:
5532642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not l or not l.LangSys: continue
5542642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		l.LangSys.mapFeatures(featureMap)
5552642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5562642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod@_add_method(otTables.ScriptList)
557398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahboddef mapFeatures(self, featureMap):
5582642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	for s in self.ScriptRecord:
5592642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		if not s or not s.Script: continue
5602642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		s.Script.mapFeatures(featureMap)
5612642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
5622642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
563f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Options(object):
564f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
565f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  class UnknownOptionError(Exception):
566f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    pass
567f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
568f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, **kwargs):
569f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
570f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**kwargs)
571f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
572f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def set(self, **kwargs):
573f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    for k,v in kwargs.items():
574f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
575f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        raise self.UnknownOptionError("Unknown option '%s'" % k)
576f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      setattr(self, k, v)
577f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
578f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv, ignore_unknown=False):
579f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    ret = []
580f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    opts = {}
581f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for a in argv:
582f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      orig_a = a
583f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not a.startswith('--'):
584f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        ret.append(a)
585f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        continue
586f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      a = a[2:]
587f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      i = a.find('=')
588f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      op = '='
589f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if i == -1:
590f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if a.startswith("no-"):
591f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a[3:]
592f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = False
593f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
594f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a
595f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = True
596f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      else:
597f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        k = a[:i]
598f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if k[-1] in "-+":
599f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          op = k[-1]+'='  # Ops is '-=' or '+=' now.
600f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = k[:-1]
601f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = a[i+1:]
602f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      k = k.replace('-', '_')
603f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
604f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if ignore_unknown == True or k in ignore_unknown:
605f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          ret.append(orig_a)
606f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          continue
607f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
608f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          raise self.UnknownOptionError("Unknown option '%s'" % a)
609f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
610f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      ov = getattr(self, k)
611f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if isinstance(ov, bool):
612f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = bool(v)
613f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, int):
614f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = int(v)
615f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, list):
616f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = v.split(',')
617f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if vv == ['']:
618f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          vv = []
619f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
620f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if op == '=':
621f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = vv
622f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '+=':
623f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
624f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v.extend(vv)
625f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '-=':
626f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
627f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          for x in vv:
628f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod            if x in v:
629f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod              v.remove(x)
630f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
631f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          assert 0
632f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
633f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      opts[k] = v
634f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**opts)
635f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
636f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return ret
637f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
638f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
6399e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbodclass Merger(object):
64045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
641f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def __init__(self, options=None, log=None):
642f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
643f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not log:
644f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			log = Logger()
645f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not options:
646f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			options = Options()
647f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
648f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.options = options
649f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.log = log
65045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
651f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def merge(self, fontfiles):
65245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
65345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = ttLib.TTFont()
65445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
65545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
65645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Settle on a mega glyph order.
65745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
658f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
65945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		glyphOrders = [font.getGlyphOrder() for font in fonts]
66045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
66145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Reload fonts and set new glyph names on them.
66245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# TODO Is it necessary to reload font?  I think it is.  At least
66345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# it's safer, in case tables were loaded to provide glyph names.
664f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
6653235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod		for font,glyphOrder in zip(fonts, glyphOrders):
6663235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod			font.setGlyphOrder(glyphOrder)
66745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega.setGlyphOrder(megaGlyphOrder)
66845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6692642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		for font in fonts:
6702642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod			self._preMerge(font)
6712642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
672f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
67345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		allTags.remove('GlyphOrder')
67445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for tag in allTags:
675f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
67645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6772642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod			tables = [font.get(tag, NotImplemented) for font in fonts]
6782772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod
6792772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod			clazz = ttLib.getTableClass(tag)
68092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			table = clazz(tag).merge(self, tables)
6812772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod			# XXX Clean this up and use:  table = mergeObjects(tables)
6822772d8496e6979f6b7992d35233e2b973d7b86b5Behdad Esfahbod
68392fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if table is not NotImplemented and table is not False:
68465f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod				mega[tag] = table
685b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Merged '%s'." % tag)
68665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod			else:
6879e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				self.log("Dropped '%s'." % tag)
688b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod			self.log.lapse("merge '%s'" % tag)
68945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
6902642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		self._postMerge(mega)
6912642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
69245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
69345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
69445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	def _mergeGlyphOrders(self, glyphOrders):
695c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		"""Modifies passed-in glyphOrders to reflect new glyph names.
696c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		Returns glyphOrder for the merged font."""
69745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Simply append font index to the glyph name for now.
698c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# TODO Even this simplistic numbering can result in conflicts.
699c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# But then again, we have to improve this soon anyway.
70045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = []
70145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for n,glyphOrder in enumerate(glyphOrders):
70245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			for i,glyphName in enumerate(glyphOrder):
703f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod				glyphName += "#" + repr(n)
70445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				glyphOrder[i] = glyphName
70545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				mega.append(glyphName)
70645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
70745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
7086baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod	def mergeObjects(self, returnTable, logic, tables):
70992fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		# Right now we don't use self at all.  Will use in the future
71092fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		# for options and logging.
71192fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
71292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		allKeys = set.union(set(), *(vars(table).keys() for table in tables if table is not NotImplemented))
71347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		for key in allKeys:
714e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader			try:
7156baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod				mergeLogic = logic[key]
716e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader			except KeyError:
7179e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				try:
7186baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod					mergeLogic = logic['*']
7199e6adb6bd6b92765f72d0a2acf871405832aa11eBehdad Esfahbod				except KeyError:
7206baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod					raise Exception("Don't know how to merge key %s of class %s" %
7216baf26ea74247a0bed5f0b52d13bc3ef1c931204Behdad Esfahbod							(key, returnTable.__class__.__name__))
72292fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if mergeLogic is NotImplemented:
723e219c6c76f116a38e0187b4388d99dd59eff30bcRoozbeh Pournader				continue
72492fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
72592fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod			if value is not NotImplemented:
72692fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod				setattr(returnTable, key, value)
72792fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod
72892fd5665772996966aaa0709462ebae5a72d4e19Behdad Esfahbod		return returnTable
72947bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
7302642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	def _preMerge(self, font):
7312642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7322642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GDEF = font.get('GDEF')
7332642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GSUB = font.get('GSUB')
7342642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		GPOS = font.get('GPOS')
7352642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7362642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		for t in [GSUB, GPOS]:
737398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if not t: continue
738398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
7395080331251906861fbba4a9986efcd04978207beBehdad Esfahbod			if t.table.LookupList:
740b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				lookupMap = {i:id(v) for i,v in enumerate(t.table.LookupList.Lookup)}
7415080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				t.table.LookupList.mapLookups(lookupMap)
7425080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				if t.table.FeatureList:
7435080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					# XXX Handle present FeatureList but absent LookupList
7445080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					t.table.FeatureList.mapLookups(lookupMap)
7452642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
746398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if t.table.FeatureList and t.table.ScriptList:
747b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				featureMap = {i:id(v) for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
748398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod				t.table.ScriptList.mapFeatures(featureMap)
749398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
750398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO GDEF/Lookup MarkFilteringSets
7512642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod		# TODO FeatureParams nameIDs
7522642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
7532642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod	def _postMerge(self, font):
754398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
755398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GDEF = font.get('GDEF')
756398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GSUB = font.get('GSUB')
757398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		GPOS = font.get('GPOS')
758398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
759398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		for t in [GSUB, GPOS]:
760398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if not t: continue
761398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
7625080331251906861fbba4a9986efcd04978207beBehdad Esfahbod			if t.table.LookupList:
763b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				lookupMap = {id(v):i for i,v in enumerate(t.table.LookupList.Lookup)}
7645080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				t.table.LookupList.mapLookups(lookupMap)
7655080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				if t.table.FeatureList:
7665080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					# XXX Handle present FeatureList but absent LookupList
7675080331251906861fbba4a9986efcd04978207beBehdad Esfahbod					t.table.FeatureList.mapLookups(lookupMap)
768398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
769398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod			if t.table.FeatureList and t.table.ScriptList:
7705080331251906861fbba4a9986efcd04978207beBehdad Esfahbod				# XXX Handle present ScriptList but absent FeatureList
771b76d6ff14a8283f7057847494c3c5c81ff236a6eBehdad Esfahbod				featureMap = {id(v):i for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
772398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod				t.table.ScriptList.mapFeatures(featureMap)
773398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod
774398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO GDEF/Lookup MarkFilteringSets
775398770d51c3ae0e7c267ef2b315beae2b6aa2df8Behdad Esfahbod		# TODO FeatureParams nameIDs
7762642934116cc0e3b873e37051cb57d3b29c0a9b4Behdad Esfahbod
777f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
778f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Logger(object):
779f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
780f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, verbose=False, xml=False, timing=False):
781f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.verbose = verbose
782f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.xml = xml
783f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.timing = timing
784f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = self.start_time = time.time()
785f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
786f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv):
787f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    argv = argv[:]
788f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for v in ['verbose', 'xml', 'timing']:
789f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if "--"+v in argv:
790f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        setattr(self, v, True)
791f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        argv.remove("--"+v)
792f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return argv
793f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
794f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __call__(self, *things):
795f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.verbose:
796f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
797f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print(' '.join(str(x) for x in things))
798f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
799f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def lapse(self, *things):
800f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.timing:
801f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
802f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    new_time = time.time()
803f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print("Took %0.3fs to %s" %(new_time - self.last_time,
804f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod                                 ' '.join(str(x) for x in things)))
805f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = new_time
806f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
807f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def font(self, font, file=sys.stdout):
808f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.xml:
809f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
810f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    from fontTools.misc import xmlWriter
811f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    writer = xmlWriter.XMLWriter(file)
812f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    font.disassembleInstructions = False  # Work around ttLib bug
813f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for tag in font.keys():
814f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.begintag(tag)
815f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
816f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      font[tag].toXML(writer, font)
817f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.endtag(tag)
818f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
819f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
820f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
821f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod__all__ = [
822f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Options',
823f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Merger',
824f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Logger',
825f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'main'
826f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod]
827f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
82845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args):
829f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
830f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	log = Logger()
831f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = log.parse_opts(args)
832f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
833f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	options = Options()
834f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = options.parse_opts(args)
835f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
83645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	if len(args) < 1:
837f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		print("usage: pyftmerge font...", file=sys.stderr)
83845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		sys.exit(1)
839f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
840f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	merger = Merger(options=options, log=log)
841f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	font = merger.merge(args)
84245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	outfile = 'merged.ttf'
84345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	font.save(outfile)
844b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("compile and save font")
845b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod
846b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.last_time = log.start_time
847b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("make one with everything(TOTAL TIME)")
84845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
84945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__":
85045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	main(sys.argv[1:])
851