Skip to content

Commit

Permalink
cherry pick meterial design icons
Browse files Browse the repository at this point in the history
  • Loading branch information
laishulu committed Jan 2, 2025
1 parent 14d827e commit 0054db6
Show file tree
Hide file tree
Showing 3 changed files with 336 additions and 10 deletions.
316 changes: 316 additions & 0 deletions font-patcher.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,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__))
Loading

0 comments on commit 0054db6

Please sign in to comment.