mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-12-22 06:50:44 -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
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
# Change the script version when you edit this script:
|
# Change the script version when you edit this script:
|
||||||
script_version = "3.5.2"
|
script_version = "3.5.8"
|
||||||
|
|
||||||
version = "2.3.3"
|
version = "2.3.3"
|
||||||
projectName = "Nerd Fonts"
|
projectName = "Nerd Fonts"
|
||||||
|
@ -21,6 +21,7 @@ from argparse import RawTextHelpFormatter
|
||||||
import errno
|
import errno
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
|
from enum import Enum
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -194,6 +195,14 @@ def panose_check_to_text(value, panose = False):
|
||||||
return "Panose says \"monospaced\""
|
return "Panose says \"monospaced\""
|
||||||
return "Panose is invalid" + (" ({})".format(list(panose)) if panose else "")
|
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):
|
def is_monospaced(font):
|
||||||
""" Check if a font is probably monospaced """
|
""" Check if a font is probably monospaced """
|
||||||
# Some fonts lie (or have not any Panose flag set), spot check 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
|
# We believe our own check more then Panose ;-D
|
||||||
return (width_mono, None if width_mono else glyph)
|
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):
|
def get_advance_width(font, extended, minimum):
|
||||||
""" Get the maximum/minimum advance width in the extended(?) range """
|
""" Get the maximum/minimum advance width in the extended(?) range """
|
||||||
width = 0
|
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, True, True), get_advance_width(font, False, True),
|
||||||
get_advance_width(font, False, False), get_advance_width(font, True, False))
|
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:
|
class font_patcher:
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
|
@ -267,7 +301,7 @@ class font_patcher:
|
||||||
self.setup_version()
|
self.setup_version()
|
||||||
self.get_essential_references()
|
self.get_essential_references()
|
||||||
self.setup_name_backup(font)
|
self.setup_name_backup(font)
|
||||||
if self.args.single:
|
if not self.args.nonmono:
|
||||||
self.assert_monospace()
|
self.assert_monospace()
|
||||||
self.remove_ligatures()
|
self.remove_ligatures()
|
||||||
self.setup_patch_set()
|
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.
|
# 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.
|
# 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()
|
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
|
# 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:
|
if self.font_dim['height'] * 1.8 < self.font_dim['width'] * 2:
|
||||||
|
@ -693,12 +720,14 @@ class font_patcher:
|
||||||
print(" {} and {}".format(
|
print(" {} and {}".format(
|
||||||
report_advance_widths(self.sourceFont),
|
report_advance_widths(self.sourceFont),
|
||||||
panose_check_to_text(panose_mono, self.sourceFont.os2_panose)))
|
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")
|
print(" Warning: Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless")
|
||||||
if offending_char is not None:
|
if offending_char is not None:
|
||||||
print(" Offending char: 0x{:X}".format(offending_char))
|
print(" Offending char: 0x{:X}".format(offending_char))
|
||||||
if self.args.single <= 1:
|
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")
|
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):
|
def setup_patch_set(self):
|
||||||
|
@ -779,6 +808,9 @@ class font_patcher:
|
||||||
0xf0dd: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}},
|
0xf0dd: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}},
|
||||||
0xf0de: {'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 = {
|
CUSTOM_ATTR = {
|
||||||
# 'pa' == preserve aspect ratio
|
# 'pa' == preserve aspect ratio
|
||||||
|
@ -877,7 +909,8 @@ class font_patcher:
|
||||||
# Define the character ranges
|
# Define the character ranges
|
||||||
# Symbol font ranges
|
# Symbol font ranges
|
||||||
self.patch_set = [
|
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': 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': 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},
|
{'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
|
# and we try to sort this out here
|
||||||
# See also https://glyphsapp.com/learn/vertical-metrics
|
# See also https://glyphsapp.com/learn/vertical-metrics
|
||||||
# See also https://github.com/source-foundry/font-line
|
# See also https://github.com/source-foundry/font-line
|
||||||
hhea_height = self.sourceFont.hhea_ascent - self.sourceFont.hhea_descent
|
(hhea_btb, typo_btb, win_btb, win_gap) = get_btb_metrics(self.sourceFont)
|
||||||
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
|
|
||||||
use_typo = self.sourceFont.os2_use_typo_metrics != 0
|
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
|
# 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
|
our_btb = typo_btb if use_typo else win_btb
|
||||||
if our_btb == hhea_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:
|
else:
|
||||||
# We trust the WIN metric more, see experiments in #1056
|
# 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))
|
print("{}: WARNING Font vertical metrics inconsistent (HHEA {} / TYPO {} / WIN {}), using WIN".format(projectName, hhea_btb, typo_btb, win_btb))
|
||||||
our_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))
|
# 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}
|
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0}
|
||||||
|
|
||||||
if metrics == 0:
|
if metrics == Metric.HHEA:
|
||||||
self.font_dim['ymin'] = self.sourceFont.hhea_descent + half_gap(self.sourceFont.hhea_linegap, False)
|
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)
|
self.font_dim['ymax'] = self.sourceFont.hhea_ascent + half_gap(self.sourceFont.hhea_linegap, True)
|
||||||
elif metrics == 1:
|
elif metrics == Metric.TYPO:
|
||||||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent + half_gap(self.sourceFont.os2_typolinegap, False)
|
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)
|
self.font_dim['ymax'] = self.sourceFont.os2_typoascent + half_gap(self.sourceFont.os2_typolinegap, True)
|
||||||
else:
|
elif metrics == Metric.WIN:
|
||||||
self.font_dim['ymin'] = -self.sourceFont.os2_windescent + half_gap(win_gap, False)
|
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)
|
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
|
# Calculate font height
|
||||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
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_descent = self.sourceFont.os2_typodescent
|
||||||
self.sourceFont.hhea_linegap = self.sourceFont.os2_typolinegap
|
self.sourceFont.hhea_linegap = self.sourceFont.os2_typolinegap
|
||||||
self.sourceFont.os2_use_typo_metrics = 1
|
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
|
# Step 2
|
||||||
# Find the biggest char width and advance width
|
# Find the biggest char width and advance width
|
||||||
|
@ -1093,7 +1127,6 @@ class font_patcher:
|
||||||
""" Copies symbol glyphs into self.sourceFont """
|
""" Copies symbol glyphs into self.sourceFont """
|
||||||
progressText = ''
|
progressText = ''
|
||||||
careful = False
|
careful = False
|
||||||
glyphSetLength = 0
|
|
||||||
sourceFontCounter = 0
|
sourceFontCounter = 0
|
||||||
|
|
||||||
if self.args.careful:
|
if self.args.careful:
|
||||||
|
@ -1114,14 +1147,12 @@ class font_patcher:
|
||||||
glyphSetLength = len(symbolFontSelection)
|
glyphSetLength = len(symbolFontSelection)
|
||||||
|
|
||||||
if not self.args.quiet:
|
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
|
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
|
||||||
width_warning = False
|
width_warning = False
|
||||||
|
|
||||||
for index, sym_glyph in enumerate(symbolFontSelection):
|
for index, sym_glyph in enumerate(symbolFontSelection):
|
||||||
index = max(1, index)
|
|
||||||
|
|
||||||
sym_attr = attributes.get(sym_glyph.unicode)
|
sym_attr = attributes.get(sym_glyph.unicode)
|
||||||
if sym_attr is None:
|
if sym_attr is None:
|
||||||
sym_attr = attributes['default']
|
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.stderr.write("{}: Please use at least version: {}\n".format(projectName, minimumVersion))
|
||||||
sys.exit(1)
|
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():
|
def setup_arguments():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=(
|
description=(
|
||||||
|
@ -1699,7 +1758,12 @@ def setup_arguments():
|
||||||
|
|
||||||
|
|
||||||
def main():
|
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()
|
check_fontforge_min_version()
|
||||||
args = setup_arguments()
|
args = setup_arguments()
|
||||||
patcher = font_patcher(args)
|
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