diff --git a/font-patcher b/font-patcher index 435c2ed00e..f7cefc060f 100755 --- a/font-patcher +++ b/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()) - - 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.familyname = " ".join(familyname.split()) + font.fullname = " ".join(fullname.split()) + font.fontname = " ".join(fontname.split()) + + 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__":