mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-11-09 16:43:14 -06:00
Compare commits
2 commits
b71e7d3aef
...
2d64ecab8b
Author | SHA1 | Date | |
---|---|---|---|
2d64ecab8b | |||
9eb507579a |
4 changed files with 523 additions and 362 deletions
201
.task.mk
201
.task.mk
|
@ -1,7 +1,7 @@
|
||||||
# }> [github.com/daylinmorgan/task.mk] <{ #
|
# }> [github.com/daylinmorgan/task.mk] <{ #
|
||||||
# Copyright (c) 2022 Daylin Morgan
|
# Copyright (c) 2022 Daylin Morgan
|
||||||
# MIT License
|
# MIT License
|
||||||
# version: v22.9.19-5-g5f593e3-dev
|
# version: v22.9.28-dev
|
||||||
#
|
#
|
||||||
# task.mk should be included at the bottom of your Makefile with `-include .task.mk`
|
# task.mk should be included at the bottom of your Makefile with `-include .task.mk`
|
||||||
# See below for the standard configuration options that should be set prior to including this file.
|
# See below for the standard configuration options that should be set prior to including this file.
|
||||||
|
@ -12,12 +12,13 @@ ACCENT_STYLE ?= b_yellow
|
||||||
PARAMS_STYLE ?= $(ACCENT_STYLE)
|
PARAMS_STYLE ?= $(ACCENT_STYLE)
|
||||||
GOAL_STYLE ?= $(ACCENT_STYLE)
|
GOAL_STYLE ?= $(ACCENT_STYLE)
|
||||||
MSG_STYLE ?= faint
|
MSG_STYLE ?= faint
|
||||||
DIVIDER_STYLE ?= default
|
|
||||||
DIVIDER ?= ─
|
DIVIDER ?= ─
|
||||||
|
DIVIDER_STYLE ?= default
|
||||||
HELP_SEP ?= │
|
HELP_SEP ?= │
|
||||||
|
WRAP ?= 100
|
||||||
# python f-string literals
|
# python f-string literals
|
||||||
EPILOG ?=
|
EPILOG ?=
|
||||||
USAGE ?={ansi.$(HEADER_STYLE)}usage{ansi.end}:\n make <recipe>\n
|
USAGE ?={ansi.header}usage{ansi.end}:\n make <recipe>\n
|
||||||
INHERIT_SHELL ?=
|
INHERIT_SHELL ?=
|
||||||
# ---- [builtin recipes] ---- #
|
# ---- [builtin recipes] ---- #
|
||||||
ifeq (help,$(firstword $(MAKECMDGOALS)))
|
ifeq (help,$(firstword $(MAKECMDGOALS)))
|
||||||
|
@ -25,10 +26,8 @@ ifeq (help,$(firstword $(MAKECMDGOALS)))
|
||||||
export HELP_ARGS
|
export HELP_ARGS
|
||||||
endif
|
endif
|
||||||
## h, help | show this help
|
## h, help | show this help
|
||||||
.PHONY: help h
|
h help:
|
||||||
help h:
|
$(call py,help_py) || { echo "exiting early!"; exit 1; }
|
||||||
$(call py,help_py)
|
|
||||||
.PHONY: _help
|
|
||||||
_help: export SHOW_HIDDEN=true
|
_help: export SHOW_HIDDEN=true
|
||||||
_help: help
|
_help: help
|
||||||
ifdef PRINT_VARS
|
ifdef PRINT_VARS
|
||||||
|
@ -40,20 +39,21 @@ endif
|
||||||
### | args: -ws --hidden
|
### | args: -ws --hidden
|
||||||
### task.mk builtins: | args: -d --hidden
|
### task.mk builtins: | args: -d --hidden
|
||||||
## _print-ansi | show all possible ansi color code combinations
|
## _print-ansi | show all possible ansi color code combinations
|
||||||
.PHONY:
|
|
||||||
_print-ansi:
|
_print-ansi:
|
||||||
$(call py,print_ansi_py)
|
$(call py,print_ansi_py)
|
||||||
# functions to take f-string literals and pass to python print
|
# functions to take f-string literals and pass to python print
|
||||||
tprint = $(call py,info_py,$(1))
|
tprint = $(call py,print_py,$(1))
|
||||||
tprint-sh = $(call pysh,info_py,$(1))
|
tprint-sh = $(call pysh,print_py,$(1))
|
||||||
tconfirm = $(call py,confirm_py,$(1))
|
tconfirm = $(call py,confirm_py,$(1))
|
||||||
## _update-task.mk | downloads latest development version of task.mk
|
## _update-task.mk | downloads latest development version of task.mk
|
||||||
_update-task.mk:
|
_update-task.mk:
|
||||||
$(call tprint,{a.b_cyan}Updating task.mk{a.end})
|
$(call tprint,{a.b_cyan}Updating task.mk{a.end})
|
||||||
curl https://raw.githubusercontent.com/daylinmorgan/task.mk/main/task.mk -o .task.mk
|
curl https://raw.githubusercontent.com/daylinmorgan/task.mk/main/task.mk -o .task.mk
|
||||||
export MAKEFILE_LIST
|
.PHONY: h help _help _print-ansi _update-task.mk
|
||||||
|
TASK_MAKEFILE_LIST := $(filter-out $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)),$(MAKEFILE_LIST))
|
||||||
|
export MAKEFILE_LIST MAKE TASK_MAKEFILE_LIST
|
||||||
ifndef INHERIT_SHELL
|
ifndef INHERIT_SHELL
|
||||||
SHELL := /bin/bash
|
SHELL := $(shell which bash)
|
||||||
endif
|
endif
|
||||||
# ---- [python/bash script runner] ---- #
|
# ---- [python/bash script runner] ---- #
|
||||||
define _newline
|
define _newline
|
||||||
|
@ -84,8 +84,10 @@ import argparse
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
$(ansi_py)
|
import subprocess
|
||||||
$(quit_make_py)
|
import sys
|
||||||
|
from textwrap import wrap
|
||||||
|
$(utils_py)
|
||||||
MaxLens = namedtuple("MaxLens", "goal msg")
|
MaxLens = namedtuple("MaxLens", "goal msg")
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r"^## (?P<goal>.*?) \| (?P<msg>.*?)(?:\s?\| args: (?P<msgargs>.*?))?$$|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<rawargs>.*?))?$$"
|
r"^## (?P<goal>.*?) \| (?P<msg>.*?)(?:\s?\| args: (?P<msgargs>.*?))?$$|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<rawargs>.*?))?$$"
|
||||||
|
@ -100,9 +102,11 @@ def parseargs(argstring):
|
||||||
parser.add_argument("-gs", "--goal-style", type=str)
|
parser.add_argument("-gs", "--goal-style", type=str)
|
||||||
parser.add_argument("--hidden", action="store_true")
|
parser.add_argument("--hidden", action="store_true")
|
||||||
return parser.parse_args(argstring.split())
|
return parser.parse_args(argstring.split())
|
||||||
|
def divider(len):
|
||||||
|
return ansi.style(f" {cfg.div*len}", "div_style")
|
||||||
def gen_makefile():
|
def gen_makefile():
|
||||||
makefile = ""
|
makefile = ""
|
||||||
for file in os.getenv("MAKEFILE_LIST").split():
|
for file in os.getenv("MAKEFILE_LIST", "").split():
|
||||||
with open(file, "r") as f:
|
with open(file, "r") as f:
|
||||||
makefile += f.read() + "\n\n"
|
makefile += f.read() + "\n\n"
|
||||||
return makefile
|
return makefile
|
||||||
|
@ -132,58 +136,80 @@ def recipe_help_header(goal):
|
||||||
item[0].get("msgargs", ""),
|
item[0].get("msgargs", ""),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return f" {ansi.style(goal,'$(GOAL_STYLE)')}:"
|
return f" {ansi.style(goal,'goal')}"
|
||||||
|
def get_goal_deps(goal="task.mk"):
|
||||||
|
make = os.getenv("MAKE", "make")
|
||||||
|
cmd = [make, "-p", "-n", "-i"]
|
||||||
|
for file in os.getenv("TASK_MAKEFILE_LIST", "").split():
|
||||||
|
cmd.extend(["-f", file])
|
||||||
|
database = subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
dep_pattern = re.compile(r"^" + goal + ":(.*)?")
|
||||||
|
for line in database.splitlines():
|
||||||
|
match = dep_pattern.search(line)
|
||||||
|
if match and match.groups()[0]:
|
||||||
|
return wrap(
|
||||||
|
f"{ansi.style('deps','default')}: {ansi.style(match.groups()[0].strip(),'msg')}",
|
||||||
|
width=cfg.wrap,
|
||||||
|
initial_indent=" ",
|
||||||
|
subsequent_indent=" ",
|
||||||
|
)
|
||||||
def parse_goal(file, goal):
|
def parse_goal(file, goal):
|
||||||
goals = goal_pattern.findall(file)
|
goals = goal_pattern.findall(file)
|
||||||
matched_goal = [i for i in goals if goal in i.split()]
|
matched_goal = [i for i in goals if goal in i.split()]
|
||||||
output = []
|
output = []
|
||||||
if matched_goal:
|
if matched_goal:
|
||||||
output.append(recipe_help_header(matched_goal[0]))
|
output.append(recipe_help_header(matched_goal[0]))
|
||||||
|
deps = get_goal_deps(matched_goal[0])
|
||||||
|
if deps:
|
||||||
|
output.extend(deps)
|
||||||
lines = file.splitlines()
|
lines = file.splitlines()
|
||||||
loc = [n for n, l in enumerate(lines) if l.startswith(f"{matched_goal[0]}:")][0]
|
loc = [n for n, l in enumerate(lines) if l.startswith(f"{matched_goal[0]}:")][0]
|
||||||
recipe = []
|
recipe = []
|
||||||
for line in lines[loc + 1 :]:
|
for line in lines[loc + 1 :]:
|
||||||
if not line.startswith("\t"):
|
if not line.startswith("\t"):
|
||||||
break
|
break
|
||||||
recipe.append(line)
|
recipe.append(f" {line.strip()}")
|
||||||
output.append(divider(max((len(l) for l in recipe)) + 5))
|
output.append(divider(max((len(l.strip()) for l in recipe))))
|
||||||
output.append("\n".join(recipe) + "\n")
|
output.append("\n".join(recipe))
|
||||||
else:
|
else:
|
||||||
output.append(f"{ansi.b_red}ERROR{ansi.end} Failed to find goal: {goal}")
|
deps = get_goal_deps(goal)
|
||||||
|
if deps:
|
||||||
|
output.append(recipe_help_header(goal))
|
||||||
|
output.extend(deps)
|
||||||
|
if not output:
|
||||||
|
output.append(f"{ansi.style('ERROR','b_red')}: Failed to find goal: {goal}")
|
||||||
return output
|
return output
|
||||||
def fmt_goal(goal, msg, max_goal_len, argstr):
|
def fmt_goal(goal, msg, max_goal_len, argstr):
|
||||||
args = parseargs(argstr)
|
args = parseargs(argstr)
|
||||||
goal_style = args.goal_style.strip() if args.goal_style else "$(GOAL_STYLE)"
|
goal_style = args.goal_style.strip() if args.goal_style else "goal"
|
||||||
msg_style = args.msg_style.strip() if args.msg_style else "$(MSG_STYLE)"
|
msg_style = args.msg_style.strip() if args.msg_style else "msg"
|
||||||
return (
|
return (
|
||||||
ansi.style(f" {goal:>{max_goal_len}}", goal_style)
|
ansi.style(f" {goal:>{max_goal_len}}", goal_style)
|
||||||
+ f" $(HELP_SEP) "
|
+ f" $(HELP_SEP) "
|
||||||
+ ansi.style(msg, msg_style)
|
+ ansi.style(msg, msg_style)
|
||||||
)
|
)
|
||||||
def divider(len):
|
|
||||||
return ansi.style(f" {'$(DIVIDER)'*len}", "$(DIVIDER_STYLE)")
|
|
||||||
def fmt_rawmsg(msg, argstr, maxlens):
|
def fmt_rawmsg(msg, argstr, maxlens):
|
||||||
args = parseargs(argstr)
|
args = parseargs(argstr)
|
||||||
lines = []
|
lines = []
|
||||||
msg_style = args.msg_style.strip() if args.msg_style else "$(MSG_STYLE)"
|
msg_style = args.msg_style.strip() if args.msg_style else "msg"
|
||||||
if not os.getenv("SHOW_HIDDEN") and args.hidden:
|
if not os.getenv("SHOW_HIDDEN") and args.hidden:
|
||||||
return []
|
return []
|
||||||
if msg:
|
if msg:
|
||||||
if args.align == "sep":
|
if args.align == "sep":
|
||||||
lines.append(
|
lines.append(
|
||||||
f"{' '*(maxlens.goal+len('$(HELP_SEP)')+4)}{ansi.style(msg,msg_style)}"
|
f"{' '*(maxlens.goal+len(cfg.sep)+4)}{ansi.style(msg,msg_style)}"
|
||||||
)
|
)
|
||||||
elif args.align == "center":
|
elif args.align == "center":
|
||||||
lines.append(f" {ansi.style(msg.center(sum(maxlens)),msg_style)}")
|
lines.append(f" {ansi.style(msg.center(sum(maxlens)),msg_style)}")
|
||||||
else:
|
else:
|
||||||
lines.append(f" {ansi.style(msg,msg_style)}")
|
lines.append(f" {ansi.style(msg,msg_style)}")
|
||||||
if args.divider:
|
if args.divider:
|
||||||
lines.append(divider(len("$(HELP_SEP)") + sum(maxlens) + 2))
|
lines.append(divider(len(cfg.sep) + sum(maxlens) + 2))
|
||||||
if args.whitespace:
|
if args.whitespace:
|
||||||
lines.append("\n")
|
lines.append("\n")
|
||||||
return lines
|
return lines
|
||||||
def print_help():
|
def print_help():
|
||||||
lines = [f"""$(USAGE)"""]
|
lines = [cfg.usage]
|
||||||
items = list(parse_help(gen_makefile()))
|
items = list(parse_help(gen_makefile()))
|
||||||
maxlens = MaxLens(
|
maxlens = MaxLens(
|
||||||
*(max((len(item[x]) for item in items if x in item)) for x in ["goal", "msg"])
|
*(max((len(item[x]) for item in items if x in item)) for x in ["goal", "msg"])
|
||||||
|
@ -197,25 +223,73 @@ def print_help():
|
||||||
)
|
)
|
||||||
if "rawmsg" in item:
|
if "rawmsg" in item:
|
||||||
lines.extend(fmt_rawmsg(item["rawmsg"], item.get("rawargs", ""), maxlens))
|
lines.extend(fmt_rawmsg(item["rawmsg"], item.get("rawargs", ""), maxlens))
|
||||||
lines.append(f"""$(EPILOG)""")
|
lines.append(cfg.epilog)
|
||||||
print("\n".join(lines))
|
print("\n".join(lines))
|
||||||
def print_arg_help(help_args):
|
def print_arg_help(help_args):
|
||||||
|
print(f"{ansi.style('task.mk recipe help','header')}\n")
|
||||||
for arg in help_args.split():
|
for arg in help_args.split():
|
||||||
print(f"{ansi.style('task.mk recipe help','$(HEADER_STYLE)')}\n")
|
|
||||||
print("\n".join(parse_goal(gen_makefile(), arg)))
|
print("\n".join(parse_goal(gen_makefile(), arg)))
|
||||||
|
print()
|
||||||
def main():
|
def main():
|
||||||
help_args = os.getenv("HELP_ARGS")
|
help_args = os.getenv("HELP_ARGS")
|
||||||
if help_args:
|
if help_args:
|
||||||
quit_make()
|
|
||||||
print_arg_help(help_args)
|
print_arg_help(help_args)
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print_help()
|
print_help()
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
endef
|
endef
|
||||||
define ansi_py
|
define print_py
|
||||||
|
$(utils_py)
|
||||||
|
sys.stderr.write(f"""$(2)\n""")
|
||||||
|
endef
|
||||||
|
define print_ansi_py
|
||||||
|
$(utils_py)
|
||||||
|
sep = f"$(HELP_SEP)"
|
||||||
|
codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
|
||||||
|
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
|
||||||
|
print(f"{codes_names[code]:>20} {sep} {code+'*****'+ansi.end} {sep} {repr(code)}")
|
||||||
|
endef
|
||||||
|
define vars_py
|
||||||
|
import os
|
||||||
|
$(utils_py)
|
||||||
|
vars = "$2".split()
|
||||||
|
length = max((len(v) for v in vars))
|
||||||
|
print(f"{ansi.header}vars{ansi.end}:\n")
|
||||||
|
for v in vars:
|
||||||
|
print(f" {ansi.params}{v:<{length}}{ansi.end} = {os.getenv(v)}")
|
||||||
|
print()
|
||||||
|
endef
|
||||||
|
define confirm_py
|
||||||
|
import sys
|
||||||
|
$(utils_py)
|
||||||
|
def confirm():
|
||||||
|
"""
|
||||||
|
Ask user to enter Y or N (case-insensitive).
|
||||||
|
:return: True if the answer is Y.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
answer = ""
|
||||||
|
while answer not in ["y", "n"]:
|
||||||
|
answer = input(f"""$(2) {a.b_red}[Y/n]{a.end} """).lower()
|
||||||
|
return answer == "y"
|
||||||
|
if confirm():
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
endef
|
||||||
|
define utils_py
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
div: str
|
||||||
|
sep: str
|
||||||
|
epilog: str
|
||||||
|
usage: str
|
||||||
|
wrap: int
|
||||||
color2byte = dict(
|
color2byte = dict(
|
||||||
black=0,
|
black=0,
|
||||||
red=1,
|
red=1,
|
||||||
|
@ -247,6 +321,7 @@ class Ansi:
|
||||||
)
|
)
|
||||||
for name, byte in state2byte.items():
|
for name, byte in state2byte.items():
|
||||||
self.setcode(name, f"\033[{byte}m")
|
self.setcode(name, f"\033[{byte}m")
|
||||||
|
self.add_cfg()
|
||||||
def setcode(self, name, escape_code):
|
def setcode(self, name, escape_code):
|
||||||
"""create attr for style and escape code"""
|
"""create attr for style and escape code"""
|
||||||
if not sys.stdout.isatty() or os.getenv("NO_COLOR", False):
|
if not sys.stdout.isatty() or os.getenv("NO_COLOR", False):
|
||||||
|
@ -256,6 +331,9 @@ class Ansi:
|
||||||
def custom(self, fg=None, bg=None):
|
def custom(self, fg=None, bg=None):
|
||||||
"""use custom color"""
|
"""use custom color"""
|
||||||
code, end = "\033[", "m"
|
code, end = "\033[", "m"
|
||||||
|
if not sys.stdout.isatty() or os.getenv("NO_COLOR", False):
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
if fg:
|
if fg:
|
||||||
if isinstance(fg, int):
|
if isinstance(fg, int):
|
||||||
code += f"38;5;{fg}"
|
code += f"38;5;{fg}"
|
||||||
|
@ -277,6 +355,17 @@ class Ansi:
|
||||||
print("Expected one or three values for bg as a list")
|
print("Expected one or three values for bg as a list")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return code + end
|
return code + end
|
||||||
|
def add_cfg(self):
|
||||||
|
cfg_styles = {
|
||||||
|
"header": "$(HEADER_STYLE)",
|
||||||
|
"accent": "$(ACCENT_STYLE)",
|
||||||
|
"params": "$(PARAMS_STYLE)",
|
||||||
|
"goal": "$(GOAL_STYLE)",
|
||||||
|
"msg": "$(MSG_STYLE)",
|
||||||
|
"div_style": "$(DIVIDER_STYLE)",
|
||||||
|
}
|
||||||
|
for name, style in cfg_styles.items():
|
||||||
|
self.setcode(name, getattr(self, style))
|
||||||
def style(self, text, style):
|
def style(self, text, style):
|
||||||
if style not in self.__dict__:
|
if style not in self.__dict__:
|
||||||
print(f"unknown style: {style}")
|
print(f"unknown style: {style}")
|
||||||
|
@ -284,49 +373,7 @@ class Ansi:
|
||||||
else:
|
else:
|
||||||
return f"{self.__dict__[style]}{text}{self.__dict__['end']}"
|
return f"{self.__dict__[style]}{text}{self.__dict__['end']}"
|
||||||
a = ansi = Ansi()
|
a = ansi = Ansi()
|
||||||
endef
|
cfg = Config(
|
||||||
define info_py
|
"$(DIVIDER)", "$(HELP_SEP)", f"""$(EPILOG)""", f"""$(USAGE)""", int("$(WRAP)")
|
||||||
$(ansi_py)
|
)
|
||||||
print(f"""$(2)""")
|
|
||||||
endef
|
|
||||||
define print_ansi_py
|
|
||||||
$(ansi_py)
|
|
||||||
sep = f"$(HELP_SEP)"
|
|
||||||
codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
|
|
||||||
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
|
|
||||||
print(f"{codes_names[code]:>20} {sep} {code+'*****'+ansi.end} {sep} {repr(code)}")
|
|
||||||
endef
|
|
||||||
define vars_py
|
|
||||||
import os
|
|
||||||
$(ansi_py)
|
|
||||||
vars = "$2".split()
|
|
||||||
length = max((len(v) for v in vars))
|
|
||||||
print(f"{ansi.$(HEADER_STYLE)}vars:{ansi.end}\n")
|
|
||||||
for v in vars:
|
|
||||||
print(f" {ansi.b_magenta}{v:<{length}}{ansi.end} = {os.getenv(v)}")
|
|
||||||
print()
|
|
||||||
endef
|
|
||||||
define confirm_py
|
|
||||||
import sys
|
|
||||||
$(ansi_py)
|
|
||||||
$(quit_make_py)
|
|
||||||
def confirm():
|
|
||||||
"""
|
|
||||||
Ask user to enter Y or N (case-insensitive).
|
|
||||||
:return: True if the answer is Y.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
answer = ""
|
|
||||||
while answer not in ["y", "n"]:
|
|
||||||
answer = input(f"""$(2) {a.b_red}[Y/n]{a.end} """).lower()
|
|
||||||
return answer == "y"
|
|
||||||
if confirm():
|
|
||||||
sys.exit()
|
|
||||||
else:
|
|
||||||
quit_make()
|
|
||||||
endef
|
|
||||||
define quit_make_py
|
|
||||||
import os, signal
|
|
||||||
def quit_make():
|
|
||||||
os.kill(os.getppid(), signal.SIGQUIT)
|
|
||||||
endef
|
endef
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -26,7 +26,7 @@ update-fonts:
|
||||||
## check | check fc-list for MonoLisa
|
## check | check fc-list for MonoLisa
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check:
|
check:
|
||||||
$(call msg, Checking System for Fonts)
|
$(call msg,Checking System for Fonts)
|
||||||
@fc-list | grep "MonoLisa"
|
@fc-list | grep "MonoLisa"
|
||||||
|
|
||||||
## update-src | update nerd fonts source
|
## update-src | update nerd fonts source
|
||||||
|
@ -45,5 +45,5 @@ lint:
|
||||||
clean:
|
clean:
|
||||||
@rm -r patched/*
|
@rm -r patched/*
|
||||||
|
|
||||||
USAGE = {a.b_green}Update MonoLisa with Nerd Fonts! {a.end}\n\n{a.$(HEADER_STYLE)}usage{a.end}:\n make <recipe>\n
|
USAGE = {a.b_green}Update MonoLisa with Nerd Fonts! {a.end}\n\n{a.header}usage{a.end}:\n make <recipe>\n
|
||||||
-include .task.mk
|
-include .task.mk
|
||||||
|
|
618
bin/font-patcher
618
bin/font-patcher
|
@ -1,14 +1,14 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding=utf8
|
# coding=utf8
|
||||||
# Nerd Fonts Version: 2.2.2
|
# Nerd Fonts Version: 2.3.0-RC
|
||||||
# Script version is further down
|
# Script version is further down
|
||||||
|
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
# Change the script version when you edit this script:
|
# Change the script version when you edit this script:
|
||||||
script_version = "3.0.6"
|
script_version = "3.1.1"
|
||||||
|
|
||||||
version = "2.2.2"
|
version = "2.3.0-RC"
|
||||||
projectName = "Nerd Fonts"
|
projectName = "Nerd Fonts"
|
||||||
projectNameAbbreviation = "NF"
|
projectNameAbbreviation = "NF"
|
||||||
projectNameSingular = projectName[:-1]
|
projectNameSingular = projectName[:-1]
|
||||||
|
@ -82,15 +82,32 @@ class TableHEADWriter:
|
||||||
i += 4
|
i += 4
|
||||||
extra = 0
|
extra = 0
|
||||||
for j in range(4):
|
for j in range(4):
|
||||||
|
extra = extra << 8
|
||||||
if i + j <= end:
|
if i + j <= end:
|
||||||
extra += ord(self.f.read(1))
|
extra += ord(self.f.read(1))
|
||||||
extra = extra << 8
|
|
||||||
checksum = (checksum + extra) & 0xFFFFFFFF
|
checksum = (checksum + extra) & 0xFFFFFFFF
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
def find_head_table(self):
|
def find_head_table(self, idx):
|
||||||
""" Search all tables for the HEAD table and store its metadata """
|
""" 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()
|
numtables = self.getshort()
|
||||||
self.f.seek(3*2, 1)
|
self.f.seek(3*2, 1)
|
||||||
|
|
||||||
|
@ -102,7 +119,7 @@ class TableHEADWriter:
|
||||||
self.tab_length = self.getlong()
|
self.tab_length = self.getlong()
|
||||||
if tab_name == b'head':
|
if tab_name == b'head':
|
||||||
return
|
return
|
||||||
raise Exception('No HEAD table found')
|
raise Exception('No HEAD table found in font idx {}'.format(idx))
|
||||||
|
|
||||||
def goto(self, where):
|
def goto(self, where):
|
||||||
""" Go to a named location in the file or to the specified index """
|
""" Go to a named location in the file or to the specified index """
|
||||||
|
@ -146,60 +163,97 @@ class TableHEADWriter:
|
||||||
self.modified = False
|
self.modified = False
|
||||||
self.f = open(filename, 'r+b')
|
self.f = open(filename, 'r+b')
|
||||||
|
|
||||||
self.find_head_table()
|
self.find_head_table(0)
|
||||||
|
|
||||||
self.flags = self.getshort('flags')
|
self.flags = self.getshort('flags')
|
||||||
self.lowppem = self.getshort('lowestRecPPEM')
|
self.lowppem = self.getshort('lowestRecPPEM')
|
||||||
self.checksum_adj = self.getlong('checksumAdjustment')
|
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:
|
class font_patcher:
|
||||||
def __init__(self):
|
def __init__(self, args):
|
||||||
self.args = None # class 'argparse.Namespace'
|
self.args = args # class 'argparse.Namespace'
|
||||||
self.sym_font_args = []
|
self.sym_font_args = []
|
||||||
self.config = None # class 'configparser.ConfigParser'
|
self.config = None # class 'configparser.ConfigParser'
|
||||||
self.sourceFont = None # class 'fontforge.font'
|
self.sourceFont = None # class 'fontforge.font'
|
||||||
self.octiconsExactEncodingPosition = True
|
|
||||||
self.patch_set = None # class 'list'
|
self.patch_set = None # class 'list'
|
||||||
self.font_dim = None # class 'dict'
|
self.font_dim = None # class 'dict'
|
||||||
self.onlybitmaps = 0
|
self.onlybitmaps = 0
|
||||||
self.extension = ""
|
|
||||||
self.essential = set()
|
self.essential = set()
|
||||||
self.setup_arguments()
|
|
||||||
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
||||||
if not os.path.isfile(self.args.font):
|
|
||||||
sys.exit("{}: Font file does not exist: {}".format(projectName, self.args.font))
|
def patch(self, font):
|
||||||
if not os.access(self.args.font, os.R_OK):
|
self.sourceFont = font
|
||||||
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")
|
|
||||||
self.setup_version()
|
self.setup_version()
|
||||||
self.get_essential_references()
|
self.get_essential_references()
|
||||||
self.setup_name_backup()
|
self.setup_name_backup(font)
|
||||||
|
if self.args.single:
|
||||||
|
self.assert_monospace()
|
||||||
self.remove_ligatures()
|
self.remove_ligatures()
|
||||||
make_sure_path_exists(self.args.outputdir)
|
|
||||||
self.check_position_conflicts()
|
|
||||||
self.setup_patch_set()
|
self.setup_patch_set()
|
||||||
self.setup_line_dimensions()
|
self.setup_line_dimensions()
|
||||||
self.get_sourcefont_dimensions()
|
self.get_sourcefont_dimensions()
|
||||||
self.sourceFont.encoding = 'UnicodeFull' # Update the font encoding to ensure that the Unicode glyphs are available
|
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
|
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:
|
if self.args.single:
|
||||||
# Force width to be equal on all glyphs to ensure the font is considered monospaced on Windows.
|
# 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
|
symfont.em = self.sourceFont.em
|
||||||
PreviousSymbolFilename = patch['Filename']
|
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']
|
SrcStart = patch['SrcStart']
|
||||||
SrcEnd = patch['SrcEnd']
|
|
||||||
if not SrcStart:
|
if not SrcStart:
|
||||||
SrcStart = patch['SymStart']
|
SrcStart = patch['SymStart']
|
||||||
if not SrcEnd:
|
self.copy_glyphs(SrcStart, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleGlyph'], patch['Name'], patch['Attributes'])
|
||||||
SrcEnd = patch['SymEnd']
|
|
||||||
self.copy_glyphs(SrcStart, SrcEnd, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleGlyph'], patch['Name'], patch['Attributes'])
|
|
||||||
|
|
||||||
if symfont:
|
if symfont:
|
||||||
symfont.close()
|
symfont.close()
|
||||||
|
@ -262,21 +313,40 @@ class font_patcher:
|
||||||
self.sourceFont["grave"].glyphclass="baseglyph"
|
self.sourceFont["grave"].glyphclass="baseglyph"
|
||||||
|
|
||||||
|
|
||||||
def generate(self):
|
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'.
|
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||||
if self.sourceFont.fullname != None:
|
sourceFonts[0].generateTtc(outfile, sourceFonts[1:], flags=(str('opentype'), str('PfEd-comments')), layer=layer)
|
||||||
outfile = self.args.outputdir + "/" + self.sourceFont.fullname + self.extension
|
message = "\nGenerated: {} fonts in '{}'".format(len(sourceFonts), outfile)
|
||||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
|
||||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fullname, outfile)
|
|
||||||
else:
|
else:
|
||||||
outfile = self.args.outputdir + "/" + self.sourceFont.cidfontname + self.extension
|
fontname = sourceFont.fullname
|
||||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
if not fontname:
|
||||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fontname, outfile)
|
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
|
# Adjust flags that can not be changed via fontforge
|
||||||
try:
|
try:
|
||||||
source_font = TableHEADWriter(self.args.font)
|
source_font = TableHEADWriter(self.args.font)
|
||||||
dest_font = TableHEADWriter(outfile)
|
dest_font = TableHEADWriter(outfile)
|
||||||
|
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:
|
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))
|
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'
|
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
||||||
|
@ -301,130 +371,19 @@ class font_patcher:
|
||||||
print("\nPost Processed: {}".format(outfile))
|
print("\nPost Processed: {}".format(outfile))
|
||||||
|
|
||||||
|
|
||||||
def setup_arguments(self):
|
def setup_name_backup(self, font):
|
||||||
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):
|
|
||||||
""" Store the original font names to be able to rename the font multiple times """
|
""" Store the original font names to be able to rename the font multiple times """
|
||||||
self.original_fontname = self.sourceFont.fontname
|
font.persistent = {
|
||||||
self.original_fullname = self.sourceFont.fullname
|
"fontname": font.fontname,
|
||||||
self.original_familyname = self.sourceFont.familyname
|
"fullname": font.fullname,
|
||||||
|
"familyname": font.familyname,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def setup_font_names(self):
|
def setup_font_names(self, font):
|
||||||
self.sourceFont.fontname = self.original_fontname
|
font.fontname = font.persistent["fontname"]
|
||||||
self.sourceFont.fullname = self.original_fullname
|
font.fullname = font.persistent["fullname"]
|
||||||
self.sourceFont.familyname = self.original_familyname
|
font.familyname = font.persistent["familyname"]
|
||||||
verboseAdditionalFontNameSuffix = " " + projectNameSingular
|
verboseAdditionalFontNameSuffix = " " + projectNameSingular
|
||||||
if self.args.windows: # attempt to shorten here on the additional name BEFORE trimming later
|
if self.args.windows: # attempt to shorten here on the additional name BEFORE trimming later
|
||||||
additionalFontNameSuffix = " " + projectNameAbbreviation
|
additionalFontNameSuffix = " " + projectNameAbbreviation
|
||||||
|
@ -471,14 +430,14 @@ class font_patcher:
|
||||||
verboseAdditionalFontNameSuffix += " Mono"
|
verboseAdditionalFontNameSuffix += " Mono"
|
||||||
|
|
||||||
if FontnameParserOK and self.args.makegroups:
|
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
|
# Use fullname if it is 'equal' to the fontname
|
||||||
if self.sourceFont.fullname:
|
if font.fullname:
|
||||||
use_fullname |= self.sourceFont.fontname.lower() == FontnameTools.postscript_char_filter(self.sourceFont.fullname).lower()
|
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)
|
# Use fullname for any of these source fonts (that are impossible to disentangle from the fontname, we need the blanks)
|
||||||
for hit in [ 'Meslo' ]:
|
for hit in [ 'Meslo' ]:
|
||||||
use_fullname |= self.sourceFont.fontname.lower().startswith(hit.lower())
|
use_fullname |= font.fontname.lower().startswith(hit.lower())
|
||||||
parser_name = self.sourceFont.fullname if use_fullname else self.sourceFont.fontname
|
parser_name = font.fullname if use_fullname else font.fontname
|
||||||
# Gohu fontnames hide the weight, but the file names are ok...
|
# Gohu fontnames hide the weight, but the file names are ok...
|
||||||
if parser_name.startswith('Gohu'):
|
if parser_name.startswith('Gohu'):
|
||||||
parser_name = os.path.splitext(os.path.basename(self.args.font))[0]
|
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)
|
# have an internal style defined (in sfnt_names)
|
||||||
# using '([^-]*?)' to get the item before the first dash "-"
|
# using '([^-]*?)' to get the item before the first dash "-"
|
||||||
# using '([^-]*(?!.*-))' to get the item after the last 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
|
familyname = fontname
|
||||||
|
|
||||||
# fullname (filename) can always use long/verbose font name, even in windows
|
# fullname (filename) can always use long/verbose font name, even in windows
|
||||||
if self.sourceFont.fullname != None:
|
if font.fullname != None:
|
||||||
fullname = self.sourceFont.fullname + verboseAdditionalFontNameSuffix
|
fullname = font.fullname + verboseAdditionalFontNameSuffix
|
||||||
else:
|
else:
|
||||||
fullname = self.sourceFont.cidfontname + verboseAdditionalFontNameSuffix
|
fullname = font.cidfontname + verboseAdditionalFontNameSuffix
|
||||||
|
|
||||||
fontname = fontname + additionalFontNameSuffix.replace(" ", "")
|
fontname = fontname + additionalFontNameSuffix.replace(" ", "")
|
||||||
|
|
||||||
|
@ -513,13 +472,13 @@ class font_patcher:
|
||||||
# parse fontname if it fails:
|
# parse fontname if it fails:
|
||||||
try:
|
try:
|
||||||
# search tuple:
|
# 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
|
# String ID is at the second index in the Tuple lists
|
||||||
sfntNamesStringIDIndex = 2
|
sfntNamesStringIDIndex = 2
|
||||||
|
|
||||||
# now we have the correct item:
|
# now we have the correct item:
|
||||||
subFamily = self.sourceFont.sfnt_names[subFamilyTupleIndex][sfntNamesStringIDIndex]
|
subFamily = font.sfnt_names[subFamilyTupleIndex][sfntNamesStringIDIndex]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
sys.stderr.write("{}: Could not find 'SubFamily' for given font, falling back to parsed fontname\n".format(projectName))
|
sys.stderr.write("{}: Could not find 'SubFamily' for given font, falling back to parsed fontname\n".format(projectName))
|
||||||
subFamily = fallbackStyle
|
subFamily = fallbackStyle
|
||||||
|
@ -629,22 +588,22 @@ class font_patcher:
|
||||||
|
|
||||||
if not (FontnameParserOK and self.args.makegroups):
|
if not (FontnameParserOK and self.args.makegroups):
|
||||||
# replace any extra whitespace characters:
|
# replace any extra whitespace characters:
|
||||||
self.sourceFont.familyname = " ".join(familyname.split())
|
font.familyname = " ".join(familyname.split())
|
||||||
self.sourceFont.fullname = " ".join(fullname.split())
|
font.fullname = " ".join(fullname.split())
|
||||||
self.sourceFont.fontname = " ".join(fontname.split())
|
font.fontname = " ".join(fontname.split())
|
||||||
|
|
||||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
font.appendSFNTName(str('English (US)'), str('Preferred Family'), font.familyname)
|
||||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
font.appendSFNTName(str('English (US)'), str('Family'), font.familyname)
|
||||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
font.appendSFNTName(str('English (US)'), str('Compatible Full'), font.fullname)
|
||||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
font.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||||
else:
|
else:
|
||||||
fam_suffix = projectNameSingular if not self.args.windows else projectNameAbbreviation
|
fam_suffix = projectNameSingular if not self.args.windows else projectNameAbbreviation
|
||||||
fam_suffix += ' Mono' if self.args.single else ''
|
fam_suffix += ' Mono' if self.args.single else ''
|
||||||
n.inject_suffix(verboseAdditionalFontNameSuffix, additionalFontNameSuffix, fam_suffix)
|
n.inject_suffix(verboseAdditionalFontNameSuffix, additionalFontNameSuffix, fam_suffix)
|
||||||
n.rename_font(self.sourceFont)
|
n.rename_font(font)
|
||||||
|
|
||||||
self.sourceFont.comment = projectInfo
|
font.comment = projectInfo
|
||||||
self.sourceFont.fontlog = projectInfo
|
font.fontlog = projectInfo
|
||||||
|
|
||||||
|
|
||||||
def setup_version(self):
|
def setup_version(self):
|
||||||
|
@ -678,10 +637,19 @@ class font_patcher:
|
||||||
print("No configfile given, skipping configfile related actions")
|
print("No configfile given, skipping configfile related actions")
|
||||||
|
|
||||||
|
|
||||||
def check_position_conflicts(self):
|
def assert_monospace(self):
|
||||||
# Prevent glyph encoding position conflicts between glyph sets
|
# Check if the sourcefont is monospaced
|
||||||
if self.args.fontawesome and self.args.octicons:
|
width_mono = is_monospaced(self.sourceFont)
|
||||||
self.octiconsExactEncodingPosition = False
|
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):
|
def setup_patch_set(self):
|
||||||
|
@ -805,28 +773,28 @@ class font_patcher:
|
||||||
# Define the character ranges
|
# Define the character ranges
|
||||||
# Symbol font ranges
|
# Symbol font ranges
|
||||||
self.patch_set = [
|
self.patch_set = [
|
||||||
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE534, 'SrcStart': 0xE5FA, 'SrcEnd': 0xE634, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'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, 'SrcEnd': 0xE7C5, 'ScaleGlyph': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
|
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'ScaleGlyph': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
|
||||||
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0A0, 'SymEnd': 0xE0A2, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0A0, 'SymEnd': 0xE0A2, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||||
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0B0, 'SymEnd': 0xE0B3, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0B0, 'SymEnd': 0xE0B3, 'SrcStart': None, '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': 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, '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, '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': 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, '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, '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.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, 'SrcEnd': None, 'ScaleGlyph': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA},
|
{'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, 'SrcEnd': 0xE2A9, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize
|
{'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, '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': 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, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
|
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
|
||||||
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'SrcEnd': 0xFD46, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||||
{'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'SrcEnd': 0xE3EB, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, '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.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': 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': 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': 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': 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': 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': 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': self.octiconsExactEncodingPosition, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'SrcEnd': 0xF4A9, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
|
{'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, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'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': 0x0000, 'SrcEnd': 0x0000, 'ScaleGlyph': None, 'Attributes': CUSTOM_ATTR}
|
{'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):
|
def setup_line_dimensions(self):
|
||||||
|
@ -846,11 +814,6 @@ class font_patcher:
|
||||||
self.sourceFont.hhea_ascent = self.sourceFont.os2_winascent
|
self.sourceFont.hhea_ascent = self.sourceFont.os2_winascent
|
||||||
self.sourceFont.hhea_descent = -self.sourceFont.os2_windescent
|
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):
|
def get_essential_references(self):
|
||||||
"""Find glyphs that are needed for the basic glyphs"""
|
"""Find glyphs that are needed for the basic glyphs"""
|
||||||
# Sometimes basic glyphs are constructed from multiple other 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['ymin'] = self.sourceFont.os2_typodescent
|
||||||
self.font_dim['ymax'] = self.sourceFont.os2_typoascent
|
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
|
# Find the biggest char width
|
||||||
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
||||||
#
|
#
|
||||||
|
@ -890,22 +885,10 @@ class font_patcher:
|
||||||
continue
|
continue
|
||||||
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
||||||
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']:
|
if xmax > self.font_dim['xmax']:
|
||||||
self.font_dim['xmax'] = xmax
|
self.font_dim['xmax'] = xmax
|
||||||
|
# print("New MAXWIDTH-B {} {} {}".format(glyph, self.sourceFont[glyph].width, 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_scale_factor(self, sym_dim):
|
def get_scale_factor(self, sym_dim):
|
||||||
|
@ -921,19 +904,16 @@ class font_patcher:
|
||||||
return scale_ratio
|
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 """
|
""" Copies symbol glyphs into self.sourceFont """
|
||||||
progressText = ''
|
progressText = ''
|
||||||
careful = False
|
careful = False
|
||||||
glyphSetLength = 0
|
glyphSetLength = 0
|
||||||
|
sourceFontCounter = 0
|
||||||
|
|
||||||
if self.args.careful:
|
if self.args.careful:
|
||||||
careful = True
|
careful = True
|
||||||
|
|
||||||
if exactEncoding is False:
|
|
||||||
sourceFontList = list(range(sourceFontStart, sourceFontEnd + 1))
|
|
||||||
sourceFontCounter = 0
|
|
||||||
|
|
||||||
# Create glyphs from symbol font
|
# Create glyphs from symbol font
|
||||||
#
|
#
|
||||||
# If we are going to copy all Glyphs, then assume we want to be careful
|
# 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 ]
|
possible_codes += [ v for v, s, r in sym_glyph.altuni if v > currentSourceFontGlyph ]
|
||||||
currentSourceFontGlyph = min(possible_codes)
|
currentSourceFontGlyph = min(possible_codes)
|
||||||
else:
|
else:
|
||||||
# use source font defined hex values based on passed in start and end
|
# use source font defined hex values based on passed in start (fills gaps; symbols are packed)
|
||||||
currentSourceFontGlyph = sourceFontList[sourceFontCounter]
|
currentSourceFontGlyph = sourceFontStart + sourceFontCounter
|
||||||
sourceFontCounter += 1
|
sourceFontCounter += 1
|
||||||
|
|
||||||
if self.args.quiet is False:
|
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.stderr.write("{}: Please use at least version: {}\n".format(projectName, minimumVersion))
|
||||||
sys.exit(1)
|
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():
|
def main():
|
||||||
|
print("{} Patcher v{} ({}) executing".format(projectName, version, script_version))
|
||||||
check_fontforge_min_version()
|
check_fontforge_min_version()
|
||||||
patcher = font_patcher()
|
args = setup_arguments()
|
||||||
patcher.patch()
|
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")
|
print("\nDone with Patch Sets, generating font...\n")
|
||||||
patcher.setup_font_names()
|
for f in sourceFonts:
|
||||||
patcher.generate()
|
patcher.setup_font_names(f)
|
||||||
|
patcher.generate(sourceFonts)
|
||||||
|
|
||||||
# This mainly helps to improve CI runtime
|
# This mainly helps to improve CI runtime
|
||||||
if patcher.args.alsowindows:
|
if patcher.args.alsowindows:
|
||||||
patcher.args.windows = True
|
patcher.args.windows = True
|
||||||
patcher.setup_font_names()
|
for f in sourceFonts:
|
||||||
patcher.generate()
|
patcher.setup_font_names(f)
|
||||||
|
patcher.generate(sourceFonts)
|
||||||
|
|
||||||
|
for f in sourceFonts:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue