merge.py revision 47bee9cfbd47dc22895003cc94ab91f9075ca27f
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
8f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom __future__ import print_function, division
9f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.misc.py23 import *
10f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools import ttLib, cffLib
11f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom fontTools.ttLib.tables import otTables
12f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbodfrom functools import reduce
1345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodimport sys
14f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodimport time
1545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
1645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
1745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef _add_method(*clazzes):
18c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	"""Returns a decorator function that adds a new method to one or
19c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	more classes."""
20c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	def wrapper(method):
21c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod		for clazz in clazzes:
22c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod			assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
23f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod			assert not hasattr(clazz, method.__name__), \
24c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod				"Oops, class '%s' has method '%s'." % (clazz.__name__,
25f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod								       method.__name__)
26f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		setattr(clazz, method.__name__, method)
27c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod		return None
28c855f3ab69f31004b6aaca33ac549d384f1efc54Behdad Esfahbod	return wrapper
2945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
3047bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader# General utility functions for merging values from different fonts
3147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef assert_equal(lst):
3247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	first = lst[0]
3347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	assert all([item == first for item in lst])
3447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
3547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournaderdef first(lst):
3647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	return lst[0]
3747bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
3845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
39be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('maxp'))
400bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
4147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	logic = {
4247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'*': max,
4347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'tableVersion': assert_equal,
4447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'numGlyphs': sum,
4547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'maxStorage': max, # FIXME: may need to be changed to sum
4647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'maxFunctionDefs': sum,
4747bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		'maxInstructionDefs': sum,
4847bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	}
4945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	# TODO When we correctly merge hinting data, update these values:
5045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
5147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	m._mergeKeys(self, logic)
5265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	return True
5365f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
54be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('head'))
550bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
56f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# TODO Check that unitsPerEm are the same.
57f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# TODO Use bitwise ops for flags, macStyle, fontDirectionHint
58f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	minMembers = ['xMin', 'yMin']
59f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Negate some members
60f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	for key in minMembers:
610bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
62f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			setattr(table, key, -getattr(table, key))
63f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Get max over members
64f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod	allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
656942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	for key in allKeys:
660bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		setattr(self, key, max(getattr(table, key) for table in m.tables))
67f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Negate them back
68f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	for key in minMembers:
690bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
70f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			setattr(table, key, -getattr(table, key))
71f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		setattr(self, key, -getattr(self, key))
72f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	return True
73f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
74be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('hhea'))
750bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
7665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	# TODO Check that ascent, descent, slope, etc are the same.
7765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	minMembers = ['descent', 'minLeftSideBearing', 'minRightSideBearing']
7865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	# Negate some members
7965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	for key in minMembers:
800bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
8165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod			setattr(table, key, -getattr(table, key))
8265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	# Get max over members
83f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod	allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
846942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	for key in allKeys:
850bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		setattr(self, key, max(getattr(table, key) for table in m.tables))
8665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	# Negate them back
8765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	for key in minMembers:
880bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
8965f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod			setattr(table, key, -getattr(table, key))
9065f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod		setattr(self, key, -getattr(self, key))
9165f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	return True
9265f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
93be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('OS/2'))
940bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
9571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Check that weight/width/subscript/superscript/etc are the same.
9671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Bitwise ops for UnicodeRange/CodePageRange.
9771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Pretty much all fields generated here have bogus values.
9871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# Get max over members
99f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod	allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
1006942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	for key in allKeys:
1010bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		setattr(self, key, max(getattr(table, key) for table in m.tables))
10271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	return True
10371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
104be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('post'))
1050bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
106f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# TODO Check that italicAngle, underlinePosition, underlineThickness are the same.
107f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	minMembers = ['underlinePosition', 'minMemType42', 'minMemType1']
108f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Negate some members
109f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	for key in minMembers:
1100bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
111f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			setattr(table, key, -getattr(table, key))
112f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Get max over members
113f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod	allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
1146942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	if 'mapping' in allKeys:
1156942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod		allKeys.remove('mapping')
1166942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	allKeys.remove('extraNames')
1176942b22252c4f42c6470ebd1c94fc9e193814a30Behdad Esfahbod	for key in allKeys:
1180bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		setattr(self, key, max(getattr(table, key) for table in m.tables))
119f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	# Negate them back
120f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	for key in minMembers:
1210bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod		for table in m.tables:
122f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			setattr(table, key, -getattr(table, key))
123f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		setattr(self, key, -getattr(self, key))
124f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	self.mapping = {}
1250bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod	for table in m.tables:
126f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if hasattr(table, 'mapping'):
127f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			self.mapping.update(table.mapping)
128f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	self.extraNames = []
129f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	return True
130f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
131be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('vmtx'),
132be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod             ttLib.getTableClass('hmtx'))
1330bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
13465f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	self.metrics = {}
1350bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod	for table in m.tables:
13665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod		self.metrics.update(table.metrics)
13765f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod	return True
13865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod
139be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('loca'))
1400bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
141f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	return True # Will be computed automatically
142f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
143be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('glyf'))
1440bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
145f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	self.glyphs = {}
1460bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod	for table in m.tables:
147436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod		for g in table.glyphs.values():
148436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# Drop hints for now, since we don't remap
149436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# functions / CVT values.
150436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			g.removeHinting()
151436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# Expand composite glyphs to load their
152436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			# composite glyph names.
153436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod			if g.isComposite():
154436503372a7a4aeb529a360e66114ce1b1b6d9ceBehdad Esfahbod				g.expand(table)
155f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.glyphs.update(table.glyphs)
156f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	return True
157f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
158be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('prep'),
159be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod	     ttLib.getTableClass('fpgm'),
160be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod	     ttLib.getTableClass('cvt '))
1610bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
16260eb80455e6b574c5074a9b4a2e7504a283175dcBehdad Esfahbod	return False # TODO We don't merge hinting data currently.
16345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
164be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod@_add_method(ttLib.getTableClass('cmap'))
1650bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahboddef merge(self, m):
16671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Handle format=14.
1670bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod	cmapTables = [t for table in m.tables for t in table.tables
16871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		      if t.platformID == 3 and t.platEncID in [1, 10]]
16971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Better handle format-4 and format-12 coexisting in same font.
17071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	# TODO Insert both a format-4 and format-12 if needed.
171be4ecc7c67917d95b44d16b32d8d72446162f3ddBehdad Esfahbod	module = ttLib.getTableModule('cmap')
17271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	assert all(t.format in [4, 12] for t in cmapTables)
17371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	format = max(t.format for t in cmapTables)
17471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable = module.cmap_classes[format](format)
17571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.cmap = {}
17671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platformID = 3
17771294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.platEncID = max(t.platEncID for t in cmapTables)
17871294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	cmapTable.language = 0
17971294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	for table in cmapTables:
18071294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		# TODO handle duplicates.
18171294def6730c37839f03dee519b319f982587eaBehdad Esfahbod		cmapTable.cmap.update(table.cmap)
18271294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tableVersion = 0
18371294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.tables = [cmapTable]
18471294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	self.numSubTables = len(self.tables)
18571294def6730c37839f03dee519b319f982587eaBehdad Esfahbod	return True
18671294def6730c37839f03dee519b319f982587eaBehdad Esfahbod
187c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod@_add_method(ttLib.getTableClass('GDEF'))
188c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahboddef merge(self, m):
189c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	self.table = otTables.GDEF()
19060eb80455e6b574c5074a9b4a2e7504a283175dcBehdad Esfahbod	self.table.Version = 1.0 # TODO version 1.2...
191c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
192c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	if any(t.table.LigCaretList for t in m.tables):
193c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		glyphs = []
194c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		ligGlyphs = []
195c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		for table in m.tables:
196c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod			if table.table.LigCaretList:
197c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				glyphs.extend(table.table.LigCaretList.Coverage.glyphs)
198c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				ligGlyphs.extend(table.table.LigCaretList.LigGlyph)
199c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		coverage = otTables.Coverage()
200c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		coverage.glyphs = glyphs
201c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		ligCaretList = otTables.LigCaretList()
202c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		ligCaretList.Coverage = coverage
203c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		ligCaretList.LigGlyph = ligGlyphs
204c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		ligCaretList.GlyphCount = len(ligGlyphs)
205c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.LigCaretList = ligCaretList
206c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	else:
207c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.LigCaretList = None
208c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
209c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	if any(t.table.MarkAttachClassDef for t in m.tables):
210c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		classDefs = {}
211c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		for table in m.tables:
212c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod			if table.table.MarkAttachClassDef:
213c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				classDefs.update(table.table.MarkAttachClassDef.classDefs)
214c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.MarkAttachClassDef = otTables.MarkAttachClassDef()
215c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.MarkAttachClassDef.classDefs = classDefs
216c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	else:
217c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.MarkAttachClassDef = None
218c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
219c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	if any(t.table.GlyphClassDef for t in m.tables):
220c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		classDefs = {}
221c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		for table in m.tables:
222c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod			if table.table.GlyphClassDef:
223c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				classDefs.update(table.table.GlyphClassDef.classDefs)
224c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.GlyphClassDef = otTables.GlyphClassDef()
225c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.GlyphClassDef.classDefs = classDefs
226c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	else:
227c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.GlyphClassDef = None
228c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
229c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	if any(t.table.AttachList for t in m.tables):
230c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		glyphs = []
231c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		attachPoints = []
232c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		for table in m.tables:
233c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod			if table.table.AttachList:
234c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				glyphs.extend(table.table.AttachList.Coverage.glyphs)
235c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod				attachPoints.extend(table.table.AttachList.AttachPoint)
236c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		coverage = otTables.Coverage()
237c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		coverage.glyphs = glyphs
238c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		attachList = otTables.AttachList()
239c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		attachList.Coverage = coverage
240c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		attachList.AttachPoint = attachPoints
241c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		attachList.GlyphCount = len(attachPoints)
242c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.AttachList = attachList
243c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	else:
244c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod		self.table.AttachList = None
245c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
246c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod	return True
247c14ab48ae84ce77bbcc0942baaa18f8099edb447Behdad Esfahbod
248f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
249f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Options(object):
250f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
251f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  class UnknownOptionError(Exception):
252f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    pass
253f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
254f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  _drop_tables_default = ['fpgm', 'prep', 'cvt ', 'gasp']
255f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  drop_tables = _drop_tables_default
256f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
257f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, **kwargs):
258f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
259f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**kwargs)
260f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
261f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def set(self, **kwargs):
262f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    for k,v in kwargs.items():
263f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
264f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        raise self.UnknownOptionError("Unknown option '%s'" % k)
265f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      setattr(self, k, v)
266f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
267f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv, ignore_unknown=False):
268f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    ret = []
269f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    opts = {}
270f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for a in argv:
271f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      orig_a = a
272f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not a.startswith('--'):
273f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        ret.append(a)
274f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        continue
275f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      a = a[2:]
276f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      i = a.find('=')
277f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      op = '='
278f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if i == -1:
279f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if a.startswith("no-"):
280f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a[3:]
281f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = False
282f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
283f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = a
284f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = True
285f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      else:
286f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        k = a[:i]
287f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if k[-1] in "-+":
288f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          op = k[-1]+'='  # Ops is '-=' or '+=' now.
289f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          k = k[:-1]
290f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = a[i+1:]
291f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      k = k.replace('-', '_')
292f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if not hasattr(self, k):
293f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if ignore_unknown == True or k in ignore_unknown:
294f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          ret.append(orig_a)
295f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          continue
296f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
297f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          raise self.UnknownOptionError("Unknown option '%s'" % a)
298f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
299f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      ov = getattr(self, k)
300f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if isinstance(ov, bool):
301f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = bool(v)
302f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, int):
303f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        v = int(v)
304f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      elif isinstance(ov, list):
305f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = v.split(',')
306f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if vv == ['']:
307f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          vv = []
308f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
309f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        if op == '=':
310f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = vv
311f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '+=':
312f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
313f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v.extend(vv)
314f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        elif op == '-=':
315f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          v = ov
316f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          for x in vv:
317f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod            if x in v:
318f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod              v.remove(x)
319f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        else:
320f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod          assert 0
321f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
322f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      opts[k] = v
323f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.set(**opts)
324f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
325f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return ret
326f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
327f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
32845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodclass Merger:
32945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
330f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def __init__(self, options=None, log=None):
331f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
332f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not log:
333f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			log = Logger()
334f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		if not options:
335f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			options = Options()
336f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
337f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.options = options
338f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		self.log = log
33945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
340f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	def merge(self, fontfiles):
34145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
34245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = ttLib.TTFont()
34345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
34445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
34545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Settle on a mega glyph order.
34645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		#
347f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
34845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		glyphOrders = [font.getGlyphOrder() for font in fonts]
34945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
35045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Reload fonts and set new glyph names on them.
35145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# TODO Is it necessary to reload font?  I think it is.  At least
35245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# it's safer, in case tables were loaded to provide glyph names.
353f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
3543235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod		for font,glyphOrder in zip(fonts, glyphOrders):
3553235a04ea938845d656a68c242591d37148ea353Behdad Esfahbod			font.setGlyphOrder(glyphOrder)
35645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega.setGlyphOrder(megaGlyphOrder)
35745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
358f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
35945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		allTags.remove('GlyphOrder')
36045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for tag in allTags:
361f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
362f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod			if tag in self.options.drop_tables:
363b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Dropping '%s'." % tag)
364f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod				continue
365f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
36645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			clazz = ttLib.getTableClass(tag)
36745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
36845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			if not hasattr(clazz, 'merge'):
369b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Don't know how to merge '%s', dropped." % tag)
37045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				continue
37145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
37245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			# TODO For now assume all fonts have the same tables.
3730bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod			self.tables = [font[tag] for font in fonts]
37445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			table = clazz(tag)
3750bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod			if table.merge (self):
37665f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod				mega[tag] = table
377b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Merged '%s'." % tag)
37865f19d8440f5fa5d94d1f04593f16cd9f21f176bBehdad Esfahbod			else:
379b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod				self.log("Dropped '%s'.  No need to merge explicitly." % tag)
380b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod			self.log.lapse("merge '%s'" % tag)
3810bf4f561da7972ab90f6bee87fa30923e201adf5Behdad Esfahbod			del self.tables
38245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
38345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
38445d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
38545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	def _mergeGlyphOrders(self, glyphOrders):
386c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		"""Modifies passed-in glyphOrders to reflect new glyph names.
387c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		Returns glyphOrder for the merged font."""
38845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		# Simply append font index to the glyph name for now.
389c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# TODO Even this simplistic numbering can result in conflicts.
390c2e27fd88f54fcc081049dabce59889cdc87450eBehdad Esfahbod		# But then again, we have to improve this soon anyway.
39145d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		mega = []
39245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		for n,glyphOrder in enumerate(glyphOrders):
39345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod			for i,glyphName in enumerate(glyphOrder):
394f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod				glyphName += "#" + repr(n)
39545d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				glyphOrder[i] = glyphName
39645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod				mega.append(glyphName)
39745d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		return mega
39845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
39947bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader	def _mergeKeys(self, return_table, logic):
40047bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		logic['tableTag'] = assert_equal
40147bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		allKeys = set.union(set(), *(vars(table).keys() for table in self.tables))
40247bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader		for key in allKeys:
40347bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader			merge_logic = logic.get(key, logic['*'])
40447bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader			key_value = merge_logic([getattr(table, key) for table in self.tables])
40547bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader			setattr(return_table, key, key_value)
40647bee9cfbd47dc22895003cc94ab91f9075ca27fRoozbeh Pournader
407f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
408f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbodclass Logger(object):
409f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
410f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __init__(self, verbose=False, xml=False, timing=False):
411f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.verbose = verbose
412f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.xml = xml
413f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.timing = timing
414f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = self.start_time = time.time()
415f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
416f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def parse_opts(self, argv):
417f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    argv = argv[:]
418f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for v in ['verbose', 'xml', 'timing']:
419f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      if "--"+v in argv:
420f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        setattr(self, v, True)
421f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod        argv.remove("--"+v)
422f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    return argv
423f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
424f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def __call__(self, *things):
425f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.verbose:
426f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
427f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print(' '.join(str(x) for x in things))
428f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
429f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def lapse(self, *things):
430f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.timing:
431f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
432f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    new_time = time.time()
433f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod    print("Took %0.3fs to %s" %(new_time - self.last_time,
434f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod                                 ' '.join(str(x) for x in things)))
435f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    self.last_time = new_time
436f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
437f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  def font(self, font, file=sys.stdout):
438f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    if not self.xml:
439f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      return
440f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    from fontTools.misc import xmlWriter
441f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    writer = xmlWriter.XMLWriter(file)
442f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    font.disassembleInstructions = False  # Work around ttLib bug
443f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod    for tag in font.keys():
444f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.begintag(tag)
445f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
446f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      font[tag].toXML(writer, font)
447f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.endtag(tag)
448f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod      writer.newline()
449f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
450f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
451f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod__all__ = [
452f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Options',
453f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Merger',
454f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'Logger',
455f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod  'main'
456f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod]
457f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
45845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahboddef main(args):
459f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
460f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	log = Logger()
461f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = log.parse_opts(args)
462f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
463f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	options = Options()
464f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	args = options.parse_opts(args)
465f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
46645d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	if len(args) < 1:
467f63e80e3fc4ef5406109f1fbe2746e26927fdebfBehdad Esfahbod		print("usage: pyftmerge font...", file=sys.stderr)
46845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod		sys.exit(1)
469f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod
470f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	merger = Merger(options=options, log=log)
471f2d5982826530296fd7c8f9e2d2a4dc3e070934dBehdad Esfahbod	font = merger.merge(args)
47245d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	outfile = 'merged.ttf'
47345d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	font.save(outfile)
474b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("compile and save font")
475b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod
476b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.last_time = log.start_time
477b640f7435c3d29c57310b1a5f7d8e5b538a4c1e3Behdad Esfahbod	log.lapse("make one with everything(TOTAL TIME)")
47845d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod
47945d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbodif __name__ == "__main__":
48045d2f38aa51169f400062f2153a74277b9d4d04eBehdad Esfahbod	main(sys.argv[1:])
481