update NF batteries

This commit is contained in:
Daylin Morgan 2022-09-16 13:56:39 -05:00
parent 51a57bbbfa
commit 86020c4474
2 changed files with 107 additions and 54 deletions

View file

@ -1,23 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
# coding=utf8 # coding=utf8
# Nerd Fonts Version: 2.2.1 # Nerd Fonts Version: 2.2.2
# Script version is further down # Script version is further down
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.0.3" script_version = "3.0.6"
version = "2.2.1" version = "2.2.2"
projectName = "Nerd Fonts" projectName = "Nerd Fonts"
projectNameAbbreviation = "NF" projectNameAbbreviation = "NF"
projectNameSingular = projectName[:-1] projectNameSingular = projectName[:-1]
import sys import sys
try:
import psMat
except ImportError:
sys.exit(projectName + ": FontForge module is probably not installed. [See: http://designwithfontforge.com/en-US/Installing_Fontforge.html]")
import re import re
import os import os
import argparse import argparse
@ -30,12 +26,13 @@ try:
except ImportError: except ImportError:
sys.exit(projectName + ": configparser module is probably not installed. Try `pip install configparser` or equivalent") sys.exit(projectName + ": configparser module is probably not installed. Try `pip install configparser` or equivalent")
try: try:
import psMat
import fontforge import fontforge
except ImportError: except ImportError:
sys.exit( sys.exit(
projectName + ( projectName + (
": FontForge module could not be loaded. Try installing fontforge python bindings " ": FontForge module could not be loaded. Try installing fontforge python bindings "
"[e.g. on Linux Debian or Ubuntu: `sudo apt install fontforge python-fontforge`]" "[e.g. on Linux Debian or Ubuntu: `sudo apt install fontforge python3-fontforge`]"
) )
) )
@ -167,6 +164,7 @@ class font_patcher:
self.font_dim = None # class 'dict' self.font_dim = None # class 'dict'
self.onlybitmaps = 0 self.onlybitmaps = 0
self.extension = "" self.extension = ""
self.essential = set()
self.setup_arguments() self.setup_arguments()
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True) self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
if not os.path.isfile(self.args.font): if not os.path.isfile(self.args.font):
@ -181,6 +179,7 @@ class font_patcher:
except Exception: except Exception:
sys.exit(projectName + ": Can not open font, try to open with fontforge interactively to get more information") sys.exit(projectName + ": Can not open font, try to open with fontforge interactively to get more information")
self.setup_version() self.setup_version()
self.get_essential_references()
self.setup_name_backup() self.setup_name_backup()
self.remove_ligatures() self.remove_ligatures()
make_sure_path_exists(self.args.outputdir) make_sure_path_exists(self.args.outputdir)
@ -219,6 +218,10 @@ class font_patcher:
PreviousSymbolFilename = "" PreviousSymbolFilename = ""
symfont = None symfont = None
if not os.path.isdir(self.args.glyphdir):
sys.exit("{}: Can not find symbol glyph directory {} "
"(probably you need to download the src/glyphs/ directory?)".format(projectName, self.args.glyphdir))
for patch in self.patch_set: for patch in self.patch_set:
if patch['Enabled']: if patch['Enabled']:
if PreviousSymbolFilename != patch['Filename']: if PreviousSymbolFilename != patch['Filename']:
@ -226,7 +229,13 @@ class font_patcher:
if symfont: if symfont:
symfont.close() symfont.close()
symfont = None symfont = None
symfont = fontforge.open(self.args.glyphdir + patch['Filename']) if not os.path.isfile(self.args.glyphdir + patch['Filename']):
sys.exit("{}: Can not find symbol source for '{}'\n{:>{}} (i.e. {})".format(
projectName, patch['Name'], '', len(projectName), self.args.glyphdir + patch['Filename']))
if not os.access(self.args.glyphdir + patch['Filename'], os.R_OK):
sys.exit("{}: Can not open symbol source for '{}'\n{:>{}} (i.e. {})".format(
projectName, patch['Name'], '', len(projectName), self.args.glyphdir + patch['Filename']))
symfont = fontforge.open(os.path.join(self.args.glyphdir, patch['Filename']))
# Match the symbol font size to the source font size # Match the symbol font size to the source font size
symfont.em = self.sourceFont.em symfont.em = self.sourceFont.em
@ -320,6 +329,7 @@ class font_patcher:
parser.add_argument('-out', '--outputdir', dest='outputdir', default=".", type=str, nargs='?', help='The directory to output the patched font file to') parser.add_argument('-out', '--outputdir', dest='outputdir', default=".", type=str, nargs='?', help='The directory to output the patched font file to')
parser.add_argument('--glyphdir', dest='glyphdir', default=__dir__ + "/src/glyphs/", type=str, nargs='?', help='Path to glyphs to be used for patching') parser.add_argument('--glyphdir', dest='glyphdir', default=__dir__ + "/src/glyphs/", type=str, nargs='?', help='Path to glyphs to be used for patching')
parser.add_argument('--makegroups', dest='makegroups', default=False, action='store_true', help='Use alternative method to name patched fonts (experimental)') parser.add_argument('--makegroups', dest='makegroups', default=False, action='store_true', help='Use alternative method to name patched fonts (experimental)')
parser.add_argument('--variable-width-glyphs', dest='nonmono', default=False, action='store_true', help='Do not adjust advance width (no "overhang")')
# progress bar arguments - https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse # progress bar arguments - https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse
progressbars_group_parser = parser.add_mutually_exclusive_group(required=False) progressbars_group_parser = parser.add_mutually_exclusive_group(required=False)
@ -332,7 +342,7 @@ class font_patcher:
sym_font_group = parser.add_argument_group('Symbol Fonts') sym_font_group = parser.add_argument_group('Symbol Fonts')
sym_font_group.add_argument('--fontawesome', dest='fontawesome', default=False, action='store_true', help='Add Font Awesome Glyphs (http://fontawesome.io/)') sym_font_group.add_argument('--fontawesome', dest='fontawesome', default=False, action='store_true', help='Add Font Awesome Glyphs (http://fontawesome.io/)')
sym_font_group.add_argument('--fontawesomeextension', dest='fontawesomeextension', default=False, action='store_true', help='Add Font Awesome Extension Glyphs (https://andrelzgava.github.io/font-awesome-extension/)') sym_font_group.add_argument('--fontawesomeextension', dest='fontawesomeextension', default=False, action='store_true', help='Add Font Awesome Extension Glyphs (https://andrelzgava.github.io/font-awesome-extension/)')
sym_font_group.add_argument('--fontlinux', '--fontlogos', dest='fontlinux', default=False, action='store_true', help='Add Font Linux and other open source Glyphs (https://github.com/Lukas-W/font-logos)') sym_font_group.add_argument('--fontlogos', '--fontlinux', dest='fontlogos', default=False, action='store_true', help='Add Font Logos Glyphs (https://github.com/Lukas-W/font-logos)')
sym_font_group.add_argument('--octicons', dest='octicons', default=False, action='store_true', help='Add Octicons Glyphs (https://octicons.github.com)') sym_font_group.add_argument('--octicons', dest='octicons', default=False, action='store_true', help='Add Octicons Glyphs (https://octicons.github.com)')
sym_font_group.add_argument('--codicons', dest='codicons', default=False, action='store_true', help='Add Codicons Glyphs (https://github.com/microsoft/vscode-codicons)') sym_font_group.add_argument('--codicons', dest='codicons', default=False, action='store_true', help='Add Codicons Glyphs (https://github.com/microsoft/vscode-codicons)')
sym_font_group.add_argument('--powersymbols', dest='powersymbols', default=False, action='store_true', help='Add IEC Power Symbols (https://unicodepowersymbol.com/)') sym_font_group.add_argument('--powersymbols', dest='powersymbols', default=False, action='store_true', help='Add IEC Power Symbols (https://unicodepowersymbol.com/)')
@ -351,7 +361,7 @@ class font_patcher:
if self.args.complete: if self.args.complete:
self.args.fontawesome = True self.args.fontawesome = True
self.args.fontawesomeextension = True self.args.fontawesomeextension = True
self.args.fontlinux = True self.args.fontlogos = True
self.args.octicons = True self.args.octicons = True
self.args.codicons = True self.args.codicons = True
self.args.powersymbols = True self.args.powersymbols = True
@ -380,6 +390,10 @@ class font_patcher:
if self.args.alsowindows: if self.args.alsowindows:
self.args.windows = False self.args.windows = False
if self.args.nonmono and self.args.single:
print("Warniung: Specified contradicting --variable-width-glyphs and --use-single-width-glyph. Ignoring --variable-width-glyphs.")
self.args.nonmono = False
# this one also works but it needs to be updated every time a font is added # this one also works but it needs to be updated every time a font is added
# it was a conditional in self.setup_font_names() before, but it was missing # it was a conditional in self.setup_font_names() before, but it was missing
# a symbol font, so it would name the font complete without being so sometimes. # a symbol font, so it would name the font complete without being so sometimes.
@ -389,7 +403,7 @@ class font_patcher:
# self.args.complete = all([ # self.args.complete = all([
# self.args.fontawesome is True, # self.args.fontawesome is True,
# self.args.fontawesomeextension is True, # self.args.fontawesomeextension is True,
# self.args.fontlinux is True, # self.args.fontlogos is True,
# self.args.octicons is True, # self.args.octicons is True,
# self.args.powersymbols is True, # self.args.powersymbols is True,
# self.args.pomicons is True, # self.args.pomicons is True,
@ -436,9 +450,9 @@ class font_patcher:
if self.args.pomicons: if self.args.pomicons:
additionalFontNameSuffix += " P" additionalFontNameSuffix += " P"
verboseAdditionalFontNameSuffix += " Plus Pomicons" verboseAdditionalFontNameSuffix += " Plus Pomicons"
if self.args.fontlinux: if self.args.fontlogos:
additionalFontNameSuffix += " L" additionalFontNameSuffix += " L"
verboseAdditionalFontNameSuffix += " Plus Font Logos (Font Linux)" verboseAdditionalFontNameSuffix += " Plus Font Logos"
if self.args.material: if self.args.material:
additionalFontNameSuffix += " MDI" additionalFontNameSuffix += " MDI"
verboseAdditionalFontNameSuffix += " Plus Material Design Icons" verboseAdditionalFontNameSuffix += " Plus Material Design Icons"
@ -522,6 +536,8 @@ class font_patcher:
maxFamilyLength = 31 maxFamilyLength = 31
maxFontLength = maxFamilyLength - len('-' + subFamily) maxFontLength = maxFamilyLength - len('-' + subFamily)
familyname += " " + projectNameAbbreviation familyname += " " + projectNameAbbreviation
if self.args.single:
familyname += "M"
fullname += " Windows Compatible" fullname += " Windows Compatible"
# now make sure less than 32 characters name length # now make sure less than 32 characters name length
@ -673,7 +689,7 @@ class font_patcher:
# Supported params: overlap | careful # Supported params: overlap | careful
# Powerline dividers # Powerline dividers
SYM_ATTR_POWERLINE = { SYM_ATTR_POWERLINE = {
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': ''}, 'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
# Arrow tips # Arrow tips
0xe0b0: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, 0xe0b0: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
@ -706,23 +722,23 @@ class font_patcher:
0xe0c3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, 0xe0c3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
# Small squares # Small squares
0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
0xe0c5: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0c5: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
# Bigger squares # Bigger squares
0xe0c6: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0c6: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
# Waveform # Waveform
0xe0c8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, 0xe0c8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
# Hexagons # Hexagons
0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
# Legos # Legos
0xe0ce: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0ce: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
0xe0cf: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': ''}, 0xe0cf: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {}},
0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, 0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
# Top and bottom trapezoid # Top and bottom trapezoid
@ -732,22 +748,22 @@ class font_patcher:
SYM_ATTR_DEFAULT = { SYM_ATTR_DEFAULT = {
# 'pa' == preserve aspect ratio # 'pa' == preserve aspect ratio
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': ''} 'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}}
} }
SYM_ATTR_FONTA = { SYM_ATTR_FONTA = {
# 'pa' == preserve aspect ratio # 'pa' == preserve aspect ratio
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': ''}, 'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
# Don't center these arrows vertically # Don't center these arrows vertically
0xf0dc: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': ''}, 0xf0dc: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}},
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': {}}
} }
CUSTOM_ATTR = { CUSTOM_ATTR = {
# 'pa' == preserve aspect ratio # 'pa' == preserve aspect ratio
'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': ''} 'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
} }
# Most glyphs we want to maximize during the scale. However, there are some # Most glyphs we want to maximize during the scale. However, there are some
@ -789,7 +805,7 @@ 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': 0xE531, 'SrcStart': 0xE5FA, 'SrcEnd': 0xE631, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE534, 'SrcStart': 0xE5FA, 'SrcEnd': 0xE634, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'SrcEnd': 0xE7C5, 'ScaleGlyph': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'SrcEnd': 0xE7C5, 'ScaleGlyph': 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, 'SrcEnd': None, 'ScaleGlyph': 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, 'SrcEnd': None, 'ScaleGlyph': 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, 'SrcEnd': None, 'ScaleGlyph': 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, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
@ -804,7 +820,7 @@ class font_patcher:
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off) {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'SrcEnd': 0xFD46, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'SrcEnd': 0xFD46, 'ScaleGlyph': None, '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, 'SrcEnd': 0xE3EB, 'ScaleGlyph': None, '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, 'SrcEnd': 0xE3EB, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.fontlinux, 'Name': "Font Logos (Font Linux)", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'SrcEnd': None , 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'SrcEnd': None , 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'SrcEnd': 0xF505, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'SrcEnd': 0xF505, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
@ -835,6 +851,17 @@ class font_patcher:
self.sourceFont.hhea_linegap = 0 self.sourceFont.hhea_linegap = 0
self.sourceFont.os2_typolinegap = 0 self.sourceFont.os2_typolinegap = 0
def get_essential_references(self):
"""Find glyphs that are needed for the basic glyphs"""
# Sometimes basic glyphs are constructed from multiple other glyphs.
# Find out which other glyphs are also needed to keep the basic
# glyphs intact.
# 0x00-0x17f is the Latin Extended-A range
for glyph in range(0x21, 0x17f):
if not glyph in self.sourceFont:
continue
for r in self.sourceFont[glyph].references:
self.essential.add(self.sourceFont[r[0]].unicode)
def get_sourcefont_dimensions(self): def get_sourcefont_dimensions(self):
# Initial font dimensions # Initial font dimensions
@ -886,10 +913,7 @@ class font_patcher:
# We want to preserve x/y aspect ratio, so find biggest scale factor that allows symbol to fit # We want to preserve x/y aspect ratio, so find biggest scale factor that allows symbol to fit
scale_ratio_x = self.font_dim['width'] / sym_dim['width'] scale_ratio_x = self.font_dim['width'] / sym_dim['width']
scale_ratio_y = self.font_dim['height'] / sym_dim['height']
# font_dim['height'] represents total line height, keep our symbols sized based upon font's em
# NOTE: is this comment correct? font_dim['height'] isn't used here
scale_ratio_y = self.sourceFont.em / sym_dim['height']
if scale_ratio_x > scale_ratio_y: if scale_ratio_x > scale_ratio_y:
scale_ratio = scale_ratio_y scale_ratio = scale_ratio_y
else: else:
@ -927,6 +951,8 @@ class font_patcher:
if self.args.quiet is False: if self.args.quiet is False:
sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n") sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n")
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
for index, sym_glyph in enumerate(symbolFontSelection): for index, sym_glyph in enumerate(symbolFontSelection):
index = max(1, index) index = max(1, index)
@ -936,8 +962,17 @@ class font_patcher:
sym_attr = attributes['default'] sym_attr = attributes['default']
if exactEncoding: if exactEncoding:
# use the exact same hex values for the source font as for the symbol font # Use the exact same hex values for the source font as for the symbol font.
currentSourceFontGlyph = sym_glyph.encoding # Problem is we do not know the codepoint of the sym_glyph and because it
# came from a selection.byGlyphs there might be skipped over glyphs.
# The iteration is still in the order of the selection by codepoint,
# so we take the next allowed codepoint of the current glyph
possible_codes = [ ]
if sym_glyph.unicode > currentSourceFontGlyph:
possible_codes += [ sym_glyph.unicode ]
if sym_glyph.altuni:
possible_codes += [ v for v, s, r in sym_glyph.altuni if v > currentSourceFontGlyph ]
currentSourceFontGlyph = min(possible_codes)
else: else:
# use source font defined hex values based on passed in start and end # use source font defined hex values based on passed in start and end
currentSourceFontGlyph = sourceFontList[sourceFontCounter] currentSourceFontGlyph = sourceFontList[sourceFontCounter]
@ -952,10 +987,11 @@ class font_patcher:
sys.stdout.flush() sys.stdout.flush()
# check if a glyph already exists in this location # check if a glyph already exists in this location
if careful or 'careful' in sym_attr['params']: if careful or 'careful' in sym_attr['params'] or currentSourceFontGlyph in self.essential:
if currentSourceFontGlyph in self.sourceFont: if currentSourceFontGlyph in self.sourceFont:
if self.args.quiet is False: if self.args.quiet is False:
print(" Found existing Glyph at {:X}. Skipping...".format(currentSourceFontGlyph)) careful_type = 'essential' if currentSourceFontGlyph in self.essential else 'existing'
print(" Found {} Glyph at {:X}. Skipping...".format(careful_type, currentSourceFontGlyph))
# We don't want to touch anything so move to next Glyph # We don't want to touch anything so move to next Glyph
continue continue
else: else:
@ -1012,13 +1048,10 @@ class font_patcher:
# Currently stretching vertically for both monospace and double-width # Currently stretching vertically for both monospace and double-width
scale_ratio_y = self.font_dim['height'] / sym_dim['height'] scale_ratio_y = self.font_dim['height'] / sym_dim['height']
if 'overlap' in sym_attr['params']: overlap = sym_attr['params'].get('overlap')
overlap = sym_attr['params']['overlap']
else:
overlap = 0
if scale_ratio_x != 1 or scale_ratio_y != 1: if scale_ratio_x != 1 or scale_ratio_y != 1:
if overlap != 0: if overlap:
scale_ratio_x *= 1 + overlap scale_ratio_x *= 1 + overlap
scale_ratio_y *= 1 + overlap scale_ratio_y *= 1 + overlap
self.sourceFont[currentSourceFontGlyph].transform(psMat.scale(scale_ratio_x, scale_ratio_y)) self.sourceFont[currentSourceFontGlyph].transform(psMat.scale(scale_ratio_x, scale_ratio_y))
@ -1044,7 +1077,7 @@ class font_patcher:
# Right align # Right align
x_align_distance += self.font_dim['width'] - sym_dim['width'] x_align_distance += self.font_dim['width'] - sym_dim['width']
if overlap != 0: if overlap:
overlap_width = self.font_dim['width'] * overlap overlap_width = self.font_dim['width'] * overlap
if sym_attr['align'] == 'l': if sym_attr['align'] == 'l':
x_align_distance -= overlap_width x_align_distance -= overlap_width
@ -1054,20 +1087,26 @@ class font_patcher:
align_matrix = psMat.translate(x_align_distance, y_align_distance) align_matrix = psMat.translate(x_align_distance, y_align_distance)
self.sourceFont[currentSourceFontGlyph].transform(align_matrix) self.sourceFont[currentSourceFontGlyph].transform(align_matrix)
# Ensure after horizontal adjustments and centering that the glyph
# does not overlap the bearings (edges)
if not overlap:
self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])
# Needed for setting 'advance width' on each glyph so they do not overlap, # Needed for setting 'advance width' on each glyph so they do not overlap,
# also ensures the font is considered monospaced on Windows by setting the # also ensures the font is considered monospaced on Windows by setting the
# same width for all character glyphs. This needs to be done for all glyphs, # same width for all character glyphs. This needs to be done for all glyphs,
# even the ones that are empty and didn't go through the scaling operations. # even the ones that are empty and didn't go through the scaling operations.
# It should come after setting the glyph bearings
self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph]) self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])
# Ensure after horizontal adjustments and centering that the glyph # Re-remove negative bearings for target font with variable advance width
# does not overlap the bearings (edges) if self.args.nonmono:
self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph]) self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])
# Check if the inserted glyph is scaled correctly for monospace # Check if the inserted glyph is scaled correctly for monospace
if self.args.single: if self.args.single:
(xmin, _, xmax, _) = self.sourceFont[currentSourceFontGlyph].boundingBox() (xmin, _, xmax, _) = self.sourceFont[currentSourceFontGlyph].boundingBox()
if int(xmax - xmin) > self.font_dim['width'] * (1 + overlap): if int(xmax - xmin) > self.font_dim['width'] * (1 + (overlap or 0)):
print("\n Warning: Scaled glyph U+{:X} wider than one monospace width ({} / {} (overlap {}))".format( print("\n Warning: Scaled glyph U+{:X} wider than one monospace width ({} / {} (overlap {}))".format(
currentSourceFontGlyph, int(xmax - xmin), self.font_dim['width'], overlap)) currentSourceFontGlyph, int(xmax - xmin), self.font_dim['width'], overlap))
@ -1114,11 +1153,15 @@ class font_patcher:
self.font_dim.width is set with self.get_sourcefont_dimensions(). self.font_dim.width is set with self.get_sourcefont_dimensions().
""" """
try: try:
# Fontforge handles the width change like this:
# - Keep existing left_side_bearing
# - Set width
# - Calculate and set new right_side_bearing
glyph.width = self.font_dim['width'] glyph.width = self.font_dim['width']
except: except:
pass pass
def prepareScaleGlyph(self, scaleGlyph, symbolFont): def prepareScaleGlyph(self, scaleGlyph, symbolFont, destGlyph):
""" Prepare raw ScaleGlyph data for use """ """ Prepare raw ScaleGlyph data for use """
# The GlyphData is a dict with these (possible) entries: # The GlyphData is a dict with these (possible) entries:
# 'GlyphsToScale': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled # 'GlyphsToScale': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled
@ -1153,13 +1196,14 @@ class font_patcher:
else: else:
scaleGlyph['scales'] = [] scaleGlyph['scales'] = []
for group in scaleGlyph['GlyphsToScale']: for group in scaleGlyph['GlyphsToScale']:
sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ]) sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph)
scaleGlyph['scales'].append(self.get_scale_factor(sym_dim)) scaleGlyph['scales'].append(self.get_scale_factor(sym_dim))
def get_glyph_scale(self, unicode_value, scaleGlyph, symbolFont): def get_glyph_scale(self, unicode_value, scaleGlyph, symbolFont):
""" Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """ """ Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """
# Potentially destorys the contents of self.sourceFont[unicode_value]
if not 'scales' in scaleGlyph: if not 'scales' in scaleGlyph:
self.prepareScaleGlyph(scaleGlyph, symbolFont) self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[unicode_value])
for glyph_list, scale in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales']): for glyph_list, scale in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales']):
if unicode_value in glyph_list: if unicode_value in glyph_list:
return scale return scale
@ -1181,13 +1225,22 @@ def make_sure_path_exists(path):
if exception.errno != errno.EEXIST: if exception.errno != errno.EEXIST:
raise raise
def get_multiglyph_boundingBox(glyphs): def get_multiglyph_boundingBox(glyphs, destGlyph = None):
""" Returns dict of the dimensions of multiple glyphs combined """ """ Returns dict of the dimensions of multiple glyphs combined(, as if they are copied into destGlyph) """
# If destGlyph is given the glyph(s) are first copied over into that
# glyph and measured in that font (to avoid rounding errors)
# Leaves the destGlyph in unknown state!
bbox = [ None, None, None, None ] bbox = [ None, None, None, None ]
for glyph in glyphs: for glyph in glyphs:
if glyph is None: if glyph is None:
# Glyph has been in defining range but is not in the actual font # Glyph has been in defining range but is not in the actual font
continue continue
if destGlyph:
glyph.font.selection.select(glyph)
glyph.font.copy()
destGlyph.font.selection.select(destGlyph)
destGlyph.font.paste()
glyph = destGlyph
gbb = glyph.boundingBox() gbb = glyph.boundingBox()
bbox[0] = gbb[0] if bbox[0] is None or bbox[0] > gbb[0] else bbox[0] bbox[0] = gbb[0] if bbox[0] is None or bbox[0] > gbb[0] else bbox[0]
bbox[1] = gbb[1] if bbox[1] is None or bbox[1] > gbb[1] else bbox[1] bbox[1] = gbb[1] if bbox[1] is None or bbox[1] > gbb[1] else bbox[1]

Binary file not shown.