mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-12-21 22:40:44 -06:00
Compare commits
3 commits
870329f52a
...
c06928af07
Author | SHA1 | Date | |
---|---|---|---|
c06928af07 | |||
90ef76cd4a | |||
59b79dc2ac |
6 changed files with 637 additions and 31 deletions
250
.task.mk
Normal file
250
.task.mk
Normal file
|
@ -0,0 +1,250 @@
|
|||
# }> [github.com/daylinmorgan/task.mk] <{ #
|
||||
# Copyright (c) 2022 Daylin Morgan
|
||||
# MIT License
|
||||
# 22.9.5
|
||||
#
|
||||
# task.mk should be included at the bottom of your Makefile.
|
||||
# See below for the standard configuration options that should be set prior to including this file.
|
||||
# You can update your .task.mk with `make _update-task.mk`
|
||||
|
||||
# ---- CONFIG ---- #
|
||||
HEADER_COLOR ?= b_cyan
|
||||
PARAMS_COLOR ?= b_magenta
|
||||
ACCENT_COLOR ?= b_yellow
|
||||
GOAL_COLOR ?= $(ACCENT_COLOR)
|
||||
MSG_COLOR ?= faint
|
||||
HELP_SEP ?= |
|
||||
HELP_SORT ?= # sort goals alphabetically
|
||||
|
||||
# python f-string literals
|
||||
EPILOG ?=
|
||||
define USAGE ?=
|
||||
{ansi.$(HEADER_COLOR)}usage{ansi.end}:
|
||||
make <recipe>
|
||||
|
||||
endef
|
||||
|
||||
# ---- [buitlin recipes] ---- #
|
||||
|
||||
|
||||
## h, help | show this help
|
||||
.PHONY: help h
|
||||
help h:
|
||||
$(call py,help_py)
|
||||
|
||||
.PHONY: _help
|
||||
_help: export SHOW_HIDDEN=true
|
||||
_help: help
|
||||
|
||||
ifdef PRINT_VARS
|
||||
|
||||
$(foreach v,$(PRINT_VARS),$(eval export $(v)))
|
||||
|
||||
.PHONY: vars v
|
||||
vars v:
|
||||
$(call py,vars_py,$(PRINT_VARS))
|
||||
|
||||
endif
|
||||
|
||||
## _print-ansi | show all possible ansi color code combinations
|
||||
.PHONY:
|
||||
_print-ansi:
|
||||
$(call py,print_ansi_py)
|
||||
|
||||
# functions to take f-string literals and pass to python print
|
||||
tprint = $(call py,info_py,$(1))
|
||||
tprint-sh = $(call pysh,info_py,$(1))
|
||||
|
||||
_update-task.mk:
|
||||
$(call tprint,Updating task.mk)
|
||||
curl https://raw.githubusercontent.com/daylinmorgan/task.mk/main/task.mk -o .task.mk
|
||||
|
||||
export MAKEFILE_LIST
|
||||
|
||||
# ---- [python/bash script runner] ---- #
|
||||
|
||||
# modified from https://unix.stackexchange.com/a/223093
|
||||
define \n
|
||||
|
||||
|
||||
endef
|
||||
|
||||
escape_shellstring = $(subst `,\`,$(subst ",\",$(subst $$,\$$,$(subst \,\\,$1))))
|
||||
|
||||
escape_printf = $(subst \,\\,$(subst %,%%,$1))
|
||||
|
||||
create_string = $(subst $(\n),\n,$(call escape_shellstring,$(call escape_printf,$1)))
|
||||
|
||||
|
||||
ifdef DEBUG
|
||||
define py
|
||||
@printf "Python Script:\n"
|
||||
@printf -- "----------------\n"
|
||||
@printf "$(call create_string,$($(1)))\n"
|
||||
@printf -- "----------------\n"
|
||||
@printf "$(call create_string,$($(1)))" | python3
|
||||
endef
|
||||
define tbash
|
||||
@printf "Bash Script:\n"
|
||||
@printf -- "----------------\n"
|
||||
@printf "$(call create_string,$($(1)))\n"
|
||||
@printf -- "----------------\n"
|
||||
@printf "$(call create_string,$($(1)))" | bash
|
||||
endef
|
||||
else
|
||||
py = @printf "$(call create_string,$($(1)))" | python3
|
||||
tbash = @printf "$(call create_string,$($(1)))" | bash
|
||||
endif
|
||||
|
||||
pysh = printf "$(call create_string,$($(1)))" | python3
|
||||
|
||||
# ---- [python scripts] ---- #
|
||||
|
||||
|
||||
define help_py
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
$(ansi_py)
|
||||
|
||||
pattern = re.compile(r"^## (.*) \| (.*)")
|
||||
|
||||
makefile = ""
|
||||
for file in os.getenv("MAKEFILE_LIST").split():
|
||||
with open(file, "r") as f:
|
||||
makefile += f.read() + "\n\n"
|
||||
|
||||
|
||||
def get_help(file):
|
||||
for line in file.splitlines():
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"):
|
||||
continue
|
||||
else:
|
||||
yield match.groups()
|
||||
|
||||
|
||||
print(f"""$(USAGE)""")
|
||||
|
||||
goals = list(get_help(makefile))
|
||||
if os.getenv("SORT_HELP",False):
|
||||
goals.sort(key=lambda i: i[0])
|
||||
goal_len = max(len(goal[0]) for goal in goals)
|
||||
|
||||
for goal, msg in goals:
|
||||
print(
|
||||
f"{ansi.$(GOAL_COLOR)}{goal:>{goal_len}}{ansi.end} $(HELP_SEP) {ansi.$(MSG_COLOR)}{msg}{ansi.end}"
|
||||
)
|
||||
|
||||
print(f"""$(EPILOG)""")
|
||||
|
||||
|
||||
endef
|
||||
|
||||
define ansi_py
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
color2byte = dict(
|
||||
black=0,
|
||||
red=1,
|
||||
green=2,
|
||||
yellow=3,
|
||||
blue=4,
|
||||
magenta=5,
|
||||
cyan=6,
|
||||
white=7,
|
||||
)
|
||||
|
||||
state2byte = dict(
|
||||
bold=1, faint=2, italic=3, underline=4, blink=5, fast_blink=6, crossed=9
|
||||
)
|
||||
|
||||
|
||||
def fg(byte):
|
||||
return 30 + byte
|
||||
|
||||
|
||||
def bg(byte):
|
||||
return 40 + byte
|
||||
|
||||
|
||||
class Ansi:
|
||||
"""ANSI color codes"""
|
||||
|
||||
def setcode(self, name, escape_code):
|
||||
if not sys.stdout.isatty() or os.getenv("NO_COLOR", False):
|
||||
setattr(self, name, "")
|
||||
else:
|
||||
setattr(self, name, escape_code)
|
||||
|
||||
def __init__(self):
|
||||
self.setcode("end", "\033[0m")
|
||||
for name, byte in color2byte.items():
|
||||
self.setcode(name, f"\033[{fg(byte)}m")
|
||||
self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m")
|
||||
self.setcode(f"d_{name}", f"\033[2;{fg(byte)}m")
|
||||
for bgname, bgbyte in color2byte.items():
|
||||
self.setcode(f"{name}_on_{bgname}", f"\033[{bg(bgbyte)};{fg(byte)}m")
|
||||
for name, byte in state2byte.items():
|
||||
self.setcode(name, f"\033[{byte}m")
|
||||
|
||||
|
||||
a = ansi = Ansi()
|
||||
|
||||
|
||||
endef
|
||||
|
||||
define info_py
|
||||
|
||||
|
||||
$(ansi_py)
|
||||
|
||||
print(f"""$(2)""")
|
||||
|
||||
|
||||
endef
|
||||
|
||||
define print_ansi_py
|
||||
|
||||
|
||||
$(ansi_py)
|
||||
|
||||
codes_names = {
|
||||
getattr(ansi, attr): attr
|
||||
for attr in dir(ansi)
|
||||
if attr[0:1] != "_" and attr != "end" and attr != "setcode"
|
||||
}
|
||||
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
|
||||
print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end))
|
||||
|
||||
|
||||
|
||||
endef
|
||||
|
||||
define vars_py
|
||||
|
||||
|
||||
|
||||
import os
|
||||
|
||||
$(ansi_py)
|
||||
|
||||
vars = "$2".split()
|
||||
length = max((len(v) for v in vars))
|
||||
|
||||
print(f"{ansi.$(HEADER_COLOR)}vars:{ansi.end}\n")
|
||||
|
||||
for v in vars:
|
||||
print(f" {ansi.b_magenta}{v:<{length}}{ansi.end} = {os.getenv(v)}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
endef
|
||||
|
27
Makefile
27
Makefile
|
@ -6,33 +6,52 @@ ML_TYPES := $(shell find MonoLisa -mindepth 1 -type d -printf "%f ")
|
|||
UNKNOWN := $(filter-out $(OK_TYPES),$(ML_TYPES))
|
||||
$(if $(UNKNOWN),$(error unknown font type in ./MonoLisa: $(UNKNOWN)))
|
||||
|
||||
msg = $(call tprint,{a.bold}==>{a.end} {a.b_magenta}$(1){a.end} {a.bold}<=={a.end})
|
||||
|
||||
## patch | add nerd fonts to MonoLisa
|
||||
.PHONY: patch
|
||||
patch: $(addprefix patch-,$(ML_TYPES))
|
||||
|
||||
patch-%: ./bin/font-patcher
|
||||
@echo "==> Patching MonoLisa $* Files <=="
|
||||
$(call msg, Patching MonoLisa $* Files)
|
||||
@./bin/patch-monolisa $* $(ARGS)
|
||||
|
||||
## update-fonts | move fonts and update fc-cache
|
||||
.PHONY: update-fonts
|
||||
update-fonts:
|
||||
@echo "==> Adding Fonts To System <=="
|
||||
$(call msg,Adding Fonts To System)
|
||||
@./bin/update-fonts
|
||||
@fc-cache -f -v
|
||||
|
||||
## check | check fc-list for MonoLisa
|
||||
.PHONY: check
|
||||
check:
|
||||
@echo "==> Checking System For Fonts <=="
|
||||
$(call msg, Checking System for Fonts)
|
||||
@fc-list | grep "MonoLisa"
|
||||
|
||||
## update-src | update nerd fonts source
|
||||
.PHONY: update-src
|
||||
update-src:
|
||||
@echo "==> Updating Source File <=="
|
||||
$(call msg,Updating Source Files)
|
||||
@./bin/update-src
|
||||
|
||||
## lint | check shell scripts
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@shfmt -w -s $(shell shfmt -f bin/)
|
||||
|
||||
## clean | remove patched fonts
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -r patched/*
|
||||
|
||||
define USAGE
|
||||
{a.b_green}Update MonoLisa with Nerd Fonts! {a.end}
|
||||
|
||||
{a.$(HEADER_COLOR)}usage{a.end}:
|
||||
make <recipe>
|
||||
|
||||
endef
|
||||
|
||||
-include .task.mk
|
||||
$(if $(filter help,$(MAKECMDGOALS)),.task.mk: ; curl -fsSL https://raw.githubusercontent.com/daylinmorgan/task.mk/v22.9.5/task.mk -o .task.mk)
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
tested w/ MonoLisa v1.808
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/help.svg" width=400>
|
||||
</p>
|
||||
|
||||
## Before You Begin
|
||||
|
||||
First you will need to install `fontforge`
|
||||
|
|
111
assets/help.svg
Normal file
111
assets/help.svg
Normal file
|
@ -0,0 +1,111 @@
|
|||
<svg class="rich-terminal shadow" viewBox="0 0 585.3333333333334 399.5333333333333" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@2.1.0/patched-fonts/FiraCode/Regular/complete/Fira%20Code%20Regular%20Nerd%20Font%20Complete.ttf") format("truetype");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@2.1.0/patched-fonts/FiraCode/Bold/complete/Fira%20Code%20Bold%20Nerd%20Font%20Complete.ttf") format("truetype");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-747138606-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-747138606-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
-webkit-filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7));
|
||||
filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7));
|
||||
}
|
||||
.terminal-747138606-r1 { fill: #a6e3a1;font-weight: bold }
|
||||
.terminal-747138606-r2 { fill: #c6d0f5 }
|
||||
.terminal-747138606-r3 { fill: #94e2d5;font-weight: bold }
|
||||
.terminal-747138606-r4 { fill: #f9e2af;font-weight: bold }
|
||||
.terminal-747138606-r5 { fill: #8288a5 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-747138606-clip-terminal">
|
||||
<rect x="0" y="0" width="548.0" height="316.2" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-0">
|
||||
<rect x="0" y="1.5" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-1">
|
||||
<rect x="0" y="25.9" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-2">
|
||||
<rect x="0" y="50.3" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-3">
|
||||
<rect x="0" y="74.7" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-4">
|
||||
<rect x="0" y="99.1" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-5">
|
||||
<rect x="0" y="123.5" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-6">
|
||||
<rect x="0" y="147.9" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-7">
|
||||
<rect x="0" y="172.3" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-8">
|
||||
<rect x="0" y="196.7" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-9">
|
||||
<rect x="0" y="221.1" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-10">
|
||||
<rect x="0" y="245.5" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-747138606-line-11">
|
||||
<rect x="0" y="269.9" width="549" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#1e1e2e" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="10.1667" y="1" width="565" height="365.2" rx="8"/><text class="terminal-747138606-title" fill="#c6d0f5" text-anchor="middle" x="282" y="27">help</text>
|
||||
<g transform="translate(32,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(18.166666666666664, 41) scale(.95)" clip-path="url(#terminal-747138606-clip-terminal)">
|
||||
|
||||
<g class="terminal-747138606-matrix">
|
||||
<text class="terminal-747138606-r1" x="0" y="20" textLength="402.6" clip-path="url(#terminal-747138606-line-0)">Update MonoLisa with Nerd Fonts! </text><text class="terminal-747138606-r2" x="549" y="20" textLength="12.2" clip-path="url(#terminal-747138606-line-0)">
|
||||
</text><text class="terminal-747138606-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-747138606-line-1)">
|
||||
</text><text class="terminal-747138606-r3" x="0" y="68.8" textLength="61" clip-path="url(#terminal-747138606-line-2)">usage</text><text class="terminal-747138606-r2" x="61" y="68.8" textLength="12.2" clip-path="url(#terminal-747138606-line-2)">:</text><text class="terminal-747138606-r2" x="549" y="68.8" textLength="12.2" clip-path="url(#terminal-747138606-line-2)">
|
||||
</text><text class="terminal-747138606-r2" x="97.6" y="93.2" textLength="158.6" clip-path="url(#terminal-747138606-line-3)">make <recipe></text><text class="terminal-747138606-r2" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-747138606-line-3)">
|
||||
</text><text class="terminal-747138606-r2" x="549" y="117.6" textLength="12.2" clip-path="url(#terminal-747138606-line-4)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="142" textLength="146.4" clip-path="url(#terminal-747138606-line-5)">       patch</text><text class="terminal-747138606-r2" x="146.4" y="142" textLength="36.6" clip-path="url(#terminal-747138606-line-5)"> | </text><text class="terminal-747138606-r5" x="183" y="142" textLength="317.2" clip-path="url(#terminal-747138606-line-5)">add nerd fonts to MonoLisa</text><text class="terminal-747138606-r2" x="549" y="142" textLength="12.2" clip-path="url(#terminal-747138606-line-5)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="166.4" textLength="146.4" clip-path="url(#terminal-747138606-line-6)">update-fonts</text><text class="terminal-747138606-r2" x="146.4" y="166.4" textLength="36.6" clip-path="url(#terminal-747138606-line-6)"> | </text><text class="terminal-747138606-r5" x="183" y="166.4" textLength="366" clip-path="url(#terminal-747138606-line-6)">move fonts and update fc-cache</text><text class="terminal-747138606-r2" x="549" y="166.4" textLength="12.2" clip-path="url(#terminal-747138606-line-6)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="190.8" textLength="146.4" clip-path="url(#terminal-747138606-line-7)">       check</text><text class="terminal-747138606-r2" x="146.4" y="190.8" textLength="36.6" clip-path="url(#terminal-747138606-line-7)"> | </text><text class="terminal-747138606-r5" x="183" y="190.8" textLength="317.2" clip-path="url(#terminal-747138606-line-7)">check fc-list for MonoLisa</text><text class="terminal-747138606-r2" x="549" y="190.8" textLength="12.2" clip-path="url(#terminal-747138606-line-7)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="215.2" textLength="146.4" clip-path="url(#terminal-747138606-line-8)">  update-src</text><text class="terminal-747138606-r2" x="146.4" y="215.2" textLength="36.6" clip-path="url(#terminal-747138606-line-8)"> | </text><text class="terminal-747138606-r5" x="183" y="215.2" textLength="292.8" clip-path="url(#terminal-747138606-line-8)">update nerd fonts source</text><text class="terminal-747138606-r2" x="549" y="215.2" textLength="12.2" clip-path="url(#terminal-747138606-line-8)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="239.6" textLength="146.4" clip-path="url(#terminal-747138606-line-9)">        lint</text><text class="terminal-747138606-r2" x="146.4" y="239.6" textLength="36.6" clip-path="url(#terminal-747138606-line-9)"> | </text><text class="terminal-747138606-r5" x="183" y="239.6" textLength="231.8" clip-path="url(#terminal-747138606-line-9)">check shell scripts</text><text class="terminal-747138606-r2" x="549" y="239.6" textLength="12.2" clip-path="url(#terminal-747138606-line-9)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="264" textLength="146.4" clip-path="url(#terminal-747138606-line-10)">       clean</text><text class="terminal-747138606-r2" x="146.4" y="264" textLength="36.6" clip-path="url(#terminal-747138606-line-10)"> | </text><text class="terminal-747138606-r5" x="183" y="264" textLength="244" clip-path="url(#terminal-747138606-line-10)">remove patched fonts</text><text class="terminal-747138606-r2" x="549" y="264" textLength="12.2" clip-path="url(#terminal-747138606-line-10)">
|
||||
</text><text class="terminal-747138606-r4" x="0" y="288.4" textLength="146.4" clip-path="url(#terminal-747138606-line-11)">     h, help</text><text class="terminal-747138606-r2" x="146.4" y="288.4" textLength="36.6" clip-path="url(#terminal-747138606-line-11)"> | </text><text class="terminal-747138606-r5" x="183" y="288.4" textLength="170.8" clip-path="url(#terminal-747138606-line-11)">show this help</text><text class="terminal-747138606-r2" x="549" y="288.4" textLength="12.2" clip-path="url(#terminal-747138606-line-11)">
|
||||
</text><text class="terminal-747138606-r2" x="549" y="312.8" textLength="12.2" clip-path="url(#terminal-747138606-line-12)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9 KiB |
276
bin/font-patcher
276
bin/font-patcher
|
@ -1,11 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
# Nerd Fonts Version: 2.1.0
|
||||
# script version: 3.0.1
|
||||
# Nerd Fonts Version: 2.2.1
|
||||
# Script version is further down
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
version = "2.1.0"
|
||||
# Change the script version when you edit this script:
|
||||
script_version = "3.0.3"
|
||||
|
||||
version = "2.2.1"
|
||||
projectName = "Nerd Fonts"
|
||||
projectNameAbbreviation = "NF"
|
||||
projectNameSingular = projectName[:-1]
|
||||
|
@ -36,6 +39,122 @@ except ImportError:
|
|||
)
|
||||
)
|
||||
|
||||
# This is for experimenting
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])) + '/bin/scripts/name_parser/')
|
||||
try:
|
||||
from FontnameParser import FontnameParser
|
||||
from FontnameTools import FontnameTools
|
||||
FontnameParserOK = True
|
||||
except ImportError:
|
||||
FontnameParserOK = False
|
||||
|
||||
class TableHEADWriter:
|
||||
""" Access to the HEAD table without external dependencies """
|
||||
def getlong(self, pos = None):
|
||||
""" Get four bytes from the font file as integer number """
|
||||
if pos:
|
||||
self.goto(pos)
|
||||
return (ord(self.f.read(1)) << 24) + (ord(self.f.read(1)) << 16) + (ord(self.f.read(1)) << 8) + ord(self.f.read(1))
|
||||
|
||||
def getshort(self, pos = None):
|
||||
""" Get two bytes from the font file as integer number """
|
||||
if pos:
|
||||
self.goto(pos)
|
||||
return (ord(self.f.read(1)) << 8) + ord(self.f.read(1))
|
||||
|
||||
def putlong(self, num, pos = None):
|
||||
""" Put number as four bytes into font file """
|
||||
if pos:
|
||||
self.goto(pos)
|
||||
self.f.write(bytearray([(num >> 24) & 0xFF, (num >> 16) & 0xFF ,(num >> 8) & 0xFF, num & 0xFF]))
|
||||
self.modified = True
|
||||
|
||||
def putshort(self, num, pos = None):
|
||||
""" Put number as two bytes into font file """
|
||||
if pos:
|
||||
self.goto(pos)
|
||||
self.f.write(bytearray([(num >> 8) & 0xFF, num & 0xFF]))
|
||||
self.modified = True
|
||||
|
||||
def calc_checksum(self, start, end, checksum = 0):
|
||||
""" Calculate a font table checksum, optionally ignoring another embedded checksum value (for table 'head') """
|
||||
self.f.seek(start)
|
||||
for i in range(start, end - 4, 4):
|
||||
checksum += self.getlong()
|
||||
checksum &= 0xFFFFFFFF
|
||||
i += 4
|
||||
extra = 0
|
||||
for j in range(4):
|
||||
if i + j <= end:
|
||||
extra += ord(self.f.read(1))
|
||||
extra = extra << 8
|
||||
checksum = (checksum + extra) & 0xFFFFFFFF
|
||||
return checksum
|
||||
|
||||
def find_head_table(self):
|
||||
""" Search all tables for the HEAD table and store its metadata """
|
||||
self.f.seek(4)
|
||||
numtables = self.getshort()
|
||||
self.f.seek(3*2, 1)
|
||||
|
||||
for i in range(numtables):
|
||||
tab_name = self.f.read(4)
|
||||
self.tab_check_offset = self.f.tell()
|
||||
self.tab_check = self.getlong()
|
||||
self.tab_offset = self.getlong()
|
||||
self.tab_length = self.getlong()
|
||||
if tab_name == b'head':
|
||||
return
|
||||
raise Exception('No HEAD table found')
|
||||
|
||||
def goto(self, where):
|
||||
""" Go to a named location in the file or to the specified index """
|
||||
if type(where) is str:
|
||||
positions = {'checksumAdjustment': 2+2+4,
|
||||
'flags': 2+2+4+4+4,
|
||||
'lowestRecPPEM': 2+2+4+4+4+2+2+8+8+2+2+2+2+2,
|
||||
}
|
||||
where = self.tab_offset + positions[where]
|
||||
self.f.seek(where)
|
||||
|
||||
|
||||
def calc_full_checksum(self, check = False):
|
||||
""" Calculate the whole file's checksum """
|
||||
self.f.seek(0, 2)
|
||||
self.end = self.f.tell()
|
||||
full_check = self.calc_checksum(0, self.end, (-self.checksum_adj) & 0xFFFFFFFF)
|
||||
if check and (0xB1B0AFBA - full_check) & 0xFFFFFFFF != self.checksum_adj:
|
||||
sys.exit("Checksum of whole font is bad")
|
||||
return full_check
|
||||
|
||||
def calc_table_checksum(self, check = False):
|
||||
tab_check_new = self.calc_checksum(self.tab_offset, self.tab_offset + self.tab_length - 1, (-self.checksum_adj) & 0xFFFFFFFF)
|
||||
if check and tab_check_new != self.tab_check:
|
||||
sys.exit("Checksum of 'head' in font is bad")
|
||||
return tab_check_new
|
||||
|
||||
def reset_table_checksum(self):
|
||||
new_check = self.calc_table_checksum()
|
||||
self.putlong(new_check, self.tab_check_offset)
|
||||
|
||||
def reset_full_checksum(self):
|
||||
new_adj = (0xB1B0AFBA - self.calc_full_checksum()) & 0xFFFFFFFF
|
||||
self.putlong(new_adj, 'checksumAdjustment')
|
||||
|
||||
def close(self):
|
||||
self.f.close()
|
||||
|
||||
|
||||
def __init__(self, filename):
|
||||
self.modified = False
|
||||
self.f = open(filename, 'r+b')
|
||||
|
||||
self.find_head_table()
|
||||
|
||||
self.flags = self.getshort('flags')
|
||||
self.lowppem = self.getshort('lowestRecPPEM')
|
||||
self.checksum_adj = self.getlong('checksumAdjustment')
|
||||
|
||||
|
||||
class font_patcher:
|
||||
def __init__(self):
|
||||
|
@ -44,7 +163,6 @@ class font_patcher:
|
|||
self.config = None # class 'configparser.ConfigParser'
|
||||
self.sourceFont = None # class 'fontforge.font'
|
||||
self.octiconsExactEncodingPosition = True
|
||||
self.fontlinuxExactEncodingPosition = True
|
||||
self.patch_set = None # class 'list'
|
||||
self.font_dim = None # class 'dict'
|
||||
self.onlybitmaps = 0
|
||||
|
@ -62,7 +180,8 @@ class font_patcher:
|
|||
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_font_names()
|
||||
self.setup_version()
|
||||
self.setup_name_backup()
|
||||
self.remove_ligatures()
|
||||
make_sure_path_exists(self.args.outputdir)
|
||||
self.check_position_conflicts()
|
||||
|
@ -81,7 +200,7 @@ class font_patcher:
|
|||
|
||||
def patch(self):
|
||||
|
||||
print("{} Patcher v{} executing\n".format(projectName, version))
|
||||
print("{} Patcher v{} ({}) executing\n".format(projectName, version, script_version))
|
||||
|
||||
if self.args.single:
|
||||
# Force width to be equal on all glyphs to ensure the font is considered monospaced on Windows.
|
||||
|
@ -124,19 +243,53 @@ class font_patcher:
|
|||
|
||||
if symfont:
|
||||
symfont.close()
|
||||
print("\nDone with Patch Sets, generating font...")
|
||||
|
||||
# The grave accent and fontforge:
|
||||
# If the type is 'auto' fontforge changes it to 'mark' on export.
|
||||
# We can not prevent this. So set it to 'baseglyph' instead, as
|
||||
# that resembles the most common expectations.
|
||||
# This is not needed with fontforge March 2022 Release anymore.
|
||||
if "grave" in self.sourceFont:
|
||||
self.sourceFont["grave"].glyphclass="baseglyph"
|
||||
|
||||
|
||||
def generate(self):
|
||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||
if self.sourceFont.fullname != None:
|
||||
self.sourceFont.generate(self.args.outputdir + "/" + self.sourceFont.fullname + self.extension, flags=(str('opentype'), str('PfEd-comments')))
|
||||
print("\nGenerated: {}".format(self.sourceFont.fontname))
|
||||
outfile = self.args.outputdir + "/" + self.sourceFont.fullname + self.extension
|
||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fullname, outfile)
|
||||
else:
|
||||
self.sourceFont.generate(self.args.outputdir + "/" + self.sourceFont.cidfontname + self.extension, flags=(str('opentype'), str('PfEd-comments')))
|
||||
print("\nGenerated: {}".format(self.sourceFont.fullname))
|
||||
outfile = self.args.outputdir + "/" + self.sourceFont.cidfontname + self.extension
|
||||
self.sourceFont.generate(outfile, flags=(str('opentype'), str('PfEd-comments')))
|
||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fontname, outfile)
|
||||
|
||||
# Adjust flags that can not be changed via fontforge
|
||||
try:
|
||||
source_font = TableHEADWriter(self.args.font)
|
||||
dest_font = TableHEADWriter(outfile)
|
||||
if source_font.flags & 0x08 == 0 and dest_font.flags & 0x08 != 0:
|
||||
print("Changing flags from 0x{:X} to 0x{:X}".format(dest_font.flags, dest_font.flags & ~0x08))
|
||||
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
||||
if source_font.lowppem != dest_font.lowppem:
|
||||
print("Changing lowestRecPPEM from {} to {}".format(dest_font.lowppem, source_font.lowppem))
|
||||
dest_font.putshort(source_font.lowppem, 'lowestRecPPEM')
|
||||
if dest_font.modified:
|
||||
dest_font.reset_table_checksum()
|
||||
dest_font.reset_full_checksum()
|
||||
except Exception as error:
|
||||
print("Can not handle font flags ({})".format(repr(error)))
|
||||
finally:
|
||||
try:
|
||||
source_font.close()
|
||||
dest_font.close()
|
||||
except:
|
||||
pass
|
||||
print(message)
|
||||
|
||||
if self.args.postprocess:
|
||||
subprocess.call([self.args.postprocess, self.args.outputdir + "/" + self.sourceFont.fullname + self.extension])
|
||||
print("\nPost Processed: {}".format(self.sourceFont.fullname))
|
||||
subprocess.call([self.args.postprocess, outfile])
|
||||
print("\nPost Processed: {}".format(outfile))
|
||||
|
||||
|
||||
def setup_arguments(self):
|
||||
|
@ -166,12 +319,14 @@ class font_patcher:
|
|||
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)')
|
||||
|
||||
# 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')
|
||||
|
@ -189,6 +344,9 @@ class font_patcher:
|
|||
|
||||
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
|
||||
|
@ -219,6 +377,9 @@ class font_patcher:
|
|||
font_complete = False
|
||||
self.args.complete = font_complete
|
||||
|
||||
if self.args.alsowindows:
|
||||
self.args.windows = 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.
|
||||
|
@ -239,7 +400,17 @@ class font_patcher:
|
|||
# ])
|
||||
|
||||
|
||||
def setup_name_backup(self):
|
||||
""" Store the original font names to be able to rename the font multiple times """
|
||||
self.original_fontname = self.sourceFont.fontname
|
||||
self.original_fullname = self.sourceFont.fullname
|
||||
self.original_familyname = self.sourceFont.familyname
|
||||
|
||||
|
||||
def setup_font_names(self):
|
||||
self.sourceFont.fontname = self.original_fontname
|
||||
self.sourceFont.fullname = self.original_fullname
|
||||
self.sourceFont.familyname = self.original_familyname
|
||||
verboseAdditionalFontNameSuffix = " " + projectNameSingular
|
||||
if self.args.windows: # attempt to shorten here on the additional name BEFORE trimming later
|
||||
additionalFontNameSuffix = " " + projectNameAbbreviation
|
||||
|
@ -285,6 +456,27 @@ class font_patcher:
|
|||
additionalFontNameSuffix += " M"
|
||||
verboseAdditionalFontNameSuffix += " Mono"
|
||||
|
||||
if FontnameParserOK and self.args.makegroups:
|
||||
use_fullname = type(self.sourceFont.fullname) == str # Usually the fullname is better to parse
|
||||
# Use fullname if it is 'equal' to the fontname
|
||||
if self.sourceFont.fullname:
|
||||
use_fullname |= self.sourceFont.fontname.lower() == FontnameTools.postscript_char_filter(self.sourceFont.fullname).lower()
|
||||
# Use fullname for any of these source fonts (that are impossible to disentangle from the fontname, we need the blanks)
|
||||
for hit in [ 'Meslo' ]:
|
||||
use_fullname |= self.sourceFont.fontname.lower().startswith(hit.lower())
|
||||
parser_name = self.sourceFont.fullname if use_fullname else self.sourceFont.fontname
|
||||
# Gohu fontnames hide the weight, but the file names are ok...
|
||||
if parser_name.startswith('Gohu'):
|
||||
parser_name = os.path.splitext(os.path.basename(self.args.font))[0]
|
||||
n = FontnameParser(parser_name)
|
||||
if not n.parse_ok:
|
||||
print("Have only minimal naming information, check resulting name. Maybe omit --makegroups option")
|
||||
n.drop_for_powerline()
|
||||
n.enable_short_families(True, "Noto")
|
||||
n.set_for_windows(self.args.windows)
|
||||
|
||||
# All the following stuff is ignored in makegroups-mode
|
||||
|
||||
# basically split the font name around the dash "-" to get the fontname and the style (e.g. Bold)
|
||||
# this does not seem very reliable so only use the style here as a fallback if the font does not
|
||||
# have an internal style defined (in sfnt_names)
|
||||
|
@ -343,8 +535,9 @@ class font_patcher:
|
|||
familyname += " Mono"
|
||||
|
||||
# Don't truncate the subfamily to keep fontname unique. MacOS treats fonts with
|
||||
# the same name as the same font, even if subFamily is different.
|
||||
fontname += '-' + subFamily
|
||||
# the same name as the same font, even if subFamily is different. Make sure to
|
||||
# keep the resulting fontname (PostScript name) valid by removing spaces.
|
||||
fontname += '-' + subFamily.replace(' ', '')
|
||||
|
||||
# rename font
|
||||
#
|
||||
|
@ -418,18 +611,28 @@ class font_patcher:
|
|||
fullname = replace_font_name(fullname, additionalFontNameReplacements2)
|
||||
fontname = replace_font_name(fontname, additionalFontNameReplacements2)
|
||||
|
||||
# replace any extra whitespace characters:
|
||||
self.sourceFont.familyname = " ".join(familyname.split())
|
||||
self.sourceFont.fullname = " ".join(fullname.split())
|
||||
self.sourceFont.fontname = " ".join(fontname.split())
|
||||
if not (FontnameParserOK and self.args.makegroups):
|
||||
# replace any extra whitespace characters:
|
||||
self.sourceFont.familyname = " ".join(familyname.split())
|
||||
self.sourceFont.fullname = " ".join(fullname.split())
|
||||
self.sourceFont.fontname = " ".join(fontname.split())
|
||||
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
else:
|
||||
fam_suffix = projectNameSingular if not self.args.windows else projectNameAbbreviation
|
||||
fam_suffix += ' Mono' if self.args.single else ''
|
||||
n.inject_suffix(verboseAdditionalFontNameSuffix, additionalFontNameSuffix, fam_suffix)
|
||||
n.rename_font(self.sourceFont)
|
||||
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
self.sourceFont.comment = projectInfo
|
||||
self.sourceFont.fontlog = projectInfo
|
||||
|
||||
|
||||
def setup_version(self):
|
||||
""" Add the Nerd Font version to the original version """
|
||||
# print("Version was {}".format(sourceFont.version))
|
||||
if self.sourceFont.version != None:
|
||||
self.sourceFont.version += ";" + projectName + " " + version
|
||||
|
@ -463,8 +666,6 @@ class font_patcher:
|
|||
# Prevent glyph encoding position conflicts between glyph sets
|
||||
if self.args.fontawesome and self.args.octicons:
|
||||
self.octiconsExactEncodingPosition = False
|
||||
if self.args.fontawesome or self.args.octicons:
|
||||
self.fontlinuxExactEncodingPosition = False
|
||||
|
||||
|
||||
def setup_patch_set(self):
|
||||
|
@ -603,7 +804,7 @@ class font_patcher:
|
|||
{'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
|
||||
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'SrcEnd': 0xFD46, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'SrcEnd': 0xE3EB, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.fontlinux, 'Name': "Font Logos (Font Linux)", 'Filename': "font-logos.ttf", 'Exact': self.fontlinuxExactEncodingPosition, 'SymStart': 0xF100, 'SymEnd': 0xF12D, 'SrcStart': 0xF300, 'SrcEnd': 0xF32D, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.fontlinux, 'Name': "Font Logos (Font Linux)", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'SrcEnd': None , 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'SrcEnd': 0xF505, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
|
||||
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': self.octiconsExactEncodingPosition, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'SrcEnd': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
|
||||
|
@ -653,7 +854,9 @@ class font_patcher:
|
|||
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
||||
#
|
||||
# 0x00-0x17f is the Latin Extended-A range
|
||||
for glyph in range(0x00, 0x17f):
|
||||
for glyph in range(0x21, 0x17f):
|
||||
if glyph in range(0x7F, 0xBF):
|
||||
continue # ignore special characters like '1/4' etc
|
||||
try:
|
||||
(_, _, xmax, _) = self.sourceFont[glyph].boundingBox()
|
||||
except TypeError:
|
||||
|
@ -665,6 +868,17 @@ class font_patcher:
|
|||
|
||||
# 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):
|
||||
|
@ -1030,6 +1244,14 @@ def main():
|
|||
check_fontforge_min_version()
|
||||
patcher = font_patcher()
|
||||
patcher.patch()
|
||||
print("\nDone with Patch Sets, generating font...\n")
|
||||
patcher.setup_font_names()
|
||||
patcher.generate()
|
||||
# This mainly helps to improve CI runtime
|
||||
if patcher.args.alsowindows:
|
||||
patcher.args.windows = True
|
||||
patcher.setup_font_names()
|
||||
patcher.generate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue