mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-12-21 22:40:44 -06:00
update NF batteries
This commit is contained in:
parent
b71e7d3aef
commit
9eb507579a
2 changed files with 376 additions and 262 deletions
638
bin/font-patcher
638
bin/font-patcher
|
@ -1,14 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
# Nerd Fonts Version: 2.2.2
|
||||
# Nerd Fonts Version: 2.3.0-RC
|
||||
# Script version is further down
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Change the script version when you edit this script:
|
||||
script_version = "3.0.6"
|
||||
script_version = "3.1.1"
|
||||
|
||||
version = "2.2.2"
|
||||
version = "2.3.0-RC"
|
||||
projectName = "Nerd Fonts"
|
||||
projectNameAbbreviation = "NF"
|
||||
projectNameSingular = projectName[:-1]
|
||||
|
@ -82,15 +82,32 @@ class TableHEADWriter:
|
|||
i += 4
|
||||
extra = 0
|
||||
for j in range(4):
|
||||
extra = extra << 8
|
||||
if i + j <= end:
|
||||
extra += ord(self.f.read(1))
|
||||
extra = extra << 8
|
||||
checksum = (checksum + extra) & 0xFFFFFFFF
|
||||
return checksum
|
||||
|
||||
def find_head_table(self):
|
||||
def find_head_table(self, idx):
|
||||
""" Search all tables for the HEAD table and store its metadata """
|
||||
self.f.seek(4)
|
||||
# Use font with index idx if this is a font collection file
|
||||
self.f.seek(0, 0)
|
||||
tag = self.f.read(4)
|
||||
if tag == b'ttcf':
|
||||
self.f.seek(2*2, 1)
|
||||
self.num_fonts = self.getlong()
|
||||
if (idx >= self.num_fonts):
|
||||
raise Exception('Trying to access subfont index {} but have only {} fonts'.format(idx, num_fonts))
|
||||
for _ in range(idx + 1):
|
||||
offset = self.getlong()
|
||||
self.f.seek(offset, 0)
|
||||
elif idx != 0:
|
||||
raise Exception('Trying to access subfont but file is no collection')
|
||||
else:
|
||||
self.f.seek(0, 0)
|
||||
self.num_fonts = 1
|
||||
|
||||
self.f.seek(4, 1)
|
||||
numtables = self.getshort()
|
||||
self.f.seek(3*2, 1)
|
||||
|
||||
|
@ -102,7 +119,7 @@ class TableHEADWriter:
|
|||
self.tab_length = self.getlong()
|
||||
if tab_name == b'head':
|
||||
return
|
||||
raise Exception('No HEAD table found')
|
||||
raise Exception('No HEAD table found in font idx {}'.format(idx))
|
||||
|
||||
def goto(self, where):
|
||||
""" Go to a named location in the file or to the specified index """
|
||||
|
@ -146,60 +163,97 @@ class TableHEADWriter:
|
|||
self.modified = False
|
||||
self.f = open(filename, 'r+b')
|
||||
|
||||
self.find_head_table()
|
||||
self.find_head_table(0)
|
||||
|
||||
self.flags = self.getshort('flags')
|
||||
self.lowppem = self.getshort('lowestRecPPEM')
|
||||
self.checksum_adj = self.getlong('checksumAdjustment')
|
||||
|
||||
def check_panose_monospaced(font):
|
||||
""" Check if the font's Panose flags say it is monospaced """
|
||||
# https://forum.high-logic.com/postedfiles/Panose.pdf
|
||||
panose = list(font.os2_panose)
|
||||
if panose[0] < 2 or panose[0] > 5:
|
||||
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
|
||||
|
||||
def is_monospaced(font):
|
||||
""" Check if a font is probably monospaced """
|
||||
# Some fonts lie (or have not any Panose flag set), spot check monospaced:
|
||||
width = -1
|
||||
width_mono = True
|
||||
for glyph in [ 0x49, 0x4D, 0x57, 0x61, 0x69, 0x2E ]: # wide and slim glyphs 'I', 'M', 'W', 'a', 'i', '.'
|
||||
if not glyph in font:
|
||||
# A 'strange' font, believe Panose
|
||||
return check_panose_monospaced(font) == 1
|
||||
# print(" -> {} {}".format(glyph, font[glyph].width))
|
||||
if width < 0:
|
||||
width = font[glyph].width
|
||||
continue
|
||||
if font[glyph].width != width:
|
||||
# Exception for fonts like Code New Roman Regular or Hermit Light/Bold:
|
||||
# Allow small 'i' and dot to be smaller than normal
|
||||
# I believe the source fonts are buggy
|
||||
if glyph in [ 0x69, 0x2E ]:
|
||||
if width > font[glyph].width:
|
||||
continue
|
||||
(xmin, _, xmax, _) = font[glyph].boundingBox()
|
||||
if width > xmax - xmin:
|
||||
continue
|
||||
width_mono = False
|
||||
break
|
||||
# We believe our own check more then Panose ;-D
|
||||
return width_mono
|
||||
|
||||
def get_advance_width(font, extended, minimum):
|
||||
""" Get the maximum/minimum advance width in the extended(?) range """
|
||||
width = 0
|
||||
if extended:
|
||||
end = 0x17f
|
||||
else:
|
||||
end = 0x07e
|
||||
for glyph in range(0x21, end):
|
||||
if not glyph in font:
|
||||
continue
|
||||
if glyph in range(0x7F, 0xBF):
|
||||
continue # ignore special characters like '1/4' etc
|
||||
if width == 0:
|
||||
width = font[glyph].width
|
||||
continue
|
||||
if not minimum and width < font[glyph].width:
|
||||
width = font[glyph].width
|
||||
elif minimum and width > font[glyph].width:
|
||||
width = font[glyph].width
|
||||
return width
|
||||
|
||||
|
||||
class font_patcher:
|
||||
def __init__(self):
|
||||
self.args = None # class 'argparse.Namespace'
|
||||
def __init__(self, args):
|
||||
self.args = args # class 'argparse.Namespace'
|
||||
self.sym_font_args = []
|
||||
self.config = None # class 'configparser.ConfigParser'
|
||||
self.sourceFont = None # class 'fontforge.font'
|
||||
self.octiconsExactEncodingPosition = True
|
||||
self.patch_set = None # class 'list'
|
||||
self.font_dim = None # class 'dict'
|
||||
self.onlybitmaps = 0
|
||||
self.extension = ""
|
||||
self.essential = set()
|
||||
self.setup_arguments()
|
||||
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
||||
if not os.path.isfile(self.args.font):
|
||||
sys.exit("{}: Font file does not exist: {}".format(projectName, self.args.font))
|
||||
if not os.access(self.args.font, os.R_OK):
|
||||
sys.exit("{}: Can not open font file for reading: {}".format(projectName, self.args.font))
|
||||
if len(fontforge.fontsInFile(self.args.font)) > 1:
|
||||
sys.exit("{}: Font file contains {} fonts, can only handle single font files".format(projectName,
|
||||
len(fontforge.fontsInFile(self.args.font))))
|
||||
try:
|
||||
self.sourceFont = fontforge.open(self.args.font, 1) # 1 = ("fstypepermitted",))
|
||||
except Exception:
|
||||
sys.exit(projectName + ": Can not open font, try to open with fontforge interactively to get more information")
|
||||
|
||||
def patch(self, font):
|
||||
self.sourceFont = font
|
||||
self.setup_version()
|
||||
self.get_essential_references()
|
||||
self.setup_name_backup()
|
||||
self.setup_name_backup(font)
|
||||
if self.args.single:
|
||||
self.assert_monospace()
|
||||
self.remove_ligatures()
|
||||
make_sure_path_exists(self.args.outputdir)
|
||||
self.check_position_conflicts()
|
||||
self.setup_patch_set()
|
||||
self.setup_line_dimensions()
|
||||
self.get_sourcefont_dimensions()
|
||||
self.sourceFont.encoding = 'UnicodeFull' # Update the font encoding to ensure that the Unicode glyphs are available
|
||||
self.onlybitmaps = self.sourceFont.onlybitmaps # Fetch this property before adding outlines. NOTE self.onlybitmaps initialized and never used
|
||||
if self.args.extension == "":
|
||||
self.extension = os.path.splitext(self.args.font)[1]
|
||||
else:
|
||||
self.extension = '.' + self.args.extension
|
||||
if re.match("\.ttc$", self.extension, re.IGNORECASE):
|
||||
sys.exit(projectName + ": Can not create True Type Collections")
|
||||
|
||||
|
||||
def patch(self):
|
||||
|
||||
print("{} Patcher v{} ({}) executing\n".format(projectName, version, script_version))
|
||||
|
||||
if self.args.single:
|
||||
# Force width to be equal on all glyphs to ensure the font is considered monospaced on Windows.
|
||||
|
@ -241,14 +295,11 @@ class font_patcher:
|
|||
symfont.em = self.sourceFont.em
|
||||
PreviousSymbolFilename = patch['Filename']
|
||||
|
||||
# If patch table doesn't include a source start and end, re-use the symbol font values
|
||||
# If patch table doesn't include a source start, re-use the symbol font values
|
||||
SrcStart = patch['SrcStart']
|
||||
SrcEnd = patch['SrcEnd']
|
||||
if not SrcStart:
|
||||
SrcStart = patch['SymStart']
|
||||
if not SrcEnd:
|
||||
SrcEnd = patch['SymEnd']
|
||||
self.copy_glyphs(SrcStart, SrcEnd, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleGlyph'], patch['Name'], patch['Attributes'])
|
||||
self.copy_glyphs(SrcStart, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleGlyph'], patch['Name'], patch['Attributes'])
|
||||
|
||||
if symfont:
|
||||
symfont.close()
|
||||
|
@ -262,30 +313,49 @@ class font_patcher:
|
|||
self.sourceFont["grave"].glyphclass="baseglyph"
|
||||
|
||||
|
||||
def generate(self):
|
||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||
if self.sourceFont.fullname != None:
|
||||
outfile = self.args.outputdir + "/" + self.sourceFont.fullname + self.extension
|
||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fullname, outfile)
|
||||
def generate(self, sourceFonts):
|
||||
sourceFont = sourceFonts[0]
|
||||
if len(sourceFonts) > 1:
|
||||
layer = None
|
||||
# use first non-background layer
|
||||
for l in sourceFont.layers:
|
||||
if not sourceFont.layers[l].is_background:
|
||||
layer = l
|
||||
break
|
||||
outfile = os.path.normpath(os.path.join(self.args.outputdir, sourceFont.familyname + ".ttc"))
|
||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||
sourceFonts[0].generateTtc(outfile, sourceFonts[1:], flags=(str('opentype'), str('PfEd-comments')), layer=layer)
|
||||
message = "\nGenerated: {} fonts in '{}'".format(len(sourceFonts), outfile)
|
||||
else:
|
||||
outfile = self.args.outputdir + "/" + self.sourceFont.cidfontname + self.extension
|
||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fontname, outfile)
|
||||
fontname = sourceFont.fullname
|
||||
if not fontname:
|
||||
fontname = sourceFont.cidfontname
|
||||
outfile = os.path.normpath(os.path.join(self.args.outputdir, fontname + self.args.extension))
|
||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||
bitmaps = str()
|
||||
if len(self.sourceFont.bitmapSizes):
|
||||
print("Preserving bitmaps {}".format(self.sourceFont.bitmapSizes))
|
||||
bitmaps = str('otf') # otf/ttf, both is bf_ttf
|
||||
sourceFont.generate(outfile, bitmap_type=bitmaps, flags=(str('opentype'), str('PfEd-comments')))
|
||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fullname, outfile)
|
||||
|
||||
# Adjust flags that can not be changed via fontforge
|
||||
try:
|
||||
source_font = TableHEADWriter(self.args.font)
|
||||
dest_font = TableHEADWriter(outfile)
|
||||
if source_font.flags & 0x08 == 0 and dest_font.flags & 0x08 != 0:
|
||||
print("Changing flags from 0x{:X} to 0x{:X}".format(dest_font.flags, dest_font.flags & ~0x08))
|
||||
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
||||
if source_font.lowppem != dest_font.lowppem:
|
||||
print("Changing lowestRecPPEM from {} to {}".format(dest_font.lowppem, source_font.lowppem))
|
||||
dest_font.putshort(source_font.lowppem, 'lowestRecPPEM')
|
||||
if dest_font.modified:
|
||||
dest_font.reset_table_checksum()
|
||||
dest_font.reset_full_checksum()
|
||||
for idx in range(source_font.num_fonts):
|
||||
print("{}: Tweaking {}/{}".format(projectName, idx + 1, source_font.num_fonts))
|
||||
source_font.find_head_table(idx)
|
||||
dest_font.find_head_table(idx)
|
||||
if source_font.flags & 0x08 == 0 and dest_font.flags & 0x08 != 0:
|
||||
print("Changing flags from 0x{:X} to 0x{:X}".format(dest_font.flags, dest_font.flags & ~0x08))
|
||||
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
||||
if source_font.lowppem != dest_font.lowppem:
|
||||
print("Changing lowestRecPPEM from {} to {}".format(dest_font.lowppem, source_font.lowppem))
|
||||
dest_font.putshort(source_font.lowppem, 'lowestRecPPEM')
|
||||
if dest_font.modified:
|
||||
dest_font.reset_table_checksum()
|
||||
dest_font.reset_full_checksum()
|
||||
except Exception as error:
|
||||
print("Can not handle font flags ({})".format(repr(error)))
|
||||
finally:
|
||||
|
@ -301,130 +371,19 @@ class font_patcher:
|
|||
print("\nPost Processed: {}".format(outfile))
|
||||
|
||||
|
||||
def setup_arguments(self):
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
'Nerd Fonts Font Patcher: patches a given font with programming and development related glyphs\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/master/changelog.md'),
|
||||
formatter_class=RawTextHelpFormatter
|
||||
)
|
||||
|
||||
# optional arguments
|
||||
parser.add_argument('font', help='The path to the font to patch (e.g., Inconsolata.otf)')
|
||||
parser.add_argument('-v', '--version', action='version', version=projectName + ": %(prog)s (" + version + ")")
|
||||
parser.add_argument('-s', '--mono', '--use-single-width-glyphs', dest='single', default=False, action='store_true', help='Whether to generate the glyphs as single-width not double-width (default is double-width)')
|
||||
parser.add_argument('-l', '--adjust-line-height', dest='adjustLineHeight', default=False, action='store_true', help='Whether to adjust line heights (attempt to center powerline separators more evenly)')
|
||||
parser.add_argument('-q', '--quiet', '--shutup', dest='quiet', default=False, action='store_true', help='Do not generate verbose output')
|
||||
parser.add_argument('-w', '--windows', dest='windows', default=False, action='store_true', help='Limit the internal font name to 31 characters (for Windows compatibility)')
|
||||
parser.add_argument('-c', '--complete', dest='complete', default=False, action='store_true', help='Add all available Glyphs')
|
||||
parser.add_argument('--careful', dest='careful', default=False, action='store_true', help='Do not overwrite existing glyphs if detected')
|
||||
parser.add_argument('--removeligs', '--removeligatures', dest='removeligatures', default=False, action='store_true', help='Removes ligatures specificed in JSON configuration file')
|
||||
parser.add_argument('--postprocess', dest='postprocess', default=False, type=str, nargs='?', help='Specify a Script for Post Processing')
|
||||
parser.add_argument('--configfile', dest='configfile', default=False, type=str, nargs='?', help='Specify a file path for JSON configuration file (see sample: src/config.sample.json)')
|
||||
parser.add_argument('--custom', dest='custom', default=False, type=str, nargs='?', help='Specify a custom symbol font. All new glyphs will be copied, with no scaling applied.')
|
||||
parser.add_argument('-ext', '--extension', dest='extension', default="", type=str, nargs='?', help='Change font file type to create (e.g., ttf, otf)')
|
||||
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('--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
|
||||
progressbars_group_parser = parser.add_mutually_exclusive_group(required=False)
|
||||
progressbars_group_parser.add_argument('--progressbars', dest='progressbars', action='store_true', help='Show percentage completion progress bars per Glyph Set')
|
||||
progressbars_group_parser.add_argument('--no-progressbars', dest='progressbars', action='store_false', help='Don\'t show percentage completion progress bars per Glyph Set')
|
||||
parser.set_defaults(progressbars=True)
|
||||
parser.add_argument('--also-windows', dest='alsowindows', default=False, action='store_true', help='Create two fonts, the normal and the --windows version')
|
||||
|
||||
# symbol fonts to include arguments
|
||||
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('--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('--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('--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('--pomicons', dest='pomicons', default=False, action='store_true', help='Add Pomicon Glyphs (https://github.com/gabrielelana/pomicons)')
|
||||
sym_font_group.add_argument('--powerline', dest='powerline', default=False, action='store_true', help='Add Powerline Glyphs')
|
||||
sym_font_group.add_argument('--powerlineextra', dest='powerlineextra', default=False, action='store_true', help='Add Powerline Glyphs (https://github.com/ryanoasis/powerline-extra-symbols)')
|
||||
sym_font_group.add_argument('--material', '--materialdesignicons', '--mdi', dest='material', default=False, action='store_true', help='Add Material Design Icons (https://github.com/templarian/MaterialDesign)')
|
||||
sym_font_group.add_argument('--weather', '--weathericons', dest='weather', default=False, action='store_true', help='Add Weather Icons (https://github.com/erikflowers/weather-icons)')
|
||||
|
||||
self.args = parser.parse_args()
|
||||
|
||||
if self.args.makegroups and not FontnameParserOK:
|
||||
sys.exit(projectName + ": FontnameParser module missing (bin/scripts/name_parser/Fontname*), can not --makegroups".format(projectName))
|
||||
|
||||
# if you add a new font, set it to True here inside the if condition
|
||||
if self.args.complete:
|
||||
self.args.fontawesome = True
|
||||
self.args.fontawesomeextension = True
|
||||
self.args.fontlogos = True
|
||||
self.args.octicons = True
|
||||
self.args.codicons = True
|
||||
self.args.powersymbols = True
|
||||
self.args.pomicons = True
|
||||
self.args.powerline = True
|
||||
self.args.powerlineextra = True
|
||||
self.args.material = True
|
||||
self.args.weather = True
|
||||
|
||||
if not self.args.complete:
|
||||
# add the list of arguments for each symbol font to the list self.sym_font_args
|
||||
for action in sym_font_group._group_actions:
|
||||
self.sym_font_args.append(action.__dict__['option_strings'])
|
||||
|
||||
# determine whether or not all symbol fonts are to be used
|
||||
font_complete = True
|
||||
for sym_font_arg_aliases in self.sym_font_args:
|
||||
found = False
|
||||
for alias in sym_font_arg_aliases:
|
||||
if alias in sys.argv:
|
||||
found = True
|
||||
if found is not True:
|
||||
font_complete = False
|
||||
self.args.complete = font_complete
|
||||
|
||||
if self.args.alsowindows:
|
||||
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
|
||||
# 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.
|
||||
# that's why i did the above.
|
||||
#
|
||||
# if you add a new font, put it in here too, as the others are
|
||||
# self.args.complete = all([
|
||||
# self.args.fontawesome is True,
|
||||
# self.args.fontawesomeextension is True,
|
||||
# self.args.fontlogos is True,
|
||||
# self.args.octicons is True,
|
||||
# self.args.powersymbols is True,
|
||||
# self.args.pomicons is True,
|
||||
# self.args.powerline is True,
|
||||
# self.args.powerlineextra is True,
|
||||
# self.args.material is True,
|
||||
# self.args.weather is True
|
||||
# ])
|
||||
|
||||
|
||||
def setup_name_backup(self):
|
||||
def setup_name_backup(self, font):
|
||||
""" Store the original font names to be able to rename the font multiple times """
|
||||
self.original_fontname = self.sourceFont.fontname
|
||||
self.original_fullname = self.sourceFont.fullname
|
||||
self.original_familyname = self.sourceFont.familyname
|
||||
font.persistent = {
|
||||
"fontname": font.fontname,
|
||||
"fullname": font.fullname,
|
||||
"familyname": font.familyname,
|
||||
}
|
||||
|
||||
|
||||
def setup_font_names(self):
|
||||
self.sourceFont.fontname = self.original_fontname
|
||||
self.sourceFont.fullname = self.original_fullname
|
||||
self.sourceFont.familyname = self.original_familyname
|
||||
def setup_font_names(self, font):
|
||||
font.fontname = font.persistent["fontname"]
|
||||
font.fullname = font.persistent["fullname"]
|
||||
font.familyname = font.persistent["familyname"]
|
||||
verboseAdditionalFontNameSuffix = " " + projectNameSingular
|
||||
if self.args.windows: # attempt to shorten here on the additional name BEFORE trimming later
|
||||
additionalFontNameSuffix = " " + projectNameAbbreviation
|
||||
|
@ -471,14 +430,14 @@ class font_patcher:
|
|||
verboseAdditionalFontNameSuffix += " Mono"
|
||||
|
||||
if FontnameParserOK and self.args.makegroups:
|
||||
use_fullname = type(self.sourceFont.fullname) == str # Usually the fullname is better to parse
|
||||
use_fullname = type(font.fullname) == str # Usually the fullname is better to parse
|
||||
# Use fullname if it is 'equal' to the fontname
|
||||
if self.sourceFont.fullname:
|
||||
use_fullname |= self.sourceFont.fontname.lower() == FontnameTools.postscript_char_filter(self.sourceFont.fullname).lower()
|
||||
if font.fullname:
|
||||
use_fullname |= font.fontname.lower() == FontnameTools.postscript_char_filter(font.fullname).lower()
|
||||
# Use fullname for any of these source fonts (that are impossible to disentangle from the fontname, we need the blanks)
|
||||
for hit in [ 'Meslo' ]:
|
||||
use_fullname |= self.sourceFont.fontname.lower().startswith(hit.lower())
|
||||
parser_name = self.sourceFont.fullname if use_fullname else self.sourceFont.fontname
|
||||
use_fullname |= font.fontname.lower().startswith(hit.lower())
|
||||
parser_name = font.fullname if use_fullname else font.fontname
|
||||
# Gohu fontnames hide the weight, but the file names are ok...
|
||||
if parser_name.startswith('Gohu'):
|
||||
parser_name = os.path.splitext(os.path.basename(self.args.font))[0]
|
||||
|
@ -496,16 +455,16 @@ class font_patcher:
|
|||
# have an internal style defined (in sfnt_names)
|
||||
# using '([^-]*?)' to get the item before the first dash "-"
|
||||
# using '([^-]*(?!.*-))' to get the item after the last dash "-"
|
||||
fontname, fallbackStyle = re.match("^([^-]*).*?([^-]*(?!.*-))$", self.sourceFont.fontname).groups()
|
||||
fontname, fallbackStyle = re.match("^([^-]*).*?([^-]*(?!.*-))$", font.fontname).groups()
|
||||
|
||||
# dont trust 'sourceFont.familyname'
|
||||
# dont trust 'font.familyname'
|
||||
familyname = fontname
|
||||
|
||||
# fullname (filename) can always use long/verbose font name, even in windows
|
||||
if self.sourceFont.fullname != None:
|
||||
fullname = self.sourceFont.fullname + verboseAdditionalFontNameSuffix
|
||||
if font.fullname != None:
|
||||
fullname = font.fullname + verboseAdditionalFontNameSuffix
|
||||
else:
|
||||
fullname = self.sourceFont.cidfontname + verboseAdditionalFontNameSuffix
|
||||
fullname = font.cidfontname + verboseAdditionalFontNameSuffix
|
||||
|
||||
fontname = fontname + additionalFontNameSuffix.replace(" ", "")
|
||||
|
||||
|
@ -513,13 +472,13 @@ class font_patcher:
|
|||
# parse fontname if it fails:
|
||||
try:
|
||||
# search tuple:
|
||||
subFamilyTupleIndex = [x[1] for x in self.sourceFont.sfnt_names].index("SubFamily")
|
||||
subFamilyTupleIndex = [x[1] for x in font.sfnt_names].index("SubFamily")
|
||||
|
||||
# String ID is at the second index in the Tuple lists
|
||||
sfntNamesStringIDIndex = 2
|
||||
|
||||
# now we have the correct item:
|
||||
subFamily = self.sourceFont.sfnt_names[subFamilyTupleIndex][sfntNamesStringIDIndex]
|
||||
subFamily = font.sfnt_names[subFamilyTupleIndex][sfntNamesStringIDIndex]
|
||||
except IndexError:
|
||||
sys.stderr.write("{}: Could not find 'SubFamily' for given font, falling back to parsed fontname\n".format(projectName))
|
||||
subFamily = fallbackStyle
|
||||
|
@ -629,22 +588,22 @@ class font_patcher:
|
|||
|
||||
if not (FontnameParserOK and self.args.makegroups):
|
||||
# replace any extra whitespace characters:
|
||||
self.sourceFont.familyname = " ".join(familyname.split())
|
||||
self.sourceFont.fullname = " ".join(fullname.split())
|
||||
self.sourceFont.fontname = " ".join(fontname.split())
|
||||
font.familyname = " ".join(familyname.split())
|
||||
font.fullname = " ".join(fullname.split())
|
||||
font.fontname = " ".join(fontname.split())
|
||||
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
font.appendSFNTName(str('English (US)'), str('Preferred Family'), font.familyname)
|
||||
font.appendSFNTName(str('English (US)'), str('Family'), font.familyname)
|
||||
font.appendSFNTName(str('English (US)'), str('Compatible Full'), font.fullname)
|
||||
font.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
else:
|
||||
fam_suffix = projectNameSingular if not self.args.windows else projectNameAbbreviation
|
||||
fam_suffix += ' Mono' if self.args.single else ''
|
||||
n.inject_suffix(verboseAdditionalFontNameSuffix, additionalFontNameSuffix, fam_suffix)
|
||||
n.rename_font(self.sourceFont)
|
||||
n.rename_font(font)
|
||||
|
||||
self.sourceFont.comment = projectInfo
|
||||
self.sourceFont.fontlog = projectInfo
|
||||
font.comment = projectInfo
|
||||
font.fontlog = projectInfo
|
||||
|
||||
|
||||
def setup_version(self):
|
||||
|
@ -678,10 +637,19 @@ class font_patcher:
|
|||
print("No configfile given, skipping configfile related actions")
|
||||
|
||||
|
||||
def check_position_conflicts(self):
|
||||
# Prevent glyph encoding position conflicts between glyph sets
|
||||
if self.args.fontawesome and self.args.octicons:
|
||||
self.octiconsExactEncodingPosition = False
|
||||
def assert_monospace(self):
|
||||
# Check if the sourcefont is monospaced
|
||||
width_mono = is_monospaced(self.sourceFont)
|
||||
panose_mono = check_panose_monospaced(self.sourceFont)
|
||||
# The following is in fact "width_mono != panose_mono", but only if panose_mono is not 'unknown'
|
||||
if (width_mono and panose_mono == 0) or (not width_mono and panose_mono == 1):
|
||||
print(" Warning: Monospaced check: Panose assumed to be wrong")
|
||||
print(" Glyph widths {} / {} - {} and Panose says \"monospace {}\" ({})".format(get_advance_width(self.sourceFont, False, True),
|
||||
get_advance_width(self.sourceFont, False, False), get_advance_width(self.sourceFont, True, False), panose_mono, list(self.sourceFont.os2_panose)))
|
||||
if not width_mono:
|
||||
print(" Warning: Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless")
|
||||
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")
|
||||
|
||||
|
||||
def setup_patch_set(self):
|
||||
|
@ -805,28 +773,28 @@ 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': 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': 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.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0A3, 'SymEnd': 0xE0A3, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0B4, 'SymEnd': 0xE0C8, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CA, 'SymEnd': 0xE0CA, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CC, 'SymEnd': 0xE0D4, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.pomicons, 'Name': "Pomicons", 'Filename': "Pomicons.otf", 'Exact': True, 'SymStart': 0xE000, 'SymEnd': 0xE00A, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.fontawesome, 'Name': "Font Awesome", 'Filename': "font-awesome/FontAwesome.otf", 'Exact': True, 'SymStart': 0xF000, 'SymEnd': 0xF2E0, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA},
|
||||
{'Enabled': self.args.fontawesomeextension, 'Name': "Font Awesome Extension", 'Filename': "font-awesome-extension.ttf", 'Exact': False, 'SymStart': 0xE000, 'SymEnd': 0xE0A9, 'SrcStart': 0xE200, 'SrcEnd': 0xE2A9, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize
|
||||
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': 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, '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.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.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': 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': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'SrcEnd': 0xF4A9, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
|
||||
{'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': 0x0000, 'SrcEnd': 0x0000, 'ScaleGlyph': None, 'Attributes': CUSTOM_ATTR}
|
||||
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE534, 'SrcStart': 0xE5FA, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, '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, '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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0A3, 'SymEnd': 0xE0A3, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0B4, 'SymEnd': 0xE0C8, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CA, 'SymEnd': 0xE0CA, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CC, 'SymEnd': 0xE0D4, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'Enabled': self.args.pomicons, 'Name': "Pomicons", 'Filename': "Pomicons.otf", 'Exact': True, 'SymStart': 0xE000, 'SymEnd': 0xE00A, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.fontawesome, 'Name': "Font Awesome", 'Filename': "font-awesome/FontAwesome.otf", 'Exact': True, 'SymStart': 0xF000, 'SymEnd': 0xF2E0, 'SrcStart': None, 'ScaleGlyph': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA},
|
||||
{'Enabled': self.args.fontawesomeextension, 'Name': "Font Awesome Extension", 'Filename': "font-awesome-extension.ttf", 'Exact': False, 'SymStart': 0xE000, 'SymEnd': 0xE0A9, 'SrcStart': 0xE200, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize
|
||||
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'ScaleGlyph': 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, '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, '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, '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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
|
||||
{'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': CUSTOM_ATTR}
|
||||
]
|
||||
|
||||
def setup_line_dimensions(self):
|
||||
|
@ -846,11 +814,6 @@ class font_patcher:
|
|||
self.sourceFont.hhea_ascent = self.sourceFont.os2_winascent
|
||||
self.sourceFont.hhea_descent = -self.sourceFont.os2_windescent
|
||||
|
||||
# Line gap add extra space on the bottom of the line which
|
||||
# doesn't allow the powerline glyphs to fill the entire line.
|
||||
self.sourceFont.hhea_linegap = 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.
|
||||
|
@ -877,6 +840,38 @@ class font_patcher:
|
|||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent
|
||||
self.font_dim['ymax'] = self.sourceFont.os2_typoascent
|
||||
|
||||
# Calculate font height
|
||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
||||
if self.font_dim['height'] == 0:
|
||||
# This can only happen if the input font is empty
|
||||
# Assume we are using our prepared templates
|
||||
self.font_dim = {
|
||||
'xmin' : 0,
|
||||
'ymin' : -self.sourceFont.descent,
|
||||
'xmax' : self.sourceFont.em,
|
||||
'ymax' : self.sourceFont.ascent,
|
||||
'width' : self.sourceFont.em,
|
||||
'height': self.sourceFont.descent + self.sourceFont.ascent,
|
||||
}
|
||||
|
||||
# Line gap add extra space on the bottom of the line which
|
||||
# doesn't allow the powerline glyphs to fill the entire line.
|
||||
# Put half of the gap into the 'cell', each top and bottom
|
||||
gap = max(self.sourceFont.hhea_linegap, self.sourceFont.os2_typolinegap) # TODO probably wrong
|
||||
if self.sourceFont.os2_use_typo_metrics:
|
||||
gap = self.sourceFont.os2_typolinegap
|
||||
self.sourceFont.hhea_linegap = 0
|
||||
self.sourceFont.os2_typolinegap = 0
|
||||
if gap > 0:
|
||||
gap_top = int(gap / 2)
|
||||
gap_bottom = gap - gap_top
|
||||
self.font_dim['ymin'] -= gap_bottom
|
||||
self.font_dim['ymax'] += gap_top
|
||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
||||
self.sourceFont.os2_typoascent = self.sourceFont.os2_typoascent + gap_top
|
||||
self.sourceFont.os2_typodescent = self.sourceFont.os2_typodescent - gap_bottom
|
||||
# TODO Check what to do with win and hhea values
|
||||
|
||||
# Find the biggest char width
|
||||
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
||||
#
|
||||
|
@ -890,22 +885,10 @@ class font_patcher:
|
|||
continue
|
||||
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
||||
self.font_dim['width'] = self.sourceFont[glyph].width
|
||||
# print("New MAXWIDTH-A {} {} {}".format(glyph, self.sourceFont[glyph].width, xmax))
|
||||
if xmax > self.font_dim['xmax']:
|
||||
self.font_dim['xmax'] = xmax
|
||||
|
||||
# Calculate font height
|
||||
self.font_dim['height'] = abs(self.font_dim['ymin']) + self.font_dim['ymax']
|
||||
if self.font_dim['height'] == 0:
|
||||
# This can only happen if the input font is empty
|
||||
# Assume we are using our prepared templates
|
||||
self.font_dim = {
|
||||
'xmin' : 0,
|
||||
'ymin' : -self.sourceFont.descent,
|
||||
'xmax' : self.sourceFont.em,
|
||||
'ymax' : self.sourceFont.ascent,
|
||||
'width' : self.sourceFont.em,
|
||||
'height': abs(self.sourceFont.descent) + self.sourceFont.ascent,
|
||||
}
|
||||
# print("New MAXWIDTH-B {} {} {}".format(glyph, self.sourceFont[glyph].width, xmax))
|
||||
|
||||
|
||||
def get_scale_factor(self, sym_dim):
|
||||
|
@ -921,19 +904,16 @@ class font_patcher:
|
|||
return scale_ratio
|
||||
|
||||
|
||||
def copy_glyphs(self, sourceFontStart, sourceFontEnd, symbolFont, symbolFontStart, symbolFontEnd, exactEncoding, scaleGlyph, setName, attributes):
|
||||
def copy_glyphs(self, sourceFontStart, symbolFont, symbolFontStart, symbolFontEnd, exactEncoding, scaleGlyph, setName, attributes):
|
||||
""" Copies symbol glyphs into self.sourceFont """
|
||||
progressText = ''
|
||||
careful = False
|
||||
glyphSetLength = 0
|
||||
sourceFontCounter = 0
|
||||
|
||||
if self.args.careful:
|
||||
careful = True
|
||||
|
||||
if exactEncoding is False:
|
||||
sourceFontList = list(range(sourceFontStart, sourceFontEnd + 1))
|
||||
sourceFontCounter = 0
|
||||
|
||||
# Create glyphs from symbol font
|
||||
#
|
||||
# If we are going to copy all Glyphs, then assume we want to be careful
|
||||
|
@ -974,8 +954,8 @@ class font_patcher:
|
|||
possible_codes += [ v for v, s, r in sym_glyph.altuni if v > currentSourceFontGlyph ]
|
||||
currentSourceFontGlyph = min(possible_codes)
|
||||
else:
|
||||
# use source font defined hex values based on passed in start and end
|
||||
currentSourceFontGlyph = sourceFontList[sourceFontCounter]
|
||||
# use source font defined hex values based on passed in start (fills gaps; symbols are packed)
|
||||
currentSourceFontGlyph = sourceFontStart + sourceFontCounter
|
||||
sourceFontCounter += 1
|
||||
|
||||
if self.args.quiet is False:
|
||||
|
@ -1292,19 +1272,153 @@ def check_fontforge_min_version():
|
|||
sys.stderr.write("{}: Please use at least version: {}\n".format(projectName, minimumVersion))
|
||||
sys.exit(1)
|
||||
|
||||
def setup_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
'Nerd Fonts Font Patcher: patches a given font with programming and development related glyphs\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/master/changelog.md'),
|
||||
formatter_class=RawTextHelpFormatter
|
||||
)
|
||||
|
||||
# optional arguments
|
||||
parser.add_argument('font', help='The path to the font to patch (e.g., Inconsolata.otf)')
|
||||
parser.add_argument('-v', '--version', action='version', version=projectName + ": %(prog)s (" + version + ")")
|
||||
parser.add_argument('-s', '--mono', '--use-single-width-glyphs', dest='single', default=False, action='count', help='Whether to generate the glyphs as single-width not double-width (default is double-width)')
|
||||
parser.add_argument('-l', '--adjust-line-height', dest='adjustLineHeight', default=False, action='store_true', help='Whether to adjust line heights (attempt to center powerline separators more evenly)')
|
||||
parser.add_argument('-q', '--quiet', '--shutup', dest='quiet', default=False, action='store_true', help='Do not generate verbose output')
|
||||
parser.add_argument('-w', '--windows', dest='windows', default=False, action='store_true', help='Limit the internal font name to 31 characters (for Windows compatibility)')
|
||||
parser.add_argument('-c', '--complete', dest='complete', default=False, action='store_true', help='Add all available Glyphs')
|
||||
parser.add_argument('--careful', dest='careful', default=False, action='store_true', help='Do not overwrite existing glyphs if detected')
|
||||
parser.add_argument('--removeligs', '--removeligatures', dest='removeligatures', default=False, action='store_true', help='Removes ligatures specificed in JSON configuration file')
|
||||
parser.add_argument('--postprocess', dest='postprocess', default=False, type=str, nargs='?', help='Specify a Script for Post Processing')
|
||||
parser.add_argument('--configfile', dest='configfile', default=False, type=str, nargs='?', help='Specify a file path for JSON configuration file (see sample: src/config.sample.json)')
|
||||
parser.add_argument('--custom', dest='custom', default=False, type=str, nargs='?', help='Specify a custom symbol font. All new glyphs will be copied, with no scaling applied.')
|
||||
parser.add_argument('-ext', '--extension', dest='extension', default="", type=str, nargs='?', help='Change font file type to create (e.g., ttf, otf)')
|
||||
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('--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
|
||||
progressbars_group_parser = parser.add_mutually_exclusive_group(required=False)
|
||||
progressbars_group_parser.add_argument('--progressbars', dest='progressbars', action='store_true', help='Show percentage completion progress bars per Glyph Set')
|
||||
progressbars_group_parser.add_argument('--no-progressbars', dest='progressbars', action='store_false', help='Don\'t show percentage completion progress bars per Glyph Set')
|
||||
parser.set_defaults(progressbars=True)
|
||||
parser.add_argument('--also-windows', dest='alsowindows', default=False, action='store_true', help='Create two fonts, the normal and the --windows version')
|
||||
|
||||
# symbol fonts to include arguments
|
||||
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('--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('--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('--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('--pomicons', dest='pomicons', default=False, action='store_true', help='Add Pomicon Glyphs (https://github.com/gabrielelana/pomicons)')
|
||||
sym_font_group.add_argument('--powerline', dest='powerline', default=False, action='store_true', help='Add Powerline Glyphs')
|
||||
sym_font_group.add_argument('--powerlineextra', dest='powerlineextra', default=False, action='store_true', help='Add Powerline Glyphs (https://github.com/ryanoasis/powerline-extra-symbols)')
|
||||
sym_font_group.add_argument('--material', '--materialdesignicons', '--mdi', dest='material', default=False, action='store_true', help='Add Material Design Icons (https://github.com/templarian/MaterialDesign)')
|
||||
sym_font_group.add_argument('--weather', '--weathericons', dest='weather', default=False, action='store_true', help='Add Weather Icons (https://github.com/erikflowers/weather-icons)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.makegroups and not FontnameParserOK:
|
||||
sys.exit(projectName + ": FontnameParser module missing (bin/scripts/name_parser/Fontname*), can not --makegroups".format(projectName))
|
||||
|
||||
# if you add a new font, set it to True here inside the if condition
|
||||
if args.complete:
|
||||
args.fontawesome = True
|
||||
args.fontawesomeextension = True
|
||||
args.fontlogos = True
|
||||
args.octicons = True
|
||||
args.codicons = True
|
||||
args.powersymbols = True
|
||||
args.pomicons = True
|
||||
args.powerline = True
|
||||
args.powerlineextra = True
|
||||
args.material = True
|
||||
args.weather = True
|
||||
|
||||
if not args.complete:
|
||||
sym_font_args = []
|
||||
# add the list of arguments for each symbol font to the list sym_font_args
|
||||
for action in sym_font_group._group_actions:
|
||||
sym_font_args.append(action.__dict__['option_strings'])
|
||||
|
||||
# determine whether or not all symbol fonts are to be used
|
||||
font_complete = True
|
||||
for sym_font_arg_aliases in sym_font_args:
|
||||
found = False
|
||||
for alias in sym_font_arg_aliases:
|
||||
if alias in sys.argv:
|
||||
found = True
|
||||
if found is not True:
|
||||
font_complete = False
|
||||
args.complete = font_complete
|
||||
|
||||
if args.alsowindows:
|
||||
args.windows = False
|
||||
|
||||
if args.nonmono and args.single:
|
||||
print("Warniung: Specified contradicting --variable-width-glyphs and --use-single-width-glyph. Ignoring --variable-width-glyphs.")
|
||||
args.nonmono = False
|
||||
|
||||
make_sure_path_exists(args.outputdir)
|
||||
if not os.path.isfile(args.font):
|
||||
sys.exit("{}: Font file does not exist: {}".format(projectName, args.font))
|
||||
if not os.access(args.font, os.R_OK):
|
||||
sys.exit("{}: Can not open font file for reading: {}".format(projectName, args.font))
|
||||
is_ttc = len(fontforge.fontsInFile(args.font)) > 1
|
||||
|
||||
if args.extension == "":
|
||||
args.extension = os.path.splitext(args.font)[1]
|
||||
else:
|
||||
args.extension = '.' + args.extension
|
||||
if re.match("\.ttc$", args.extension, re.IGNORECASE):
|
||||
if not is_ttc:
|
||||
sys.exit(projectName + ": Can not create True Type Collections from single font files")
|
||||
else:
|
||||
if is_ttc:
|
||||
sys.exit(projectName + ": Can not create single font files from True Type Collections")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
print("{} Patcher v{} ({}) executing".format(projectName, version, script_version))
|
||||
check_fontforge_min_version()
|
||||
patcher = font_patcher()
|
||||
patcher.patch()
|
||||
args = setup_arguments()
|
||||
patcher = font_patcher(args)
|
||||
|
||||
sourceFonts = []
|
||||
all_fonts = fontforge.fontsInFile(args.font)
|
||||
for i, subfont in enumerate(all_fonts):
|
||||
print("\n{}: Processing {} ({}/{})".format(projectName, subfont, i + 1, len(all_fonts)))
|
||||
try:
|
||||
sourceFonts.append(fontforge.open("{}({})".format(args.font, subfont), 1)) # 1 = ("fstypepermitted",))
|
||||
except Exception:
|
||||
sys.exit("{}: Can not open font '{}', try to open with fontforge interactively to get more information".format(
|
||||
projectName, subfont))
|
||||
|
||||
patcher.patch(sourceFonts[-1])
|
||||
|
||||
print("\nDone with Patch Sets, generating font...\n")
|
||||
patcher.setup_font_names()
|
||||
patcher.generate()
|
||||
for f in sourceFonts:
|
||||
patcher.setup_font_names(f)
|
||||
patcher.generate(sourceFonts)
|
||||
|
||||
# This mainly helps to improve CI runtime
|
||||
if patcher.args.alsowindows:
|
||||
patcher.args.windows = True
|
||||
patcher.setup_font_names()
|
||||
patcher.generate()
|
||||
for f in sourceFonts:
|
||||
patcher.setup_font_names(f)
|
||||
patcher.generate(sourceFonts)
|
||||
|
||||
for f in sourceFonts:
|
||||
f.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue