-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathfont-patcher.patch
316 lines (294 loc) · 17.3 KB
/
font-patcher.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
diff --git a/font-patcher b/font-patcher
index 51e3ff1..f639f3f 100755
--- a/font-patcher
+++ b/font-patcher
@@ -13,6 +13,14 @@ projectName = "Nerd Fonts"
projectNameAbbreviation = "NF"
projectNameSingular = projectName[:-1]
+# FOR SARASA
+projectName = "Nerds"
+projectNameAbbreviation = ""
+projectNameSingular = projectName[:-1]
+subFamily = ""
+looseName = "Sarasa Term SC Nerd"
+compactName = "SarasaTermSCNerd"
+
import sys
import re
import os
@@ -187,7 +195,10 @@ def check_panose_monospaced(font):
return -1 # invalid Panose info
panose_mono = ((panose[0] == 2 and panose[3] == 9) or
(panose[0] == 3 and panose[3] == 3))
- return 1 if panose_mono else 0
+
+ # FOR SARASA
+ return 1
+ # return 1 if panose_mono else 0
def panose_check_to_text(value, panose = False):
""" Convert value from check_panose_monospaced() to human readable string """
@@ -208,6 +219,10 @@ def panose_proportion_to_text(value):
def is_monospaced(font):
""" Check if a font is probably monospaced """
# Some fonts lie (or have not any Panose flag set), spot check monospaced:
+
+ # FOR SARASA
+ return (True, None)
+
width = -1
width_mono = True
for glyph in [ 0x49, 0x4D, 0x57, 0x61, 0x69, 0x6d, 0x2E ]: # wide and slim glyphs 'I', 'M', 'W', 'a', 'i', 'm', '.'
@@ -448,6 +463,13 @@ class font_patcher:
outfile = os.path.normpath(os.path.join(
sanitize_filename(self.args.outputdir, True),
sanitize_filename(fontname) + self.args.extension))
+
+ # FOR SARASA
+ outfile = os.path.normpath(os.path.join(
+ sanitize_filename(self.args.outputdir, True),
+ f'{compactName}-{self.get_subfamily()}.ttf'
+ ))
+
bitmaps = str()
if len(sourceFont.bitmapSizes):
logger.debug("Preserving bitmaps %s", repr(sourceFont.bitmapSizes))
@@ -505,6 +527,10 @@ class font_patcher:
logger.critical("Source font is a variable open type font (VF) and the patch results will most likely not be what you want")
print(message)
+ # FOR SARASA: build hdmx table
+ print("Building hdmx table and fix post table")
+ post_fix(self.args.font, outfile)
+
if self.args.postprocess:
subprocess.call([self.args.postprocess, outfile])
print("\n")
@@ -558,6 +584,10 @@ class font_patcher:
additionalFontNameSuffix += " WEA"
verboseAdditionalFontNameSuffix += " Plus Weather Icons"
+ # FOR SARASA: clean file name
+ additionalFontNameSuffix = "" + projectNameSingular
+ verboseAdditionalFontNameSuffix = "" + projectNameSingular
+
# add mono signifier to beginning of name suffix
if self.args.single:
variant_abbrev = "M"
@@ -569,12 +599,20 @@ class font_patcher:
variant_abbrev = ""
variant_full = ""
+ # FOR SARASA: clean file name
+ variant_abbrev = ""
+ variant_full = ""
+
ps_suffix = projectNameAbbreviation + variant_abbrev + additionalFontNameSuffix
# add 'Nerd Font' to beginning of name suffix
verboseAdditionalFontNameSuffix = " " + projectNameSingular + variant_full + verboseAdditionalFontNameSuffix
additionalFontNameSuffix = " " + projectNameSingular + variant_full + additionalFontNameSuffix
+ # FOR SARASA: clean file name
+ additionalFontNameSuffix = "" + projectNameSingular
+ verboseAdditionalFontNameSuffix = "" + projectNameSingular
+
if FontnameParserOK and self.args.makegroups > 0:
user_supplied_name = False # User supplied names are kept unchanged
if not isinstance(self.args.force_name, str):
@@ -623,6 +661,10 @@ class font_patcher:
# dont trust 'font.familyname'
familyname = fontname
+ # FOR SARASA
+ familyname = looseName
+ fallbackStyle = self.get_subfamily()
+
# fullname (filename) can always use long/verbose font name, even in windows
if font.fullname != None:
fullname = font.fullname + verboseAdditionalFontNameSuffix
@@ -725,12 +767,14 @@ class font_patcher:
'Powerline': ''
}
+ # FOR SARASA
projectInfo = (
- "Patched with '" + projectName + " Patcher' (https://github.com/ryanoasis/nerd-fonts)\n\n"
+ # "Patched with '" + projectName + " Patcher' (https://github.com/ryanoasis/nerd-fonts)\n\n"
+ "Patched with 'Sarasa Term SC Nerd Patcher' (https://github.com/laishulu/Sarasa-Term-SC-Nerd)\n\n"
"* Website: https://www.nerdfonts.com\n"
"* Version: " + version + "\n"
- "* Development Website: https://github.com/ryanoasis/nerd-fonts\n"
- "* Changelog: https://github.com/ryanoasis/nerd-fonts/blob/-/changelog.md"
+ # "* Development Website: https://github.com/ryanoasis/nerd-fonts\n"
+ # "* Changelog: https://github.com/ryanoasis/nerd-fonts/blob/-/changelog.md"
)
familyname = replace_font_name(familyname, reservedFontNameReplacements)
@@ -763,6 +807,29 @@ class font_patcher:
n.inject_suffix(verboseAdditionalFontNameSuffix, ps_suffix, short_family)
n.rename_font(font)
+
+ # FOR SARASA
+ font.familyname = looseName
+ subFamily = self.get_subfamily()
+ font.fullname = f"{looseName} {en_subfamily(subFamily)}"
+ font.fontname = f"{compactName}-{subFamily}"
+ uniqueID = f"{font.fullname}; Sarasa v{self.sourceFont.version}"
+
+ font.appendSFNTName(str("English (US)"), str("UniqueID"), uniqueID)
+ font.appendSFNTName(str("Chinese (PRC)"), str("UniqueID"), uniqueID)
+ font.appendSFNTName(str('English (US)'), str('Fullname'), font.fullname)
+ font.appendSFNTName(str("Chinese (PRC)"), str("Fullname"), zh_family(font.fullname))
+
+ font.appendSFNTName(str('English (US)'), str('Family'), font.familyname)
+ font.appendSFNTName(str('Chinese (PRC)'), str('Family'), zh_family(font.familyname))
+ font.appendSFNTName(str('English (US)'), str('SubFamily'), en_subfamily(subFamily))
+ font.appendSFNTName(str('Chinese (PRC)'), str('SubFamily'), zh_subfamily(subFamily))
+
+ font.appendSFNTName(str('English (US)'), str('Preferred Family'), font.familyname)
+ font.appendSFNTName(str('Chinese (PRC)'), str('Preferred Family'), zh_family(font.familyname))
+ font.appendSFNTName(str('English (US)'), str('Preferred Styles'), en_subfamily(subFamily))
+ font.appendSFNTName(str('Chinese (PRC)'), str('Preferred Styles'), zh_subfamily(subFamily))
+
font.comment = projectInfo
font.fontlog = projectInfo
@@ -776,6 +843,10 @@ class font_patcher:
self.sourceFont.version = str(self.sourceFont.cidversion) + ";" + projectName + " " + version
self.sourceFont.sfntRevision = None # Auto-set (refreshed) by fontforge
self.sourceFont.appendSFNTName(str('English (US)'), str('Version'), "Version " + self.sourceFont.version)
+
+ # FOR SARASA: set version
+ self.sourceFont.appendSFNTName(str('Chinese (PRC)'), str('Version'), "版本 " + self.sourceFont.version)
+
# The Version SFNT name is later reused by the NameParser for UniqueID
# print("Version now is {}".format(sourceFont.version))
@@ -842,7 +913,6 @@ class font_patcher:
if width_mono:
force_panose_monospaced(self.sourceFont)
-
def setup_patch_set(self):
""" Creates list of dicts to with instructions on copying glyphs from each symbol font into self.sourceFont """
@@ -1139,7 +1209,21 @@ class font_patcher:
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, # Power, Power On/Off, Power On, Sleep
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
{'Enabled': False , 'Name': "Material legacy", 'Filename': "materialdesign/materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
- {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF1AF0,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ # FOR SARASA
+ # {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF1AF0,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ # {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF165C,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF0553,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0565,'SymEnd': 0xF0E32,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0E40,'SymEnd': 0xF0E85,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0E8E,'SymEnd': 0xF118E,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF119E,'SymEnd': 0xF11AE,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF11E3,'SymEnd': 0xF126F,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF129E,'SymEnd': 0xF13ED,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF13FE,'SymEnd': 0xF149F,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF14B0,'SymEnd': 0xF14CF,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF14DE,'SymEnd': 0xF165C,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+ {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF19E8,'SymEnd': 0xF19FD,'SrcStart': None, 'ScaleRules': MDI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
+
{'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleRules': WEATH_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF381, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
@@ -1253,6 +1337,10 @@ class font_patcher:
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0, 'iconheight': 0, 'ypadding': 0}
+ # FOR SARASA
+ self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0, 'iconheight': 0, 'ypadding': 0, 'em': 0}
+ self.font_dim['em'] = self.sourceFont.em
+
if metrics == Metric.HHEA:
self.font_dim['ymin'] = self.sourceFont.hhea_descent - half_gap(self.sourceFont.hhea_linegap, False)
self.font_dim['ymax'] = self.sourceFont.hhea_ascent + half_gap(self.sourceFont.hhea_linegap, True)
@@ -1361,9 +1449,13 @@ class font_patcher:
""" Get the target width (1 or 2 'cell') for a given stretch parameter """
# For monospaced fonts all chars need to be maximum 'one' space wide
# other fonts allows double width glyphs for 'pa' or if requested with '2'
- if self.args.single or ('pa' not in stretch and '2' not in stretch) or '1' in stretch:
- return 1
- return 2
+
+ # FOR SARASA
+ return 1
+
+ # if self.args.single or ('pa' not in stretch and '2' not in stretch) or '1' in stretch:
+ # return 1
+ # return 2
def get_scale_factors(self, sym_dim, stretch, overlap=None):
""" Get scale in x and y as tuple """
@@ -1791,6 +1883,10 @@ class font_patcher:
return (scale, box)
return None
+ # FOR SARASA: extract subFamily from font name
+ def get_subfamily(self):
+ file_name = self.args.font.split('.')[-2]
+ return file_name.split('-')[-1]
def half_gap(gap, top):
""" Divides integer value into two new integers """
@@ -2210,6 +2306,76 @@ def main():
for f in sourceFonts:
f.close()
+def zh_family(name):
+ res = name.replace(looseName, "更纱终端书呆黑体-简")
+ res = res.replace(compactName, "更纱终端书呆黑体-简")
+ return res
+
+def en_subfamily(compact):
+ return compact.replace("Italic", " Italic").strip()
+
+def zh_subfamily(compact):
+ sub_family_dict = {
+ "ExtraLight": "特细体",
+ "ExtraLightItalic":"特细斜体",
+ "Light":"细体",
+ "LightItalic":"细斜体",
+ "Regular":"常规体",
+ "Italic":"斜体",
+ "SemiBold":"中粗体",
+ "SemiBoldItalic":"中粗斜体",
+ "Bold":"粗体",
+ "BoldItalic":"粗斜体"
+ }
+ return sub_family_dict[compact]
+
+# FOR SARASA: build hdmx table
+import sys
+import math
+from fontTools.ttLib import TTFont, newTable
+
+def post_fix(src_file, dst_file):
+ dst_font = TTFont(dst_file, recalcBBoxes=False)
+ build_hdmx(dst_font)
+ fix_isFixedPitch(dst_font)
+
+ src_font = TTFont(src_file)
+ dst_font["OS/2"].xAvgCharWidth = src_font["OS/2"].xAvgCharWidth
+ src_font.close()
+
+ dst_font.save(dst_file)
+ dst_font.close()
+
+def build_hdmx(font):
+ headFlagInstructionsMayAlterAdvanceWidth = 0x0010
+ sarasaHintPpemMin = 11
+ sarasaHintPpemMax = 48
+
+ originalFontHead = font["head"]
+ originalFontHmtx = font["hmtx"]
+
+ originalFontHead.flags |= headFlagInstructionsMayAlterAdvanceWidth
+
+ hdmxTable = newTable("hdmx")
+ hdmxTable.hdmx = {}
+
+ # build hdmx table for odd and hinted ppems only.
+ for ppem in range(
+ math.floor(sarasaHintPpemMin / 2) * 2 + 1, sarasaHintPpemMax + 1, 2
+ ):
+ halfUpm = originalFontHead.unitsPerEm / 2
+ halfPpem = math.ceil(ppem / 2)
+ hdmxTable.hdmx[ppem] = {
+ name: math.ceil(width / halfUpm) * halfPpem
+ for name, (width, _) in originalFontHmtx.metrics.items()
+ }
+
+ font["hdmx"] = hdmxTable
+
+def fix_isFixedPitch(font):
+ post = font["post"].__dict__
+ post["isFixedPitch"] = 1
+
if __name__ == "__main__":
__dir__ = os.path.dirname(os.path.abspath(__file__))