subset.py revision 7adbdd624289d2143149c490fef8d9ede32629f6
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# Copyright 2013 Google, Inc. All Rights Reserved.
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# Licensed under the Apache License, Version 2.0(the "License");
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# you may not use this file except in compliance with the License.
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com# You may obtain a copy of the License at
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#
7e4fafb146e85cdfcf9d5418597b6818aa0754adatfarina@chromium.org#     http://www.apache.org/licenses/LICENSE-2.0
8bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com#
9a4e13c85b23fe7530ae89a84ef671ebd5e451e80bsalomon@google.com# Unless required by applicable law or agreed to in writing, software
10170bd792e17469769d145b7dc15dea6cd01b7966bsalomon@google.com# distributed under the License is distributed on an "AS IS" BASIS,
11a4e13c85b23fe7530ae89a84ef671ebd5e451e80bsalomon@google.com# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com# See the License for the specific language governing permissions and
131e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org# limitations under the License.
1451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com#
151e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org# Google Author(s): Behdad Esfahbod
1651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
1780214e26c57c5fea954006400852e8999e201923robertphillips@google.com"""Python OpenType Layout Subsetter.
181e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
191e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgLater grown into full OpenType subsetter, supporting all standard tables.
20d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com"""
211e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
2280214e26c57c5fea954006400852e8999e201923robertphillips@google.comimport sys
2380214e26c57c5fea954006400852e8999e201923robertphillips@google.comimport struct
241e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport time
251e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport array
2680214e26c57c5fea954006400852e8999e201923robertphillips@google.com
2780214e26c57c5fea954006400852e8999e201923robertphillips@google.comimport fontTools.ttLib
281e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport fontTools.ttLib.tables
291e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport fontTools.ttLib.tables.otTables
301e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport fontTools.cffLib
311e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgimport fontTools.misc.psCharStrings
321e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
33d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com
341e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef _add_method(*clazzes):
351e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  """Returns a decorator function that adds a new method to one or
3680214e26c57c5fea954006400852e8999e201923robertphillips@google.com  more classes."""
3780214e26c57c5fea954006400852e8999e201923robertphillips@google.com  def wrapper(method):
381e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org    for clazz in clazzes:
39d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com      assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
401e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org      assert not hasattr(clazz, method.func_name), \
41d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com          "Oops, class '%s' has method '%s'." % (clazz.__name__,
421e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org                                                 method.func_name)
431e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org      setattr(clazz, method.func_name, method)
4480214e26c57c5fea954006400852e8999e201923robertphillips@google.com    return None
4580214e26c57c5fea954006400852e8999e201923robertphillips@google.com  return wrapper
461e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
47d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.comdef _uniq_sort(l):
481e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return sorted(set(l))
491e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
501e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef _set_update(s, *others):
511e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  # Jython's set.update only takes one other argument.
521e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  # Emulate real set.update...
531e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  for other in others:
541e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org    s.update(other)
5580214e26c57c5fea954006400852e8999e201923robertphillips@google.com
561e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
571e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org@_add_method(fontTools.ttLib.tables.otTables.Coverage)
581e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef intersect(self, glyphs):
591e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Returns ascending list of matching coverage values."
6080214e26c57c5fea954006400852e8999e201923robertphillips@google.com  return [i for i,g in enumerate(self.glyphs) if g in glyphs]
611e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
62d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com@_add_method(fontTools.ttLib.tables.otTables.Coverage)
631e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef intersect_glyphs(self, glyphs):
641e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Returns set of intersecting glyphs."
651e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return set(g for g in self.glyphs if g in glyphs)
661e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
6780214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.Coverage)
681e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef subset(self, glyphs):
6980214e26c57c5fea954006400852e8999e201923robertphillips@google.com  "Returns ascending list of remaining coverage values."
701e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  indices = self.intersect(glyphs)
71d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com  self.glyphs = [g for g in self.glyphs if g in glyphs]
721e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return indices
731e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
74e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.Coverage)
751e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef remap(self, coverage_map):
761e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Remaps coverage."
771e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  self.glyphs = [self.glyphs[i] for i in coverage_map]
781e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
79d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
80e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.orgdef intersect(self, glyphs):
811e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Returns ascending list of matching class values."
821e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return _uniq_sort(
831e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org     ([0] if any(g not in self.classDefs for g in glyphs) else []) +
8480214e26c57c5fea954006400852e8999e201923robertphillips@google.com      [v for g,v in self.classDefs.iteritems() if g in glyphs])
851e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
8680214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
8780214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef intersect_class(self, glyphs, klass):
881e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Returns set of glyphs matching class."
89d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com  if klass == 0:
901e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org    return set(g for g in glyphs if g not in self.classDefs)
911e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return set(g for g,v in self.classDefs.iteritems()
921e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org              if v == klass and g in glyphs)
931e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
9480214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
9580214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef subset(self, glyphs, remap=False):
961e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  "Returns ascending list of remaining classes."
9780214e26c57c5fea954006400852e8999e201923robertphillips@google.com  self.classDefs = dict((g,v) for g,v in self.classDefs.iteritems() if g in glyphs)
981e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  # Note: while class 0 has the special meaning of "not matched",
991e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  # if no glyph will ever /not match/, we can optimize class 0 out too.
10080214e26c57c5fea954006400852e8999e201923robertphillips@google.com  indices = _uniq_sort(
1011e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org     ([0] if any(g not in self.classDefs for g in glyphs) else []) +
10280214e26c57c5fea954006400852e8999e201923robertphillips@google.com      self.classDefs.values())
1031e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  if remap:
1041e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org    self.remap(indices)
1051e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org  return indices
1061e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org
10780214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
1081e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef remap(self, class_map):
10980214e26c57c5fea954006400852e8999e201923robertphillips@google.com  "Remaps classes."
11080214e26c57c5fea954006400852e8999e201923robertphillips@google.com  self.classDefs = dict((g,class_map.index(v))
1111e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org                         for g,v in self.classDefs.iteritems())
112d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com
1131e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst)
1141e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.orgdef closure_glyphs(self, s, cur_glyphs=None):
115bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
116bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  if self.Format in [1, 2]:
117bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    s.glyphs.update(v for g,v in self.mapping.iteritems() if g in cur_glyphs)
11880214e26c57c5fea954006400852e8999e201923robertphillips@google.com  else:
119bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    assert 0, "unknown format: %s" % self.Format
120bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com
121bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com@_add_method(fontTools.ttLib.tables.otTables.SingleSubst)
122bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.comdef subset_glyphs(self, s):
123bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  if self.Format in [1, 2]:
124bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    self.mapping = dict((g,v) for g,v in self.mapping.iteritems()
125bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com                        if g in s.glyphs and v in s.glyphs)
12608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    return bool(self.mapping)
12708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  else:
12880214e26c57c5fea954006400852e8999e201923robertphillips@google.com    assert 0, "unknown format: %s" % self.Format
12980214e26c57c5fea954006400852e8999e201923robertphillips@google.com
13080214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst)
13180214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef closure_glyphs(self, s, cur_glyphs=None):
13280214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
13380214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if self.Format == 1:
13480214e26c57c5fea954006400852e8999e201923robertphillips@google.com    indices = self.Coverage.intersect(cur_glyphs)
13580214e26c57c5fea954006400852e8999e201923robertphillips@google.com    _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
13680214e26c57c5fea954006400852e8999e201923robertphillips@google.com  else:
13780214e26c57c5fea954006400852e8999e201923robertphillips@google.com    assert 0, "unknown format: %s" % self.Format
13880214e26c57c5fea954006400852e8999e201923robertphillips@google.com
13980214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst)
14080214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef subset_glyphs(self, s):
14180214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if self.Format == 1:
14280214e26c57c5fea954006400852e8999e201923robertphillips@google.com    indices = self.Coverage.subset(s.glyphs)
14380214e26c57c5fea954006400852e8999e201923robertphillips@google.com    self.Sequence = [self.Sequence[i] for i in indices]
14480214e26c57c5fea954006400852e8999e201923robertphillips@google.com    # Now drop rules generating glyphs we don't want
14580214e26c57c5fea954006400852e8999e201923robertphillips@google.com    indices = [i for i,seq in enumerate(self.Sequence)
14680214e26c57c5fea954006400852e8999e201923robertphillips@google.com         if all(sub in s.glyphs for sub in seq.Substitute)]
1472880df2609eba09b555ca37be04b6ad89290c765Tom Hudson    self.Sequence = [self.Sequence[i] for i in indices]
14880214e26c57c5fea954006400852e8999e201923robertphillips@google.com    self.Coverage.remap(indices)
14980214e26c57c5fea954006400852e8999e201923robertphillips@google.com    self.SequenceCount = len(self.Sequence)
15080214e26c57c5fea954006400852e8999e201923robertphillips@google.com    return bool(self.SequenceCount)
15180214e26c57c5fea954006400852e8999e201923robertphillips@google.com  else:
1528182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    assert 0, "unknown format: %s" % self.Format
1538182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
1548182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.AlternateSubst)
15580214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef closure_glyphs(self, s, cur_glyphs=None):
15680214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
15780214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if self.Format == 1:
15880214e26c57c5fea954006400852e8999e201923robertphillips@google.com    _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.iteritems()
15980214e26c57c5fea954006400852e8999e201923robertphillips@google.com                            if g in cur_glyphs))
16080214e26c57c5fea954006400852e8999e201923robertphillips@google.com  else:
16180214e26c57c5fea954006400852e8999e201923robertphillips@google.com    assert 0, "unknown format: %s" % self.Format
1622880df2609eba09b555ca37be04b6ad89290c765Tom Hudson
16380214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.AlternateSubst)
16480214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef subset_glyphs(self, s):
16580214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if self.Format == 1:
16680214e26c57c5fea954006400852e8999e201923robertphillips@google.com    self.alternates = dict((g,vlist)
1678182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                           for g,vlist in self.alternates.iteritems()
1688182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                           if g in s.glyphs and
1698182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                              all(v in s.glyphs for v in vlist))
17080214e26c57c5fea954006400852e8999e201923robertphillips@google.com    return bool(self.alternates)
17180214e26c57c5fea954006400852e8999e201923robertphillips@google.com  else:
17280214e26c57c5fea954006400852e8999e201923robertphillips@google.com    assert 0, "unknown format: %s" % self.Format
17380214e26c57c5fea954006400852e8999e201923robertphillips@google.com
17480214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.LigatureSubst)
17580214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef closure_glyphs(self, s, cur_glyphs=None):
17680214e26c57c5fea954006400852e8999e201923robertphillips@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
1772880df2609eba09b555ca37be04b6ad89290c765Tom Hudson  if self.Format == 1:
17880214e26c57c5fea954006400852e8999e201923robertphillips@google.com    _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
17980214e26c57c5fea954006400852e8999e201923robertphillips@google.com                             if all(c in s.glyphs for c in seq.Component)]
18080214e26c57c5fea954006400852e8999e201923robertphillips@google.com                            for g,seqs in self.ligatures.iteritems()
1818182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                            if g in cur_glyphs))
1828182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  else:
1838182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    assert 0, "unknown format: %s" % self.Format
18480214e26c57c5fea954006400852e8999e201923robertphillips@google.com
18580214e26c57c5fea954006400852e8999e201923robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.LigatureSubst)
18680214e26c57c5fea954006400852e8999e201923robertphillips@google.comdef subset_glyphs(self, s):
18708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if self.Format == 1:
188e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems()
189607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                          if g in s.glyphs)
190607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.ligatures = dict((g,[seq for seq in seqs
191607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                              if seq.LigGlyph in s.glyphs and
192607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                                 all(c in s.glyphs for c in seq.Component)])
193607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                           for g,seqs in self.ligatures.iteritems())
194607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems() if v)
195607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    return bool(self.ligatures)
196607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  else:
197607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
198607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com
199607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
200607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef closure_glyphs(self, s, cur_glyphs=None):
201607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
202607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if self.Format == 1:
203607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    indices = self.Coverage.intersect(cur_glyphs)
204607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    if(not indices or
205607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com        not all(c.intersect(s.glyphs)
206607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                 for c in self.LookAheadCoverage + self.BacktrackCoverage)):
207607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com      return
208607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    s.glyphs.update(self.Substitute[i] for i in indices)
209607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  else:
210607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
211607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com
212607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
213607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef subset_glyphs(self, s):
214607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if self.Format == 1:
215607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    indices = self.Coverage.subset(s.glyphs)
216607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.Substitute = [self.Substitute[i] for i in indices]
217607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    # Now drop rules generating glyphs we don't want
218607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    indices = [i for i,sub in enumerate(self.Substitute)
219607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com         if sub in s.glyphs]
220607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.Substitute = [self.Substitute[i] for i in indices]
221607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.Coverage.remap(indices)
222607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.GlyphCount = len(self.Substitute)
223607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    return bool(self.GlyphCount and
224607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                 all(c.subset(s.glyphs)
225607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com                      for c in self.LookAheadCoverage+self.BacktrackCoverage))
226607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  else:
227607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
228607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com
229607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.SinglePos)
230607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef subset_glyphs(self, s):
231607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if self.Format == 1:
232607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    return len(self.Coverage.subset(s.glyphs))
233e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  elif self.Format == 2:
234e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    indices = self.Coverage.subset(s.glyphs)
235e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.Value = [self.Value[i] for i in indices]
236607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.ValueCount = len(self.Value)
237e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    return bool(self.ValueCount)
238e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  else:
239e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    assert 0, "unknown format: %s" % self.Format
240e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
241607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.SinglePos)
242607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef prune_post_subset(self, options):
2437b11289b4e4d117bbcee6d2460b057d0fcf6e437robertphillips@google.com  if not options.hinting:
2444c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com    # Drop device tables
245607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.ValueFormat &= ~0x00F0
246607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  return True
247e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
2484c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.PairPos)
249607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef subset_glyphs(self, s):
250607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if self.Format == 1:
251607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    indices = self.Coverage.subset(s.glyphs)
252607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.PairSet = [self.PairSet[i] for i in indices]
253607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    for p in self.PairSet:
254607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com      p.PairValueRecord = [r for r in p.PairValueRecord
255e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org                           if r.SecondGlyph in s.glyphs]
256607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com      p.PairValueCount = len(p.PairValueRecord)
257e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.PairSet = [p for p in self.PairSet if p.PairValueCount]
258607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    self.PairSetCount = len(self.PairSet)
259607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    return bool(self.PairSetCount)
260e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  elif self.Format == 2:
261e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    class1_map = self.ClassDef1.subset(s.glyphs, remap=True)
262e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    class2_map = self.ClassDef2.subset(s.glyphs, remap=True)
263e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.Class1Record = [self.Class1Record[i] for i in class1_map]
264e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    for c in self.Class1Record:
265e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org      c.Class2Record = [c.Class2Record[i] for i in class2_map]
266e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.Class1Count = len(class1_map)
267e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    self.Class2Count = len(class2_map)
268e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    return bool(self.Class1Count and
269e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org                 self.Class2Count and
270e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org                 self.Coverage.subset(s.glyphs))
271e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  else:
272e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    assert 0, "unknown format: %s" % self.Format
273e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
274e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.PairPos)
275e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.orgdef prune_post_subset(self, options):
2764c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com  if not options.hinting:
277607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    # Drop device tables
278cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.ValueFormat1 &= ~0x00F0
279d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    self.ValueFormat2 &= ~0x00F0
280cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com  return True
2817b11289b4e4d117bbcee6d2460b057d0fcf6e437robertphillips@google.com
2824c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.CursivePos)
2834c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.comdef subset_glyphs(self, s):
284e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  if self.Format == 1:
285d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com    indices = self.Coverage.subset(s.glyphs)
2864c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com    self.EntryExitRecord = [self.EntryExitRecord[i] for i in indices]
2874c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com    self.EntryExitCount = len(self.EntryExitRecord)
2884c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com    return bool(self.EntryExitCount)
2894c2a2f7c5e8ec77771153f94c454adf21fd33805robertphillips@google.com  else:
290607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
291607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com
2927b11289b4e4d117bbcee6d2460b057d0fcf6e437robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.Anchor)
293607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef prune_hints(self):
294607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  # Drop device tables / contour anchor point
295607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  self.Format = 1
296607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com
297607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.CursivePos)
298607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.comdef prune_post_subset(self, options):
299607fe077c893fdb230e29631be096de614a14e2arobertphillips@google.com  if not options.hinting:
300cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    for rec in self.EntryExitRecord:
301cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      if rec.EntryAnchor: rec.EntryAnchor.prune_hints()
302d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org      if rec.ExitAnchor: rec.ExitAnchor.prune_hints()
303d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return True
304d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
305d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.MarkBasePos)
306d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
307d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if self.Format == 1:
308cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    mark_indices = self.MarkCoverage.subset(s.glyphs)
309cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
310cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                                 for i in mark_indices]
311cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
312cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    base_indices = self.BaseCoverage.subset(s.glyphs)
313cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.BaseArray.BaseRecord = [self.BaseArray.BaseRecord[i]
314cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                                 for i in base_indices]
315cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.BaseArray.BaseCount = len(self.BaseArray.BaseRecord)
316cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    # Prune empty classes
317cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
318cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.ClassCount = len(class_indices)
319d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    for m in self.MarkArray.MarkRecord:
320cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      m.Class = class_indices.index(m.Class)
321cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    for b in self.BaseArray.BaseRecord:
322cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      b.BaseAnchor = [b.BaseAnchor[i] for i in class_indices]
323cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    return bool(self.ClassCount and
324cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                 self.MarkArray.MarkCount and
325cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                 self.BaseArray.BaseCount)
326cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com  else:
327cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
328cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com
329cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MarkBasePos)
330cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.comdef prune_post_subset(self, options):
331cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    if not options.hinting:
332cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      for m in self.MarkArray.MarkRecord:
333cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com        m.MarkAnchor.prune_hints()
334cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      for b in self.BaseArray.BaseRecord:
335cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com        for a in b.BaseAnchor:
336cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com          a.prune_hints()
337cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    return True
338d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
339cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MarkLigPos)
340cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.comdef subset_glyphs(self, s):
341cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com  if self.Format == 1:
342cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    mark_indices = self.MarkCoverage.subset(s.glyphs)
343cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
344cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                                 for i in mark_indices]
345cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
346cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    ligature_indices = self.LigatureCoverage.subset(s.glyphs)
347cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.LigatureArray.LigatureAttach = [self.LigatureArray.LigatureAttach[i]
348d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                         for i in ligature_indices]
349cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.LigatureArray.LigatureCount = len(self.LigatureArray.LigatureAttach)
350cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    # Prune empty classes
351cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
352cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.ClassCount = len(class_indices)
353cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    for m in self.MarkArray.MarkRecord:
354cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      m.Class = class_indices.index(m.Class)
355cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    for l in self.LigatureArray.LigatureAttach:
356cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      for c in l.ComponentRecord:
357cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com        c.LigatureAnchor = [c.LigatureAnchor[i] for i in class_indices]
358cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    return bool(self.ClassCount and
359cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                 self.MarkArray.MarkCount and
360cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                 self.LigatureArray.LigatureCount)
361d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  else:
362cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    assert 0, "unknown format: %s" % self.Format
363cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com
364cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MarkLigPos)
365cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.comdef prune_post_subset(self, options):
366cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    if not options.hinting:
367cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      for m in self.MarkArray.MarkRecord:
368cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com        m.MarkAnchor.prune_hints()
369cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      for l in self.LigatureArray.LigatureAttach:
370cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com        for c in l.ComponentRecord:
371cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com          for a in c.LigatureAnchor:
372cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com            a.prune_hints()
373d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    return True
374cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com
375cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.MarkMarkPos)
376cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.comdef subset_glyphs(self, s):
377cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com  if self.Format == 1:
378d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    mark1_indices = self.Mark1Coverage.subset(s.glyphs)
379cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.Mark1Array.MarkRecord = [self.Mark1Array.MarkRecord[i]
380cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com                                  for i in mark1_indices]
381cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com    self.Mark1Array.MarkCount = len(self.Mark1Array.MarkRecord)
382100abf49e10544bc4f436bf1f38e6929779621f4bsalomon@google.com    mark2_indices = self.Mark2Coverage.subset(s.glyphs)
38308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    self.Mark2Array.Mark2Record = [self.Mark2Array.Mark2Record[i]
38408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                                   for i in mark2_indices]
38508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    self.Mark2Array.MarkCount = len(self.Mark2Array.Mark2Record)
3862880df2609eba09b555ca37be04b6ad89290c765Tom Hudson    # Prune empty classes
38708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    class_indices = _uniq_sort(v.Class for v in self.Mark1Array.MarkRecord)
38808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    self.ClassCount = len(class_indices)
3898182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    for m in self.Mark1Array.MarkRecord:
39008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      m.Class = class_indices.index(m.Class)
39108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    for b in self.Mark2Array.Mark2Record:
39208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      b.Mark2Anchor = [b.Mark2Anchor[i] for i in class_indices]
39308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    return bool(self.ClassCount and
39408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                 self.Mark1Array.MarkCount and
39508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                 self.Mark2Array.MarkCount)
396edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org  else:
397edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org    assert 0, "unknown format: %s" % self.Format
398edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org
399edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.MarkMarkPos)
400edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.orgdef prune_post_subset(self, options):
401edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org    if not options.hinting:
402edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org      # Drop device tables or contour anchor point
403edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org      for m in self.Mark1Array.MarkRecord:
404edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org        m.MarkAnchor.prune_hints()
405edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org      for b in self.Mark2Array.Mark2Record:
406edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org        for m in rec.Mark2Anchor:
407edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org          m.prune_hints()
408edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org    return True
409edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org
410edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
411edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org             fontTools.ttLib.tables.otTables.MultipleSubst,
412edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org             fontTools.ttLib.tables.otTables.AlternateSubst,
4136fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.LigatureSubst,
4146fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
4156fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.SinglePos,
4166fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.PairPos,
4176fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.CursivePos,
4186fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkBasePos,
4196fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkLigPos,
4206fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkMarkPos)
4216fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef subset_lookups(self, lookup_indices):
4226fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  pass
4236fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4246fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
4256fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MultipleSubst,
4266fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.AlternateSubst,
4276fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.LigatureSubst,
4286fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
4296fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.SinglePos,
4306fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.PairPos,
4316fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.CursivePos,
4326fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkBasePos,
4336fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkLigPos,
4346fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkMarkPos)
4356fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef collect_lookups(self):
4366fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  return []
4376fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4386fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
4396fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MultipleSubst,
4406fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.AlternateSubst,
4416fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.LigatureSubst,
4426fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextSubst,
4436fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
4446fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
4456fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.SinglePos,
4466fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.PairPos,
4476fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.CursivePos,
4486fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkBasePos,
4496fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkLigPos,
4506fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MarkMarkPos,
4516fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
4526fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
4536fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef prune_pre_subset(self, options):
4546fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  return True
4556fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4566fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
4576fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.MultipleSubst,
4586fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.AlternateSubst,
4596fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.LigatureSubst,
4606fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
4616fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextSubst,
4626fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
4636fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
4646fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
4656fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef prune_post_subset(self, options):
4666fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  return True
4676fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4686fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
4696fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.AlternateSubst,
4706fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
4716fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef may_have_non_1to1(self):
4726fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  return False
4736fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4746fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst,
4756fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.LigatureSubst,
4766fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextSubst,
4776fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst)
4786fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef may_have_non_1to1(self):
4796fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  return True
4806fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4816fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
4826fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
4836fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
4846fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
4856fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.orgdef __classify_context(self):
4866fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
4876fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org  class ContextHelper(object):
4886fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org    def __init__(self, klass, Format):
4896fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      if klass.__name__.endswith('Subst'):
4906fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Typ = 'Sub'
4916fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Type = 'Subst'
4926fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      else:
4936fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Typ = 'Pos'
4946fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Type = 'Pos'
4956fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      if klass.__name__.startswith('Chain'):
4966fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Chain = 'Chain'
4976fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      else:
4986fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Chain = ''
4996fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      ChainTyp = Chain+Typ
5006fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
5016fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      self.Typ = Typ
5026fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      self.Type = Type
5036fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      self.Chain = Chain
5046fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      self.ChainTyp = ChainTyp
5056fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
5066fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      self.LookupRecord = Type+'LookupRecord'
5076fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org
5086fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      if Format == 1:
5096fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        Coverage = lambda r: r.Coverage
5106fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        ChainCoverage = lambda r: r.Coverage
5116fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        ContextData = lambda r:(None,)
5126fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        ChainContextData = lambda r:(None, None, None)
5136fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org        RuleData = lambda r:(r.Input,)
51408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
51508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        SetRuleData = None
51608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainSetRuleData = None
51708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      elif Format == 2:
51808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        Coverage = lambda r: r.Coverage
51908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainCoverage = lambda r: r.Coverage
52008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ContextData = lambda r:(r.ClassDef,)
52108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainContextData = lambda r:(r.LookAheadClassDef,
52208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                                      r.InputClassDef,
52308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                                      r.BacktrackClassDef)
52408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        RuleData = lambda r:(r.Class,)
52508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainRuleData = lambda r:(r.LookAhead, r.Input, r.Backtrack)
52608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        def SetRuleData(r, d):(r.Class,) = d
52708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        def ChainSetRuleData(r, d):(r.LookAhead, r.Input, r.Backtrack) = d
52808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      elif Format == 3:
52908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        Coverage = lambda r: r.Coverage[0]
53008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainCoverage = lambda r: r.InputCoverage[0]
53108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ContextData = None
53208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainContextData = None
53308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        RuleData = lambda r: r.Coverage
53408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainRuleData = lambda r:(r.LookAheadCoverage +
53508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                                   r.InputCoverage +
53608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                                   r.BacktrackCoverage)
53708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        SetRuleData = None
53808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        ChainSetRuleData = None
53908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      else:
54008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        assert 0, "unknown format: %s" % Format
54108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
54208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      if Chain:
54308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Coverage = ChainCoverage
54408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.ContextData = ChainContextData
54508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleData = ChainRuleData
54608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.SetRuleData = ChainSetRuleData
54708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      else:
54808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Coverage = Coverage
54908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.ContextData = ContextData
55008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleData = RuleData
55108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.SetRuleData = SetRuleData
55208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
55308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      if Format == 1:
55408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Rule = ChainTyp+'Rule'
55508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleCount = ChainTyp+'RuleCount'
55608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleSet = ChainTyp+'RuleSet'
55708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleSetCount = ChainTyp+'RuleSetCount'
55808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
55908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      elif Format == 2:
56008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Rule = ChainTyp+'ClassRule'
56108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleCount = ChainTyp+'ClassRuleCount'
56208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleSet = ChainTyp+'ClassSet'
56308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.RuleSetCount = ChainTyp+'ClassSetCount'
56408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Intersect = lambda glyphs, c, r: c.intersect_class(glyphs, r)
56508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
56608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
56708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        self.Input = 'Input' if Chain else 'Class'
56808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
56908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if self.Format not in [1, 2, 3]:
57008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    return None  # Don't shoot the messenger; let it go
57108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if not hasattr(self.__class__, "__ContextHelpers"):
57208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    self.__class__.__ContextHelpers = {}
57308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if self.Format not in self.__class__.__ContextHelpers:
57408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    helper = ContextHelper(self.__class__, self.Format)
57508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    self.__class__.__ContextHelpers[self.Format] = helper
57608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  return self.__class__.__ContextHelpers[self.Format]
57708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
57808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
57908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com             fontTools.ttLib.tables.otTables.ChainContextSubst)
58008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.comdef closure_glyphs(self, s, cur_glyphs=None):
58108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if cur_glyphs == None: cur_glyphs = s.glyphs
58208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  c = self.__classify_context()
58308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
58408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  indices = c.Coverage(self).intersect(s.glyphs)
58508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if not indices:
58608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    return []
58708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  cur_glyphs = c.Coverage(self).intersect_glyphs(s.glyphs);
58808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com
58908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  if self.Format == 1:
59008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    ContextData = c.ContextData(self)
59108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    rss = getattr(self, c.RuleSet)
59208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    for i in indices:
59308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      if not rss[i]: continue
59408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      for r in getattr(rss[i], c.Rule):
59508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        if not r: continue
59608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com        if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
59708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com          for cd,klist in zip(ContextData, c.RuleData(r))):
59808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com          chaos = False
59908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com          for ll in getattr(r, c.LookupRecord):
60008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            if not ll: continue
60108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            seqi = ll.SequenceIndex
60208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            if chaos:
60308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com              pos_glyphs = s.glyphs
60408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            else:
60508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com              if seqi == 0:
60608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                pos_glyphs = set([c.Coverage(self).glyphs[i]])
60708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com              else:
60808eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com                pos_glyphs = set([r.Input[seqi - 1]])
60908eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
61008eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            chaos = chaos or lookup.may_have_non_1to1()
61108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com            lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
61208eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com  elif self.Format == 2:
61308eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    ClassDef = getattr(self, c.ClassDef)
61408eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    indices = ClassDef.intersect(cur_glyphs)
61508eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    ContextData = c.ContextData(self)
61608eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    rss = getattr(self, c.RuleSet)
61708eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com    for i in indices:
618cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      if not rss[i]: continue
6198cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for r in getattr(rss[i], c.Rule):
6208cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        if not r: continue
6218cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
6228cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          for cd,klist in zip(ContextData, c.RuleData(r))):
6238cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          chaos = False
6248cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          for ll in getattr(r, c.LookupRecord):
6258cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            if not ll: continue
6268cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            seqi = ll.SequenceIndex
6278cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            if chaos:
6288cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              pos_glyphs = s.glyphs
6298cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            else:
6308cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              if seqi == 0:
6318cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                pos_glyphs = ClassDef.intersect_class(cur_glyphs, i)
6328cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              else:
6338cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                pos_glyphs = ClassDef.intersect_class(s.glyphs,
6348cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                                                      getattr(r, c.Input)[seqi - 1])
6358cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
6368cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            chaos = chaos or lookup.may_have_non_1to1()
6378cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
6388cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  elif self.Format == 3:
6398cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    if not all(x.intersect(s.glyphs) for x in c.RuleData(self)):
6408cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      return []
641306ab9d5de38f2a547fd1d69aedbe69b5c6617ccskia.committer@gmail.com    r = self
6428cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    chaos = False
6438cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    for ll in getattr(r, c.LookupRecord):
6448cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if not ll: continue
6458cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      seqi = ll.SequenceIndex
6468cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if chaos:
6478cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        pos_glyphs = s.glyphs
6488cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      else:
6498cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        if seqi == 0:
6508cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          pos_glyphs = cur_glyphs
6518cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        else:
6528cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          pos_glyphs = r.InputCoverage[seqi].intersect_glyphs(s.glyphs)
6538cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
6548cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      chaos = chaos or lookup.may_have_non_1to1()
6558cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
6568cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
6578cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
6588cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
6598cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
6608cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
6618cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
6628cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
6638cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef subset_glyphs(self, s):
6648cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  c = self.__classify_context()
6658cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
6668cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
6678cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    indices = self.Coverage.subset(s.glyphs)
6688cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = getattr(self, c.RuleSet)
6698cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = [rss[i] for i in indices]
6708cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    for rs in rss:
6718cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if not rs: continue
6728cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      ss = getattr(rs, c.Rule)
6738cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      ss = [r for r in ss
6748cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            if r and all(all(g in s.glyphs for g in glist)
6758cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              for glist in c.RuleData(r))]
6768cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      setattr(rs, c.Rule, ss)
6778cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      setattr(rs, c.RuleCount, len(ss))
6788cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    # Prune empty subrulesets
6798cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
6808cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    setattr(self, c.RuleSet, rss)
6818cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    setattr(self, c.RuleSetCount, len(rss))
6828cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return bool(rss)
6838cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  elif self.Format == 2:
6848cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    if not self.Coverage.subset(s.glyphs):
6858cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      return False
6868cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    indices = getattr(self, c.ClassDef).subset(self.Coverage.glyphs,
6878cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                                                 remap=False)
6888cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = getattr(self, c.RuleSet)
6898cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = [rss[i] for i in indices]
6908cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    ContextData = c.ContextData(self)
6918cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    klass_maps = [x.subset(s.glyphs, remap=True) for x in ContextData]
6928cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    for rs in rss:
6938cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if not rs: continue
6948cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      ss = getattr(rs, c.Rule)
6958cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      ss = [r for r in ss
6968cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org            if r and all(all(k in klass_map for k in klist)
6978cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              for klass_map,klist in zip(klass_maps, c.RuleData(r)))]
6988cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      setattr(rs, c.Rule, ss)
6998cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      setattr(rs, c.RuleCount, len(ss))
7008cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7018cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      # Remap rule classes
7028cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for r in ss:
7038cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        c.SetRuleData(r, [[klass_map.index(k) for k in klist]
7048cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org               for klass_map,klist in zip(klass_maps, c.RuleData(r))])
7058cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    # Prune empty subrulesets
7068cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
7078cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    setattr(self, c.RuleSet, rss)
7088cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    setattr(self, c.RuleSetCount, len(rss))
7098cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return bool(rss)
7108cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  elif self.Format == 3:
7118cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return all(x.subset(s.glyphs) for x in c.RuleData(self))
7128cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7138cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7148cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7158cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
7168cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
7178cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
7188cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
7198cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef subset_lookups(self, lookup_indices):
7208cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  c = self.__classify_context()
7218cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7228cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format in [1, 2]:
7238cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    for rs in getattr(self, c.RuleSet):
7248cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if not rs: continue
7258cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for r in getattr(rs, c.Rule):
7268cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        if not r: continue
7278cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        setattr(r, c.LookupRecord,
7288cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                 [ll for ll in getattr(r, c.LookupRecord)
7298cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org                  if ll and ll.LookupListIndex in lookup_indices])
7308cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org        for ll in getattr(r, c.LookupRecord):
7318cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          if not ll: continue
7328cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org          ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
7338cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  elif self.Format == 3:
7348cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    setattr(self, c.LookupRecord,
7358cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             [ll for ll in getattr(self, c.LookupRecord)
7368cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org              if ll and ll.LookupListIndex in lookup_indices])
7378cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    for ll in getattr(self, c.LookupRecord):
7388cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      if not ll: continue
7398cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
7408cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7418cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7428cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7438cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
7448cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextSubst,
7458cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ContextPos,
7468cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ChainContextPos)
7478cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef collect_lookups(self):
7488cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  c = self.__classify_context()
7498cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7508cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format in [1, 2]:
7518cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return [ll.LookupListIndex
7528cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for rs in getattr(self, c.RuleSet) if rs
7538cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for r in getattr(rs, c.Rule) if r
7548cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for ll in getattr(r, c.LookupRecord) if ll]
7558cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  elif self.Format == 3:
7568cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return [ll.LookupListIndex
7578cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      for ll in getattr(self, c.LookupRecord) if ll]
7588cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7598cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7608cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7618cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst)
7628cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef closure_glyphs(self, s, cur_glyphs=None):
7638cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
7648cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    self.ExtSubTable.closure_glyphs(s, cur_glyphs)
7658cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7668cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7678cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7688cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst)
7698cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef may_have_non_1to1(self):
7708cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
7718cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return self.ExtSubTable.may_have_non_1to1()
7728cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7738cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7748cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7758cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
7768cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ExtensionPos)
7778cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef prune_pre_subset(self, options):
7788cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
7798cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return self.ExtSubTable.prune_pre_subset(options)
7808cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7818cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7828cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7838cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
7848cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ExtensionPos)
7858cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef subset_glyphs(self, s):
7868cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
7878cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return self.ExtSubTable.subset_glyphs(s)
7888cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
7898cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    assert 0, "unknown format: %s" % self.Format
7908cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org
7918cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
7928cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org             fontTools.ttLib.tables.otTables.ExtensionPos)
7938cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.orgdef prune_post_subset(self, options):
7948cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  if self.Format == 1:
7958cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org    return self.ExtSubTable.prune_post_subset(options)
7968cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org  else:
79751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    assert 0, "unknown format: %s" % self.Format
79851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
799a4e13c85b23fe7530ae89a84ef671ebd5e451e80bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
800705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com             fontTools.ttLib.tables.otTables.ExtensionPos)
801705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.comdef subset_lookups(self, lookup_indices):
802705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  if self.Format == 1:
803705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    return self.ExtSubTable.subset_lookups(lookup_indices)
804705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  else:
805705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    assert 0, "unknown format: %s" % self.Format
806705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com
807705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
808705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com             fontTools.ttLib.tables.otTables.ExtensionPos)
809705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.comdef collect_lookups(self):
81051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  if self.Format == 1:
811705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    return self.ExtSubTable.collect_lookups()
812705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  else:
813e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    assert 0, "unknown format: %s" % self.Format
814e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
815705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
816e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.orgdef closure_glyphs(self, s, cur_glyphs=None):
817e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  for st in self.SubTable:
818e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    if not st: continue
819e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    st.closure_glyphs(s, cur_glyphs)
820e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
821705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
82251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef prune_pre_subset(self, options):
82351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  ret = False
824705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  for st in self.SubTable:
825705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    if not st: continue
826705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    if st.prune_pre_subset(options): ret = True
827705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  return ret
828705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com
829705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
830705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.comdef subset_glyphs(self, s):
831705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  self.SubTable = [st for st in self.SubTable if st and st.subset_glyphs(s)]
832705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  self.SubTableCount = len(self.SubTable)
83351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return bool(self.SubTableCount)
83451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
835705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
83651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef prune_post_subset(self, options):
83751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  ret = False
838705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  for st in self.SubTable:
839705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    if not st: continue
840705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com    if st.prune_post_subset(options): ret = True
841705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  return ret
84251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
84351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
8448182fa0cac76e7e6d583aebba060229230516887bsalomon@google.comdef subset_lookups(self, lookup_indices):
8458182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  for s in self.SubTable:
8468182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    s.subset_lookups(lookup_indices)
8478182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
8488182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
849e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.orgdef collect_lookups(self):
850e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  return _uniq_sort(sum((st.collect_lookups() for st in self.SubTable
851e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org                         if st), []))
8528182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
8538182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Lookup)
8548182fa0cac76e7e6d583aebba060229230516887bsalomon@google.comdef may_have_non_1to1(self):
8558182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  return any(st.may_have_non_1to1() for st in self.SubTable if st)
8568182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
8578182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.LookupList)
8588182fa0cac76e7e6d583aebba060229230516887bsalomon@google.comdef prune_pre_subset(self, options):
85951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  ret = False
86051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  for l in self.Lookup:
86151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    if not l: continue
8628182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    if l.prune_pre_subset(options): ret = True
86351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return ret
86451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
86551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.LookupList)
86651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_glyphs(self, s):
867e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  "Returns the indices of nonempty lookups."
86851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return [i for i,l in enumerate(self.Lookup) if l and l.subset_glyphs(s)]
8698182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
870e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.LookupList)
871e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.orgdef prune_post_subset(self, options):
8728182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  ret = False
873e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  for l in self.Lookup:
874e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    if not l: continue
875e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org    if l.prune_post_subset(options): ret = True
8768182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  return ret
87751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
8788182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.LookupList)
87951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_lookups(self, lookup_indices):
88051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.Lookup = [self.Lookup[i] for i in lookup_indices
88151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                 if i < self.LookupCount]
88251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.LookupCount = len(self.Lookup)
8838ccf590b89cec1a5974b6f4b7b49ca67cc5036cfskia.committer@gmail.com  for l in self.Lookup:
88451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    l.subset_lookups(lookup_indices)
88551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
88651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.LookupList)
88751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef closure_lookups(self, lookup_indices):
88851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  lookup_indices = _uniq_sort(lookup_indices)
88951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  recurse = lookup_indices
89051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  while True:
89151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    recurse_lookups = sum((self.Lookup[i].collect_lookups()
89251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                            for i in recurse if i < self.LookupCount), [])
89351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    recurse_lookups = [l for l in recurse_lookups
89451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                       if l not in lookup_indices and l < self.LookupCount]
89551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    if not recurse_lookups:
89651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com      return _uniq_sort(lookup_indices)
89751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    recurse_lookups = _uniq_sort(recurse_lookups)
89851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    lookup_indices.extend(recurse_lookups)
89951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    recurse = recurse_lookups
90051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
90151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Feature)
90251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_lookups(self, lookup_indices):
90351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.LookupListIndex = [l for l in self.LookupListIndex
90451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                          if l in lookup_indices]
90551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  # Now map them.
90651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.LookupListIndex = [lookup_indices.index(l)
90751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                          for l in self.LookupListIndex]
90851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.LookupCount = len(self.LookupListIndex)
90951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return self.LookupCount
91051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
91151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Feature)
912705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.comdef collect_lookups(self):
913705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com  return self.LookupListIndex[:]
914705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com
91551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
91651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_lookups(self, lookup_indices):
91751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  "Returns the indices of nonempty features."
91851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  feature_indices = [i for i,f in enumerate(self.FeatureRecord)
91951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                     if f.Feature.subset_lookups(lookup_indices)]
92051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.subset_features(feature_indices)
921e0e7cfe44bb9d66d76120a79e5275c294bacaa22commit-bot@chromium.org  return feature_indices
92251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
92351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
92451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef collect_lookups(self, feature_indices):
92551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return _uniq_sort(sum((self.FeatureRecord[i].Feature.collect_lookups()
92651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                         for i in feature_indices
92751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                          if i < self.FeatureCount), []))
92851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
92951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
93051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_features(self, feature_indices):
93151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
93251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.FeatureCount = len(self.FeatureRecord)
93351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return bool(self.FeatureCount)
93451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
9358ccf590b89cec1a5974b6f4b7b49ca67cc5036cfskia.committer@gmail.com@_add_method(fontTools.ttLib.tables.otTables.DefaultLangSys,
93651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com             fontTools.ttLib.tables.otTables.LangSys)
93751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_features(self, feature_indices):
9388ccf590b89cec1a5974b6f4b7b49ca67cc5036cfskia.committer@gmail.com  if self.ReqFeatureIndex in feature_indices:
93951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    self.ReqFeatureIndex = feature_indices.index(self.ReqFeatureIndex)
94051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  else:
94151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    self.ReqFeatureIndex = 65535
94251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.FeatureIndex = [f for f in self.FeatureIndex if f in feature_indices]
94351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  # Now map them.
94451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.FeatureIndex = [feature_indices.index(f) for f in self.FeatureIndex
94551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                       if f in feature_indices]
94651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.FeatureCount = len(self.FeatureIndex)
94751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return bool(self.FeatureCount or self.ReqFeatureIndex != 65535)
948705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com
949e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org@_add_method(fontTools.ttLib.tables.otTables.DefaultLangSys,
950705e84094494613a4659fcabe29f76eb003f9809bsalomon@google.com             fontTools.ttLib.tables.otTables.LangSys)
95151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef collect_features(self):
95251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  feature_indices = self.FeatureIndex[:]
95351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  if self.ReqFeatureIndex != 65535:
95451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    feature_indices.append(self.ReqFeatureIndex)
95551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return _uniq_sort(feature_indices)
956a444430281ea35cb76fb42516978b4a93221c2c7bsalomon@google.com
957a444430281ea35cb76fb42516978b4a93221c2c7bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.Script)
958a444430281ea35cb76fb42516978b4a93221c2c7bsalomon@google.comdef subset_features(self, feature_indices):
959a444430281ea35cb76fb42516978b4a93221c2c7bsalomon@google.com  if(self.DefaultLangSys and
960a444430281ea35cb76fb42516978b4a93221c2c7bsalomon@google.com      not self.DefaultLangSys.subset_features(feature_indices)):
9618182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    self.DefaultLangSys = None
96251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.LangSysRecord = [l for l in self.LangSysRecord
9638182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                        if l.LangSys.subset_features(feature_indices)]
964d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.LangSysCount = len(self.LangSysRecord)
96551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return bool(self.LangSysCount or self.DefaultLangSys)
966c490f801b063a0837501feab3d12b73d71f46312jvanverth@google.com
9672880df2609eba09b555ca37be04b6ad89290c765Tom Hudson@_add_method(fontTools.ttLib.tables.otTables.Script)
96834cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.comdef collect_features(self):
96934cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  feature_indices = [l.LangSys.collect_features() for l in self.LangSysRecord]
97034cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  if self.DefaultLangSys:
971d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    feature_indices.append(self.DefaultLangSys.collect_features())
97234cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  return _uniq_sort(sum(feature_indices, []))
97334cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com
97451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.ScriptList)
975d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_features(self, feature_indices):
976d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.ScriptRecord = [s for s in self.ScriptRecord
97751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com                       if s.Script.subset_features(feature_indices)]
97851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.ScriptCount = len(self.ScriptRecord)
97951a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  return bool(self.ScriptCount)
98051a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
98151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.tables.otTables.ScriptList)
98251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef collect_features(self):
98349f085dddff10473b6ebf832a974288300224e60bsalomon  return _uniq_sort(sum((s.Script.collect_features()
9848182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com                         for s in self.ScriptRecord), []))
98551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com
98651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.getTableClass('GSUB'))
98734cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.comdef closure_glyphs(self, s):
98849f085dddff10473b6ebf832a974288300224e60bsalomon  s.table = self.table
98934cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  feature_indices = self.table.ScriptList.collect_features()
99034cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
99134cd70a5810b3cf37b44de1ce080a911a8b342c8bsalomon@google.com  while True:
99251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    orig_glyphs = s.glyphs.copy()
99351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    for i in lookup_indices:
99451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com      if i >= self.table.LookupList.LookupCount: continue
99551a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com      if not self.table.LookupList.Lookup[i]: continue
99651a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com      self.table.LookupList.Lookup[i].closure_glyphs(s)
9978182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    if orig_glyphs == s.glyphs:
99851a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com      break
9998182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  del s.table
10008182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com
100151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com@_add_method(fontTools.ttLib.getTableClass('GSUB'),
100251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com             fontTools.ttLib.getTableClass('GPOS'))
100351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_glyphs(self, s):
100451a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  s.glyphs = s.glyphs_gsubed
10058182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  lookup_indices = self.table.LookupList.subset_glyphs(s)
10068182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  self.subset_lookups(lookup_indices)
100751a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  self.prune_lookups()
1008e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org  return True
1009e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
1010e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GSUB'),
101151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com             fontTools.ttLib.getTableClass('GPOS'))
101251a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.comdef subset_lookups(self, lookup_indices):
101351a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com  """Retrains specified lookups, then removes empty features, language
1014d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org     systems, and scripts."""
1015d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.LookupList.subset_lookups(lookup_indices)
1016d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
1017d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.ScriptList.subset_features(feature_indices)
1018d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1019d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1020d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org             fontTools.ttLib.getTableClass('GPOS'))
1021d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef prune_lookups(self):
1022d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  "Remove unreferenced lookups"
1023d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  feature_indices = self.table.ScriptList.collect_features()
1024d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
1025d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
1026d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.subset_lookups(lookup_indices)
1027d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1028d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1029d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org             fontTools.ttLib.getTableClass('GPOS'))
1030d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_feature_tags(self, feature_tags):
1031d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  feature_indices = [i for i,f in
1032d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                     enumerate(self.table.FeatureList.FeatureRecord)
1033d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                     if f.FeatureTag in feature_tags]
1034d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.FeatureList.subset_features(feature_indices)
1035d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.ScriptList.subset_features(feature_indices)
1036d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1037d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1038d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org             fontTools.ttLib.getTableClass('GPOS'))
1039d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef prune_pre_subset(self, options):
1040d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if '*' not in options.layout_features:
1041d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    self.subset_feature_tags(options.layout_features)
1042d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.prune_lookups()
1043d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.LookupList.prune_pre_subset(options);
1044d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return True
1045d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1046d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1047d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org             fontTools.ttLib.getTableClass('GPOS'))
1048d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef prune_post_subset(self, options):
1049d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.table.LookupList.prune_post_subset(options);
1050d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return True
1051d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1052d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('GDEF'))
1053d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1054d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  glyphs = s.glyphs_gsubed
1055d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  table = self.table
1056d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if table.LigCaretList:
1057d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    indices = table.LigCaretList.Coverage.subset(glyphs)
1058d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i]
1059d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                   for i in indices]
1060d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
1061d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if not table.LigCaretList.LigGlyphCount:
1062d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org      table.LigCaretList = None
1063d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if table.MarkAttachClassDef:
1064d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
1065d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                              table.MarkAttachClassDef.
1066d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                                classDefs.iteritems()
1067d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                              if g in glyphs)
1068d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if not table.MarkAttachClassDef.classDefs:
1069d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org      table.MarkAttachClassDef = None
1070d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if table.GlyphClassDef:
1071d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.GlyphClassDef.classDefs = dict((g,v) for g,v in
1072d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                         table.GlyphClassDef.
1073d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                           classDefs.iteritems()
1074d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                         if g in glyphs)
1075d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if not table.GlyphClassDef.classDefs:
1076d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org      table.GlyphClassDef = None
1077d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if table.AttachList:
1078d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    indices = table.AttachList.Coverage.subset(glyphs)
1079d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
1080d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                                    for i in indices]
1081d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
1082d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if not table.AttachList.GlyphCount:
1083d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org      table.AttachList = None
1084d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return bool(table.LigCaretList or
1085d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org               table.MarkAttachClassDef or
1086d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org               table.GlyphClassDef or
1087d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org               table.AttachList)
1088d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1089d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('kern'))
1090d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef prune_pre_subset(self, options):
1091d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  # Prune unknown kern table types
1092d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.kernTables = [t for t in self.kernTables if hasattr(t, 'kernTable')]
1093d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return bool(self.kernTables)
1094d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1095d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('kern'))
1096d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1097d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  glyphs = s.glyphs_gsubed
1098d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  for t in self.kernTables:
1099d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.iteritems()
1100d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                       if a in glyphs and b in glyphs)
1101d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.kernTables = [t for t in self.kernTables if t.kernTable]
1102d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return bool(self.kernTables)
1103d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1104d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('vmtx'),
1105d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org             fontTools.ttLib.getTableClass('hmtx'))
1106d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1107d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.metrics = dict((g,v) for g,v in self.metrics.iteritems() if g in s.glyphs)
1108d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return bool(self.metrics)
1109d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1110d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('hdmx'))
1111d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1112d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.hdmx = dict((sz,_dict((g,v) for g,v in l.iteritems() if g in s.glyphs))
1113d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                   for sz,l in self.hdmx.iteritems())
1114d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return bool(self.hdmx)
1115d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1116d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('VORG'))
1117d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1118d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.iteritems()
1119d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org                             if g in s.glyphs)
1120d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.numVertOriginYMetrics = len(self.VOriginRecords)
1121d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return True  # Never drop; has default metrics
1122d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1123d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('post'))
1124d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef prune_pre_subset(self, options):
1125d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if not options.glyph_names:
1126d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    self.formatType = 3.0
1127d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return True
1128d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1129d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableClass('post'))
1130d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef subset_glyphs(self, s):
1131d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  self.extraNames = []  # This seems to do it
11322880df2609eba09b555ca37be04b6ad89290c765Tom Hudson  return True
1133d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1134d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
1135d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef getComponentNamesFast(self, glyfTable):
1136d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
1137d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    return []  # Not composite
1138d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  data = self.data
1139d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  i = 10
1140d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  components = []
1141d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  more = 1
1142d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  while more:
1143d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    flags, glyphID = struct.unpack(">HH", data[i:i+4])
1144d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    i += 4
1145d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    flags = int(flags)
1146d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    components.append(glyfTable.getGlyphName(int(glyphID)))
1147d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1148d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if flags & 0x0001: i += 4  # ARG_1_AND_2_ARE_WORDS
1149d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    else: i += 2
1150d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    if flags & 0x0008: i += 2  # WE_HAVE_A_SCALE
1151d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    elif flags & 0x0040: i += 4  # WE_HAVE_AN_X_AND_Y_SCALE
1152d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    elif flags & 0x0080: i += 8  # WE_HAVE_A_TWO_BY_TWO
1153d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    more = flags & 0x0020  # MORE_COMPONENTS
1154d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1155d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  return components
1156d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org
1157d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
1158d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.orgdef remapComponentsFast(self, indices):
1159d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
1160d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    return  # Not composite
1161d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  data = array.array("B", self.data)
1162d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  i = 10
1163d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  more = 1
1164d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  while more:
1165d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    flags =(data[i] << 8) | data[i+1]
1166d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    glyphID =(data[i+2] << 8) | data[i+3]
1167d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    # Remap
1168d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    glyphID = indices.index(glyphID)
1169d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    data[i+2] = glyphID >> 8
1170a4e13c85b23fe7530ae89a84ef671ebd5e451e80bsalomon@google.com    data[i+3] = glyphID & 0xFF
117151a6286c241c1dc750d263ed9676079c898148b0bsalomon@google.com    i += 4
1172e4fafb146e85cdfcf9d5418597b6818aa0754adatfarina@chromium.org    flags = int(flags)
1173bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com
1174bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    if flags & 0x0001: i += 4  # ARG_1_AND_2_ARE_WORDS
117580214e26c57c5fea954006400852e8999e201923robertphillips@google.com    else: i += 2
1176bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    if flags & 0x0008: i += 2  # WE_HAVE_A_SCALE
1177bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    elif flags & 0x0040: i += 4  # WE_HAVE_AN_X_AND_Y_SCALE
1178bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    elif flags & 0x0080: i += 8  # WE_HAVE_A_TWO_BY_TWO
1179bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    more = flags & 0x0020  # MORE_COMPONENTS
1180bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com
1181bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  self.data = data.tostring()
1182bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com
1183bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
1184bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.comdef dropInstructionsFast(self):
1185d9f2dea5328c9ab455852f2e4928cca7c71c6b05reed@google.com  if not self.data:
1186bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    return
1187bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  numContours = struct.unpack(">h", self.data[:2])[0]
1188bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  data = array.array("B", self.data)
118980214e26c57c5fea954006400852e8999e201923robertphillips@google.com  i = 10
11908182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com  if numContours >= 0:
11912047f00e4698f83499ab91911999a65c21a951c9epoger@google.com    i += 2 * numContours  # endPtsOfContours
11922047f00e4698f83499ab91911999a65c21a951c9epoger@google.com    instructionLen =(data[i] << 8) | data[i+1]
1193bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    # Zero it
119449f085dddff10473b6ebf832a974288300224e60bsalomon    data[i] = data [i+1] = 0
11958182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    i += 2
11968182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com    if instructionLen:
11978182fa0cac76e7e6d583aebba060229230516887bsalomon@google.com      # Splice it out
1198bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com      data = data[:i] + data[i+instructionLen:]
1199bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com  else:
1200bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    more = 1
1201bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    while more:
120280214e26c57c5fea954006400852e8999e201923robertphillips@google.com      flags =(data[i] << 8) | data[i+1]
1203bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com      # Turn instruction flag off
12041e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org      flags &= ~0x0100  # WE_HAVE_INSTRUCTIONS
12051e1c36f4f89ad39e1d248edb745919e493242c68vandebo@chromium.org      data[i+0] = flags >> 8
120680214e26c57c5fea954006400852e8999e201923robertphillips@google.com      data[i+1] = flags & 0xFF
1207e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org      i += 4
1208e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org      flags = int(flags)
1209e5b2af955b7d06815ddd405659ad62a2a8355ca3commit-bot@chromium.org
1210cc6493bbef7c9c2adf4b1ed8701e2ed015ae745drobertphillips@google.com      if flags & 0x0001: i += 4  # ARG_1_AND_2_ARE_WORDS
121108eacc144711cd5d33513b2fba7a635ad28b7208robertphillips@google.com      else: i += 2
12126fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      if flags & 0x0008: i += 2  # WE_HAVE_A_SCALE
1213edf32d5b0e7694833287024e03da38521a0adf05junov@chromium.org      elif flags & 0x0040: i += 4  # WE_HAVE_AN_X_AND_Y_SCALE
12146fbe54c663bd0eed6f6519c31a4c8e291db2613bcommit-bot@chromium.org      elif flags & 0x0080: i += 8  # WE_HAVE_A_TWO_BY_TWO
12158cdf0f52ff395d4053f7ed5c20861c42eba25d31junov@chromium.org      more = flags & 0x0020  # MORE_COMPONENTS
1216e7b3d29a1289e64130dd0ec905d94feedc9d1064bsalomon@google.com
1217edb26fdb8349a727b226e90cbeab06cd25f5cac0bsalomon@google.com    # Cut off
1218d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org    data = data[:i]
1219d3e5842db0cb169e10d6da1e62c94ba5cf182bb4commit-bot@chromium.org  if len(data) % 4:
1220e7b3d29a1289e64130dd0ec905d94feedc9d1064bsalomon@google.com    # add pad bytes
1221bdee9fc778d4387d805d717f2cd7fc7074991fdbreed@google.com    nPadBytes = 4 -(len(data) % 4)
1222    for i in range(nPadBytes):
1223      data.append(0)
1224  self.data = data.tostring()
1225
1226@_add_method(fontTools.ttLib.getTableClass('glyf'))
1227def closure_glyphs(self, s):
1228  decompose = s.glyphs
1229  # I don't know if component glyphs can be composite themselves.
1230  # We handle them anyway.
1231  while True:
1232    components = set()
1233    for g in decompose:
1234      if g not in self.glyphs:
1235        continue
1236      gl = self.glyphs[g]
1237      if hasattr(gl, "data"):
1238        for c in gl.getComponentNamesFast(self):
1239          if c not in s.glyphs:
1240            components.add(c)
1241      else:
1242        # TTX seems to expand gid0..3 always
1243        if gl.isComposite():
1244          for c in gl.components:
1245            if c.glyphName not in s.glyphs:
1246              components.add(c.glyphName)
1247    components = set(c for c in components if c not in s.glyphs)
1248    if not components:
1249      break
1250    decompose = components
1251    s.glyphs.update(components)
1252
1253@_add_method(fontTools.ttLib.getTableClass('glyf'))
1254def prune_pre_subset(self, options):
1255  if options.notdef_glyph and not options.notdef_outline:
1256    g = self[self.glyphOrder[0]]
1257    # Yay, easy!
1258    g.__dict__.clear()
1259    g.data = ""
1260  return True
1261
1262@_add_method(fontTools.ttLib.getTableClass('glyf'))
1263def subset_glyphs(self, s):
1264  self.glyphs = dict((g,v) for g,v in self.glyphs.iteritems() if g in s.glyphs)
1265  indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
1266  for v in self.glyphs.itervalues():
1267    if hasattr(v, "data"):
1268      v.remapComponentsFast(indices)
1269    else:
1270      pass  # No need
1271  self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs]
1272  # Don't drop empty 'glyf' tables, otherwise 'loca' doesn't get subset.
1273  return True
1274
1275@_add_method(fontTools.ttLib.getTableClass('glyf'))
1276def prune_post_subset(self, options):
1277  if not options.hinting:
1278    for v in self.glyphs.itervalues():
1279      if hasattr(v, "data"):
1280        v.dropInstructionsFast()
1281      else:
1282        v.program = fontTools.ttLib.tables.ttProgram.Program()
1283        v.program.fromBytecode([])
1284  return True
1285
1286@_add_method(fontTools.ttLib.getTableClass('CFF '))
1287def prune_pre_subset(self, options):
1288  cff = self.cff
1289  # CFF table should have one font only
1290  cff.fontNames = cff.fontNames[:1]
1291
1292  if options.notdef_glyph and not options.notdef_outline:
1293    for fontname in cff.keys():
1294      font = cff[fontname]
1295      c,_ = font.CharStrings.getItemAndSelector('.notdef')
1296      c.bytecode = '\x0e' # endchar
1297      c.program = None
1298
1299  return True # bool(cff.fontNames)
1300
1301@_add_method(fontTools.ttLib.getTableClass('CFF '))
1302def subset_glyphs(self, s):
1303  cff = self.cff
1304  for fontname in cff.keys():
1305    font = cff[fontname]
1306    cs = font.CharStrings
1307
1308    # Load all glyphs
1309    for g in font.charset:
1310      if g not in s.glyphs: continue
1311      c,sel = cs.getItemAndSelector(g)
1312
1313    if cs.charStringsAreIndexed:
1314      indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
1315      csi = cs.charStringsIndex
1316      csi.items = [csi.items[i] for i in indices]
1317      csi.count = len(csi.items)
1318      del csi.file, csi.offsets
1319      if hasattr(font, "FDSelect"):
1320        sel = font.FDSelect
1321        sel.format = None
1322        sel.gidArray = [sel.gidArray[i] for i in indices]
1323      cs.charStrings = dict((g,indices.index(v))
1324                            for g,v in cs.charStrings.iteritems()
1325                            if g in s.glyphs)
1326    else:
1327      cs.charStrings = dict((g,v)
1328                            for g,v in cs.charStrings.iteritems()
1329                            if g in s.glyphs)
1330    font.charset = [g for g in font.charset if g in s.glyphs]
1331    font.numGlyphs = len(font.charset)
1332
1333  return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
1334
1335@_add_method(fontTools.misc.psCharStrings.T2CharString)
1336def subset_subroutines(self, subrs, gsubrs):
1337  p = self.program
1338  for i in xrange(1, len(p)):
1339    if p[i] == 'callsubr':
1340      assert type(p[i-1]) is int
1341      p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
1342    elif p[i] == 'callgsubr':
1343      assert type(p[i-1]) is int
1344      p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
1345
1346@_add_method(fontTools.ttLib.getTableClass('CFF '))
1347def prune_post_subset(self, options):
1348  cff = self.cff
1349
1350  class _MarkingT2Decompiler(fontTools.misc.psCharStrings.SimpleT2Decompiler):
1351
1352    def __init__(self, localSubrs, globalSubrs):
1353      fontTools.misc.psCharStrings.SimpleT2Decompiler.__init__(self,
1354                                                               localSubrs,
1355                                                               globalSubrs)
1356      for subrs in [localSubrs, globalSubrs]:
1357        if subrs and not hasattr(subrs, "_used"):
1358          subrs._used = set()
1359
1360    def op_callsubr(self, index):
1361      self.localSubrs._used.add(self.operandStack[-1]+self.localBias)
1362      fontTools.misc.psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
1363
1364    def op_callgsubr(self, index):
1365      self.globalSubrs._used.add(self.operandStack[-1]+self.globalBias)
1366      fontTools.misc.psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
1367
1368  class _NonrecursingT2Decompiler(fontTools.misc.psCharStrings.SimpleT2Decompiler):
1369
1370    def __init__(self, localSubrs, globalSubrs):
1371      fontTools.misc.psCharStrings.SimpleT2Decompiler.__init__(self,
1372                                                               localSubrs,
1373                                                               globalSubrs)
1374
1375    def op_callsubr(self, index):
1376      self.pop()
1377
1378    def op_callgsubr(self, index):
1379      self.pop()
1380
1381  for fontname in cff.keys():
1382    font = cff[fontname]
1383    cs = font.CharStrings
1384
1385    # Drop unused FontDictionaries
1386    if hasattr(font, "FDSelect"):
1387      sel = font.FDSelect
1388      indices = _uniq_sort(sel.gidArray)
1389      sel.gidArray = [indices.index (ss) for ss in sel.gidArray]
1390      arr = font.FDArray
1391      arr.items = [arr[i] for i in indices]
1392      arr.count = len(arr.items)
1393      del arr.file, arr.offsets
1394
1395    # Mark all used subroutines
1396    for g in font.charset:
1397      c,sel = cs.getItemAndSelector(g)
1398      subrs = getattr(c.private, "Subrs", [])
1399      decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs)
1400      decompiler.execute(c)
1401
1402    # Renumber subroutines to remove unused ones
1403    all_subrs = [font.GlobalSubrs]
1404    if hasattr(font, 'FDSelect'):
1405      all_subrs.extend(fd.Private.Subrs for fd in font.FDArray if hasattr(fd.Private, 'Subrs'))
1406    elif hasattr(font.Private, 'Subrs'):
1407      all_subrs.append(font.Private.Subrs)
1408    # Prepare
1409    for subrs in all_subrs:
1410      if not subrs: continue
1411      if not hasattr(subrs, '_used'):
1412        subrs._used = set()
1413      subrs._used = _uniq_sort(subrs._used)
1414      subrs._old_bias = fontTools.misc.psCharStrings.calcSubrBias(subrs)
1415      subrs._new_bias = fontTools.misc.psCharStrings.calcSubrBias(subrs._used)
1416    # Renumber glyph charstrings
1417    for g in font.charset:
1418      c,sel = cs.getItemAndSelector(g)
1419      subrs = getattr(c.private, "Subrs", [])
1420      c.subset_subroutines (subrs, font.GlobalSubrs)
1421    # Renumber subroutines themselves
1422    for subrs in all_subrs:
1423      if not subrs: continue
1424      decompiler = _NonrecursingT2Decompiler(subrs, font.GlobalSubrs)
1425      for i in xrange (subrs.count):
1426        if i not in subrs._used: continue
1427        decompiler.reset()
1428        decompiler.execute(subrs[i])
1429        if subrs == font.GlobalSubrs:
1430          if not hasattr(font, 'FDSelect') and hasattr(font.Private, 'Subrs'):
1431            local_subrs = font.Private.Subrs
1432          else:
1433            local_subrs = []
1434        else:
1435          local_subrs = subrs
1436        subrs[i].subset_subroutines (local_subrs, font.GlobalSubrs)
1437    # Cleanup
1438    for subrs in all_subrs:
1439      if not subrs: continue
1440      subrs.items = [subrs.items[i] for i in subrs._used]
1441      del subrs.file, subrs.offsets
1442      del subrs._used, subrs._old_bias, subrs._new_bias
1443
1444    if not options.hinting:
1445      pass  # TODO(behdad) Drop hints
1446
1447  return True
1448
1449@_add_method(fontTools.ttLib.getTableClass('cmap'))
1450def closure_glyphs(self, s):
1451  tables = [t for t in self.tables
1452            if t.platformID == 3 and t.platEncID in [1, 10]]
1453  for u in s.unicodes_requested:
1454    found = False
1455    for table in tables:
1456      if u in table.cmap:
1457        s.glyphs.add(table.cmap[u])
1458        found = True
1459        break
1460    if not found:
1461      s.log("No glyph for Unicode value %s; skipping." % u)
1462
1463@_add_method(fontTools.ttLib.getTableClass('cmap'))
1464def prune_pre_subset(self, options):
1465  if not options.legacy_cmap:
1466    # Drop non-Unicode / non-Symbol cmaps
1467    self.tables = [t for t in self.tables
1468                   if t.platformID == 3 and t.platEncID in [0, 1, 10]]
1469  if not options.symbol_cmap:
1470    self.tables = [t for t in self.tables
1471                   if t.platformID == 3 and t.platEncID in [1, 10]]
1472  # TODO(behdad) Only keep one subtable?
1473  # For now, drop format=0 which can't be subset_glyphs easily?
1474  self.tables = [t for t in self.tables if t.format != 0]
1475  return bool(self.tables)
1476
1477@_add_method(fontTools.ttLib.getTableClass('cmap'))
1478def subset_glyphs(self, s):
1479  s.glyphs = s.glyphs_cmaped
1480  for t in self.tables:
1481    # For reasons I don't understand I need this here
1482    # to force decompilation of the cmap format 14.
1483    try:
1484      getattr(t, "asdf")
1485    except AttributeError:
1486      pass
1487    if t.format == 14:
1488      # TODO(behdad) XXX We drop all the default-UVS mappings(g==None).
1489      t.uvsDict = dict((v,[(u,g) for u,g in l if g in s.glyphs])
1490                       for v,l in t.uvsDict.iteritems())
1491      t.uvsDict = dict((v,l) for v,l in t.uvsDict.iteritems() if l)
1492    else:
1493      t.cmap = dict((u,g) for u,g in t.cmap.iteritems()
1494                    if g in s.glyphs_requested or u in s.unicodes_requested)
1495  self.tables = [t for t in self.tables
1496                 if (t.cmap if t.format != 14 else t.uvsDict)]
1497  # TODO(behdad) Convert formats when needed.
1498  # In particular, if we have a format=12 without non-BMP
1499  # characters, either drop format=12 one or convert it
1500  # to format=4 if there's not one.
1501  return bool(self.tables)
1502
1503@_add_method(fontTools.ttLib.getTableClass('name'))
1504def prune_pre_subset(self, options):
1505  if '*' not in options.name_IDs:
1506    self.names = [n for n in self.names if n.nameID in options.name_IDs]
1507  if not options.name_legacy:
1508    self.names = [n for n in self.names
1509                  if n.platformID == 3 and n.platEncID == 1]
1510  if '*' not in options.name_languages:
1511    self.names = [n for n in self.names if n.langID in options.name_languages]
1512  return True  # Retain even if empty
1513
1514
1515# TODO(behdad) OS/2 ulUnicodeRange / ulCodePageRange?
1516# TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
1517# TODO(behdad) Drop empty GSUB/GPOS, and GDEF if no GSUB/GPOS left
1518# TODO(behdad) Drop GDEF subitems if unused by lookups
1519# TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
1520# TODO(behdad) Text direction considerations.
1521# TODO(behdad) Text script / language considerations.
1522
1523
1524class Options(object):
1525
1526  class UnknownOptionError(Exception):
1527    pass
1528
1529  _drop_tables_default = ['BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ',
1530                          'PCLT', 'LTSH']
1531  _drop_tables_default += ['Feat', 'Glat', 'Gloc', 'Silf', 'Sill']  # Graphite
1532  _drop_tables_default += ['CBLC', 'CBDT', 'sbix', 'COLR', 'CPAL']  # Color
1533  _no_subset_tables_default = ['gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2',
1534                               'loca', 'name', 'cvt ', 'fpgm', 'prep']
1535  _hinting_tables_default = ['cvt ', 'fpgm', 'prep', 'hdmx', 'VDMX']
1536
1537  # Based on HarfBuzz shapers
1538  _layout_features_groups = {
1539    # Default shaper
1540    'common': ['ccmp', 'liga', 'locl', 'mark', 'mkmk', 'rlig'],
1541    'horizontal': ['calt', 'clig', 'curs', 'kern', 'rclt'],
1542    'vertical':  ['valt', 'vert', 'vkrn', 'vpal', 'vrt2'],
1543    'ltr': ['ltra', 'ltrm'],
1544    'rtl': ['rtla', 'rtlm'],
1545    # Complex shapers
1546    'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
1547               'cswh', 'mset'],
1548    'hangul': ['ljmo', 'vjmo', 'tjmo'],
1549    'tibetal': ['abvs', 'blws', 'abvm', 'blwm'],
1550    'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
1551              'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
1552              'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
1553  }
1554  _layout_features_default = _uniq_sort(sum(
1555      _layout_features_groups.itervalues(), []))
1556
1557  drop_tables = _drop_tables_default
1558  no_subset_tables = _no_subset_tables_default
1559  hinting_tables = _hinting_tables_default
1560  layout_features = _layout_features_default
1561  hinting = False
1562  glyph_names = False
1563  legacy_cmap = False
1564  symbol_cmap = False
1565  name_IDs = [1, 2]  # Family and Style
1566  name_legacy = False
1567  name_languages = [0x0409]  # English
1568  notdef_glyph = True # gid0 for TrueType / .notdef for CFF
1569  notdef_outline = False # No need for notdef to have an outline really
1570  recommended_glyphs = False  # gid1, gid2, gid3 for TrueType
1571  recalc_bounds = False # Recalculate font bounding boxes
1572  canonical_order = False # Order tables as recommended
1573  flavor = None # May be 'woff'
1574
1575  def __init__(self, **kwargs):
1576
1577    self.set(**kwargs)
1578
1579  def set(self, **kwargs):
1580    for k,v in kwargs.iteritems():
1581      if not hasattr(self, k):
1582        raise self.UnknownOptionError("Unknown option '%s'" % a)
1583      setattr(self, k, v)
1584
1585  def parse_opts(self, argv, ignore_unknown=False):
1586    ret = []
1587    opts = {}
1588    for a in argv:
1589      orig_a = a
1590      if not a.startswith('--'):
1591        ret.append(a)
1592        continue
1593      a = a[2:]
1594      i = a.find('=')
1595      op = '='
1596      if i == -1:
1597        if a.startswith("no-"):
1598          k = a[3:]
1599          v = False
1600        else:
1601          k = a
1602          v = True
1603      else:
1604        k = a[:i]
1605        if k[-1] in "-+":
1606          op = k[-1]+'='  # Ops is '-=' or '+=' now.
1607          k = k[:-1]
1608        v = a[i+1:]
1609      k = k.replace('-', '_')
1610      if not hasattr(self, k):
1611        if ignore_unknown == True or k in ignore_unknown:
1612          ret.append(orig_a)
1613          continue
1614        else:
1615          raise self.UnknownOptionError("Unknown option '%s'" % a)
1616
1617      ov = getattr(self, k)
1618      if isinstance(ov, bool):
1619        v = bool(v)
1620      elif isinstance(ov, int):
1621        v = int(v)
1622      elif isinstance(ov, list):
1623        vv = v.split(',')
1624        if vv == ['']:
1625          vv = []
1626        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
1627        if op == '=':
1628          v = vv
1629        elif op == '+=':
1630          v = ov
1631          v.extend(vv)
1632        elif op == '-=':
1633          v = ov
1634          for x in vv:
1635            if x in v:
1636              v.remove(x)
1637        else:
1638          assert 0
1639
1640      opts[k] = v
1641    self.set(**opts)
1642
1643    return ret
1644
1645
1646class Subsetter(object):
1647
1648  def __init__(self, options=None, log=None):
1649
1650    if not log:
1651      log = Logger()
1652    if not options:
1653      options = Options()
1654
1655    self.options = options
1656    self.log = log
1657    self.unicodes_requested = set()
1658    self.glyphs_requested = set()
1659    self.glyphs = set()
1660
1661  def populate(self, glyphs=[], unicodes=[], text=""):
1662    self.unicodes_requested.update(unicodes)
1663    if isinstance(text, str):
1664      text = text.decode("utf8")
1665    for u in text:
1666      self.unicodes_requested.add(ord(u))
1667    self.glyphs_requested.update(glyphs)
1668    self.glyphs.update(glyphs)
1669
1670  def _prune_pre_subset(self, font):
1671
1672    for tag in font.keys():
1673      if tag == 'GlyphOrder': continue
1674
1675      if(tag in self.options.drop_tables or
1676         (tag in self.options.hinting_tables and not self.options.hinting)):
1677        self.log(tag, "dropped")
1678        del font[tag]
1679        continue
1680
1681      clazz = fontTools.ttLib.getTableClass(tag)
1682
1683      if hasattr(clazz, 'prune_pre_subset'):
1684        table = font[tag]
1685        retain = table.prune_pre_subset(self.options)
1686        self.log.lapse("prune  '%s'" % tag)
1687        if not retain:
1688          self.log(tag, "pruned to empty; dropped")
1689          del font[tag]
1690          continue
1691        else:
1692          self.log(tag, "pruned")
1693
1694  def _closure_glyphs(self, font):
1695
1696    self.glyphs = self.glyphs_requested.copy()
1697
1698    if 'cmap' in font:
1699      font['cmap'].closure_glyphs(self)
1700    self.glyphs_cmaped = self.glyphs
1701
1702    if self.options.notdef_glyph:
1703      if 'glyf' in font:
1704        self.glyphs.add(font.getGlyphName(0))
1705        self.log("Added gid0 to subset")
1706      else:
1707        self.glyphs.add('.notdef')
1708        self.log("Added .notdef to subset")
1709    if self.options.recommended_glyphs:
1710      if 'glyf' in font:
1711        for i in range(4):
1712          self.glyphs.add(font.getGlyphName(i))
1713        self.log("Added first four glyphs to subset")
1714
1715    if 'GSUB' in font:
1716      self.log("Closing glyph list over 'GSUB': %d glyphs before" %
1717                len(self.glyphs))
1718      self.log.glyphs(self.glyphs, font=font)
1719      font['GSUB'].closure_glyphs(self)
1720      self.log("Closed  glyph list over 'GSUB': %d glyphs after" %
1721                len(self.glyphs))
1722      self.log.glyphs(self.glyphs, font=font)
1723      self.log.lapse("close glyph list over 'GSUB'")
1724    self.glyphs_gsubed = self.glyphs.copy()
1725
1726    if 'glyf' in font:
1727      self.log("Closing glyph list over 'glyf': %d glyphs before" %
1728                len(self.glyphs))
1729      self.log.glyphs(self.glyphs, font=font)
1730      font['glyf'].closure_glyphs(self)
1731      self.log("Closed  glyph list over 'glyf': %d glyphs after" %
1732                len(self.glyphs))
1733      self.log.glyphs(self.glyphs, font=font)
1734      self.log.lapse("close glyph list over 'glyf'")
1735    self.glyphs_glyfed = self.glyphs.copy()
1736
1737    self.glyphs_all = self.glyphs.copy()
1738
1739    self.log("Retaining %d glyphs: " % len(self.glyphs_all))
1740
1741  def _subset_glyphs(self, font):
1742    for tag in font.keys():
1743      if tag == 'GlyphOrder': continue
1744      clazz = fontTools.ttLib.getTableClass(tag)
1745
1746      if tag in self.options.no_subset_tables:
1747        self.log(tag, "subsetting not needed")
1748      elif hasattr(clazz, 'subset_glyphs'):
1749        table = font[tag]
1750        self.glyphs = self.glyphs_all
1751        retain = table.subset_glyphs(self)
1752        self.glyphs = self.glyphs_all
1753        self.log.lapse("subset '%s'" % tag)
1754        if not retain:
1755          self.log(tag, "subsetted to empty; dropped")
1756          del font[tag]
1757        else:
1758          self.log(tag, "subsetted")
1759      else:
1760        self.log(tag, "NOT subset; don't know how to subset; dropped")
1761        del font[tag]
1762
1763    glyphOrder = font.getGlyphOrder()
1764    glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
1765    font.setGlyphOrder(glyphOrder)
1766    font._buildReverseGlyphOrderDict()
1767    self.log.lapse("subset GlyphOrder")
1768
1769  def _prune_post_subset(self, font):
1770    for tag in font.keys():
1771      if tag == 'GlyphOrder': continue
1772      clazz = fontTools.ttLib.getTableClass(tag)
1773      if hasattr(clazz, 'prune_post_subset'):
1774        table = font[tag]
1775        retain = table.prune_post_subset(self.options)
1776        self.log.lapse("prune  '%s'" % tag)
1777        if not retain:
1778          self.log(tag, "pruned to empty; dropped")
1779          del font[tag]
1780        else:
1781          self.log(tag, "pruned")
1782
1783  def subset(self, font):
1784
1785    self._prune_pre_subset(font)
1786    self._closure_glyphs(font)
1787    self._subset_glyphs(font)
1788    self._prune_post_subset(font)
1789
1790
1791class Logger(object):
1792
1793  def __init__(self, verbose=False, xml=False, timing=False):
1794    self.verbose = verbose
1795    self.xml = xml
1796    self.timing = timing
1797    self.last_time = self.start_time = time.time()
1798
1799  def parse_opts(self, argv):
1800    argv = argv[:]
1801    for v in ['verbose', 'xml', 'timing']:
1802      if "--"+v in argv:
1803        setattr(self, v, True)
1804        argv.remove("--"+v)
1805    return argv
1806
1807  def __call__(self, *things):
1808    if not self.verbose:
1809      return
1810    print ' '.join(str(x) for x in things)
1811
1812  def lapse(self, *things):
1813    if not self.timing:
1814      return
1815    new_time = time.time()
1816    print "Took %0.3fs to %s" %(new_time - self.last_time,
1817                                 ' '.join(str(x) for x in things))
1818    self.last_time = new_time
1819
1820  def glyphs(self, glyphs, font=None):
1821    self("Names: ", sorted(glyphs))
1822    if font:
1823      reverseGlyphMap = font.getReverseGlyphMap()
1824      self("Gids : ", sorted(reverseGlyphMap[g] for g in glyphs))
1825
1826  def font(self, font, file=sys.stdout):
1827    if not self.xml:
1828      return
1829    import xmlWriter
1830    writer = xmlWriter.XMLWriter(file)
1831    font.disassembleInstructions = False  # Work around ttLib bug
1832    for tag in font.keys():
1833      writer.begintag(tag)
1834      writer.newline()
1835      font[tag].toXML(writer, font)
1836      writer.endtag(tag)
1837      writer.newline()
1838
1839
1840def load_font(fontFile,
1841              options,
1842              checkChecksums=False,
1843              dontLoadGlyphNames=False):
1844
1845  font = fontTools.ttLib.TTFont(fontFile,
1846                                checkChecksums=checkChecksums,
1847                                recalcBBoxes=options.recalc_bounds)
1848
1849  # Hack:
1850  #
1851  # If we don't need glyph names, change 'post' class to not try to
1852  # load them.  It avoid lots of headache with broken fonts as well
1853  # as loading time.
1854  #
1855  # Ideally ttLib should provide a way to ask it to skip loading
1856  # glyph names.  But it currently doesn't provide such a thing.
1857  #
1858  if dontLoadGlyphNames:
1859    post = fontTools.ttLib.getTableClass('post')
1860    saved = post.decode_format_2_0
1861    post.decode_format_2_0 = post.decode_format_3_0
1862    f = font['post']
1863    if f.formatType == 2.0:
1864      f.formatType = 3.0
1865    post.decode_format_2_0 = saved
1866
1867  return font
1868
1869def save_font(font, outfile, options):
1870  if options.flavor and not hasattr(font, 'flavor'):
1871    raise Exception("fonttools version does not support flavors.")
1872  font.flavor = options.flavor
1873  font.save(outfile, reorderTables=options.canonical_order)
1874
1875def main(args):
1876
1877  log = Logger()
1878  args = log.parse_opts(args)
1879
1880  options = Options()
1881  args = options.parse_opts(args, ignore_unknown=['text'])
1882
1883  if len(args) < 2:
1884    print >>sys.stderr, "usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]..."
1885    sys.exit(1)
1886
1887  fontfile = args[0]
1888  args = args[1:]
1889
1890  dontLoadGlyphNames =(not options.glyph_names and
1891         all(any(g.startswith(p)
1892             for p in ['gid', 'glyph', 'uni', 'U+'])
1893              for g in args))
1894
1895  font = load_font(fontfile, options, dontLoadGlyphNames=dontLoadGlyphNames)
1896  subsetter = Subsetter(options=options, log=log)
1897  log.lapse("load font")
1898
1899  names = font.getGlyphNames()
1900  log.lapse("loading glyph names")
1901
1902  glyphs = []
1903  unicodes = []
1904  text = ""
1905  for g in args:
1906    if g in names:
1907      glyphs.append(g)
1908      continue
1909    if g.startswith('--text='):
1910      text += g[7:]
1911      continue
1912    if g.startswith('uni') or g.startswith('U+'):
1913      if g.startswith('uni') and len(g) > 3:
1914        g = g[3:]
1915      elif g.startswith('U+') and len(g) > 2:
1916        g = g[2:]
1917      u = int(g, 16)
1918      unicodes.append(u)
1919      continue
1920    if g.startswith('gid') or g.startswith('glyph'):
1921      if g.startswith('gid') and len(g) > 3:
1922        g = g[3:]
1923      elif g.startswith('glyph') and len(g) > 5:
1924        g = g[5:]
1925      try:
1926        glyphs.append(font.getGlyphName(int(g), requireReal=1))
1927      except ValueError:
1928        raise Exception("Invalid glyph identifier: %s" % g)
1929      continue
1930    raise Exception("Invalid glyph identifier: %s" % g)
1931  log.lapse("compile glyph list")
1932  log("Unicodes:", unicodes)
1933  log("Glyphs:", glyphs)
1934
1935  subsetter.populate(glyphs=glyphs, unicodes=unicodes, text=text)
1936  subsetter.subset(font)
1937
1938  outfile = fontfile + '.subset'
1939
1940  save_font (font, outfile, options)
1941  log.lapse("compile and save font")
1942
1943  log.last_time = log.start_time
1944  log.lapse("make one with everything(TOTAL TIME)")
1945
1946  if log.verbose:
1947    import os
1948    log("Input  font: %d bytes" % os.path.getsize(fontfile))
1949    log("Subset font: %d bytes" % os.path.getsize(outfile))
1950
1951  log.font(font)
1952
1953  font.close()
1954
1955
1956__all__ = [
1957  'Options',
1958  'Subsetter',
1959  'Logger',
1960  'load_font',
1961  'save_font',
1962  'main'
1963]
1964
1965if __name__ == '__main__':
1966  main(sys.argv[1:])
1967