blob: dd76228cb7ef4f3551adfe1e000e136adcbb79bc [file] [log] [blame]
Behdad Esfahbod54660612013-07-21 18:16:55 -04001#!/usr/bin/python
2
3# Python OpenType Layout Subsetter
4# Writte by: Behdad Esfahbod
5
6import fontTools.ttx
Behdad Esfahbod54660612013-07-21 18:16:55 -04007
Behdad Esfahbod54660612013-07-21 18:16:55 -04008
Behdad Esfahbod02b92062013-07-21 18:40:59 -04009def add_method (*clazzes):
Behdad Esfahbod54660612013-07-21 18:16:55 -040010 def wrapper(method):
Behdad Esfahbod02b92062013-07-21 18:40:59 -040011 for clazz in clazzes:
12 setattr (clazz, method.func_name, method)
Behdad Esfahbod54660612013-07-21 18:16:55 -040013 return wrapper
14
Behdad Esfahbod54660612013-07-21 18:16:55 -040015# Subset
Behdad Esfahbod54660612013-07-21 18:16:55 -040016
17@add_method(fontTools.ttLib.tables.otTables.Coverage)
18def subset (self, glyphs):
19 indices = [i for (i,g) in enumerate (self.glyphs) if g in glyphs]
20 self.glyphs = [g for g in self.glyphs if g in glyphs]
21 return indices
Behdad Esfahbod1be53452013-07-21 22:52:15 -040022 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040023
24@add_method(fontTools.ttLib.tables.otTables.ClassDef)
25def subset (self, glyphs):
26 self.classDefs = {g:v for g,v in self.classDefs.items() if g in glyphs}
Behdad Esfahbod1be53452013-07-21 22:52:15 -040027 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040028
29@add_method(fontTools.ttLib.tables.otTables.SingleSubst)
30def subset (self, glyphs):
31 if self.Format in [1, 2]:
32 self.mapping = {g:v for g,v in self.mapping.items() if g in glyphs}
33 else:
34 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -040035 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040036
37@add_method(fontTools.ttLib.tables.otTables.MultipleSubst)
38def subset (self, glyphs):
39 if self.Format == 1:
40 indices = self.Coverage.subset (glyphs)
41 self.Sequence = [self.Sequence[i] for i in indices]
42 else:
43 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -040044 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040045
46@add_method(fontTools.ttLib.tables.otTables.AlternateSubst)
47def subset (self, glyphs):
48 if self.Format == 1:
49 self.alternates = {g:v for g,v in self.alternates.items() if g in glyphs}
50 else:
51 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -040052 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040053
54@add_method(fontTools.ttLib.tables.otTables.LigatureSubst)
55def subset (self, glyphs):
56 self.ligatures = {g:v for g,v in self.ligatures.items() if g in glyphs}
57 self.ligatures = {g:[seq for seq in seqs if all(c in glyphs for c in seq.Component)]
58 for g,seqs in self.ligatures.items()}
59 self.ligatures = {g:v for g,v in self.ligatures.items() if v}
Behdad Esfahbod1be53452013-07-21 22:52:15 -040060 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040061
Behdad Esfahbod54660612013-07-21 18:16:55 -040062@add_method(fontTools.ttLib.tables.otTables.ReverseChainSingleSubst)
63def subset (self, glyphs):
64 if self.Format == 1:
65 indices = self.Coverage.subset (glyphs)
66 self.Substitute = [self.Substitute[i] for i in indices]
67 self.GlyphCount = len (self.Substitute)
68 for c in self.LookAheadCoverage:
69 c.subset (glyphs)
70 for c in self.BacktrackCoverage:
71 c.subset (glyphs)
72 else:
73 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -040074 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040075
76@add_method(fontTools.ttLib.tables.otTables.SinglePos)
77def subset (self, glyphs):
78 if self.Format == 1:
79 self.Coverage.subset (glyphs)
80 elif self.Format == 2:
81 indices = self.Coverage.subset (glyphs)
82 self.Value = [self.Value[i] for i in indices]
83 self.ValueCount = len (self.Value)
84 else:
85 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -040086 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -040087
88@add_method(fontTools.ttLib.tables.otTables.PairPos)
89def subset (self, glyphs):
90 if self.Format == 1:
91 indices = self.Coverage.subset (glyphs)
92 self.PairSet = [self.PairSet[i] for i in indices]
93 for p in self.PairSet:
94 p.PairValueRecord = [r for r in p.PairValueRecord if r.SecondGlyph in glyphs]
95 p.PairValueCount = len (p.PairValueRecord)
96 # TODO Prune empty rules
97 self.PairSetCount = len (self.PairSet)
98 elif self.Format == 2:
99 self.Coverage.subset (glyphs)
100 self.ClassDef1.subset (glyphs)
101 self.ClassDef2.subset (glyphs)
102 # TODO Prune empty classes
103 else:
104 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400105 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400106
107@add_method(fontTools.ttLib.tables.otTables.CursivePos)
108def subset (self, glyphs):
109 if self.Format == 1:
110 indices = self.Coverage.subset (glyphs)
111 self.EntryExitRecord = [self.EntryExitRecord[i] for i in indices]
112 else:
113 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400114 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400115
116@add_method(fontTools.ttLib.tables.otTables.MarkBasePos)
117def subset (self, glyphs):
118 if self.Format == 1:
119 mark_indices = self.MarkCoverage.subset (glyphs)
120 self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i] for i in mark_indices]
121 self.MarkArray.MarkCount = len (self.MarkArray.MarkRecord)
122 base_indices = self.BaseCoverage.subset (glyphs)
123 self.BaseArray.BaseRecord = [self.BaseArray.BaseRecord[i] for i in base_indices]
124 self.BaseArray.BaseCount = len (self.BaseArray.BaseRecord)
125 # TODO Prune empty classes
126 else:
127 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400128 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400129
130@add_method(fontTools.ttLib.tables.otTables.MarkLigPos)
131def subset (self, glyphs):
132 if self.Format == 1:
133 mark_indices = self.MarkCoverage.subset (glyphs)
134 self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i] for i in mark_indices]
135 self.MarkArray.MarkCount = len (self.MarkArray.MarkRecord)
136 ligature_indices = self.LigatureCoverage.subset (glyphs)
137 self.LigatureArray.LigatureAttach = [self.LigatureArray.LigatureAttach[i] for i in ligature_indices]
138 self.LigatureArray.LigatureCount = len (self.LigatureArray.LigatureAttach)
139 # TODO Prune empty classes
140 else:
141 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400142 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400143
144@add_method(fontTools.ttLib.tables.otTables.MarkMarkPos)
145def subset (self, glyphs):
146 if self.Format == 1:
147 mark1_indices = self.Mark1Coverage.subset (glyphs)
148 self.Mark1Array.MarkRecord = [self.Mark1Array.MarkRecord[i] for i in mark1_indices]
149 self.Mark1Array.MarkCount = len (self.Mark1Array.MarkRecord)
150 mark2_indices = self.Mark2Coverage.subset (glyphs)
151 self.Mark2Array.Mark2Record = [self.Mark2Array.Mark2Record[i] for i in mark2_indices]
152 self.Mark2Array.MarkCount = len (self.Mark2Array.Mark2Record)
153 # TODO Prune empty classes
154 else:
155 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400156 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400157
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400158@add_method(fontTools.ttLib.tables.otTables.ContextSubst, fontTools.ttLib.tables.otTables.ContextPos)
Behdad Esfahbod54660612013-07-21 18:16:55 -0400159def subset (self, glyphs):
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400160 if self.Format == 1:
Behdad Esfahbodb7fef902013-07-21 22:48:08 -0400161 indices = self.Coverage.subset (glyphs)
162 self.SubRuleSet = [self.SubRuleSet[i] for i in indices]
163 self.SubRuleSetCount = len (self.SubRuleSet)
164 for rs in self.SubRuleSet:
165 rs.SubRule = [r for r in rs.SubRule
166 if all (g in glyphs for g in r.Input)]
167 rs.SubRuleCount = len (rs.SubRule)
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400168 elif self.Format == 2:
Behdad Esfahbodb7fef902013-07-21 22:48:08 -0400169 self.Coverage.subset (glyphs)
170 self.ClassDef.subset (glyphs)
171 pass
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400172 elif self.Format == 3:
Behdad Esfahbodb7fef902013-07-21 22:48:08 -0400173 for c in self.Coverage:
174 c.subset (glyphs)
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400175 else:
176 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400177 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400178
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400179@add_method(fontTools.ttLib.tables.otTables.ChainContextSubst, fontTools.ttLib.tables.otTables.ChainContextPos)
Behdad Esfahbod54660612013-07-21 18:16:55 -0400180def subset (self, glyphs):
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400181 if self.Format == 1:
Behdad Esfahbodb7fef902013-07-21 22:48:08 -0400182 indices = self.Coverage.subset (glyphs)
183 self.ChainSubRuleSet = [self.ChainSubRuleSet[i] for i in indices]
184 self.ChainSubRuleSetCount = len (self.ChainSubRuleSet)
185 for rs in self.ChainSubRuleSet:
186 rs.ChainSubRule = [r for r in rs.ChainSubRule
187 if all (g in glyphs for g in r.Backtrack + r.Input + r.LookAhead)]
188 rs.ChainSubRuleCount = len (rs.ChainSubRule)
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400189 elif self.Format == 2:
Behdad Esfahbodb7fef902013-07-21 22:48:08 -0400190 self.Coverage.subset (glyphs)
191 self.LookAheadClassDef.subset (glyphs)
192 self.BacktrackClassDef.subset (glyphs)
193 self.InputClassDef.subset (glyphs)
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400194 elif self.Format == 3:
195 for c in self.InputCoverage:
196 c.subset (glyphs)
197 for c in self.LookAheadCoverage:
198 c.subset (glyphs)
199 for c in self.BacktrackCoverage:
200 c.subset (glyphs)
201 else:
202 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400203 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400204
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400205@add_method(fontTools.ttLib.tables.otTables.ExtensionSubst, fontTools.ttLib.tables.otTables.ExtensionPos)
Behdad Esfahbod54660612013-07-21 18:16:55 -0400206def subset (self, glyphs):
207 if self.Format == 1:
208 self.ExtSubTable.subset (glyphs)
209 else:
210 assert 0, "unknown format: %s" % self.Format
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400211 return True
Behdad Esfahbod54660612013-07-21 18:16:55 -0400212
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400213@add_method(fontTools.ttLib.tables.otTables.Lookup)
214def subset (self, glyphs):
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400215 self.SubTable = [s for s in self.SubTable if s.subset (glyphs)]
216 return any (self.SubTable)
Behdad Esfahbod54660612013-07-21 18:16:55 -0400217
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400218@add_method(fontTools.ttLib.tables.otTables.GSUB, fontTools.ttLib.tables.otTables.GPOS)
219def subset (self, glyphs):
Behdad Esfahbod1be53452013-07-21 22:52:15 -0400220 self.LookupList.Lookup = [l for l in self.LookupList.Lookup if l.subset (glyphs)]
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400221
Behdad Esfahbodefb984a2013-07-21 22:26:16 -0400222@add_method(fontTools.ttLib.tables.otTables.GDEF)
223def subset (self, glyphs):
224 if self.LigCaretList:
225 indices = self.LigCaretList.Coverage.subset (glyphs)
226 self.LigCaretList.LigGlyph = [self.LigCaretList.LigGlyph[i] for i in indices]
227 self.LigCaretList.LigGlyphCount = len (self.LigCaretList.LigGlyph)
228 if self.MarkAttachClassDef:
229 self.MarkAttachClassDef.classDefs = {g:v for g,v in self.MarkAttachClassDef.classDefs.items() if g in glyphs}
230 if self.GlyphClassDef:
231 self.GlyphClassDef.classDefs = {g:v for g,v in self.GlyphClassDef.classDefs.items() if g in glyphs}
232 if self.AttachList:
233 indices = self.AttachList.Coverage.subset (glyphs)
234 self.AttachList.AttachPoint = [self.AttachList.AttachPoint[i] for i in indices]
235 self.AttachList.GlyphCount = len (self.AttachList.AttachPoint)
236
237
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400238if __name__ == '__main__':
239
240 import sys
241
242 if len (sys.argv) < 3:
243 print >>sys.stderr, "usage: pyotlss.py font-file glyph..."
244 sys.exit (1)
245
246 fontfile = sys.argv[1]
247 glyphs = sys.argv[2:]
248
249 font = fontTools.ttx.TTFont (fontfile)
250
251 names = font.getGlyphNames()
252 # Convert to glyph names
253 glyphs = [g if g in names else font.getGlyphName(int(g)) for g in glyphs]
254
Behdad Esfahbodefb984a2013-07-21 22:26:16 -0400255 for Gtag in ['GDEF', 'GSUB', 'GPOS']:
Behdad Esfahbod02b92062013-07-21 18:40:59 -0400256 if Gtag not in font:
257 continue
258 font[Gtag].table.subset (glyphs)