21
21
#
22
22
# A script for subsetting a font, using FontForge. See README for details.
23
23
24
- # TODO 2013-04-08 ensure the menu files are as compact as possible by default, similar to subset.pl
25
- # TODO 2013-05-22 in Arimo, the latin subset doesn't include ; but the greek does. why on earth is this happening?
24
+ # TODO 2013-04-08 ensure the menu files are as compact as possible by default,
25
+ # similar to subset.pl
26
+ # TODO 2013-05-22 in Arimo, the latin subset doesn't include ; but the greek
27
+ # does. why on earth is this happening?
26
28
27
29
import getopt
28
30
import os
35
37
36
38
def log_namelist (nam , unicode ):
37
39
if nam and isinstance (unicode , int ):
38
- print ("0x%0.4X" % unicode , fontforge .nameFromUnicode (unicode ), file = nam )
40
+ print (f "0x{ unicode :04X } " , fontforge .nameFromUnicode (unicode ), file = nam )
39
41
40
- def select_with_refs (font , unicode , newfont , pe = None , nam = None ):
42
+
43
+ def select_with_refs (font , unicode , newfont , pe = None , nam = None ):
41
44
newfont .selection .select (('more' , 'unicode' ), unicode )
42
45
log_namelist (nam , unicode )
43
46
if pe :
44
- print ("SelectMore(%d)" % unicode , file = pe )
47
+ print (f "SelectMore({ unicode } )" , file = pe )
45
48
try :
46
49
for ref in font [unicode ].references :
47
50
newfont .selection .select (('more' ,), ref [0 ])
48
51
log_namelist (nam , ref [0 ])
49
52
if pe :
50
- print ('SelectMore("%s")' % ref [0 ], file = pe )
53
+ print (f 'SelectMore("{ ref [0 ]} ")' , file = pe )
51
54
except Exception :
52
- print ('Resolving references on u+%04x failed' % unicode )
55
+ print (f'Resolving references on u+{ unicode :04x} failed' )
56
+
53
57
54
58
def subset_font_raw (font_in , font_out , unicodes , opts ):
55
59
if '--namelist' in opts :
56
60
# 2010-12-06 DC To allow setting namelist filenames,
57
61
# change getopt.gnu_getopt from namelist to namelist=
58
62
# and invert comments on following 2 lines
59
63
# nam_fn = opts['--namelist']
60
- nam_fn = font_out + ' .nam'
64
+ nam_fn = f' { font_out } .nam'
61
65
nam = open (nam_fn , 'w' )
62
66
else :
63
67
nam = None
@@ -68,7 +72,7 @@ def subset_font_raw(font_in, font_out, unicodes, opts):
68
72
pe = None
69
73
font = fontforge .open (font_in )
70
74
if pe :
71
- print ('Open("' + font_in + ' ")' , file = pe )
75
+ print (f 'Open("{ font_in } ")' , file = pe )
72
76
extract_vert_to_script (font_in , pe )
73
77
for i in unicodes :
74
78
select_with_refs (font , i , font , pe , nam )
@@ -83,9 +87,10 @@ def subset_font_raw(font_in, font_out, unicodes, opts):
83
87
for glyph in addl_glyphs :
84
88
font .selection .select (('more' ,), glyph )
85
89
if nam :
86
- print ("0x%0.4X" % fontforge .unicodeFromName (glyph ), glyph , file = nam )
90
+ print (f"0x{ fontforge .unicodeFromName (glyph ):0.4X} " , glyph ,
91
+ file = nam )
87
92
if pe :
88
- print ('SelectMore("%s ")' % glyph , file = pe )
93
+ print (f 'SelectMore("{ glyph } ")' , file = pe )
89
94
90
95
flags = ()
91
96
@@ -149,19 +154,20 @@ def subset_font_raw(font_in, font_out, unicodes, opts):
149
154
nam .close ()
150
155
151
156
if pe :
152
- print ('Generate("' + font_out + ' ")' , file = pe )
157
+ print (f 'Generate("{ font_out } ")' , file = pe )
153
158
pe .close ()
154
159
subprocess .call (["fontforge" , "-script" , pe_fn ])
155
160
else :
156
- font .generate (font_out , flags = flags )
161
+ font .generate (font_out , flags = flags )
157
162
font .close ()
158
163
159
164
if '--roundtrip' in opts :
160
165
# FontForge apparently contains a bug where it incorrectly calculates
161
166
# the advanceWidthMax in the hhea table, and a workaround is to open
162
167
# and re-generate
163
168
font2 = fontforge .open (font_out )
164
- font2 .generate (font_out , flags = flags )
169
+ font2 .generate (font_out , flags = flags )
170
+
165
171
166
172
def subset_font (font_in , font_out , unicodes , opts ):
167
173
font_out_raw = font_out
@@ -173,81 +179,97 @@ def subset_font(font_in, font_out, unicodes, opts):
173
179
# 2011-02-14 DC this needs to only happen with --namelist is used
174
180
# os.rename(font_out_raw + '.nam', font_out + '.nam')
175
181
182
+
176
183
def getsubset (subset , font_in ):
177
184
subsets = subset .split ('+' )
178
185
179
- quotes = [0x2013 ] # endash
180
- quotes += [0x2014 ] # emdash
181
- quotes += [0x2018 ] # quoteleft
182
- quotes += [0x2019 ] # quoteright
183
- quotes += [0x201A ] # quotesinglbase
184
- quotes += [0x201C ] # quotedblleft
185
- quotes += [0x201D ] # quotedblright
186
- quotes += [0x201E ] # quotedblbase
187
- quotes += [0x2022 ] # bullet
188
- quotes += [0x2039 ] # guilsinglleft
189
- quotes += [0x203A ] # guilsinglright
190
-
191
- latin = range (0x20 , 0x7f ) # Basic Latin (A-Z, a-z, numbers)
192
- latin += range (0xa0 , 0x100 ) # Western European symbols and diacritics
193
- latin += [0x20ac ] # Euro
194
- latin += [0x0152 ] # OE
195
- latin += [0x0153 ] # oe
196
- latin += [0x003b ] # semicolon
197
- latin += [0x00b7 ] # periodcentered
198
- latin += [0x0131 ] # dotlessi
199
- latin += [0x02c6 ] # circumflex
200
- latin += [0x02da ] # ring
201
- latin += [0x02dc ] # tilde
202
- latin += [0x2074 ] # foursuperior
203
- latin += [0x2215 ] # division slash
204
- latin += [0x2044 ] # fraction slash
205
- latin += [0xe0ff ] # PUA: Font logo
206
- latin += [0xeffd ] # PUA: Font version number
207
- latin += [0xf000 ] # PUA: font ppem size indicator: run `ftview -f 1255 10 Ubuntu-Regular.ttf` to see it in action!
186
+ quotes = [
187
+ 0x2013 , # endash
188
+ 0x2014 , # emdash
189
+ 0x2018 , # quoteleft
190
+ 0x2019 , # quoteright
191
+ 0x201A , # quotesinglbase
192
+ 0x201C , # quotedblleft
193
+ 0x201D , # quotedblright
194
+ 0x201E , # quotedblbase
195
+ 0x2022 , # bullet
196
+ 0x2039 , # guilsinglleft
197
+ 0x203A , # guilsinglright
198
+ ]
199
+
200
+ latin = [
201
+ * range (0x20 , 0x7f ), # Basic Latin (A-Z, a-z, numbers)
202
+ * range (0xa0 , 0x100 ), # Western European symbols and diacritics
203
+ 0x20ac , # Euro
204
+ 0x0152 , # OE
205
+ 0x0153 , # oe
206
+ 0x003b , # semicolon
207
+ 0x00b7 , # periodcentered
208
+ 0x0131 , # dotlessi
209
+ 0x02c6 , # circumflex
210
+ 0x02da , # ring
211
+ 0x02dc , # tilde
212
+ 0x2074 , # foursuperior
213
+ 0x2215 , # division slash
214
+ 0x2044 , # fraction slash
215
+ 0xe0ff , # PUA: Font logo
216
+ 0xeffd , # PUA: Font version number
217
+ 0xf000 , # PUA: font ppem size indicator: run
218
+ # `ftview -f 1255 10 Ubuntu-Regular.ttf` to see it in action!
219
+ ]
208
220
209
221
result = quotes
210
222
211
223
if 'menu' in subset :
212
224
font = fontforge .open (font_in )
213
- result = map (ord , font .familyname )
214
- result += [0x0020 ]
225
+ result = [
226
+ * map (ord , font .familyname ),
227
+ 0x0020 ,
228
+ ]
215
229
216
230
if 'latin' in subset :
217
231
result += latin
218
232
if 'latin-ext' in subset :
219
233
# These ranges include Extended A, B, C, D, and Additional with the
220
234
# exception of Vietnamese, which is a separate range
221
- result += (range (0x100 , 0x370 ) +
222
- range (0x1d00 , 0x1ea0 ) +
223
- range (0x1ef2 , 0x1f00 ) +
224
- range (0x2070 , 0x20d0 ) +
225
- range (0x2c60 , 0x2c80 ) +
226
- range (0xa700 , 0xa800 ))
235
+ result += [
236
+ * range (0x100 , 0x370 ),
237
+ * range (0x1d00 , 0x1ea0 ),
238
+ * range (0x1ef2 , 0x1f00 ),
239
+ * range (0x2070 , 0x20d0 ),
240
+ * range (0x2c60 , 0x2c80 ),
241
+ * range (0xa700 , 0xa800 ),
242
+ ]
227
243
if 'vietnamese' in subset :
228
- # 2011-07-16 DC: Charset from http://vietunicode.sourceforge.net/charset/ + U+1ef9 from Fontaine
244
+ # 2011-07-16 DC: Charset from
245
+ # http://vietunicode.sourceforge.net/charset/ + U+1ef9 from Fontaine
229
246
result += [0x00c0 , 0x00c1 , 0x00c2 , 0x00c3 , 0x00C8 , 0x00C9 ,
230
247
0x00CA , 0x00CC , 0x00CD , 0x00D2 , 0x00D3 , 0x00D4 ,
231
248
0x00D5 , 0x00D9 , 0x00DA , 0x00DD , 0x00E0 , 0x00E1 ,
232
249
0x00E2 , 0x00E3 , 0x00E8 , 0x00E9 , 0x00EA , 0x00EC ,
233
250
0x00ED , 0x00F2 , 0x00F3 , 0x00F4 , 0x00F5 , 0x00F9 ,
234
251
0x00FA , 0x00FD , 0x0102 , 0x0103 , 0x0110 , 0x0111 ,
235
252
0x0128 , 0x0129 , 0x0168 , 0x0169 , 0x01A0 , 0x01A1 ,
236
- 0x01AF , 0x01B0 , 0x20AB ] + range (0x1EA0 , 0x1EFA )
253
+ 0x01AF , 0x01B0 , 0x20AB , * range (0x1EA0 , 0x1EFA )]
237
254
if 'greek' in subset :
238
- # Could probably be more aggressive here and exclude archaic characters,
239
- # but lack data
240
- result += range (0x370 , 0x400 )
255
+ # Could probably be more aggressive here and exclude archaic
256
+ # characters, but lack data
257
+ result += [ * range (0x370 , 0x400 )]
241
258
if 'greek-ext' in subset :
242
- result += range (0x370 , 0x400 ) + range (0x1f00 , 0x2000 )
259
+ result += [ * range (0x370 , 0x400 ), * range (0x1f00 , 0x2000 )]
243
260
if 'cyrillic' in subset :
244
261
# Based on character frequency analysis
245
- result += range (0x400 , 0x460 ) + [ 0x490 , 0x491 , 0x4b0 , 0x4b1 , 0x2116 ]
262
+ result += [ * range (0x400 , 0x460 ), 0x490 , 0x491 , 0x4b0 , 0x4b1 , 0x2116 ]
246
263
if 'cyrillic-ext' in subset :
247
- result += (range (0x400 , 0x530 ) +
248
- [0x20b4 , 0x2116 ] + # 0x2116 is the russian No, a number abbreviation similar to the latin #, suggested by Alexei Vanyashin
249
- range (0x2de0 , 0x2e00 ) +
250
- range (0xa640 , 0xa6a0 ))
264
+ result += [
265
+ * range (0x400 , 0x530 ),
266
+ 0x20b4 ,
267
+ # 0x2116 is the russian No, a number abbreviation similar to the
268
+ # latin #, suggested by Alexei Vanyashin
269
+ 0x2116 ,
270
+ * range (0x2de0 , 0x2e00 ),
271
+ * range (0xa640 , 0xa6a0 ),
272
+ ]
251
273
if 'arabic' in subset :
252
274
# Based on Droid Arabic Kufi 1.0
253
275
result += [0x000D , 0x0020 , 0x0621 , 0x0627 , 0x062D ,
@@ -295,7 +317,8 @@ def getsubset(subset, font_in):
295
317
0x06C8 , 0x06C9 , 0x06CA , 0x06CB , 0x06CF , 0x06CE ,
296
318
0x06D0 , 0x06D1 , 0x06D4 , 0x06FA , 0x06DD , 0x06DE ,
297
319
0x06E0 , 0x06E9 , 0x060D , 0xFD3E , 0xFD3F , 0x25CC ,
298
- # Added from https://groups.google.com/d/topic/googlefontdirectory-discuss/MwlMWMPNCXs/discussion
320
+ # Added from
321
+ # https://groups.google.com/d/topic/googlefontdirectory-discuss/MwlMWMPNCXs/discussion
299
322
0x063b , 0x063c , 0x063d , 0x063e , 0x063f , 0x0620 ,
300
323
0x0674 , 0x0674 , 0x06EC ]
301
324
@@ -308,42 +331,48 @@ def getsubset(subset, font_in):
308
331
309
332
return result
310
333
311
- # code for extracting vertical metrics from a TrueType font
312
334
335
+ # code for extracting vertical metrics from a TrueType font
313
336
class Sfnt :
314
337
def __init__ (self , data ):
315
338
version , numTables , _ , _ , _ = struct .unpack ('>IHHHH' , data [:12 ])
316
339
self .tables = {}
317
340
for i in range (numTables ):
318
- tag , checkSum , offset , length = struct .unpack ('>4sIII' , data [12 + 16 * i : 28 + 16 * i ])
341
+ tag , checkSum , offset , length = struct .unpack (
342
+ '>4sIII' , data [12 + 16 * i : 28 + 16 * i ])
319
343
self .tables [tag ] = data [offset : offset + length ]
320
344
321
345
def hhea (self ):
322
346
r = {}
323
347
d = self .tables ['hhea' ]
324
- r ['Ascender' ], r ['Descender' ], r ['LineGap' ] = struct .unpack ('>hhh' , d [4 :10 ])
348
+ r ['Ascender' ], r ['Descender' ], r ['LineGap' ] = struct .unpack (
349
+ '>hhh' , d [4 :10 ])
325
350
return r
326
351
327
352
def os2 (self ):
328
353
r = {}
329
354
d = self .tables ['OS/2' ]
330
355
r ['fsSelection' ], = struct .unpack ('>H' , d [62 :64 ])
331
- r ['sTypoAscender' ], r ['sTypoDescender' ], r ['sTypoLineGap' ] = struct .unpack ('>hhh' , d [68 :74 ])
332
- r ['usWinAscender' ], r ['usWinDescender' ] = struct .unpack ('>HH' , d [74 :78 ])
356
+ r ['sTypoAscender' ], r ['sTypoDescender' ], r ['sTypoLineGap' ] = \
357
+ struct .unpack ('>hhh' , d [68 :74 ])
358
+ r ['usWinAscender' ], r ['usWinDescender' ] = struct .unpack (
359
+ '>HH' , d [74 :78 ])
333
360
return r
334
361
362
+
335
363
def set_os2 (pe , name , val ):
336
- print ('SetOS2Value("' + name + '", %d)' % val , file = pe )
364
+ print (f'SetOS2Value("{ name } ", { val :d} )' , file = pe )
365
+
337
366
338
367
def set_os2_vert (pe , name , val ):
339
368
set_os2 (pe , name + 'IsOffset' , 0 )
340
369
set_os2 (pe , name , val )
341
370
371
+
342
372
# Extract vertical metrics data directly out of font file, and emit
343
373
# script code to set the values in the generated font. This is a (rather
344
374
# ugly) workaround for the issue described in:
345
- # http://sourceforge.net/mailarchive/forum.php?thread_name=20100906085718.GB1907%40khaled-laptop&forum_name=fontforge-users
346
-
375
+ # https://sourceforge.net/p/fontforge/mailman/fontforge-users/thread/20100906085718.GB1907@khaled-laptop/
347
376
def extract_vert_to_script (font_in , pe ):
348
377
with open (font_in , 'rb' ) as in_file :
349
378
data = in_file .read ()
@@ -357,11 +386,12 @@ def extract_vert_to_script(font_in, pe):
357
386
set_os2_vert (pe , "HHeadAscent" , hhea ['Ascender' ])
358
387
set_os2_vert (pe , "HHeadDescent" , hhea ['Descender' ])
359
388
389
+
360
390
def main (argv ):
361
- optlist , args = getopt .gnu_getopt (argv , '' , ['string=' , 'strip_names' , 'opentype-features' ,
362
- 'simplify ' , 'new ' , 'script ' ,
363
- 'nmr' , 'roundtrip' , 'subset=' ,
364
- 'namelist' , 'null' , 'nd' , 'move-display' ])
391
+ optlist , args = getopt .gnu_getopt (argv , '' , [
392
+ 'string=' , 'strip_names' , 'opentype-features ' , 'simplify ' , 'new ' ,
393
+ 'script' , 'nmr' , 'roundtrip' , 'subset=' , 'namelist' , 'null' , 'nd ' ,
394
+ 'move-display' ])
365
395
366
396
font_in , font_out = args
367
397
opts = dict (optlist )
@@ -371,5 +401,6 @@ def main(argv):
371
401
subset = getsubset (opts .get ('--subset' , 'latin' ), font_in )
372
402
subset_font (font_in , font_out , subset , opts )
373
403
404
+
374
405
if __name__ == '__main__' :
375
406
main (sys .argv [1 :])
0 commit comments