mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-11-13 18:07:52 -06:00
chore: change batteries
This commit is contained in:
parent
5aff489af5
commit
718ba2179f
2 changed files with 96 additions and 32 deletions
128
bin/font-patcher
128
bin/font-patcher
|
@ -6,7 +6,7 @@
|
|||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Change the script version when you edit this script:
|
||||
script_version = "3.5.2"
|
||||
script_version = "3.5.8"
|
||||
|
||||
version = "2.3.3"
|
||||
projectName = "Nerd Fonts"
|
||||
|
@ -21,6 +21,7 @@ from argparse import RawTextHelpFormatter
|
|||
import errno
|
||||
import subprocess
|
||||
import json
|
||||
from enum import Enum
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
|
@ -194,6 +195,14 @@ def panose_check_to_text(value, panose = False):
|
|||
return "Panose says \"monospaced\""
|
||||
return "Panose is invalid" + (" ({})".format(list(panose)) if panose else "")
|
||||
|
||||
def panose_proportion_to_text(value):
|
||||
""" Interpret a Panose proportion value (4th value) for family 2 (latin text) """
|
||||
proportion = {
|
||||
0: "Any", 1: "No Fit", 2: "Old Style", 3: "Modern", 4: "Even Width",
|
||||
5: "Extended", 6: "Condensed", 7: "Very Extended", 8: "Very Condensed",
|
||||
9: "Monospaced" }
|
||||
return proportion.get(value, "??? {}".format(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:
|
||||
|
@ -222,6 +231,20 @@ def is_monospaced(font):
|
|||
# We believe our own check more then Panose ;-D
|
||||
return (width_mono, None if width_mono else glyph)
|
||||
|
||||
def force_panose_monospaced(font):
|
||||
""" Forces the Panose flag to monospaced if they are unset or halfway ok already """
|
||||
# For some Windows applications (e.g. 'cmd'), they seem to honour the Panose table
|
||||
# https://forum.high-logic.com/postedfiles/Panose.pdf
|
||||
panose = list(font.os2_panose)
|
||||
if panose[0] == 0: # 0 (1st value) = family kind; 0 = any (default)
|
||||
panose[0] = 2 # make kind latin text and display
|
||||
print(" Setting Panose 'Family Kind' to 'Latin Text and Display' (was 'Any')")
|
||||
font.os2_panose = tuple(panose)
|
||||
if panose[0] == 2 and panose[3] != 9:
|
||||
print(" Setting Panose 'Proportion' to 'Monospaced' (was '{}')".format(panose_proportion_to_text(panose[3])))
|
||||
panose[3] = 9 # 3 (4th value) = proportion; 9 = monospaced
|
||||
font.os2_panose = tuple(panose)
|
||||
|
||||
def get_advance_width(font, extended, minimum):
|
||||
""" Get the maximum/minimum advance width in the extended(?) range """
|
||||
width = 0
|
||||
|
@ -248,6 +271,17 @@ def report_advance_widths(font):
|
|||
get_advance_width(font, True, True), get_advance_width(font, False, True),
|
||||
get_advance_width(font, False, False), get_advance_width(font, True, False))
|
||||
|
||||
def get_btb_metrics(font):
|
||||
""" Get the baseline to baseline distance for all three metrics """
|
||||
hhea_height = font.hhea_ascent - font.hhea_descent
|
||||
typo_height = font.os2_typoascent - font.os2_typodescent
|
||||
win_height = font.os2_winascent + font.os2_windescent
|
||||
win_gap = max(0, font.hhea_linegap - win_height + hhea_height)
|
||||
hhea_btb = hhea_height + font.hhea_linegap
|
||||
typo_btb = typo_height + font.os2_typolinegap
|
||||
win_btb = win_height + win_gap
|
||||
return (hhea_btb, typo_btb, win_btb, win_gap)
|
||||
|
||||
|
||||
class font_patcher:
|
||||
def __init__(self, args):
|
||||
|
@ -267,7 +301,7 @@ class font_patcher:
|
|||
self.setup_version()
|
||||
self.get_essential_references()
|
||||
self.setup_name_backup(font)
|
||||
if self.args.single:
|
||||
if not self.args.nonmono:
|
||||
self.assert_monospace()
|
||||
self.remove_ligatures()
|
||||
self.setup_patch_set()
|
||||
|
@ -280,13 +314,6 @@ class font_patcher:
|
|||
# Force width to be equal on all glyphs to ensure the font is considered monospaced on Windows.
|
||||
# This needs to be done on all characters, as some information seems to be lost from the original font file.
|
||||
self.set_sourcefont_glyph_widths()
|
||||
# For some Windows applications (e.g. 'cmd') that is not enough. But they seem to honour the Panose table
|
||||
# https://forum.high-logic.com/postedfiles/Panose.pdf
|
||||
panose = list(self.sourceFont.os2_panose)
|
||||
if panose[0] == 0 or panose[0] == 2: # 0 (1st value) = family kind; 0 = any (default); 2 = latin text and display
|
||||
panose[0] = 2 # Assert kind
|
||||
panose[3] = 9 # 3 (4th value) = propotion; 9 = monospaced
|
||||
self.sourceFont.os2_panose = tuple(panose)
|
||||
|
||||
# For very wide (almost square or wider) fonts we do not want to generate 2 cell wide Powerline glyphs
|
||||
if self.font_dim['height'] * 1.8 < self.font_dim['width'] * 2:
|
||||
|
@ -693,12 +720,14 @@ class font_patcher:
|
|||
print(" {} and {}".format(
|
||||
report_advance_widths(self.sourceFont),
|
||||
panose_check_to_text(panose_mono, self.sourceFont.os2_panose)))
|
||||
if not width_mono:
|
||||
if self.args.single and not width_mono:
|
||||
print(" Warning: Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless")
|
||||
if offending_char is not None:
|
||||
print(" Offending char: 0x{:X}".format(offending_char))
|
||||
if self.args.single <= 1:
|
||||
sys.exit(projectName + ": Font will not be patched! Give --mono (or -s, or --use-single-width-glyphs) twice to force patching")
|
||||
if width_mono:
|
||||
force_panose_monospaced(self.sourceFont)
|
||||
|
||||
|
||||
def setup_patch_set(self):
|
||||
|
@ -779,6 +808,9 @@ class font_patcher:
|
|||
0xf0dd: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}},
|
||||
0xf0de: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}}
|
||||
}
|
||||
SYM_ATTR_HEAVYBRACKETS = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {'careful': True}}
|
||||
}
|
||||
|
||||
CUSTOM_ATTR = {
|
||||
# 'pa' == preserve aspect ratio
|
||||
|
@ -877,7 +909,8 @@ class font_patcher:
|
|||
# Define the character ranges
|
||||
# Symbol font ranges
|
||||
self.patch_set = [
|
||||
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5AA, 'SrcStart': 0xE5FA, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5FF, 'SrcStart': 0xE5FA, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': True, 'Name': "Heavy Angle Brackets", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_HEAVYBRACKETS},
|
||||
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'ScaleRules': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0A0, 'SymEnd': 0xE0A2, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0B0, 'SymEnd': 0xE0B3, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
|
@ -961,39 +994,37 @@ class font_patcher:
|
|||
# and we try to sort this out here
|
||||
# See also https://glyphsapp.com/learn/vertical-metrics
|
||||
# See also https://github.com/source-foundry/font-line
|
||||
hhea_height = self.sourceFont.hhea_ascent - self.sourceFont.hhea_descent
|
||||
typo_height = self.sourceFont.os2_typoascent - self.sourceFont.os2_typodescent
|
||||
win_height = self.sourceFont.os2_winascent + self.sourceFont.os2_windescent
|
||||
win_gap = max(0, self.sourceFont.hhea_linegap - win_height + hhea_height)
|
||||
hhea_btb = hhea_height + self.sourceFont.hhea_linegap
|
||||
typo_btb = typo_height + self.sourceFont.os2_typolinegap
|
||||
win_btb = win_height + win_gap
|
||||
(hhea_btb, typo_btb, win_btb, win_gap) = get_btb_metrics(self.sourceFont)
|
||||
use_typo = self.sourceFont.os2_use_typo_metrics != 0
|
||||
|
||||
Metric = Enum('Metric', ['HHEA', 'TYPO', 'WIN'])
|
||||
|
||||
# We use either TYPO (1) or WIN (2) and compare with HHEA
|
||||
# and use HHEA (0) if the fonts seems broken
|
||||
# and use HHEA (0) if the fonts seems broken - no WIN, see #1056
|
||||
our_btb = typo_btb if use_typo else win_btb
|
||||
if our_btb == hhea_btb:
|
||||
metrics = 1 if use_typo else 2 # conforming font
|
||||
metrics = Metric.TYPO if use_typo else Metric.WIN # conforming font
|
||||
else:
|
||||
# We trust the WIN metric more, see experiments in #1056
|
||||
print("{}: WARNING Font vertical metrics inconsistent (HHEA {} / TYPO {} / WIN {}), using WIN".format(projectName, hhea_btb, typo_btb, win_btb))
|
||||
our_btb = win_btb
|
||||
metrics = 1
|
||||
metrics = Metric.WIN
|
||||
|
||||
# print("FINI hhea {} typo {} win {} use {} {} {}".format(hhea_btb, typo_btb, win_btb, use_typo, our_btb != hhea_btb, self.sourceFont.fontname))
|
||||
|
||||
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0}
|
||||
|
||||
if metrics == 0:
|
||||
self.font_dim['ymin'] = self.sourceFont.hhea_descent + half_gap(self.sourceFont.hhea_linegap, False)
|
||||
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)
|
||||
elif metrics == 1:
|
||||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent + half_gap(self.sourceFont.os2_typolinegap, False)
|
||||
elif metrics == Metric.TYPO:
|
||||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent - half_gap(self.sourceFont.os2_typolinegap, False)
|
||||
self.font_dim['ymax'] = self.sourceFont.os2_typoascent + half_gap(self.sourceFont.os2_typolinegap, True)
|
||||
else:
|
||||
self.font_dim['ymin'] = -self.sourceFont.os2_windescent + half_gap(win_gap, False)
|
||||
elif metrics == Metric.WIN:
|
||||
self.font_dim['ymin'] = -self.sourceFont.os2_windescent - half_gap(win_gap, False)
|
||||
self.font_dim['ymax'] = self.sourceFont.os2_winascent + half_gap(win_gap, True)
|
||||
else:
|
||||
pass # Will fail the metrics check some line later
|
||||
|
||||
# Calculate font height
|
||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
||||
|
@ -1021,6 +1052,9 @@ class font_patcher:
|
|||
self.sourceFont.hhea_descent = self.sourceFont.os2_typodescent
|
||||
self.sourceFont.hhea_linegap = self.sourceFont.os2_typolinegap
|
||||
self.sourceFont.os2_use_typo_metrics = 1
|
||||
(check_hhea_btb, check_typo_btb, check_win_btb, _) = get_btb_metrics(self.sourceFont)
|
||||
if check_hhea_btb != check_typo_btb or check_typo_btb != check_win_btb or check_win_btb != our_btb:
|
||||
sys.exit("{}: Error in baseline to baseline code detected".format(projectName))
|
||||
|
||||
# Step 2
|
||||
# Find the biggest char width and advance width
|
||||
|
@ -1093,7 +1127,6 @@ class font_patcher:
|
|||
""" Copies symbol glyphs into self.sourceFont """
|
||||
progressText = ''
|
||||
careful = False
|
||||
glyphSetLength = 0
|
||||
sourceFontCounter = 0
|
||||
|
||||
if self.args.careful:
|
||||
|
@ -1114,14 +1147,12 @@ class font_patcher:
|
|||
glyphSetLength = len(symbolFontSelection)
|
||||
|
||||
if not self.args.quiet:
|
||||
sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n")
|
||||
sys.stdout.write("Adding {} Glyphs from {} Set\n".format(glyphSetLength, setName))
|
||||
|
||||
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
|
||||
width_warning = False
|
||||
|
||||
for index, sym_glyph in enumerate(symbolFontSelection):
|
||||
index = max(1, index)
|
||||
|
||||
sym_attr = attributes.get(sym_glyph.unicode)
|
||||
if sym_attr is None:
|
||||
sym_attr = attributes['default']
|
||||
|
@ -1571,6 +1602,34 @@ def check_fontforge_min_version():
|
|||
sys.stderr.write("{}: Please use at least version: {}\n".format(projectName, minimumVersion))
|
||||
sys.exit(1)
|
||||
|
||||
def check_version_with_git(version):
|
||||
""" Upgraded the version to the current git tag version (starting with 'v') """
|
||||
git = subprocess.run("git describe --tags",
|
||||
cwd=os.path.dirname(__file__),
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
|
||||
).stdout.decode('utf-8')
|
||||
if len(git) == 0:
|
||||
return False
|
||||
tag = git.strip()
|
||||
if len(tag) == 0 or not tag.startswith('v'):
|
||||
return False
|
||||
tag = tag[1:]
|
||||
r = re.search('(.*?)(-[0-9]+)-g[0-9a-fA-F]+$', tag)
|
||||
if r:
|
||||
tag = r.group(1)
|
||||
patchlevel = r.group(2)
|
||||
else:
|
||||
patchlevel = ""
|
||||
# Inspired by Phaxmohdem's versiontuple https://stackoverflow.com/a/28568003
|
||||
|
||||
versiontuple = lambda v: tuple( p.zfill(8) for p in v.split(".") )
|
||||
if versiontuple(tag) > versiontuple(version):
|
||||
return tag + patchlevel
|
||||
if versiontuple(tag) == versiontuple(version) and len(patchlevel) > 0:
|
||||
return tag + patchlevel
|
||||
return False
|
||||
|
||||
def setup_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
|
@ -1699,7 +1758,12 @@ def setup_arguments():
|
|||
|
||||
|
||||
def main():
|
||||
print("{} Patcher v{} ({}) executing".format(projectName, version, script_version))
|
||||
global version
|
||||
git_version = check_version_with_git(version)
|
||||
print("{} Patcher v{} ({}) (ff {}) executing".format(
|
||||
projectName, git_version if git_version else version, script_version, fontforge.version()))
|
||||
if git_version:
|
||||
version = git_version
|
||||
check_fontforge_min_version()
|
||||
args = setup_arguments()
|
||||
patcher = font_patcher(args)
|
||||
|
|
BIN
src/glyphs/extraglyphs.sfd
Normal file
BIN
src/glyphs/extraglyphs.sfd
Normal file
Binary file not shown.
Loading…
Reference in a new issue