blob: 5cbe0c2758a228175e9822ebb717003d7902b3e5 [file] [log] [blame]
Behdad Esfahbod54660612013-07-21 18:16:55 -04001#!/usr/bin/python
Behdad Esfahbod616d36e2013-08-13 20:02:59 -04002
Behdad Esfahbod0fe6a512013-07-23 11:17:35 -04003# Copyright 2013 Google, Inc. All Rights Reserved.
4#
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04005# Licensed under the Apache License, Version 2.0(the "License");
Behdad Esfahbod0fe6a512013-07-23 11:17:35 -04006# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Google Author(s): Behdad Esfahbod
Behdad Esfahbod616d36e2013-08-13 20:02:59 -040018
19"""Python OpenType Layout Subsetter.
20
21Later grown into full OpenType subsetter, supporting all standard tables.
22"""
23
Behdad Esfahbod54660612013-07-21 18:16:55 -040024
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040025import sys
Behdad Esfahbod4cf7a802013-07-24 16:08:35 -040026import struct
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040027import time
Behdad Esfahbodd816b7f2013-08-16 16:18:40 -040028import array
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040029
30import fontTools.ttx
Behdad Esfahbod54660612013-07-21 18:16:55 -040031
Behdad Esfahbod54660612013-07-21 18:16:55 -040032
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040033def _add_method(*clazzes):
Behdad Esfahbod616d36e2013-08-13 20:02:59 -040034 """Returns a decorator function that adds a new method to one or
35 more classes."""
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040036 def wrapper(method):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040037 for clazz in clazzes:
38 assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
Behdad Esfahbode7a0d562013-08-16 10:56:30 -040039 assert not hasattr(clazz, method.func_name), \
Behdad Esfahbodd77f1572013-08-15 19:24:36 -040040 "Oops, class '%s' has method '%s'." % (clazz.__name__,
41 method.func_name)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040042 setattr(clazz, method.func_name, method)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040043 return None
44 return wrapper
Behdad Esfahbod54660612013-07-21 18:16:55 -040045
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040046def _uniq_sort(l):
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040047 return sorted(set(l))
Behdad Esfahbod78661bb2013-07-23 10:23:42 -040048
Behdad Esfahbod42d4f2b2013-08-16 16:16:22 -040049def _set_update(s, *others):
50 # Jython's set.update only takes one other argument.
51 # Emulate real set.update...
52 for other in others:
53 s.update(other)
54
Behdad Esfahbod78661bb2013-07-23 10:23:42 -040055
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040056@_add_method(fontTools.ttLib.tables.otTables.Coverage)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040057def intersect(self, glyphs):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040058 "Returns ascending list of matching coverage values."
Behdad Esfahbod4734be52013-08-14 19:47:42 -040059 return [i for i,g in enumerate(self.glyphs) if g in glyphs]
Behdad Esfahbod610b0552013-07-23 14:52:18 -040060
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040061@_add_method(fontTools.ttLib.tables.otTables.Coverage)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040062def intersect_glyphs(self, glyphs):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040063 "Returns set of intersecting glyphs."
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040064 return set(g for g in self.glyphs if g in glyphs)
Behdad Esfahbod849d25c2013-08-12 19:24:24 -040065
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040066@_add_method(fontTools.ttLib.tables.otTables.Coverage)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040067def subset(self, glyphs):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040068 "Returns ascending list of remaining coverage values."
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040069 indices = self.intersect(glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040070 self.glyphs = [g for g in self.glyphs if g in glyphs]
71 return indices
Behdad Esfahbodd1d41bc2013-07-21 23:15:32 -040072
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040073@_add_method(fontTools.ttLib.tables.otTables.Coverage)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040074def remap(self, coverage_map):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040075 "Remaps coverage."
76 self.glyphs = [self.glyphs[i] for i in coverage_map]
Behdad Esfahbod14374262013-08-08 22:26:49 -040077
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040078@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040079def intersect(self, glyphs):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040080 "Returns ascending list of matching class values."
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040081 return _uniq_sort(
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040082 ([0] if any(g not in self.classDefs for g in glyphs) else []) +
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040083 [v for g,v in self.classDefs.iteritems() if g in glyphs])
Behdad Esfahbodb8d55882013-07-23 22:17:39 -040084
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040085@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040086def intersect_class(self, glyphs, klass):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040087 "Returns set of glyphs matching class."
88 if klass == 0:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040089 return set(g for g in glyphs if g not in self.classDefs)
90 return set(g for g,v in self.classDefs.iteritems()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040091 if v == klass and g in glyphs)
Behdad Esfahbodb8d55882013-07-23 22:17:39 -040092
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040093@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -040094def subset(self, glyphs, remap=False):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040095 "Returns ascending list of remaining classes."
Behdad Esfahbodd73f2252013-08-16 10:58:25 -040096 self.classDefs = dict((g,v) for g,v in self.classDefs.iteritems() if g in glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -040097 # Note: while class 0 has the special meaning of "not matched",
98 # if no glyph will ever /not match/, we can optimize class 0 out too.
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -040099 indices = _uniq_sort(
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400100 ([0] if any(g not in self.classDefs for g in glyphs) else []) +
Behdad Esfahbod4e5d9672013-08-14 19:49:53 -0400101 self.classDefs.values())
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400102 if remap:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400103 self.remap(indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400104 return indices
Behdad Esfahbod4aa6ce32013-07-22 12:15:36 -0400105
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400106@_add_method(fontTools.ttLib.tables.otTables.ClassDef)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400107def remap(self, class_map):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400108 "Remaps classes."
Behdad Esfahbodd73f2252013-08-16 10:58:25 -0400109 self.classDefs = dict((g,class_map.index(v))
110 for g,v in self.classDefs.iteritems())
Behdad Esfahbodd1d41bc2013-07-21 23:15:32 -0400111
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400112@_add_method(fontTools.ttLib.tables.otTables.SingleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400113def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400114 if cur_glyphs == None: cur_glyphs = s.glyphs
115 if self.Format in [1, 2]:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400116 s.glyphs.update(v for g,v in self.mapping.iteritems() if g in cur_glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400117 else:
118 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400119
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400120@_add_method(fontTools.ttLib.tables.otTables.SingleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400121def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400122 if self.Format in [1, 2]:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -0400123 self.mapping = dict((g,v) for g,v in self.mapping.iteritems()
124 if g in s.glyphs and v in s.glyphs)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400125 return bool(self.mapping)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400126 else:
127 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400128
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400129@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400130def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400131 if cur_glyphs == None: cur_glyphs = s.glyphs
132 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400133 indices = self.Coverage.intersect(cur_glyphs)
Behdad Esfahboda9bfec12013-08-16 16:21:25 -0400134 _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400135 else:
136 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400137
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400138@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400139def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400140 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400141 indices = self.Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400142 self.Sequence = [self.Sequence[i] for i in indices]
143 # Now drop rules generating glyphs we don't want
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400144 indices = [i for i,seq in enumerate(self.Sequence)
145 if all(sub in s.glyphs for sub in seq.Substitute)]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400146 self.Sequence = [self.Sequence[i] for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400147 self.Coverage.remap(indices)
148 self.SequenceCount = len(self.Sequence)
149 return bool(self.SequenceCount)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400150 else:
151 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400152
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400153@_add_method(fontTools.ttLib.tables.otTables.AlternateSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400154def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400155 if cur_glyphs == None: cur_glyphs = s.glyphs
156 if self.Format == 1:
Behdad Esfahbod42d4f2b2013-08-16 16:16:22 -0400157 _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.iteritems()
158 if g in cur_glyphs))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400159 else:
160 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400161
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400162@_add_method(fontTools.ttLib.tables.otTables.AlternateSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400163def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400164 if self.Format == 1:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -0400165 self.alternates = dict((g,vlist)
166 for g,vlist in self.alternates.iteritems()
167 if g in s.glyphs and
168 all(v in s.glyphs for v in vlist))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400169 return bool(self.alternates)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400170 else:
171 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400172
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400173@_add_method(fontTools.ttLib.tables.otTables.LigatureSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400174def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400175 if cur_glyphs == None: cur_glyphs = s.glyphs
176 if self.Format == 1:
Behdad Esfahbod42d4f2b2013-08-16 16:16:22 -0400177 _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
178 if all(c in s.glyphs for c in seq.Component)]
179 for g,seqs in self.ligatures.iteritems()
180 if g in cur_glyphs))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400181 else:
182 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400183
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400184@_add_method(fontTools.ttLib.tables.otTables.LigatureSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400185def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400186 if self.Format == 1:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -0400187 self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems()
188 if g in s.glyphs)
189 self.ligatures = dict((g,[seq for seq in seqs
190 if seq.LigGlyph in s.glyphs and
191 all(c in s.glyphs for c in seq.Component)])
192 for g,seqs in self.ligatures.iteritems())
193 self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems() if v)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400194 return bool(self.ligatures)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400195 else:
196 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400197
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400198@_add_method(fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400199def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400200 if cur_glyphs == None: cur_glyphs = s.glyphs
201 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400202 indices = self.Coverage.intersect(cur_glyphs)
203 if(not indices or
204 not all(c.intersect(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400205 for c in self.LookAheadCoverage + self.BacktrackCoverage)):
206 return
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400207 s.glyphs.update(self.Substitute[i] for i in indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400208 else:
209 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400210
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400211@_add_method(fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400212def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400213 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400214 indices = self.Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400215 self.Substitute = [self.Substitute[i] for i in indices]
216 # Now drop rules generating glyphs we don't want
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400217 indices = [i for i,sub in enumerate(self.Substitute)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400218 if sub in s.glyphs]
219 self.Substitute = [self.Substitute[i] for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400220 self.Coverage.remap(indices)
221 self.GlyphCount = len(self.Substitute)
222 return bool(self.GlyphCount and
223 all(c.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400224 for c in self.LookAheadCoverage+self.BacktrackCoverage))
225 else:
226 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400227
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400228@_add_method(fontTools.ttLib.tables.otTables.SinglePos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400229def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400230 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400231 return len(self.Coverage.subset(s.glyphs))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400232 elif self.Format == 2:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400233 indices = self.Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400234 self.Value = [self.Value[i] for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400235 self.ValueCount = len(self.Value)
236 return bool(self.ValueCount)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400237 else:
238 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400239
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400240@_add_method(fontTools.ttLib.tables.otTables.SinglePos)
241def prune_post_subset(self, options):
242 if not options.hinting:
243 # Drop device tables
244 self.ValueFormat &= ~0x00F0
245 return True
246
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400247@_add_method(fontTools.ttLib.tables.otTables.PairPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400248def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400249 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400250 indices = self.Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400251 self.PairSet = [self.PairSet[i] for i in indices]
252 for p in self.PairSet:
253 p.PairValueRecord = [r for r in p.PairValueRecord
254 if r.SecondGlyph in s.glyphs]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400255 p.PairValueCount = len(p.PairValueRecord)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400256 self.PairSet = [p for p in self.PairSet if p.PairValueCount]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400257 self.PairSetCount = len(self.PairSet)
258 return bool(self.PairSetCount)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400259 elif self.Format == 2:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400260 class1_map = self.ClassDef1.subset(s.glyphs, remap=True)
261 class2_map = self.ClassDef2.subset(s.glyphs, remap=True)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400262 self.Class1Record = [self.Class1Record[i] for i in class1_map]
263 for c in self.Class1Record:
264 c.Class2Record = [c.Class2Record[i] for i in class2_map]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400265 self.Class1Count = len(class1_map)
266 self.Class2Count = len(class2_map)
267 return bool(self.Class1Count and
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400268 self.Class2Count and
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400269 self.Coverage.subset(s.glyphs))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400270 else:
271 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400272
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400273@_add_method(fontTools.ttLib.tables.otTables.PairPos)
274def prune_post_subset(self, options):
275 if not options.hinting:
276 # Drop device tables
277 self.ValueFormat1 &= ~0x00F0
278 self.ValueFormat2 &= ~0x00F0
279 return True
280
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400281@_add_method(fontTools.ttLib.tables.otTables.CursivePos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400282def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400283 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400284 indices = self.Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400285 self.EntryExitRecord = [self.EntryExitRecord[i] for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400286 self.EntryExitCount = len(self.EntryExitRecord)
287 return bool(self.EntryExitCount)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400288 else:
289 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400290
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400291@_add_method(fontTools.ttLib.tables.otTables.Anchor)
292def prune_hints(self):
293 # Drop device tables / contour anchor point
294 self.Format = 1
295
296@_add_method(fontTools.ttLib.tables.otTables.CursivePos)
297def prune_post_subset(self, options):
298 if not options.hinting:
299 for rec in self.EntryExitRecord:
300 if rec.EntryAnchor: rec.EntryAnchor.prune_hints()
301 if rec.ExitAnchor: rec.ExitAnchor.prune_hints()
302 return True
303
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400304@_add_method(fontTools.ttLib.tables.otTables.MarkBasePos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400305def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400306 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400307 mark_indices = self.MarkCoverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400308 self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
309 for i in mark_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400310 self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
311 base_indices = self.BaseCoverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400312 self.BaseArray.BaseRecord = [self.BaseArray.BaseRecord[i]
313 for i in base_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400314 self.BaseArray.BaseCount = len(self.BaseArray.BaseRecord)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400315 # Prune empty classes
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400316 class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400317 self.ClassCount = len(class_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400318 for m in self.MarkArray.MarkRecord:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400319 m.Class = class_indices.index(m.Class)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400320 for b in self.BaseArray.BaseRecord:
321 b.BaseAnchor = [b.BaseAnchor[i] for i in class_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400322 return bool(self.ClassCount and
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400323 self.MarkArray.MarkCount and
324 self.BaseArray.BaseCount)
325 else:
326 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400327
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400328@_add_method(fontTools.ttLib.tables.otTables.MarkBasePos)
329def prune_post_subset(self, options):
330 if not options.hinting:
331 for m in self.MarkArray.MarkRecord:
332 m.MarkAnchor.prune_hints()
333 for b in self.BaseArray.BaseRecord:
334 for a in b.BaseAnchor:
335 a.prune_hints()
336 return True
337
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400338@_add_method(fontTools.ttLib.tables.otTables.MarkLigPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400339def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400340 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400341 mark_indices = self.MarkCoverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400342 self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
343 for i in mark_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400344 self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
345 ligature_indices = self.LigatureCoverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400346 self.LigatureArray.LigatureAttach = [self.LigatureArray.LigatureAttach[i]
347 for i in ligature_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400348 self.LigatureArray.LigatureCount = len(self.LigatureArray.LigatureAttach)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400349 # Prune empty classes
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400350 class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400351 self.ClassCount = len(class_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400352 for m in self.MarkArray.MarkRecord:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400353 m.Class = class_indices.index(m.Class)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400354 for l in self.LigatureArray.LigatureAttach:
355 for c in l.ComponentRecord:
356 c.LigatureAnchor = [c.LigatureAnchor[i] for i in class_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400357 return bool(self.ClassCount and
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400358 self.MarkArray.MarkCount and
359 self.LigatureArray.LigatureCount)
360 else:
361 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400362
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400363@_add_method(fontTools.ttLib.tables.otTables.MarkLigPos)
364def prune_post_subset(self, options):
365 if not options.hinting:
366 for m in self.MarkArray.MarkRecord:
367 m.MarkAnchor.prune_hints()
368 for l in self.LigatureArray.LigatureAttach:
369 for c in l.ComponentRecord:
370 for a in c.LigatureAnchor:
371 a.prune_hints()
372 return True
373
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400374@_add_method(fontTools.ttLib.tables.otTables.MarkMarkPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400375def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400376 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400377 mark1_indices = self.Mark1Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400378 self.Mark1Array.MarkRecord = [self.Mark1Array.MarkRecord[i]
379 for i in mark1_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400380 self.Mark1Array.MarkCount = len(self.Mark1Array.MarkRecord)
381 mark2_indices = self.Mark2Coverage.subset(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400382 self.Mark2Array.Mark2Record = [self.Mark2Array.Mark2Record[i]
383 for i in mark2_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400384 self.Mark2Array.MarkCount = len(self.Mark2Array.Mark2Record)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400385 # Prune empty classes
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400386 class_indices = _uniq_sort(v.Class for v in self.Mark1Array.MarkRecord)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400387 self.ClassCount = len(class_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400388 for m in self.Mark1Array.MarkRecord:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400389 m.Class = class_indices.index(m.Class)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400390 for b in self.Mark2Array.Mark2Record:
391 b.Mark2Anchor = [b.Mark2Anchor[i] for i in class_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400392 return bool(self.ClassCount and
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400393 self.Mark1Array.MarkCount and
394 self.Mark2Array.MarkCount)
395 else:
396 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400397
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400398@_add_method(fontTools.ttLib.tables.otTables.MarkMarkPos)
399def prune_post_subset(self, options):
400 if not options.hinting:
401 # Drop device tables or contour anchor point
402 for m in self.Mark1Array.MarkRecord:
403 m.MarkAnchor.prune_hints()
404 for b in self.Mark2Array.Mark2Record:
405 for m in rec.Mark2Anchor:
406 m.prune_hints()
407 return True
408
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400409@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
410 fontTools.ttLib.tables.otTables.MultipleSubst,
411 fontTools.ttLib.tables.otTables.AlternateSubst,
412 fontTools.ttLib.tables.otTables.LigatureSubst,
413 fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
414 fontTools.ttLib.tables.otTables.SinglePos,
415 fontTools.ttLib.tables.otTables.PairPos,
416 fontTools.ttLib.tables.otTables.CursivePos,
417 fontTools.ttLib.tables.otTables.MarkBasePos,
418 fontTools.ttLib.tables.otTables.MarkLigPos,
419 fontTools.ttLib.tables.otTables.MarkMarkPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400420def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400421 pass
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400422
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400423@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
424 fontTools.ttLib.tables.otTables.MultipleSubst,
425 fontTools.ttLib.tables.otTables.AlternateSubst,
426 fontTools.ttLib.tables.otTables.LigatureSubst,
427 fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
428 fontTools.ttLib.tables.otTables.SinglePos,
429 fontTools.ttLib.tables.otTables.PairPos,
430 fontTools.ttLib.tables.otTables.CursivePos,
431 fontTools.ttLib.tables.otTables.MarkBasePos,
432 fontTools.ttLib.tables.otTables.MarkLigPos,
433 fontTools.ttLib.tables.otTables.MarkMarkPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400434def collect_lookups(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400435 return []
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400436
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400437@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400438 fontTools.ttLib.tables.otTables.MultipleSubst,
439 fontTools.ttLib.tables.otTables.AlternateSubst,
440 fontTools.ttLib.tables.otTables.LigatureSubst,
441 fontTools.ttLib.tables.otTables.ContextSubst,
442 fontTools.ttLib.tables.otTables.ChainContextSubst,
443 fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
444 fontTools.ttLib.tables.otTables.SinglePos,
445 fontTools.ttLib.tables.otTables.PairPos,
446 fontTools.ttLib.tables.otTables.CursivePos,
447 fontTools.ttLib.tables.otTables.MarkBasePos,
448 fontTools.ttLib.tables.otTables.MarkLigPos,
449 fontTools.ttLib.tables.otTables.MarkMarkPos,
450 fontTools.ttLib.tables.otTables.ContextPos,
451 fontTools.ttLib.tables.otTables.ChainContextPos)
452def prune_pre_subset(self, options):
453 return True
454
455@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
456 fontTools.ttLib.tables.otTables.MultipleSubst,
457 fontTools.ttLib.tables.otTables.AlternateSubst,
458 fontTools.ttLib.tables.otTables.LigatureSubst,
459 fontTools.ttLib.tables.otTables.ReverseChainSingleSubst,
460 fontTools.ttLib.tables.otTables.ContextSubst,
461 fontTools.ttLib.tables.otTables.ChainContextSubst,
462 fontTools.ttLib.tables.otTables.ContextPos,
463 fontTools.ttLib.tables.otTables.ChainContextPos)
464def prune_post_subset(self, options):
465 return True
466
467@_add_method(fontTools.ttLib.tables.otTables.SingleSubst,
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400468 fontTools.ttLib.tables.otTables.AlternateSubst,
469 fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400470def may_have_non_1to1(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400471 return False
Behdad Esfahbodaeacc152013-08-12 20:24:33 -0400472
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400473@_add_method(fontTools.ttLib.tables.otTables.MultipleSubst,
474 fontTools.ttLib.tables.otTables.LigatureSubst,
475 fontTools.ttLib.tables.otTables.ContextSubst,
476 fontTools.ttLib.tables.otTables.ChainContextSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400477def may_have_non_1to1(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400478 return True
Behdad Esfahbodaeacc152013-08-12 20:24:33 -0400479
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400480@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
481 fontTools.ttLib.tables.otTables.ChainContextSubst,
482 fontTools.ttLib.tables.otTables.ContextPos,
483 fontTools.ttLib.tables.otTables.ChainContextPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400484def __classify_context(self):
Behdad Esfahbodb178dca2013-07-23 22:51:50 -0400485
Behdad Esfahbod3d4c4712013-08-13 20:10:17 -0400486 class ContextHelper(object):
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400487 def __init__(self, klass, Format):
488 if klass.__name__.endswith('Subst'):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400489 Typ = 'Sub'
490 Type = 'Subst'
491 else:
492 Typ = 'Pos'
493 Type = 'Pos'
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400494 if klass.__name__.startswith('Chain'):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400495 Chain = 'Chain'
496 else:
497 Chain = ''
498 ChainTyp = Chain+Typ
Behdad Esfahbod9e735722013-07-23 16:35:23 -0400499
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400500 self.Typ = Typ
501 self.Type = Type
502 self.Chain = Chain
503 self.ChainTyp = ChainTyp
Behdad Esfahbod9e735722013-07-23 16:35:23 -0400504
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400505 self.LookupRecord = Type+'LookupRecord'
Behdad Esfahbod9e735722013-07-23 16:35:23 -0400506
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400507 if Format == 1:
508 Coverage = lambda r: r.Coverage
509 ChainCoverage = lambda r: r.Coverage
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400510 ContextData = lambda r:(None,)
511 ChainContextData = lambda r:(None, None, None)
512 RuleData = lambda r:(r.Input,)
513 ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400514 SetRuleData = None
515 ChainSetRuleData = None
516 elif Format == 2:
517 Coverage = lambda r: r.Coverage
518 ChainCoverage = lambda r: r.Coverage
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400519 ContextData = lambda r:(r.ClassDef,)
520 ChainContextData = lambda r:(r.LookAheadClassDef,
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400521 r.InputClassDef,
522 r.BacktrackClassDef)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400523 RuleData = lambda r:(r.Class,)
524 ChainRuleData = lambda r:(r.LookAhead, r.Input, r.Backtrack)
525 def SetRuleData(r, d):(r.Class,) = d
526 def ChainSetRuleData(r, d):(r.LookAhead, r.Input, r.Backtrack) = d
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400527 elif Format == 3:
528 Coverage = lambda r: r.Coverage[0]
529 ChainCoverage = lambda r: r.InputCoverage[0]
530 ContextData = None
531 ChainContextData = None
532 RuleData = lambda r: r.Coverage
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400533 ChainRuleData = lambda r:(r.LookAheadCoverage +
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400534 r.InputCoverage +
535 r.BacktrackCoverage)
536 SetRuleData = None
537 ChainSetRuleData = None
538 else:
539 assert 0, "unknown format: %s" % Format
Behdad Esfahbod452ab6c2013-07-23 22:57:43 -0400540
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400541 if Chain:
542 self.Coverage = ChainCoverage
543 self.ContextData = ChainContextData
544 self.RuleData = ChainRuleData
545 self.SetRuleData = ChainSetRuleData
546 else:
547 self.Coverage = Coverage
548 self.ContextData = ContextData
549 self.RuleData = RuleData
550 self.SetRuleData = SetRuleData
Behdad Esfahbod9e735722013-07-23 16:35:23 -0400551
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400552 if Format == 1:
553 self.Rule = ChainTyp+'Rule'
554 self.RuleCount = ChainTyp+'RuleCount'
555 self.RuleSet = ChainTyp+'RuleSet'
556 self.RuleSetCount = ChainTyp+'RuleSetCount'
557 self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
558 elif Format == 2:
559 self.Rule = ChainTyp+'ClassRule'
560 self.RuleCount = ChainTyp+'ClassRuleCount'
561 self.RuleSet = ChainTyp+'ClassSet'
562 self.RuleSetCount = ChainTyp+'ClassSetCount'
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400563 self.Intersect = lambda glyphs, c, r: c.intersect_class(glyphs, r)
Behdad Esfahbod89987002013-07-23 23:07:42 -0400564
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400565 self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
Behdad Esfahbod11763302013-08-14 15:33:08 -0400566 self.Input = 'Input' if Chain else 'Class'
Behdad Esfahbod27108392013-07-23 16:40:47 -0400567
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400568 if self.Format not in [1, 2, 3]:
Behdad Esfahbod318adc02013-08-13 20:09:28 -0400569 return None # Don't shoot the messenger; let it go
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400570 if not hasattr(self.__class__, "__ContextHelpers"):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400571 self.__class__.__ContextHelpers = {}
572 if self.Format not in self.__class__.__ContextHelpers:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400573 helper = ContextHelper(self.__class__, self.Format)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400574 self.__class__.__ContextHelpers[self.Format] = helper
575 return self.__class__.__ContextHelpers[self.Format]
Behdad Esfahbod44c2b3c2013-07-23 16:00:32 -0400576
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400577@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
578 fontTools.ttLib.tables.otTables.ChainContextSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400579def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400580 if cur_glyphs == None: cur_glyphs = s.glyphs
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400581 c = self.__classify_context()
Behdad Esfahbod1ab2dbf2013-07-23 17:17:21 -0400582
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400583 indices = c.Coverage(self).intersect(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400584 if not indices:
585 return []
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400586 cur_glyphs = c.Coverage(self).intersect_glyphs(s.glyphs);
Behdad Esfahbod1d4fa132013-08-08 22:59:32 -0400587
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400588 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400589 ContextData = c.ContextData(self)
590 rss = getattr(self, c.RuleSet)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400591 for i in indices:
592 if not rss[i]: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400593 for r in getattr(rss[i], c.Rule):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400594 if not r: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400595 if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
596 for cd,klist in zip(ContextData, c.RuleData(r))):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400597 chaos = False
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400598 for ll in getattr(r, c.LookupRecord):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400599 if not ll: continue
600 seqi = ll.SequenceIndex
601 if seqi == 0:
Behdad Esfahbodd3fdcc72013-08-14 17:59:31 -0400602 pos_glyphs = set([c.Coverage(self).glyphs[i]])
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400603 else:
604 if chaos:
605 pos_glyphs = s.glyphs
606 else:
Behdad Esfahbodd3fdcc72013-08-14 17:59:31 -0400607 pos_glyphs = set([r.Input[seqi - 1]])
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400608 lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400609 chaos = chaos or lookup.may_have_non_1to1()
610 lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400611 elif self.Format == 2:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400612 ClassDef = getattr(self, c.ClassDef)
613 indices = ClassDef.intersect(cur_glyphs)
614 ContextData = c.ContextData(self)
615 rss = getattr(self, c.RuleSet)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400616 for i in indices:
617 if not rss[i]: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400618 for r in getattr(rss[i], c.Rule):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400619 if not r: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400620 if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
621 for cd,klist in zip(ContextData, c.RuleData(r))):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400622 chaos = False
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400623 for ll in getattr(r, c.LookupRecord):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400624 if not ll: continue
625 seqi = ll.SequenceIndex
626 if seqi == 0:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400627 pos_glyphs = ClassDef.intersect_class(cur_glyphs, i)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400628 else:
629 if chaos:
630 pos_glyphs = s.glyphs
631 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400632 pos_glyphs = ClassDef.intersect_class(s.glyphs,
Behdad Esfahbod11763302013-08-14 15:33:08 -0400633 getattr(r, c.Input)[seqi - 1])
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400634 lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400635 chaos = chaos or lookup.may_have_non_1to1()
636 lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400637 elif self.Format == 3:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400638 if not all(x.intersect(s.glyphs) for x in c.RuleData(self)):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400639 return []
640 r = self
641 chaos = False
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400642 for ll in getattr(r, c.LookupRecord):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400643 if not ll: continue
644 seqi = ll.SequenceIndex
645 if seqi == 0:
646 pos_glyphs = cur_glyphs
647 else:
648 if chaos:
649 pos_glyphs = s.glyphs
650 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400651 pos_glyphs = r.InputCoverage[seqi].intersect_glyphs(s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400652 lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400653 chaos = chaos or lookup.may_have_non_1to1()
654 lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400655 else:
656 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod00776972013-07-23 15:33:00 -0400657
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400658@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
659 fontTools.ttLib.tables.otTables.ContextPos,
660 fontTools.ttLib.tables.otTables.ChainContextSubst,
661 fontTools.ttLib.tables.otTables.ChainContextPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400662def subset_glyphs(self, s):
663 c = self.__classify_context()
Behdad Esfahbodd8c7e102013-07-23 17:07:06 -0400664
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400665 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400666 indices = self.Coverage.subset(s.glyphs)
667 rss = getattr(self, c.RuleSet)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400668 rss = [rss[i] for i in indices]
669 for rs in rss:
670 if not rs: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400671 ss = getattr(rs, c.Rule)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400672 ss = [r for r in ss
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400673 if r and all(all(g in s.glyphs for g in glist)
674 for glist in c.RuleData(r))]
675 setattr(rs, c.Rule, ss)
676 setattr(rs, c.RuleCount, len(ss))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400677 # Prune empty subrulesets
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400678 rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
679 setattr(self, c.RuleSet, rss)
680 setattr(self, c.RuleSetCount, len(rss))
681 return bool(rss)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400682 elif self.Format == 2:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400683 if not self.Coverage.subset(s.glyphs):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400684 return False
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400685 indices = getattr(self, c.ClassDef).subset(self.Coverage.glyphs,
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400686 remap=False)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400687 rss = getattr(self, c.RuleSet)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400688 rss = [rss[i] for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400689 ContextData = c.ContextData(self)
690 klass_maps = [x.subset(s.glyphs, remap=True) for x in ContextData]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400691 for rs in rss:
692 if not rs: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400693 ss = getattr(rs, c.Rule)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400694 ss = [r for r in ss
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400695 if r and all(all(k in klass_map for k in klist)
696 for klass_map,klist in zip(klass_maps, c.RuleData(r)))]
697 setattr(rs, c.Rule, ss)
698 setattr(rs, c.RuleCount, len(ss))
Behdad Esfahbode9a3bd62013-07-23 22:41:11 -0400699
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400700 # Remap rule classes
701 for r in ss:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400702 c.SetRuleData(r, [[klass_map.index(k) for k in klist]
703 for klass_map,klist in zip(klass_maps, c.RuleData(r))])
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400704 # Prune empty subrulesets
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400705 rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
706 setattr(self, c.RuleSet, rss)
707 setattr(self, c.RuleSetCount, len(rss))
708 return bool(rss)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400709 elif self.Format == 3:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400710 return all(x.subset(s.glyphs) for x in c.RuleData(self))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400711 else:
712 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400713
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400714@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
715 fontTools.ttLib.tables.otTables.ChainContextSubst,
716 fontTools.ttLib.tables.otTables.ContextPos,
717 fontTools.ttLib.tables.otTables.ChainContextPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400718def subset_lookups(self, lookup_indices):
719 c = self.__classify_context()
Behdad Esfahbod44c2b3c2013-07-23 16:00:32 -0400720
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400721 if self.Format in [1, 2]:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400722 for rs in getattr(self, c.RuleSet):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400723 if not rs: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400724 for r in getattr(rs, c.Rule):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400725 if not r: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400726 setattr(r, c.LookupRecord,
727 [ll for ll in getattr(r, c.LookupRecord)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400728 if ll and ll.LookupListIndex in lookup_indices])
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400729 for ll in getattr(r, c.LookupRecord):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400730 if not ll: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400731 ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400732 elif self.Format == 3:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400733 setattr(self, c.LookupRecord,
734 [ll for ll in getattr(self, c.LookupRecord)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400735 if ll and ll.LookupListIndex in lookup_indices])
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400736 for ll in getattr(self, c.LookupRecord):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400737 if not ll: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400738 ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400739 else:
740 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod59dfc132013-07-23 15:39:20 -0400741
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400742@_add_method(fontTools.ttLib.tables.otTables.ContextSubst,
743 fontTools.ttLib.tables.otTables.ChainContextSubst,
744 fontTools.ttLib.tables.otTables.ContextPos,
745 fontTools.ttLib.tables.otTables.ChainContextPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400746def collect_lookups(self):
747 c = self.__classify_context()
Behdad Esfahbod44c2b3c2013-07-23 16:00:32 -0400748
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400749 if self.Format in [1, 2]:
750 return [ll.LookupListIndex
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400751 for rs in getattr(self, c.RuleSet) if rs
752 for r in getattr(rs, c.Rule) if r
753 for ll in getattr(r, c.LookupRecord) if ll]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400754 elif self.Format == 3:
755 return [ll.LookupListIndex
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400756 for ll in getattr(self, c.LookupRecord) if ll]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400757 else:
758 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod59dfc132013-07-23 15:39:20 -0400759
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400760@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400761def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400762 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400763 self.ExtSubTable.closure_glyphs(s, cur_glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400764 else:
765 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400766
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400767@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400768def may_have_non_1to1(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400769 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400770 return self.ExtSubTable.may_have_non_1to1()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400771 else:
772 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbodaeacc152013-08-12 20:24:33 -0400773
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400774@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
775 fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400776def prune_pre_subset(self, options):
777 if self.Format == 1:
778 return self.ExtSubTable.prune_pre_subset(options)
779 else:
780 assert 0, "unknown format: %s" % self.Format
781
782@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
783 fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400784def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400785 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400786 return self.ExtSubTable.subset_glyphs(s)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400787 else:
788 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod54660612013-07-21 18:16:55 -0400789
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400790@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
791 fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400792def prune_post_subset(self, options):
793 if self.Format == 1:
794 return self.ExtSubTable.prune_post_subset(options)
795 else:
796 assert 0, "unknown format: %s" % self.Format
797
798@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
799 fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400800def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400801 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400802 return self.ExtSubTable.subset_lookups(lookup_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400803 else:
804 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400805
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400806@_add_method(fontTools.ttLib.tables.otTables.ExtensionSubst,
807 fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400808def collect_lookups(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400809 if self.Format == 1:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400810 return self.ExtSubTable.collect_lookups()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400811 else:
812 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400813
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400814@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400815def closure_glyphs(self, s, cur_glyphs=None):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400816 for st in self.SubTable:
817 if not st: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400818 st.closure_glyphs(s, cur_glyphs)
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400819
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400820@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400821def prune_pre_subset(self, options):
822 ret = False
823 for st in self.SubTable:
824 if not st: continue
825 if st.prune_pre_subset(options): ret = True
826 return ret
827
828@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400829def subset_glyphs(self, s):
830 self.SubTable = [st for st in self.SubTable if st and st.subset_glyphs(s)]
831 self.SubTableCount = len(self.SubTable)
832 return bool(self.SubTableCount)
Behdad Esfahbodd1d41bc2013-07-21 23:15:32 -0400833
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400834@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400835def prune_post_subset(self, options):
836 ret = False
837 for st in self.SubTable:
838 if not st: continue
839 if st.prune_post_subset(options): ret = True
840 return ret
841
842@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400843def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400844 for s in self.SubTable:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400845 s.subset_lookups(lookup_indices)
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400846
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400847@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400848def collect_lookups(self):
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400849 return _uniq_sort(sum((st.collect_lookups() for st in self.SubTable
850 if st), []))
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400851
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400852@_add_method(fontTools.ttLib.tables.otTables.Lookup)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400853def may_have_non_1to1(self):
854 return any(st.may_have_non_1to1() for st in self.SubTable if st)
Behdad Esfahbodaeacc152013-08-12 20:24:33 -0400855
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400856@_add_method(fontTools.ttLib.tables.otTables.LookupList)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400857def prune_pre_subset(self, options):
858 ret = False
859 for l in self.Lookup:
860 if not l: continue
861 if l.prune_pre_subset(options): ret = True
862 return ret
863
864@_add_method(fontTools.ttLib.tables.otTables.LookupList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400865def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400866 "Returns the indices of nonempty lookups."
Behdad Esfahbod4734be52013-08-14 19:47:42 -0400867 return [i for i,l in enumerate(self.Lookup) if l and l.subset_glyphs(s)]
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400868
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400869@_add_method(fontTools.ttLib.tables.otTables.LookupList)
Behdad Esfahbodd77f1572013-08-15 19:24:36 -0400870def prune_post_subset(self, options):
871 ret = False
872 for l in self.Lookup:
873 if not l: continue
874 if l.prune_post_subset(options): ret = True
875 return ret
876
877@_add_method(fontTools.ttLib.tables.otTables.LookupList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400878def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400879 self.Lookup = [self.Lookup[i] for i in lookup_indices
880 if i < self.LookupCount]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400881 self.LookupCount = len(self.Lookup)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400882 for l in self.Lookup:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400883 l.subset_lookups(lookup_indices)
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400884
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400885@_add_method(fontTools.ttLib.tables.otTables.LookupList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400886def closure_lookups(self, lookup_indices):
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400887 lookup_indices = _uniq_sort(lookup_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400888 recurse = lookup_indices
889 while True:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400890 recurse_lookups = sum((self.Lookup[i].collect_lookups()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400891 for i in recurse if i < self.LookupCount), [])
892 recurse_lookups = [l for l in recurse_lookups
893 if l not in lookup_indices and l < self.LookupCount]
894 if not recurse_lookups:
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400895 return _uniq_sort(lookup_indices)
896 recurse_lookups = _uniq_sort(recurse_lookups)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400897 lookup_indices.extend(recurse_lookups)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400898 recurse = recurse_lookups
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400899
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400900@_add_method(fontTools.ttLib.tables.otTables.Feature)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400901def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400902 self.LookupListIndex = [l for l in self.LookupListIndex
903 if l in lookup_indices]
904 # Now map them.
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400905 self.LookupListIndex = [lookup_indices.index(l)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400906 for l in self.LookupListIndex]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400907 self.LookupCount = len(self.LookupListIndex)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400908 return self.LookupCount
Behdad Esfahbod54660612013-07-21 18:16:55 -0400909
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400910@_add_method(fontTools.ttLib.tables.otTables.Feature)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400911def collect_lookups(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400912 return self.LookupListIndex[:]
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400913
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400914@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400915def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400916 "Returns the indices of nonempty features."
Behdad Esfahbod4734be52013-08-14 19:47:42 -0400917 feature_indices = [i for i,f in enumerate(self.FeatureRecord)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400918 if f.Feature.subset_lookups(lookup_indices)]
919 self.subset_features(feature_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400920 return feature_indices
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400921
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400922@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400923def collect_lookups(self, feature_indices):
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400924 return _uniq_sort(sum((self.FeatureRecord[i].Feature.collect_lookups()
925 for i in feature_indices
Behdad Esfahbod1ee298d2013-08-13 20:07:09 -0400926 if i < self.FeatureCount), []))
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400927
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400928@_add_method(fontTools.ttLib.tables.otTables.FeatureList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400929def subset_features(self, feature_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400930 self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400931 self.FeatureCount = len(self.FeatureRecord)
932 return bool(self.FeatureCount)
Behdad Esfahbod356c42e2013-07-23 12:10:46 -0400933
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400934@_add_method(fontTools.ttLib.tables.otTables.DefaultLangSys,
935 fontTools.ttLib.tables.otTables.LangSys)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400936def subset_features(self, feature_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400937 if self.ReqFeatureIndex in feature_indices:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400938 self.ReqFeatureIndex = feature_indices.index(self.ReqFeatureIndex)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400939 else:
940 self.ReqFeatureIndex = 65535
941 self.FeatureIndex = [f for f in self.FeatureIndex if f in feature_indices]
942 # Now map them.
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400943 self.FeatureIndex = [feature_indices.index(f) for f in self.FeatureIndex
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400944 if f in feature_indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400945 self.FeatureCount = len(self.FeatureIndex)
946 return bool(self.FeatureCount or self.ReqFeatureIndex != 65535)
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400947
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400948@_add_method(fontTools.ttLib.tables.otTables.DefaultLangSys,
949 fontTools.ttLib.tables.otTables.LangSys)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400950def collect_features(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400951 feature_indices = self.FeatureIndex[:]
952 if self.ReqFeatureIndex != 65535:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400953 feature_indices.append(self.ReqFeatureIndex)
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400954 return _uniq_sort(feature_indices)
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400955
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400956@_add_method(fontTools.ttLib.tables.otTables.Script)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400957def subset_features(self, feature_indices):
958 if(self.DefaultLangSys and
959 not self.DefaultLangSys.subset_features(feature_indices)):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400960 self.DefaultLangSys = None
961 self.LangSysRecord = [l for l in self.LangSysRecord
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400962 if l.LangSys.subset_features(feature_indices)]
963 self.LangSysCount = len(self.LangSysRecord)
964 return bool(self.LangSysCount or self.DefaultLangSys)
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400965
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400966@_add_method(fontTools.ttLib.tables.otTables.Script)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400967def collect_features(self):
968 feature_indices = [l.LangSys.collect_features() for l in self.LangSysRecord]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400969 if self.DefaultLangSys:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400970 feature_indices.append(self.DefaultLangSys.collect_features())
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400971 return _uniq_sort(sum(feature_indices, []))
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400972
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400973@_add_method(fontTools.ttLib.tables.otTables.ScriptList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400974def subset_features(self, feature_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400975 self.ScriptRecord = [s for s in self.ScriptRecord
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400976 if s.Script.subset_features(feature_indices)]
977 self.ScriptCount = len(self.ScriptRecord)
978 return bool(self.ScriptCount)
Behdad Esfahbod77cda412013-07-22 11:46:50 -0400979
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400980@_add_method(fontTools.ttLib.tables.otTables.ScriptList)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400981def collect_features(self):
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400982 return _uniq_sort(sum((s.Script.collect_features()
983 for s in self.ScriptRecord), []))
Behdad Esfahbod78661bb2013-07-23 10:23:42 -0400984
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -0400985@_add_method(fontTools.ttLib.getTableClass('GSUB'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400986def closure_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400987 s.table = self.table
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400988 feature_indices = self.table.ScriptList.collect_features()
989 lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400990 while True:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400991 orig_glyphs = s.glyphs.copy()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400992 for i in lookup_indices:
993 if i >= self.table.LookupList.LookupCount: continue
994 if not self.table.LookupList.Lookup[i]: continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -0400995 self.table.LookupList.Lookup[i].closure_glyphs(s)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -0400996 if orig_glyphs == s.glyphs:
997 break
998 del s.table
Behdad Esfahbod610b0552013-07-23 14:52:18 -0400999
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001000@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1001 fontTools.ttLib.getTableClass('GPOS'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001002def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001003 s.glyphs = s.glyphs_gsubed
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001004 lookup_indices = self.table.LookupList.subset_glyphs(s)
1005 self.subset_lookups(lookup_indices)
1006 self.prune_lookups()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001007 return True
Behdad Esfahbod02b92062013-07-21 18:40:59 -04001008
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001009@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1010 fontTools.ttLib.getTableClass('GPOS'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001011def subset_lookups(self, lookup_indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001012 """Retrains specified lookups, then removes empty features, language
1013 systems, and scripts."""
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001014 self.table.LookupList.subset_lookups(lookup_indices)
1015 feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
1016 self.table.ScriptList.subset_features(feature_indices)
Behdad Esfahbod77cda412013-07-22 11:46:50 -04001017
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001018@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1019 fontTools.ttLib.getTableClass('GPOS'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001020def prune_lookups(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001021 "Remove unreferenced lookups"
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001022 feature_indices = self.table.ScriptList.collect_features()
1023 lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
1024 lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
1025 self.subset_lookups(lookup_indices)
Behdad Esfahbod78661bb2013-07-23 10:23:42 -04001026
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001027@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1028 fontTools.ttLib.getTableClass('GPOS'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001029def subset_feature_tags(self, feature_tags):
Behdad Esfahbod4734be52013-08-14 19:47:42 -04001030 feature_indices = [i for i,f in
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001031 enumerate(self.table.FeatureList.FeatureRecord)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001032 if f.FeatureTag in feature_tags]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001033 self.table.FeatureList.subset_features(feature_indices)
1034 self.table.ScriptList.subset_features(feature_indices)
Behdad Esfahbod356c42e2013-07-23 12:10:46 -04001035
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001036@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1037 fontTools.ttLib.getTableClass('GPOS'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001038def prune_pre_subset(self, options):
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001039 if '*' not in options.layout_features:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001040 self.subset_feature_tags(options.layout_features)
1041 self.prune_lookups()
Behdad Esfahbodd77f1572013-08-15 19:24:36 -04001042 self.table.LookupList.prune_pre_subset(options);
1043 return True
1044
1045@_add_method(fontTools.ttLib.getTableClass('GSUB'),
1046 fontTools.ttLib.getTableClass('GPOS'))
1047def prune_post_subset(self, options):
1048 self.table.LookupList.prune_post_subset(options);
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001049 return True
Behdad Esfahbod356c42e2013-07-23 12:10:46 -04001050
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001051@_add_method(fontTools.ttLib.getTableClass('GDEF'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001052def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001053 glyphs = s.glyphs_gsubed
1054 table = self.table
1055 if table.LigCaretList:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001056 indices = table.LigCaretList.Coverage.subset(glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001057 table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i]
1058 for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001059 table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001060 if not table.LigCaretList.LigGlyphCount:
1061 table.LigCaretList = None
1062 if table.MarkAttachClassDef:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001063 table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
1064 table.MarkAttachClassDef.
1065 classDefs.iteritems()
1066 if g in glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001067 if not table.MarkAttachClassDef.classDefs:
1068 table.MarkAttachClassDef = None
1069 if table.GlyphClassDef:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001070 table.GlyphClassDef.classDefs = dict((g,v) for g,v in
1071 table.GlyphClassDef.
1072 classDefs.iteritems()
1073 if g in glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001074 if not table.GlyphClassDef.classDefs:
1075 table.GlyphClassDef = None
1076 if table.AttachList:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001077 indices = table.AttachList.Coverage.subset(glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001078 table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
1079 for i in indices]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001080 table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001081 if not table.AttachList.GlyphCount:
1082 table.AttachList = None
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001083 return bool(table.LigCaretList or
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001084 table.MarkAttachClassDef or
1085 table.GlyphClassDef or
1086 table.AttachList)
Behdad Esfahbodefb984a2013-07-21 22:26:16 -04001087
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001088@_add_method(fontTools.ttLib.getTableClass('kern'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001089def prune_pre_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001090 # Prune unknown kern table types
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001091 self.kernTables = [t for t in self.kernTables if hasattr(t, 'kernTable')]
1092 return bool(self.kernTables)
Behdad Esfahbodd4e33a72013-07-24 18:51:05 -04001093
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001094@_add_method(fontTools.ttLib.getTableClass('kern'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001095def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001096 glyphs = s.glyphs_gsubed
1097 for t in self.kernTables:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001098 t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.iteritems()
1099 if a in glyphs and b in glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001100 self.kernTables = [t for t in self.kernTables if t.kernTable]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001101 return bool(self.kernTables)
Behdad Esfahbodefb984a2013-07-21 22:26:16 -04001102
Behdad Esfahbode7a0d562013-08-16 10:56:30 -04001103@_add_method(fontTools.ttLib.getTableClass('vmtx'),
1104 fontTools.ttLib.getTableClass('hmtx'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001105def subset_glyphs(self, s):
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001106 self.metrics = dict((g,v) for g,v in self.metrics.iteritems() if g in s.glyphs)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001107 return bool(self.metrics)
Behdad Esfahbodc7160442013-07-22 14:29:08 -04001108
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001109@_add_method(fontTools.ttLib.getTableClass('hdmx'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001110def subset_glyphs(self, s):
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001111 self.hdmx = dict((sz,_dict((g,v) for g,v in l.iteritems() if g in s.glyphs))
1112 for sz,l in self.hdmx.iteritems())
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001113 return bool(self.hdmx)
Behdad Esfahbod75e14fc2013-07-22 14:49:54 -04001114
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001115@_add_method(fontTools.ttLib.getTableClass('VORG'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001116def subset_glyphs(self, s):
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001117 self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.iteritems()
1118 if g in s.glyphs)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001119 self.numVertOriginYMetrics = len(self.VOriginRecords)
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001120 return True # Never drop; has default metrics
Behdad Esfahbode45d6af2013-07-22 15:29:17 -04001121
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001122@_add_method(fontTools.ttLib.getTableClass('post'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001123def prune_pre_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001124 if not options.glyph_names:
1125 self.formatType = 3.0
1126 return True
Behdad Esfahbod42648242013-07-23 12:56:06 -04001127
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001128@_add_method(fontTools.ttLib.getTableClass('post'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001129def subset_glyphs(self, s):
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001130 self.extraNames = [] # This seems to do it
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001131 return True
Behdad Esfahbod653e9742013-07-22 15:17:12 -04001132
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001133@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001134def getComponentNamesFast(self, glyfTable):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001135 if struct.unpack(">h", self.data[:2])[0] >= 0:
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001136 return [] # Not composite
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001137 data = self.data
1138 i = 10
1139 components = []
1140 more = 1
1141 while more:
1142 flags, glyphID = struct.unpack(">HH", data[i:i+4])
1143 i += 4
1144 flags = int(flags)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001145 components.append(glyfTable.getGlyphName(int(glyphID)))
Behdad Esfahbod4cf7a802013-07-24 16:08:35 -04001146
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001147 if flags & 0x0001: i += 4 # ARG_1_AND_2_ARE_WORDS
Behdad Esfahbod574ce792013-08-13 20:51:44 -04001148 else: i += 2
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001149 if flags & 0x0008: i += 2 # WE_HAVE_A_SCALE
1150 elif flags & 0x0040: i += 4 # WE_HAVE_AN_X_AND_Y_SCALE
1151 elif flags & 0x0080: i += 8 # WE_HAVE_A_TWO_BY_TWO
1152 more = flags & 0x0020 # MORE_COMPONENTS
1153
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001154 return components
Behdad Esfahbod4cf7a802013-07-24 16:08:35 -04001155
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001156@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001157def remapComponentsFast(self, indices):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001158 if struct.unpack(">h", self.data[:2])[0] >= 0:
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001159 return # Not composite
Behdad Esfahbodd816b7f2013-08-16 16:18:40 -04001160 data = array.array("B", self.data)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001161 i = 10
1162 more = 1
1163 while more:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001164 flags =(data[i] << 8) | data[i+1]
1165 glyphID =(data[i+2] << 8) | data[i+3]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001166 # Remap
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001167 glyphID = indices.index(glyphID)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001168 data[i+2] = glyphID >> 8
1169 data[i+3] = glyphID & 0xFF
1170 i += 4
1171 flags = int(flags)
Behdad Esfahbod4cf7a802013-07-24 16:08:35 -04001172
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001173 if flags & 0x0001: i += 4 # ARG_1_AND_2_ARE_WORDS
Behdad Esfahbod574ce792013-08-13 20:51:44 -04001174 else: i += 2
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001175 if flags & 0x0008: i += 2 # WE_HAVE_A_SCALE
1176 elif flags & 0x0040: i += 4 # WE_HAVE_AN_X_AND_Y_SCALE
1177 elif flags & 0x0080: i += 8 # WE_HAVE_A_TWO_BY_TWO
1178 more = flags & 0x0020 # MORE_COMPONENTS
1179
Behdad Esfahbodd816b7f2013-08-16 16:18:40 -04001180 self.data = data.tostring()
Behdad Esfahbod4cf7a802013-07-24 16:08:35 -04001181
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001182@_add_method(fontTools.ttLib.getTableModule('glyf').Glyph)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001183def dropInstructionsFast(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001184 numContours = struct.unpack(">h", self.data[:2])[0]
Behdad Esfahbodd816b7f2013-08-16 16:18:40 -04001185 data = array.array("B", self.data)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001186 i = 10
1187 if numContours >= 0:
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001188 i += 2 * numContours # endPtsOfContours
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001189 instructionLen =(data[i] << 8) | data[i+1]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001190 # Zero it
1191 data[i] = data [i+1] = 0
1192 i += 2
1193 if instructionLen:
1194 # Splice it out
1195 data = data[:i] + data[i+instructionLen:]
1196 else:
1197 more = 1
1198 while more:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001199 flags =(data[i] << 8) | data[i+1]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001200 # Turn instruction flag off
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001201 flags &= ~0x0100 # WE_HAVE_INSTRUCTIONS
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001202 data[i+0] = flags >> 8
1203 data[i+1] = flags & 0xFF
1204 i += 4
1205 flags = int(flags)
Behdad Esfahbod6ec88542013-07-24 16:52:47 -04001206
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001207 if flags & 0x0001: i += 4 # ARG_1_AND_2_ARE_WORDS
Behdad Esfahbod574ce792013-08-13 20:51:44 -04001208 else: i += 2
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001209 if flags & 0x0008: i += 2 # WE_HAVE_A_SCALE
1210 elif flags & 0x0040: i += 4 # WE_HAVE_AN_X_AND_Y_SCALE
1211 elif flags & 0x0080: i += 8 # WE_HAVE_A_TWO_BY_TWO
1212 more = flags & 0x0020 # MORE_COMPONENTS
1213
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001214 # Cut off
1215 data = data[:i]
1216 if len(data) % 4:
1217 # add pad bytes
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001218 nPadBytes = 4 -(len(data) % 4)
1219 for i in range(nPadBytes):
1220 data.append(0)
Behdad Esfahbodd816b7f2013-08-16 16:18:40 -04001221 self.data = data.tostring()
Behdad Esfahbod6ec88542013-07-24 16:52:47 -04001222
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001223@_add_method(fontTools.ttLib.getTableClass('glyf'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001224def closure_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001225 decompose = s.glyphs
1226 # I don't know if component glyphs can be composite themselves.
1227 # We handle them anyway.
1228 while True:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001229 components = set()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001230 for g in decompose:
1231 if g not in self.glyphs:
1232 continue
1233 gl = self.glyphs[g]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001234 if hasattr(gl, "data"):
1235 for c in gl.getComponentNamesFast(self):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001236 if c not in s.glyphs:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001237 components.add(c)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001238 else:
1239 # TTX seems to expand gid0..3 always
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001240 if gl.isComposite():
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001241 for c in gl.components:
1242 if c.glyphName not in s.glyphs:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001243 components.add(c.glyphName)
1244 components = set(c for c in components if c not in s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001245 if not components:
1246 break
1247 decompose = components
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001248 s.glyphs.update(components)
Behdad Esfahbodabb50a12013-07-23 12:58:37 -04001249
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001250@_add_method(fontTools.ttLib.getTableClass('glyf'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001251def subset_glyphs(self, s):
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001252 self.glyphs = dict((g,v) for g,v in self.glyphs.iteritems() if g in s.glyphs)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001253 indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001254 for v in self.glyphs.itervalues():
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001255 if hasattr(v, "data"):
1256 v.remapComponentsFast(indices)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001257 else:
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001258 pass # No need
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001259 self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001260 return bool(self.glyphs)
Behdad Esfahbod861d9152013-07-22 16:47:24 -04001261
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001262@_add_method(fontTools.ttLib.getTableClass('glyf'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001263def prune_post_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001264 if not options.hinting:
1265 for v in self.glyphs.itervalues():
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001266 if hasattr(v, "data"):
1267 v.dropInstructionsFast()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001268 else:
1269 v.program = fontTools.ttLib.tables.ttProgram.Program()
1270 v.program.fromBytecode([])
1271 return True
Behdad Esfahboded98c612013-07-23 12:37:41 -04001272
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001273@_add_method(fontTools.ttLib.getTableClass('CFF '))
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001274def prune_pre_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001275 cff = self.cff
1276 # CFF table should have one font only
1277 cff.fontNames = cff.fontNames[:1]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001278 return bool(cff.fontNames)
Behdad Esfahbod1a4e72e2013-08-13 15:46:37 -04001279
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001280@_add_method(fontTools.ttLib.getTableClass('CFF '))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001281def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001282 cff = self.cff
1283 for fontname in cff.keys():
1284 font = cff[fontname]
1285 cs = font.CharStrings
Behdad Esfahbod9290fb42013-08-14 17:48:31 -04001286
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001287 # Load all glyphs
Behdad Esfahbod9290fb42013-08-14 17:48:31 -04001288 for g in font.charset:
1289 if g not in s.glyphs: continue
1290 c,sel = cs.getItemAndSelector(g)
Behdad Esfahbod9290fb42013-08-14 17:48:31 -04001291
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001292 if cs.charStringsAreIndexed:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001293 indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001294 csi = cs.charStringsIndex
1295 csi.items = [csi.items[i] for i in indices]
Behdad Esfahbod9290fb42013-08-14 17:48:31 -04001296 csi.count = len(csi.items)
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001297 del csi.file, csi.offsets
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001298 if hasattr(font, "FDSelect"):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001299 sel = font.FDSelect
1300 sel.format = None
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001301 sel.gidArray = [sel.gidArray[i] for i in indices]
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001302 cs.charStrings = dict((g,indices.index(v))
1303 for g,v in cs.charStrings.iteritems()
1304 if g in s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001305 else:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001306 cs.charStrings = dict((g,v)
1307 for g,v in cs.charStrings.iteritems()
1308 if g in s.glyphs)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001309 font.charset = [g for g in font.charset if g in s.glyphs]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001310 font.numGlyphs = len(font.charset)
Behdad Esfahbod9290fb42013-08-14 17:48:31 -04001311
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001312 return any(cff[fontname].numGlyphs for fontname in cff.keys())
1313
Behdad Esfahbodb4ea9532013-08-14 19:37:39 -04001314@_add_method(fontTools.misc.psCharStrings.T2CharString)
1315def subset_subroutines(self, subrs, gsubrs):
1316 p = self.program
Behdad Esfahbod87c8c502013-08-16 14:44:09 -04001317 for i in xrange(1, len(p)):
Behdad Esfahbodb4ea9532013-08-14 19:37:39 -04001318 if p[i] == 'callsubr':
1319 assert type(p[i-1]) is int
1320 p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
1321 elif p[i] == 'callgsubr':
1322 assert type(p[i-1]) is int
1323 p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
1324
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001325@_add_method(fontTools.ttLib.getTableClass('CFF '))
1326def prune_post_subset(self, options):
1327 cff = self.cff
1328
1329 class _MarkingT2Decompiler(fontTools.misc.psCharStrings.SimpleT2Decompiler):
1330
1331 def __init__(self, localSubrs, globalSubrs):
1332 fontTools.misc.psCharStrings.SimpleT2Decompiler.__init__(self,
1333 localSubrs,
1334 globalSubrs)
1335 for subrs in [localSubrs, globalSubrs]:
1336 if subrs and not hasattr(subrs, "_used"):
1337 subrs._used = set()
1338
1339 def op_callsubr(self, index):
1340 self.localSubrs._used.add(self.operandStack[-1]+self.localBias)
1341 fontTools.misc.psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
1342
1343 def op_callgsubr(self, index):
1344 self.globalSubrs._used.add(self.operandStack[-1]+self.globalBias)
1345 fontTools.misc.psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
1346
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001347 class _NonrecursingT2Decompiler(fontTools.misc.psCharStrings.SimpleT2Decompiler):
1348
1349 def __init__(self, localSubrs, globalSubrs):
1350 fontTools.misc.psCharStrings.SimpleT2Decompiler.__init__(self,
1351 localSubrs,
1352 globalSubrs)
1353
1354 def op_callsubr(self, index):
1355 self.pop()
1356
1357 def op_callgsubr(self, index):
1358 self.pop()
1359
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001360 for fontname in cff.keys():
1361 font = cff[fontname]
1362 cs = font.CharStrings
1363
Behdad Esfahbod3c20a132013-08-14 19:39:00 -04001364 # Drop unused FontDictionaries
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001365 if hasattr(font, "FDSelect"):
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001366 sel = font.FDSelect
1367 indices = _uniq_sort(sel.gidArray)
1368 sel.gidArray = [indices.index (ss) for ss in sel.gidArray]
1369 arr = font.FDArray
Behdad Esfahbod3c20a132013-08-14 19:39:00 -04001370 arr.items = [arr[i] for i in indices]
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001371 arr.count = len(arr.items)
1372 del arr.file, arr.offsets
Behdad Esfahbod1a4e72e2013-08-13 15:46:37 -04001373
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001374 # Mark all used subroutines
1375 for g in font.charset:
1376 c,sel = cs.getItemAndSelector(g)
1377 subrs = getattr(c.private, "Subrs", [])
1378 decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs)
1379 decompiler.execute(c)
1380
1381 # Renumber subroutines to remove unused ones
1382 all_subrs = [font.GlobalSubrs]
1383 all_subrs.extend(fd.Private.Subrs for fd in font.FDArray if hasattr(fd.Private, 'Subrs'))
1384 # Prepare
1385 for subrs in all_subrs:
1386 if not subrs: continue
1387 if not hasattr(subrs, '_used'):
1388 subrs._used = set()
1389 subrs._used = _uniq_sort(subrs._used)
1390 subrs._old_bias = fontTools.misc.psCharStrings.calcSubrBias(subrs)
1391 subrs._new_bias = fontTools.misc.psCharStrings.calcSubrBias(subrs._used)
Behdad Esfahboded107712013-08-14 19:54:13 -04001392 # Renumber glyph charstrings
1393 for g in font.charset:
1394 c,sel = cs.getItemAndSelector(g)
1395 subrs = getattr(c.private, "Subrs", [])
1396 c.subset_subroutines (subrs, font.GlobalSubrs)
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001397 # Renumber subroutines themselves
1398 for subrs in all_subrs:
1399 if not subrs: continue
1400 decompiler = _NonrecursingT2Decompiler(subrs, font.GlobalSubrs)
Behdad Esfahbod87c8c502013-08-16 14:44:09 -04001401 for i in xrange (subrs.count):
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001402 if i not in subrs._used: continue
1403 decompiler.reset()
1404 decompiler.execute(subrs[i])
Behdad Esfahbodb4ea9532013-08-14 19:37:39 -04001405 subrs[i].subset_subroutines (subrs, font.GlobalSubrs)
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001406 # Cleanup
1407 for subrs in all_subrs:
1408 if not subrs: continue
1409 subrs.items = [subrs.items[i] for i in subrs._used]
1410 del subrs.file, subrs.offsets
1411 del subrs._used, subrs._old_bias, subrs._new_bias
1412
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001413 if not options.hinting:
Behdad Esfahbod2f3a4b92013-08-14 19:18:50 -04001414 pass # TODO(behdad) Drop hints
Behdad Esfahbodd315c912013-08-14 18:18:51 -04001415
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001416 return True
Behdad Esfahbod2b677c82013-07-23 13:37:13 -04001417
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001418@_add_method(fontTools.ttLib.getTableClass('cmap'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001419def closure_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001420 tables = [t for t in self.tables
1421 if t.platformID == 3 and t.platEncID in [1, 10]]
1422 for u in s.unicodes_requested:
1423 found = False
1424 for table in tables:
1425 if u in table.cmap:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001426 s.glyphs.add(table.cmap[u])
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001427 found = True
1428 break
1429 if not found:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001430 s.log("No glyph for Unicode value %s; skipping." % u)
Behdad Esfahbod8c8ff452013-07-31 19:47:37 -04001431
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001432@_add_method(fontTools.ttLib.getTableClass('cmap'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001433def prune_pre_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001434 if not options.legacy_cmap:
1435 # Drop non-Unicode / non-Symbol cmaps
1436 self.tables = [t for t in self.tables
1437 if t.platformID == 3 and t.platEncID in [0, 1, 10]]
1438 if not options.symbol_cmap:
1439 self.tables = [t for t in self.tables
1440 if t.platformID == 3 and t.platEncID in [1, 10]]
Behdad Esfahbod71f7c742013-08-13 20:16:16 -04001441 # TODO(behdad) Only keep one subtable?
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001442 # For now, drop format=0 which can't be subset_glyphs easily?
1443 self.tables = [t for t in self.tables if t.format != 0]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001444 return bool(self.tables)
Behdad Esfahbodabb50a12013-07-23 12:58:37 -04001445
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001446@_add_method(fontTools.ttLib.getTableClass('cmap'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001447def subset_glyphs(self, s):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001448 s.glyphs = s.glyphs_cmaped
1449 for t in self.tables:
1450 # For reasons I don't understand I need this here
1451 # to force decompilation of the cmap format 14.
1452 try:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001453 getattr(t, "asdf")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001454 except AttributeError:
1455 pass
1456 if t.format == 14:
Behdad Esfahbod71f7c742013-08-13 20:16:16 -04001457 # TODO(behdad) XXX We drop all the default-UVS mappings(g==None).
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001458 t.uvsDict = dict((v,[(u,g) for u,g in l if g in s.glyphs])
1459 for v,l in t.uvsDict.iteritems())
1460 t.uvsDict = dict((v,l) for v,l in t.uvsDict.iteritems() if l)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001461 else:
Behdad Esfahbodd73f2252013-08-16 10:58:25 -04001462 t.cmap = dict((u,g) for u,g in t.cmap.iteritems()
1463 if g in s.glyphs_requested or u in s.unicodes_requested)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001464 self.tables = [t for t in self.tables
Behdad Esfahbod4734be52013-08-14 19:47:42 -04001465 if (t.cmap if t.format != 14 else t.uvsDict)]
Behdad Esfahbod71f7c742013-08-13 20:16:16 -04001466 # TODO(behdad) Convert formats when needed.
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001467 # In particular, if we have a format=12 without non-BMP
1468 # characters, either drop format=12 one or convert it
1469 # to format=4 if there's not one.
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001470 return bool(self.tables)
Behdad Esfahbod61addb42013-07-23 11:03:49 -04001471
Behdad Esfahbod22f5cfc2013-08-13 20:25:37 -04001472@_add_method(fontTools.ttLib.getTableClass('name'))
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001473def prune_pre_subset(self, options):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001474 if '*' not in options.name_IDs:
1475 self.names = [n for n in self.names if n.nameID in options.name_IDs]
1476 if not options.name_legacy:
1477 self.names = [n for n in self.names
1478 if n.platformID == 3 and n.platEncID == 1]
1479 if '*' not in options.name_languages:
1480 self.names = [n for n in self.names if n.langID in options.name_languages]
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001481 return True # Retain even if empty
Behdad Esfahbod653e9742013-07-22 15:17:12 -04001482
Behdad Esfahbod8c646f62013-07-22 15:06:23 -04001483
Behdad Esfahbod71f7c742013-08-13 20:16:16 -04001484# TODO(behdad) OS/2 ulUnicodeRange / ulCodePageRange?
1485# TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
Behdad Esfahbod10195332013-08-14 19:55:24 -04001486# TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
Behdad Esfahbod71f7c742013-08-13 20:16:16 -04001487# TODO(behdad) Text direction considerations.
1488# TODO(behdad) Text script / language considerations.
Behdad Esfahbod56ebd042013-07-22 13:02:24 -04001489
Behdad Esfahbod8c486d82013-07-24 13:34:47 -04001490
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001491class Options(object):
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001492
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001493 class UnknownOptionError(Exception):
1494 pass
Behdad Esfahbod26d9ee72013-08-13 16:55:01 -04001495
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001496 _drop_tables_default = ['BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC',
1497 'PCLT', 'LTSH']
1498 _drop_tables_default += ['Feat', 'Glat', 'Gloc', 'Silf', 'Sill'] # Graphite
1499 _drop_tables_default += ['CBLC', 'CBDT', 'sbix', 'COLR', 'CPAL'] # Color
1500 _no_subset_tables_default = ['gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2',
1501 'loca', 'name', 'cvt ', 'fpgm', 'prep']
1502 _hinting_tables_default = ['cvt ', 'fpgm', 'prep', 'hdmx', 'VDMX']
Behdad Esfahbod26d9ee72013-08-13 16:55:01 -04001503
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001504 # Based on HarfBuzz shapers
1505 _layout_features_groups = {
1506 # Default shaper
1507 'common': ['ccmp', 'liga', 'locl', 'mark', 'mkmk', 'rlig'],
1508 'horizontal': ['calt', 'clig', 'curs', 'kern', 'rclt'],
1509 'vertical': ['valt', 'vert', 'vkrn', 'vpal', 'vrt2'],
1510 'ltr': ['ltra', 'ltrm'],
1511 'rtl': ['rtla', 'rtlm'],
1512 # Complex shapers
1513 'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
1514 'cswh', 'mset'],
1515 'hangul': ['ljmo', 'vjmo', 'tjmo'],
1516 'tibetal': ['abvs', 'blws', 'abvm', 'blwm'],
1517 'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
1518 'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
1519 'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
1520 }
1521 _layout_features_default = _uniq_sort(sum(
1522 _layout_features_groups.itervalues(), []))
Behdad Esfahbod9eeeb4e2013-08-13 16:58:50 -04001523
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001524 drop_tables = _drop_tables_default
1525 no_subset_tables = _no_subset_tables_default
1526 hinting_tables = _hinting_tables_default
1527 layout_features = _layout_features_default
1528 hinting = False
1529 glyph_names = False
1530 legacy_cmap = False
1531 symbol_cmap = False
1532 name_IDs = [1, 2] # Family and Style
1533 name_legacy = False
1534 name_languages = [0x0409] # English
1535 mandatory_glyphs = True # First four for TrueType, .notdef for CFF
Behdad Esfahbode911de12013-08-16 12:42:34 -04001536 recalc_bounds = False # Recalculate font bounding boxes
1537 canonical_order = True # Order tables as recommended
Behdad Esfahbodc6c3bb82013-08-15 17:46:20 -04001538 flavor = None # May be 'woff'
Behdad Esfahbod9eeeb4e2013-08-13 16:58:50 -04001539
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001540 def __init__(self, **kwargs):
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001541
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001542 self.set(**kwargs)
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001543
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001544 def set(self, **kwargs):
1545 for k,v in kwargs.iteritems():
1546 if not hasattr(self, k):
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001547 raise self.UnknownOptionError("Unknown option '%s'" % a)
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001548 setattr(self, k, v)
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001549
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001550 def parse_opts(self, argv, ignore_unknown=False):
1551 ret = []
1552 opts = {}
1553 for a in argv:
1554 orig_a = a
1555 if not a.startswith('--'):
1556 ret.append(a)
1557 continue
1558 a = a[2:]
1559 i = a.find('=')
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001560 op = '='
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001561 if i == -1:
1562 if a.startswith("no-"):
1563 k = a[3:]
1564 v = False
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001565 else:
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001566 k = a
1567 v = True
1568 else:
1569 k = a[:i]
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001570 if k[-1] in "-+":
1571 op = k[-1]+'=' # Ops is '-=' or '+=' now.
1572 k = k[:-1]
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001573 v = a[i+1:]
1574 k = k.replace('-', '_')
1575 if not hasattr(self, k):
1576 if ignore_unknown == True or k in ignore_unknown:
1577 ret.append(orig_a)
1578 continue
1579 else:
1580 raise self.UnknownOptionError("Unknown option '%s'" % a)
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001581
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001582 ov = getattr(self, k)
1583 if isinstance(ov, bool):
1584 v = bool(v)
1585 elif isinstance(ov, int):
1586 v = int(v)
1587 elif isinstance(ov, list):
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001588 vv = v.split(',')
1589 if vv == ['']:
1590 vv = []
Behdad Esfahbod87c8c502013-08-16 14:44:09 -04001591 vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
Behdad Esfahbod0fc55022013-08-15 19:06:48 -04001592 if op == '=':
1593 v = vv
1594 elif op == '+=':
1595 v = ov
1596 v.extend(vv)
1597 elif op == '-=':
1598 v = ov
1599 for x in vv:
1600 if x in v:
1601 v.remove(x)
1602 else:
1603 assert 0
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001604
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001605 opts[k] = v
1606 self.set(**opts)
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001607
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001608 return ret
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001609
1610
Behdad Esfahbod5d4f99d2013-08-13 20:57:59 -04001611class Subsetter(object):
1612
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001613 def __init__(self, options=None, log=None):
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001614
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001615 if not log:
1616 log = Logger()
1617 if not options:
1618 options = Options()
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001619
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001620 self.options = options
1621 self.log = log
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001622 self.unicodes_requested = set()
1623 self.glyphs_requested = set()
1624 self.glyphs = set()
Behdad Esfahbod3d513b72013-07-31 14:11:40 -04001625
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001626 def populate(self, glyphs=[], unicodes=[], text=""):
1627 self.unicodes_requested.update(unicodes)
1628 if isinstance(text, str):
1629 text = text.decode("utf8")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001630 for u in text:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001631 self.unicodes_requested.add(ord(u))
1632 self.glyphs_requested.update(glyphs)
1633 self.glyphs.update(glyphs)
Behdad Esfahbod3d513b72013-07-31 14:11:40 -04001634
Behdad Esfahbodb7f460b2013-08-13 20:48:33 -04001635 def _prune_pre_subset(self, font):
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001636
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001637 for tag in font.keys():
1638 if tag == 'GlyphOrder': continue
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001639
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001640 if(tag in self.options.drop_tables or
1641 (tag in self.options.hinting_tables and not self.options.hinting)):
1642 self.log(tag, "dropped")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001643 del font[tag]
1644 continue
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001645
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001646 clazz = fontTools.ttLib.getTableClass(tag)
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001647
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001648 if hasattr(clazz, 'prune_pre_subset'):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001649 table = font[tag]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001650 retain = table.prune_pre_subset(self.options)
1651 self.log.lapse("prune '%s'" % tag)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001652 if not retain:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001653 self.log(tag, "pruned to empty; dropped")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001654 del font[tag]
1655 continue
1656 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001657 self.log(tag, "pruned")
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001658
Behdad Esfahbodb7f460b2013-08-13 20:48:33 -04001659 def _closure_glyphs(self, font):
Behdad Esfahbod2fb90e22013-07-31 20:04:08 -04001660
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001661 self.glyphs = self.glyphs_requested.copy()
Behdad Esfahbod98259f22013-07-31 20:16:24 -04001662
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001663 if 'cmap' in font:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001664 font['cmap'].closure_glyphs(self)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001665 self.glyphs_cmaped = self.glyphs
Behdad Esfahbod2fb90e22013-07-31 20:04:08 -04001666
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001667 if self.options.mandatory_glyphs:
1668 if 'glyf' in font:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001669 for i in range(4):
1670 self.glyphs.add(font.getGlyphName(i))
1671 self.log("Added first four glyphs to subset")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001672 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001673 self.glyphs.add('.notdef')
1674 self.log("Added .notdef glyph to subset")
Behdad Esfahbod2fb90e22013-07-31 20:04:08 -04001675
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001676 if 'GSUB' in font:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001677 self.log("Closing glyph list over 'GSUB': %d glyphs before" %
1678 len(self.glyphs))
1679 self.log.glyphs(self.glyphs, font=font)
1680 font['GSUB'].closure_glyphs(self)
1681 self.log("Closed glyph list over 'GSUB': %d glyphs after" %
1682 len(self.glyphs))
1683 self.log.glyphs(self.glyphs, font=font)
1684 self.log.lapse("close glyph list over 'GSUB'")
1685 self.glyphs_gsubed = self.glyphs.copy()
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001686
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001687 if 'glyf' in font:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001688 self.log("Closing glyph list over 'glyf': %d glyphs before" %
1689 len(self.glyphs))
1690 self.log.glyphs(self.glyphs, font=font)
1691 font['glyf'].closure_glyphs(self)
1692 self.log("Closed glyph list over 'glyf': %d glyphs after" %
1693 len(self.glyphs))
1694 self.log.glyphs(self.glyphs, font=font)
1695 self.log.lapse("close glyph list over 'glyf'")
1696 self.glyphs_glyfed = self.glyphs.copy()
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001697
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001698 self.glyphs_all = self.glyphs.copy()
Behdad Esfahbod8c8ff452013-07-31 19:47:37 -04001699
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001700 self.log("Retaining %d glyphs: " % len(self.glyphs_all))
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001701
Behdad Esfahbodb7f460b2013-08-13 20:48:33 -04001702 def _subset_glyphs(self, font):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001703 for tag in font.keys():
1704 if tag == 'GlyphOrder': continue
1705 clazz = fontTools.ttLib.getTableClass(tag)
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001706
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001707 if tag in self.options.no_subset_tables:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001708 self.log(tag, "subsetting not needed")
1709 elif hasattr(clazz, 'subset_glyphs'):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001710 table = font[tag]
1711 self.glyphs = self.glyphs_all
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001712 retain = table.subset_glyphs(self)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001713 self.glyphs = self.glyphs_all
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001714 self.log.lapse("subset '%s'" % tag)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001715 if not retain:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001716 self.log(tag, "subsetted to empty; dropped")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001717 del font[tag]
1718 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001719 self.log(tag, "subsetted")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001720 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001721 self.log(tag, "NOT subset; don't know how to subset; dropped")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001722 del font[tag]
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001723
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001724 glyphOrder = font.getGlyphOrder()
1725 glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001726 font.setGlyphOrder(glyphOrder)
1727 font._buildReverseGlyphOrderDict()
1728 self.log.lapse("subset GlyphOrder")
Behdad Esfahbod2fb90e22013-07-31 20:04:08 -04001729
Behdad Esfahbodb7f460b2013-08-13 20:48:33 -04001730 def _prune_post_subset(self, font):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001731 for tag in font.keys():
1732 if tag == 'GlyphOrder': continue
1733 clazz = fontTools.ttLib.getTableClass(tag)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001734 if hasattr(clazz, 'prune_post_subset'):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001735 table = font[tag]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001736 retain = table.prune_post_subset(self.options)
1737 self.log.lapse("prune '%s'" % tag)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001738 if not retain:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001739 self.log(tag, "pruned to empty; dropped")
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001740 del font[tag]
1741 else:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001742 self.log(tag, "pruned")
Behdad Esfahbod063a2db2013-07-31 15:22:02 -04001743
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001744 def subset(self, font):
Behdad Esfahbod756af492013-08-01 12:05:26 -04001745
Behdad Esfahbodb7f460b2013-08-13 20:48:33 -04001746 self._prune_pre_subset(font)
1747 self._closure_glyphs(font)
1748 self._subset_glyphs(font)
1749 self._prune_post_subset(font)
Behdad Esfahbod98259f22013-07-31 20:16:24 -04001750
Behdad Esfahbod756af492013-08-01 12:05:26 -04001751
Behdad Esfahbod3d4c4712013-08-13 20:10:17 -04001752class Logger(object):
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001753
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001754 def __init__(self, verbose=False, xml=False, timing=False):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001755 self.verbose = verbose
1756 self.xml = xml
1757 self.timing = timing
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001758 self.last_time = self.start_time = time.time()
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001759
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001760 def parse_opts(self, argv):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001761 argv = argv[:]
1762 for v in ['verbose', 'xml', 'timing']:
1763 if "--"+v in argv:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001764 setattr(self, v, True)
1765 argv.remove("--"+v)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001766 return argv
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001767
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001768 def __call__(self, *things):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001769 if not self.verbose:
1770 return
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001771 print ' '.join(str(x) for x in things)
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001772
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001773 def lapse(self, *things):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001774 if not self.timing:
1775 return
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001776 new_time = time.time()
1777 print "Took %0.3fs to %s" %(new_time - self.last_time,
1778 ' '.join(str(x) for x in things))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001779 self.last_time = new_time
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001780
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001781 def glyphs(self, glyphs, font=None):
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001782 self("Names: ", sorted(glyphs))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001783 if font:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001784 reverseGlyphMap = font.getReverseGlyphMap()
1785 self("Gids : ", sorted(reverseGlyphMap[g] for g in glyphs))
Behdad Esfahbodf5497842013-08-08 21:57:02 -04001786
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001787 def font(self, font, file=sys.stdout):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001788 if not self.xml:
1789 return
1790 import xmlWriter
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001791 writer = xmlWriter.XMLWriter(file)
Behdad Esfahbod318adc02013-08-13 20:09:28 -04001792 font.disassembleInstructions = False # Work around ttx bug
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001793 for tag in font.keys():
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001794 writer.begintag(tag)
1795 writer.newline()
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001796 font[tag].toXML(writer, font)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001797 writer.endtag(tag)
1798 writer.newline()
Behdad Esfahboddf3d7572013-07-31 15:03:43 -04001799
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001800
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001801def load_font(fontFile,
Behdad Esfahbodadc47fd2013-08-15 18:29:25 -04001802 options,
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001803 checkChecksums=False,
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001804 dontLoadGlyphNames=False):
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001805
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001806 font = fontTools.ttx.TTFont(fontFile,
1807 checkChecksums=checkChecksums,
Behdad Esfahbode911de12013-08-16 12:42:34 -04001808 recalcBBoxes=options.recalc_bounds)
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001809
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001810 # Hack:
1811 #
1812 # If we don't need glyph names, change 'post' class to not try to
1813 # load them. It avoid lots of headache with broken fonts as well
1814 # as loading time.
1815 #
1816 # Ideally ttLib should provide a way to ask it to skip loading
1817 # glyph names. But it currently doesn't provide such a thing.
1818 #
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001819 if dontLoadGlyphNames:
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001820 post = fontTools.ttLib.getTableClass('post')
1821 saved = post.decode_format_2_0
1822 post.decode_format_2_0 = post.decode_format_3_0
1823 f = font['post']
1824 if f.formatType == 2.0:
1825 f.formatType = 3.0
1826 post.decode_format_2_0 = saved
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001827
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001828 return font
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001829
Behdad Esfahbode911de12013-08-16 12:42:34 -04001830def save_font(font, outfile, options):
Behdad Esfahbodc6c3bb82013-08-15 17:46:20 -04001831 if options.flavor and not hasattr(font, 'flavor'):
1832 raise Exception("fonttools version does not support flavors.")
1833 font.flavor = options.flavor
Behdad Esfahbode911de12013-08-16 12:42:34 -04001834 font.save(outfile, reorderTables=options.canonical_order)
Behdad Esfahbod41de4cc2013-08-15 12:09:55 -04001835
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001836
Behdad Esfahbod58280972013-08-13 20:46:52 -04001837# Cleanup module space
1838l = locals()
Behdad Esfahbodd9b63082013-08-16 17:16:11 -04001839# Some versions of Jython don't like locals().items()
1840if isinstance(l, dict):
1841 for k,v in l.items():
1842 if v == None:
1843 del l[k]
1844 del k, v
1845del l
Behdad Esfahbod58280972013-08-13 20:46:52 -04001846
1847
Behdad Esfahbodc0ded942013-08-14 13:53:33 -04001848def main(args=None):
Behdad Esfahbod23b2ee92013-08-13 20:29:10 -04001849
Behdad Esfahbodc0ded942013-08-14 13:53:33 -04001850 if args == None: args = sys.argv
Behdad Esfahbod3c472002013-08-14 13:52:27 -04001851 arg0, args = args[0], args[1:]
Behdad Esfahbod610b0552013-07-23 14:52:18 -04001852
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001853 log = Logger()
1854 args = log.parse_opts(args)
Behdad Esfahbod4ae81712013-07-22 11:57:13 -04001855
Behdad Esfahbod80c8a652013-08-14 12:55:42 -04001856 options = Options()
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001857 args = options.parse_opts(args, ignore_unknown=['text'])
Behdad Esfahbod97e17b82013-07-31 15:59:21 -04001858
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001859 if len(args) < 2:
Behdad Esfahbodf55013e2013-08-15 18:39:55 -04001860 print >>sys.stderr, "usage: %s font-file glyph... [--text=ABC]... [--option=value]..." % arg0
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001861 sys.exit(1)
Behdad Esfahbod02b92062013-07-21 18:40:59 -04001862
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001863 fontfile = args[0]
1864 args = args[1:]
Behdad Esfahbod02b92062013-07-21 18:40:59 -04001865
Behdad Esfahbod85da2682013-08-15 12:17:21 -04001866 dontLoadGlyphNames =(not options.glyph_names and
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001867 all(any(g.startswith(p)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001868 for p in ['gid', 'glyph', 'uni', 'U+'])
1869 for g in args))
Behdad Esfahbodf6b668e2013-08-13 12:20:59 -04001870
Behdad Esfahbodadc47fd2013-08-15 18:29:25 -04001871 font = load_font(fontfile, options, dontLoadGlyphNames=dontLoadGlyphNames)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001872 subsetter = Subsetter(options=options, log=log)
1873 log.lapse("load font")
Behdad Esfahbod02b92062013-07-21 18:40:59 -04001874
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001875 names = font.getGlyphNames()
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001876 log.lapse("loading glyph names")
Behdad Esfahbode7f5a892013-07-31 19:58:59 -04001877
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001878 glyphs = []
1879 unicodes = []
1880 text = ""
1881 for g in args:
1882 if g in names:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001883 glyphs.append(g)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001884 continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001885 if g.startswith('--text='):
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001886 text += g[7:]
1887 continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001888 if g.startswith('uni') or g.startswith('U+'):
1889 if g.startswith('uni') and len(g) > 3:
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001890 g = g[3:]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001891 elif g.startswith('U+') and len(g) > 2:
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001892 g = g[2:]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001893 u = int(g, 16)
1894 unicodes.append(u)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001895 continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001896 if g.startswith('gid') or g.startswith('glyph'):
1897 if g.startswith('gid') and len(g) > 3:
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001898 g = g[3:]
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001899 elif g.startswith('glyph') and len(g) > 5:
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001900 g = g[5:]
1901 try:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001902 glyphs.append(font.getGlyphName(int(g), requireReal=1))
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001903 except ValueError:
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001904 raise Exception("Invalid glyph identifier: %s" % g)
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001905 continue
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001906 raise Exception("Invalid glyph identifier: %s" % g)
1907 log.lapse("compile glyph list")
1908 log("Unicodes:", unicodes)
1909 log("Glyphs:", glyphs)
Behdad Esfahbod6df089a2013-07-31 19:27:14 -04001910
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001911 subsetter.populate(glyphs=glyphs, unicodes=unicodes, text=text)
1912 subsetter.subset(font)
Behdad Esfahbodd1d41bc2013-07-21 23:15:32 -04001913
Behdad Esfahbod34426c12013-08-14 18:30:09 -04001914 outfile = fontfile + '.subset'
1915
Behdad Esfahbodc6c3bb82013-08-15 17:46:20 -04001916 save_font (font, outfile, options)
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001917 log.lapse("compile and save font")
Behdad Esfahbodde71dca2013-07-24 12:40:54 -04001918
Behdad Esfahbod9e856ea2013-08-13 19:50:38 -04001919 log.last_time = log.start_time
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001920 log.lapse("make one with everything(TOTAL TIME)")
Behdad Esfahbodde71dca2013-07-24 12:40:54 -04001921
Behdad Esfahbod34426c12013-08-14 18:30:09 -04001922 if log.verbose:
1923 import os
1924 log("Input font: %d bytes" % os.path.getsize(fontfile))
1925 log("Subset font: %d bytes" % os.path.getsize(outfile))
1926
Behdad Esfahbod77a2b282013-08-13 19:53:30 -04001927 log.font(font)
Behdad Esfahbod8c486d82013-07-24 13:34:47 -04001928
Behdad Esfahbodc56bf482013-08-13 20:13:33 -04001929 font.close()
1930
Behdad Esfahbod8c486d82013-07-24 13:34:47 -04001931if __name__ == '__main__':
Behdad Esfahbod23b2ee92013-08-13 20:29:10 -04001932 main()