├── .gitignore ├── font_names.txt ├── LICENSE ├── Readme.md └── nerdfont-patcher.py /.gitignore: -------------------------------------------------------------------------------- 1 | Patched/ 2 | Fonts/ 3 | src/ 4 | font-patcher 5 | .markdownlint.json 6 | .vscode/ 7 | 8 | -------------------------------------------------------------------------------- /font_names.txt: -------------------------------------------------------------------------------- 1 | # Just copy and paste the names of the fonts from: 2 | # https://github.com/ryanoasis/nerd-fonts/tree/master/src/glyphs 3 | FontAwesome.otf 4 | NerdFontsSymbols 1000 EM Nerd Font Complete Blank.sfd 5 | NerdFontsSymbols 2048 EM Nerd Font Complete Blank.sfd 6 | Pomicons.otf 7 | PowerlineExtraSymbols.otf 8 | PowerlineSymbols.otf 9 | Symbols Template 1000 em.ttf 10 | Symbols Template 2048 em.ttf 11 | Symbols-1000-em Nerd Font Complete.ttf 12 | Symbols-2048-em Nerd Font Complete.ttf 13 | Unicode_IEC_symbol_font.otf 14 | devicons.ttf 15 | font-awesome-extension.ttf 16 | font-logos.ttf 17 | materialdesignicons-webfont.ttf 18 | octicons.ttf 19 | original-source.otf 20 | weathericons-regular-webfont.ttf 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bernard Solien 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # NerdFont Patcher 2 | 3 | This is a hackish wrapper for the NerdFont patcher script. 4 | I don't know if there is an official way of doing this so... 5 | Plus it now has multiprocessing support to speed up font patching. 6 | 7 | It's dumb, but it works! on Linux! and WSL for windows 10 "¯\\_(ツ)_/¯" 8 | 9 | ## Greeting and Thanks 10 | 11 | A big hello and Thanks to the guys at [Nerdfonts.com](https://www.nerdfonts.com). They've 12 | done a wonderful job so thanks, guys. 13 | 14 | ## Requirements 15 | 16 | - [Fontforge](https://fontforge.org/en-US/downloads/) (for patching the fonts) 17 | - configparser 18 | - Internet connection (for obvious reasons) 19 | - curl (to download font glyphs) 20 | 21 | ## Setup 22 | 23 | - Install fontforge: 24 | ```bash 25 | sudo apt-get install software-properties-common 26 | sudo add-apt-repository ppa:fontforge/fontforge -y 27 | sudo apt-get update 28 | sudo apt-get install fontforge 29 | ``` 30 | - Install other dependencies: 31 | ```bash 32 | sudo apt-get install python3-fontforge python-configparser 33 | ``` 34 | It will download the font-glyphs and patcher script from the nerdfonts repo. 35 | - Clone and cd into this repo 36 | ```bash 37 | git clone https://github.com/bexter989/nerdfont-patcher.git; cd nerdfont-patcher 38 | ``` 39 | - Run the script: 40 | ```bash 41 | python3 ./nerdfont-patcher.py {font dir name} {font name} 42 | ``` 43 | Patched fonts are generated in the "Patched" folder with the specified font name. 44 | 45 | ## Usage 46 | 47 | - Download and extract any monospace font (e.g., [VictorMono](https://rubjo.github.io/victor-mono/)) into the cloned folder (in this case, the `TTF` dir). 48 | - Run the script as follows: 49 | ```bash 50 | python3 ./nerdfont-patcher.py ./TTF VictorMono 51 | ``` 52 | 53 | ## Demonstration 54 | 55 | Watch a 3-minute clip on [Youtube](https://youtu.be/ryKjZStHLtU) 56 | -------------------------------------------------------------------------------- /nerdfont-patcher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | import re 7 | import multiprocessing 8 | from time import time 9 | Script = "Nerdfont-patcher.py" 10 | 11 | try: 12 | import psMat 13 | except ImportError: 14 | sys.exit(Script + ": FontForge module is probably not installed. [See: http://designwithfontforge.com/en-US/Installing_Fontforge.html]") 15 | try: 16 | import configparser 17 | except ImportError: 18 | sys.exit(Script + ": configparser module is probably not installed. Try `pip install configparser` or equivalent") 19 | try: 20 | import fontforge 21 | except ImportError: 22 | sys.exit( 23 | Script + ( 24 | ": FontForge module could not be loaded. Try installing fontforge python bindings " 25 | "[e.g. on Linux Debian or Ubuntu: `sudo apt install fontforge python-fontforge`]" 26 | ) 27 | ) 28 | 29 | def folder_exists(folder): 30 | ''' Bool: Checks if a folder exists ''' 31 | return os.path.isdir(folder) 32 | 33 | 34 | def patcher_exists(): 35 | ''' Bool: Checks if the NerdFonts patcher script exists ''' 36 | return os.path.isfile("./font-patcher") 37 | 38 | 39 | def download_patcher(): 40 | ''' Downloads the Nerd Fonts Patcher script ''' 41 | print("Downloading NerdFonts Patcher Script") 42 | command = "curl -s -L https://raw.githubusercontent.com/ryanoasis/nerd-fonts/master/font-patcher --output font-patcher" 43 | os.system(command) 44 | 45 | 46 | def download_src_fonts(): 47 | ''' Downloads Glyph source files from the Nerd Fonts Repo ''' 48 | with open("font_names.txt", "r") as f: 49 | print("Downloading glyph source fonts\n") 50 | for font_name in f.readlines(): 51 | 52 | # Removes \r\n or crlf 53 | font_name = font_name.rstrip() 54 | 55 | # Skips the comments in the font_names.txt 56 | if "#" in font_name: 57 | continue 58 | 59 | # User feedback 60 | print("Downloading {}".format(font_name)) 61 | command = "curl -s -L 'https://github.com/ryanoasis/nerd-fonts/blob/master/src/glyphs/*?raw=true' --output 'src/glyphs/*'".replace( 62 | "*", font_name) 63 | os.system(command) 64 | 65 | 66 | def split(xlist, cpu_count): 67 | ''' Splits list of length n in x number of lists ''' 68 | k, m = divmod(len(xlist), cpu_count) 69 | if list((xlist[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(cpu_count))) == [[], []]: 70 | return [] 71 | return (xlist[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(cpu_count)) 72 | 73 | 74 | def cpu_n(processes): 75 | ''' Determines the number of cpu processes to use given the number of n ''' 76 | if (len(processes) % 2 == 0) and (len(processes) > 2): 77 | return 4 78 | elif (not len(processes) % 2 == 0) and (len(processes) == 1): 79 | return 1 80 | elif (not len(processes) % 2 == 0) and (len(processes) >= 5): 81 | return 4 82 | else: 83 | return 2 84 | 85 | 86 | def fix_filenames_in(folder): 87 | ''' (ง '̀͜ '́ )ง Removes spaces from filenames within a folder ''' 88 | # NOTE: Shorter signature from Core Kramer StackOverflow 89 | # https://stackoverflow.com/questions/64468635/is-there-a-better-way-to-replace-multiple-spaces-in-file-names 90 | files = os.listdir(folder) 91 | 92 | for file_name in files: 93 | new_name = ''.join([re.sub(r'\s+', '-', i) for i in file_name]) 94 | old_filename = folder + os.sep + file_name 95 | new_filename = folder + os.sep + new_name 96 | os.rename(old_filename, new_filename) 97 | 98 | 99 | def blitzer(): 100 | pass 101 | 102 | 103 | def patch(fonts, name): 104 | ''' Font patching time! (•̀o•́)ง Go Drink a coffee. ''' 105 | # Loop over each item and patch the glyphs 106 | for i, _ in enumerate(fonts): 107 | 108 | # This will patch everything. Powerline, Weather icons, FontAwesome... EVERYTHING! 109 | # It takes a while to patch fonts so get a coffee while waiting. Oh! and the '-w 'switch is for 110 | # Windows compatibility.. Something about limiting the number of characters in the font name. 111 | # More Info here: 112 | # https://github.com/ryanoasis/nerd-fonts#option-8-patch-your-own-font 113 | print("\nPatching {}\n".format(fonts[i])) 114 | command = "fontforge -quiet -script ." + os.sep + "font-patcher -q -w -c --no-progressbars {} -ext ttf -out Patched".format(fonts[i]) + os.sep + "{}".format(name) 115 | os.system(command) 116 | 117 | 118 | 119 | if __name__ == '__main__': 120 | # Get number of arguments 121 | arg_len = len(sys.argv) 122 | 123 | # We need two args 124 | if arg_len > 1: 125 | processes = [] 126 | font_folder, name = sys.argv[1:] 127 | 128 | # Create the src folder and download all the font glyphs from the NerdFonts repo on first run 129 | if not folder_exists("." + os.sep + "src" + os.sep + "glyphs"): 130 | os.makedirs("." + os.sep + "src" + os.sep + "glyphs") 131 | download_src_fonts() 132 | 133 | # Create a folder based on the name param for the fonts 134 | if not folder_exists('.' + os.sep + 'Patched' + os.sep + '{}'.format(name)): 135 | os.makedirs('.' + os.sep + 'Patched' + os.sep + '{}'.format(name)) 136 | 137 | # Download the NerdFonts Patcher on first run 138 | if not patcher_exists(): 139 | download_patcher() 140 | 141 | # Scan the folder passed in via script arg 142 | fix_filenames_in(font_folder) 143 | fonts = os.listdir(font_folder) 144 | 145 | # Get fonts only 146 | fonts = [font_folder + os.sep + item for item in fonts if '.ttf' in item or '.otf' in item] 147 | 148 | # Get num processes to use 149 | cpus = cpu_n(fonts) 150 | 151 | # Split list into smaller lists 152 | groups = list(split(fonts, cpus)) 153 | 154 | # Real num of CPUs to use 155 | cpu_num = len(groups) 156 | 157 | # Multiprocessing 158 | ts = time() 159 | for i in range(cpu_num): 160 | process = multiprocessing.Process(target=patch, args=(groups[i],name)) 161 | processes.append(process) 162 | process.start() 163 | 164 | for proc in processes: 165 | proc.join() 166 | te = time()-ts 167 | 168 | # timer 169 | os.system('clear') 170 | print("(⌐■_■) Done!") 171 | print("Took {:.2f} seconds".format(te)) 172 | print('Fonts are in the Patched folder') 173 | # patch(fonts, name) 174 | else: 175 | print("\n") 176 | print("+------------------------------------------------------+") 177 | print("| ¯\_(ツ)_/¯ Whoopss! (ง '̀͜ '́ )ง |") 178 | print("+------------------------------------------------------+\n") 179 | print("Almost did it..! I need two arguments.\n\n1) The Folder where the fonts are, and\n2) The name of the font to Patch\n") 180 | print("Like:\n > {} ./Fonts/Agave Agave\n\n".format(sys.argv[0])) 181 | --------------------------------------------------------------------------------