mirror of
https://github.com/ryanoasis/nerd-fonts.git
synced 2025-03-17 20:42:12 +02:00
Merge pull request #783 from ryanoasis/feature/process-ttc
Feature: Process TTC files
This commit is contained in:
commit
23ae931a6f
433
font-patcher
433
font-patcher
@ -6,7 +6,7 @@
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Change the script version when you edit this script:
|
||||
script_version = "3.0.6"
|
||||
script_version = "3.1.0"
|
||||
|
||||
version = "2.2.2"
|
||||
projectName = "Nerd Fonts"
|
||||
@ -88,9 +88,26 @@ class TableHEADWriter:
|
||||
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,7 +163,7 @@ 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')
|
||||
@ -154,8 +171,8 @@ class TableHEADWriter:
|
||||
|
||||
|
||||
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'
|
||||
@ -163,43 +180,21 @@ class font_patcher:
|
||||
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)
|
||||
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.
|
||||
@ -262,30 +257,45 @@ 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'.
|
||||
sourceFont.generate(outfile, 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 +311,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 +370,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 +395,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 +412,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 +528,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):
|
||||
@ -1292,19 +1191,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='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)')
|
||||
|
||||
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__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user