blob: 5c926da5e3860432e0acb4d86495f526ae355e23 [file] [log] [blame]
Behdad Esfahbod45d2f382013-09-18 20:47:53 -04001# Copyright 2013 Google, Inc. All Rights Reserved.
2#
Roozbeh Pournader47bee9c2013-12-18 00:45:12 -08003# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
Behdad Esfahbod45d2f382013-09-18 20:47:53 -04004
5"""Font merger.
6"""
7
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -05008from __future__ import print_function, division
9from fontTools.misc.py23 import *
10from fontTools import ttLib, cffLib
11from fontTools.ttLib.tables import otTables
12from functools import reduce
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040013import sys
Behdad Esfahbodf2d59822013-09-19 16:16:39 -040014import time
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040015
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040016
17def _add_method(*clazzes):
Behdad Esfahbodc855f3a2013-09-19 20:09:23 -040018 """Returns a decorator function that adds a new method to one or
19 more classes."""
20 def wrapper(method):
21 for clazz in clazzes:
22 assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -050023 assert not hasattr(clazz, method.__name__), \
Behdad Esfahbodc855f3a2013-09-19 20:09:23 -040024 "Oops, class '%s' has method '%s'." % (clazz.__name__,
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -050025 method.__name__)
26 setattr(clazz, method.__name__, method)
Behdad Esfahbodc855f3a2013-09-19 20:09:23 -040027 return None
28 return wrapper
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040029
Roozbeh Pournader47bee9c2013-12-18 00:45:12 -080030# General utility functions for merging values from different fonts
31def assert_equal(lst):
32 first = lst[0]
33 assert all([item == first for item in lst])
34
35def first(lst):
36 return lst[0]
37
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040038
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -040039@_add_method(ttLib.getTableClass('maxp'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040040def merge(self, m):
Roozbeh Pournader47bee9c2013-12-18 00:45:12 -080041 logic = {
42 '*': max,
43 'tableVersion': assert_equal,
44 'numGlyphs': sum,
45 'maxStorage': max, # FIXME: may need to be changed to sum
46 'maxFunctionDefs': sum,
47 'maxInstructionDefs': sum,
48 }
Behdad Esfahbod45d2f382013-09-18 20:47:53 -040049 # TODO When we correctly merge hinting data, update these values:
50 # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
Roozbeh Pournader47bee9c2013-12-18 00:45:12 -080051 m._mergeKeys(self, logic)
Behdad Esfahbod65f19d82013-09-18 21:02:41 -040052 return True
53
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -040054@_add_method(ttLib.getTableClass('head'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040055def merge(self, m):
Behdad Esfahbodf2d59822013-09-19 16:16:39 -040056 # TODO Check that unitsPerEm are the same.
57 # TODO Use bitwise ops for flags, macStyle, fontDirectionHint
58 minMembers = ['xMin', 'yMin']
59 # Negate some members
60 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040061 for table in m.tables:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -040062 setattr(table, key, -getattr(table, key))
63 # Get max over members
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -050064 allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
Behdad Esfahbod6942b222013-09-20 16:57:28 -040065 for key in allKeys:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040066 setattr(self, key, max(getattr(table, key) for table in m.tables))
Behdad Esfahbodf2d59822013-09-19 16:16:39 -040067 # Negate them back
68 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040069 for table in m.tables:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -040070 setattr(table, key, -getattr(table, key))
71 setattr(self, key, -getattr(self, key))
72 return True
73
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -040074@_add_method(ttLib.getTableClass('hhea'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040075def merge(self, m):
Behdad Esfahbod65f19d82013-09-18 21:02:41 -040076 # TODO Check that ascent, descent, slope, etc are the same.
77 minMembers = ['descent', 'minLeftSideBearing', 'minRightSideBearing']
78 # Negate some members
79 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040080 for table in m.tables:
Behdad Esfahbod65f19d82013-09-18 21:02:41 -040081 setattr(table, key, -getattr(table, key))
82 # Get max over members
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -050083 allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
Behdad Esfahbod6942b222013-09-20 16:57:28 -040084 for key in allKeys:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040085 setattr(self, key, max(getattr(table, key) for table in m.tables))
Behdad Esfahbod65f19d82013-09-18 21:02:41 -040086 # Negate them back
87 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040088 for table in m.tables:
Behdad Esfahbod65f19d82013-09-18 21:02:41 -040089 setattr(table, key, -getattr(table, key))
90 setattr(self, key, -getattr(self, key))
91 return True
92
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -040093@_add_method(ttLib.getTableClass('OS/2'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -040094def merge(self, m):
Behdad Esfahbod71294de2013-09-19 19:43:17 -040095 # TODO Check that weight/width/subscript/superscript/etc are the same.
96 # TODO Bitwise ops for UnicodeRange/CodePageRange.
97 # TODO Pretty much all fields generated here have bogus values.
98 # Get max over members
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -050099 allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
Behdad Esfahbod6942b222013-09-20 16:57:28 -0400100 for key in allKeys:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400101 setattr(self, key, max(getattr(table, key) for table in m.tables))
Behdad Esfahbod71294de2013-09-19 19:43:17 -0400102 return True
103
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400104@_add_method(ttLib.getTableClass('post'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400105def merge(self, m):
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400106 # TODO Check that italicAngle, underlinePosition, underlineThickness are the same.
107 minMembers = ['underlinePosition', 'minMemType42', 'minMemType1']
108 # Negate some members
109 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400110 for table in m.tables:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400111 setattr(table, key, -getattr(table, key))
112 # Get max over members
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500113 allKeys = reduce(set.union, (list(vars(table).keys()) for table in m.tables), set())
Behdad Esfahbod6942b222013-09-20 16:57:28 -0400114 if 'mapping' in allKeys:
115 allKeys.remove('mapping')
116 allKeys.remove('extraNames')
117 for key in allKeys:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400118 setattr(self, key, max(getattr(table, key) for table in m.tables))
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400119 # Negate them back
120 for key in minMembers:
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400121 for table in m.tables:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400122 setattr(table, key, -getattr(table, key))
123 setattr(self, key, -getattr(self, key))
124 self.mapping = {}
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400125 for table in m.tables:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400126 if hasattr(table, 'mapping'):
127 self.mapping.update(table.mapping)
128 self.extraNames = []
129 return True
130
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400131@_add_method(ttLib.getTableClass('vmtx'),
132 ttLib.getTableClass('hmtx'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400133def merge(self, m):
Behdad Esfahbod65f19d82013-09-18 21:02:41 -0400134 self.metrics = {}
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400135 for table in m.tables:
Behdad Esfahbod65f19d82013-09-18 21:02:41 -0400136 self.metrics.update(table.metrics)
137 return True
138
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400139@_add_method(ttLib.getTableClass('loca'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400140def merge(self, m):
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400141 return True # Will be computed automatically
142
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400143@_add_method(ttLib.getTableClass('glyf'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400144def merge(self, m):
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400145 self.glyphs = {}
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400146 for table in m.tables:
Behdad Esfahbod43650332013-09-20 16:33:33 -0400147 for g in table.glyphs.values():
148 # Drop hints for now, since we don't remap
149 # functions / CVT values.
150 g.removeHinting()
151 # Expand composite glyphs to load their
152 # composite glyph names.
153 if g.isComposite():
154 g.expand(table)
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400155 self.glyphs.update(table.glyphs)
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400156 return True
157
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400158@_add_method(ttLib.getTableClass('prep'),
159 ttLib.getTableClass('fpgm'),
160 ttLib.getTableClass('cvt '))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400161def merge(self, m):
Behdad Esfahbod60eb8042013-09-20 16:34:58 -0400162 return False # TODO We don't merge hinting data currently.
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400163
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400164@_add_method(ttLib.getTableClass('cmap'))
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400165def merge(self, m):
Behdad Esfahbod71294de2013-09-19 19:43:17 -0400166 # TODO Handle format=14.
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400167 cmapTables = [t for table in m.tables for t in table.tables
Behdad Esfahbod71294de2013-09-19 19:43:17 -0400168 if t.platformID == 3 and t.platEncID in [1, 10]]
169 # TODO Better handle format-4 and format-12 coexisting in same font.
170 # TODO Insert both a format-4 and format-12 if needed.
Behdad Esfahbodbe4ecc72013-09-19 20:37:01 -0400171 module = ttLib.getTableModule('cmap')
Behdad Esfahbod71294de2013-09-19 19:43:17 -0400172 assert all(t.format in [4, 12] for t in cmapTables)
173 format = max(t.format for t in cmapTables)
174 cmapTable = module.cmap_classes[format](format)
175 cmapTable.cmap = {}
176 cmapTable.platformID = 3
177 cmapTable.platEncID = max(t.platEncID for t in cmapTables)
178 cmapTable.language = 0
179 for table in cmapTables:
180 # TODO handle duplicates.
181 cmapTable.cmap.update(table.cmap)
182 self.tableVersion = 0
183 self.tables = [cmapTable]
184 self.numSubTables = len(self.tables)
185 return True
186
Behdad Esfahbodc14ab482013-09-19 21:22:54 -0400187@_add_method(ttLib.getTableClass('GDEF'))
188def merge(self, m):
189 self.table = otTables.GDEF()
Behdad Esfahbod60eb8042013-09-20 16:34:58 -0400190 self.table.Version = 1.0 # TODO version 1.2...
Behdad Esfahbodc14ab482013-09-19 21:22:54 -0400191
192 if any(t.table.LigCaretList for t in m.tables):
193 glyphs = []
194 ligGlyphs = []
195 for table in m.tables:
196 if table.table.LigCaretList:
197 glyphs.extend(table.table.LigCaretList.Coverage.glyphs)
198 ligGlyphs.extend(table.table.LigCaretList.LigGlyph)
199 coverage = otTables.Coverage()
200 coverage.glyphs = glyphs
201 ligCaretList = otTables.LigCaretList()
202 ligCaretList.Coverage = coverage
203 ligCaretList.LigGlyph = ligGlyphs
204 ligCaretList.GlyphCount = len(ligGlyphs)
205 self.table.LigCaretList = ligCaretList
206 else:
207 self.table.LigCaretList = None
208
209 if any(t.table.MarkAttachClassDef for t in m.tables):
210 classDefs = {}
211 for table in m.tables:
212 if table.table.MarkAttachClassDef:
213 classDefs.update(table.table.MarkAttachClassDef.classDefs)
214 self.table.MarkAttachClassDef = otTables.MarkAttachClassDef()
215 self.table.MarkAttachClassDef.classDefs = classDefs
216 else:
217 self.table.MarkAttachClassDef = None
218
219 if any(t.table.GlyphClassDef for t in m.tables):
220 classDefs = {}
221 for table in m.tables:
222 if table.table.GlyphClassDef:
223 classDefs.update(table.table.GlyphClassDef.classDefs)
224 self.table.GlyphClassDef = otTables.GlyphClassDef()
225 self.table.GlyphClassDef.classDefs = classDefs
226 else:
227 self.table.GlyphClassDef = None
228
229 if any(t.table.AttachList for t in m.tables):
230 glyphs = []
231 attachPoints = []
232 for table in m.tables:
233 if table.table.AttachList:
234 glyphs.extend(table.table.AttachList.Coverage.glyphs)
235 attachPoints.extend(table.table.AttachList.AttachPoint)
236 coverage = otTables.Coverage()
237 coverage.glyphs = glyphs
238 attachList = otTables.AttachList()
239 attachList.Coverage = coverage
240 attachList.AttachPoint = attachPoints
241 attachList.GlyphCount = len(attachPoints)
242 self.table.AttachList = attachList
243 else:
244 self.table.AttachList = None
245
246 return True
247
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400248
249class Options(object):
250
251 class UnknownOptionError(Exception):
252 pass
253
254 _drop_tables_default = ['fpgm', 'prep', 'cvt ', 'gasp']
255 drop_tables = _drop_tables_default
256
257 def __init__(self, **kwargs):
258
259 self.set(**kwargs)
260
261 def set(self, **kwargs):
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500262 for k,v in kwargs.items():
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400263 if not hasattr(self, k):
264 raise self.UnknownOptionError("Unknown option '%s'" % k)
265 setattr(self, k, v)
266
267 def parse_opts(self, argv, ignore_unknown=False):
268 ret = []
269 opts = {}
270 for a in argv:
271 orig_a = a
272 if not a.startswith('--'):
273 ret.append(a)
274 continue
275 a = a[2:]
276 i = a.find('=')
277 op = '='
278 if i == -1:
279 if a.startswith("no-"):
280 k = a[3:]
281 v = False
282 else:
283 k = a
284 v = True
285 else:
286 k = a[:i]
287 if k[-1] in "-+":
288 op = k[-1]+'=' # Ops is '-=' or '+=' now.
289 k = k[:-1]
290 v = a[i+1:]
291 k = k.replace('-', '_')
292 if not hasattr(self, k):
293 if ignore_unknown == True or k in ignore_unknown:
294 ret.append(orig_a)
295 continue
296 else:
297 raise self.UnknownOptionError("Unknown option '%s'" % a)
298
299 ov = getattr(self, k)
300 if isinstance(ov, bool):
301 v = bool(v)
302 elif isinstance(ov, int):
303 v = int(v)
304 elif isinstance(ov, list):
305 vv = v.split(',')
306 if vv == ['']:
307 vv = []
308 vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
309 if op == '=':
310 v = vv
311 elif op == '+=':
312 v = ov
313 v.extend(vv)
314 elif op == '-=':
315 v = ov
316 for x in vv:
317 if x in v:
318 v.remove(x)
319 else:
320 assert 0
321
322 opts[k] = v
323 self.set(**opts)
324
325 return ret
326
327
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400328class Merger:
329
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400330 def __init__(self, options=None, log=None):
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400331
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400332 if not log:
333 log = Logger()
334 if not options:
335 options = Options()
336
337 self.options = options
338 self.log = log
339
340 def merge(self, fontfiles):
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400341
342 mega = ttLib.TTFont()
343
344 #
345 # Settle on a mega glyph order.
346 #
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400347 fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400348 glyphOrders = [font.getGlyphOrder() for font in fonts]
349 megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
350 # Reload fonts and set new glyph names on them.
351 # TODO Is it necessary to reload font? I think it is. At least
352 # it's safer, in case tables were loaded to provide glyph names.
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400353 fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
Behdad Esfahbod3235a042013-09-19 20:57:33 -0400354 for font,glyphOrder in zip(fonts, glyphOrders):
355 font.setGlyphOrder(glyphOrder)
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400356 mega.setGlyphOrder(megaGlyphOrder)
357
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500358 allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400359 allTags.remove('GlyphOrder')
360 for tag in allTags:
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400361
362 if tag in self.options.drop_tables:
Behdad Esfahbodb640f742013-09-19 20:12:56 -0400363 self.log("Dropping '%s'." % tag)
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400364 continue
365
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400366 clazz = ttLib.getTableClass(tag)
367
368 if not hasattr(clazz, 'merge'):
Behdad Esfahbodb640f742013-09-19 20:12:56 -0400369 self.log("Don't know how to merge '%s', dropped." % tag)
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400370 continue
371
372 # TODO For now assume all fonts have the same tables.
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400373 self.tables = [font[tag] for font in fonts]
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400374 table = clazz(tag)
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400375 if table.merge (self):
Behdad Esfahbod65f19d82013-09-18 21:02:41 -0400376 mega[tag] = table
Behdad Esfahbodb640f742013-09-19 20:12:56 -0400377 self.log("Merged '%s'." % tag)
Behdad Esfahbod65f19d82013-09-18 21:02:41 -0400378 else:
Behdad Esfahbodb640f742013-09-19 20:12:56 -0400379 self.log("Dropped '%s'. No need to merge explicitly." % tag)
380 self.log.lapse("merge '%s'" % tag)
Behdad Esfahbod0bf4f562013-09-19 20:21:04 -0400381 del self.tables
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400382
383 return mega
384
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400385 def _mergeGlyphOrders(self, glyphOrders):
Behdad Esfahbodc2e27fd2013-09-20 16:25:48 -0400386 """Modifies passed-in glyphOrders to reflect new glyph names.
387 Returns glyphOrder for the merged font."""
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400388 # Simply append font index to the glyph name for now.
Behdad Esfahbodc2e27fd2013-09-20 16:25:48 -0400389 # TODO Even this simplistic numbering can result in conflicts.
390 # But then again, we have to improve this soon anyway.
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400391 mega = []
392 for n,glyphOrder in enumerate(glyphOrders):
393 for i,glyphName in enumerate(glyphOrder):
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500394 glyphName += "#" + repr(n)
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400395 glyphOrder[i] = glyphName
396 mega.append(glyphName)
397 return mega
398
Roozbeh Pournader47bee9c2013-12-18 00:45:12 -0800399 def _mergeKeys(self, return_table, logic):
400 logic['tableTag'] = assert_equal
401 allKeys = set.union(set(), *(vars(table).keys() for table in self.tables))
402 for key in allKeys:
403 merge_logic = logic.get(key, logic['*'])
404 key_value = merge_logic([getattr(table, key) for table in self.tables])
405 setattr(return_table, key, key_value)
406
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400407
408class Logger(object):
409
410 def __init__(self, verbose=False, xml=False, timing=False):
411 self.verbose = verbose
412 self.xml = xml
413 self.timing = timing
414 self.last_time = self.start_time = time.time()
415
416 def parse_opts(self, argv):
417 argv = argv[:]
418 for v in ['verbose', 'xml', 'timing']:
419 if "--"+v in argv:
420 setattr(self, v, True)
421 argv.remove("--"+v)
422 return argv
423
424 def __call__(self, *things):
425 if not self.verbose:
426 return
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500427 print(' '.join(str(x) for x in things))
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400428
429 def lapse(self, *things):
430 if not self.timing:
431 return
432 new_time = time.time()
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500433 print("Took %0.3fs to %s" %(new_time - self.last_time,
434 ' '.join(str(x) for x in things)))
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400435 self.last_time = new_time
436
437 def font(self, font, file=sys.stdout):
438 if not self.xml:
439 return
440 from fontTools.misc import xmlWriter
441 writer = xmlWriter.XMLWriter(file)
442 font.disassembleInstructions = False # Work around ttLib bug
443 for tag in font.keys():
444 writer.begintag(tag)
445 writer.newline()
446 font[tag].toXML(writer, font)
447 writer.endtag(tag)
448 writer.newline()
449
450
451__all__ = [
452 'Options',
453 'Merger',
454 'Logger',
455 'main'
456]
457
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400458def main(args):
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400459
460 log = Logger()
461 args = log.parse_opts(args)
462
463 options = Options()
464 args = options.parse_opts(args)
465
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400466 if len(args) < 1:
Behdad Esfahbodf63e80e2013-12-18 17:14:26 -0500467 print("usage: pyftmerge font...", file=sys.stderr)
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400468 sys.exit(1)
Behdad Esfahbodf2d59822013-09-19 16:16:39 -0400469
470 merger = Merger(options=options, log=log)
471 font = merger.merge(args)
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400472 outfile = 'merged.ttf'
473 font.save(outfile)
Behdad Esfahbodb640f742013-09-19 20:12:56 -0400474 log.lapse("compile and save font")
475
476 log.last_time = log.start_time
477 log.lapse("make one with everything(TOTAL TIME)")
Behdad Esfahbod45d2f382013-09-18 20:47:53 -0400478
479if __name__ == "__main__":
480 main(sys.argv[1:])