From ee0e7911d8f0bac95da710982068a54acf4ca2a6 Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Wed, 7 Dec 2022 16:12:24 -0600 Subject: [PATCH] feat: find is dead long live python --- .flake8 | 2 + Makefile | 36 ++---- README.md | 8 +- bin/get-font-files | 28 +++++ bin/patch-monolisa | 223 ++++++++++++++++++++++++++++++++++---- bin/patch-monolisa-docker | 33 ------ 6 files changed, 245 insertions(+), 85 deletions(-) create mode 100644 .flake8 create mode 100755 bin/get-font-files delete mode 100755 bin/patch-monolisa-docker diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..2bcd70e --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 88 diff --git a/Makefile b/Makefile index 56c5a20..c107c5a 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,13 @@ ARGS ?= -c -OK_TYPES := otf ttf woff woff2 -NF_SRC := $(shell find src -type f) -ML_TYPES := $(shell find ./MonoLisa \ - -mindepth 1 \ - -maxdepth 1 \ - -not -empty \ - -type d \ - -exec basename {} \;) -UNKNOWN := $(filter-out $(OK_TYPES),$(ML_TYPES)) -$(if $(UNKNOWN),$(error unknown font type in ./MonoLisa: $(UNKNOWN))) +NF_SRC := $(shell ./bin/get-font-files src) +FONT_SRC := $(shell ./bin/get-font-files MonoLisa 'otf,ttf,woff,woff2') -msg = $(call tprint,{a.bold}==>{a.end} {a.b_magenta}$(1){a.end} {a.bold}<=={a.end}) - -## patch | add nerd fonts to MonoLisa -ifdef DOCKER -.PHONY: patch -patch: $(foreach ml-type,$(ML_TYPES),patch-$(ml-type)-docker) -else -.PHONY: patch -patch: $(addprefix patch-,$(ML_TYPES)) -endif - -patch-%: ./bin/font-patcher - $(call msg, Patching MonoLisa $* Files) - @./bin/patch-monolisa $* $(ARGS) - -patch-%-docker: ./bin/font-patcher - $(call msg, Patching Monolisa $* Files w/Docker) - @./bin/patch-monolisa-docker $* $(ARGS) +## patch | apply nerd fonts patch +patch: ./bin/font-patcher + @./bin/patch-monolisa \ + $(foreach f,$(FONT_SRC),-f '$(f)') \ + $(ARGS) ## update-fonts | move fonts and update fc-cache .PHONY: update-fonts @@ -59,5 +38,6 @@ lint: clean: @rm -r patched/* +msg = $(call tprint,{a.bold}==>{a.end} {a.b_magenta}$(1){a.end} {a.bold}<=={a.end}) USAGE = {a.b_green}Update MonoLisa with Nerd Fonts! {a.end}\n\n{a.header}usage{a.end}:\n make \n -include .task.mk diff --git a/README.md b/README.md index 4edfc42..0ea7a31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MonoLisa NF -*Most* Batteries included repo to patch MonoLisa with Nerd Fonts glyphs +_Most_ Batteries included repo to patch MonoLisa with Nerd Fonts glyphs tested w/ MonoLisa v1.808 @@ -22,7 +22,7 @@ sudo pacman -S fontforge You can also download the version for your system from the releases in the fontforge [repo](https://github.com/fontforge/fontforge). -If you prefer to use `docker` rather than install `fontforge` you can just add `DOCKER=1` to calls to `make patch`. +If you prefer to use `docker` rather than install `fontforge` you can add `ARGS='--docker'` to calls to `make patch`. ## Downloading MonoLisa @@ -54,16 +54,18 @@ make By default the complete (`-c`) flag is passed to the font-patcher script to include all icons/symbols. You can change this by specifying the `ARGS` at runtime. - ```bash ARGS="-c -w" make patch ``` +See `./bin/patch-monolisa --help` and `./bin/font-patcher --help` for available `ARGS`. + You can find your patched fonts in the `patched/` directory If like me you want to place your patched fonts in a standard location on your Unix system you can move them to `~/.local/share/fonts/MonoLisa` with the `bin/update-fonts` script. Or for simplicity you can copy the fonts and update the cache with: + ```bash make update-fonts ``` diff --git a/bin/get-font-files b/bin/get-font-files new file mode 100755 index 0000000..4518ea8 --- /dev/null +++ b/bin/get-font-files @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import sys +from pathlib import Path + + +def find_files(search_path, exts=None): + return ( + [f for ext in exts for f in search_path.glob(f"**/*.{ext}")] + if exts + else [f for f in search_path.rglob("*") if f.is_file()] + ) + + +def main(): + if len(sys.argv) == 1: + print("please specify directory to search") + exit(1) + + search_path = Path(sys.argv[1]) + exts = sys.argv[2].split(",") if len(sys.argv) == 3 else None + + for f in find_files(search_path, exts): + print(f) + + +if __name__ == "__main__": + main() diff --git a/bin/patch-monolisa b/bin/patch-monolisa index c3d0f3d..d69d2a5 100755 --- a/bin/patch-monolisa +++ b/bin/patch-monolisa @@ -1,28 +1,209 @@ -#!/usr/bin/env bash -set -e +#!/usr/bin/env python3 -exts=(otf ttf woff woff2) -ext=$1 -shift -fp_args=$@ +import argparse +import itertools +import os +import shlex +import subprocess +import sys +import threading +import time +from pathlib import Path +from typing import List, Set -if [[ -z $ext ]]; then - echo "please provide an extension" - exit 1 -fi +# EXTS = ["otf", "ttf", "woff", "woff2"] -if [[ ! " ${exts[@]} " =~ " ${ext} " ]]; then - echo "$ext is not a valid extension" - echo "Please choose one of the below:" - printf '%s\n' "${exts[@]}" - exit 1 -fi -echo "Patching all fonts with ext -> $ext" -echo "Using the following arguments for font-patcher $fp_args" +class Color: + def __init__(self): + self.red = "\033[1;31m" + self.green = "\033[1;32m" + self.yellow = "\033[1;33m" + self.magenta = "\033[1;35m" + self.cyan = "\033[1;36m" + self.end = "\033[0m" -for fontfile in MonoLisa/$ext/*.$ext; do + if os.getenv("NO_COLOR"): + for attr in self.__dict__: + setattr(self, attr, "") - fontforge -script ./bin/font-patcher $fontfile --glyphdir ./src/glyphs/ -o ./patched/$ext $fp_args -done +class Spinner: + # https://raw.githubusercontent.com/Tagar/stuff/master/spinner.py + def __init__(self, message, delay=0.1): + # self.spinner = itertools.cycle(["-", "/", "|", "\\"]) + self.spinner = itertools.cycle([f"{c} " for c in "⣾⣽⣻⢿⡿⣟⣯⣷"]) + + self.delay = delay + self.busy = False + self.spinner_visible = False + sys.stdout.write(message) + + def write_next(self): + with self._screen_lock: + if not self.spinner_visible: + sys.stdout.write(next(self.spinner)) + self.spinner_visible = True + sys.stdout.flush() + + def remove_spinner(self, cleanup=False): + with self._screen_lock: + if self.spinner_visible: + # sys.stdout.write("\b") + sys.stdout.write("\b\b\b") + self.spinner_visible = False + if cleanup: + sys.stdout.write(" ") # overwrite spinner with blank + # sys.stdout.write("\r") # move to next line + sys.stdout.write("\r\033[K") + sys.stdout.flush() + + def spinner_task(self): + while self.busy: + self.write_next() + time.sleep(self.delay) + self.remove_spinner() + + def __enter__(self): + if sys.stdout.isatty(): + self._screen_lock = threading.Lock() + self.busy = True + self.thread = threading.Thread(target=self.spinner_task) + self.thread.start() + + def __exit__(self, exc_type, exc_val, exc_traceback): + if sys.stdout.isatty(): + self.busy = False + self.remove_spinner(cleanup=True) + else: + sys.stdout.write("\r") + + +def run_cmd( + command: str, fontfile: Path, verbose: bool, ignore_error: bool = False +) -> None: + """run a subcommand + Args: + command: Subcommand to be run in subprocess. + fontfile: Path to font file that will be patched + verbose: If true, print subcommand output. + """ + + p = subprocess.run( + shlex.split(command), + stdout=None if verbose else subprocess.PIPE, + stderr=None if verbose else subprocess.STDOUT, + universal_newlines=True, + ) + + if p.returncode != 0 and not ignore_error: + print() + print(p.stdout) + err_msg = ( + f"{color.red}ERROR{color.end}: patching font file " + f"{fontfile.name} see above for font-patcher output" + ) + echo(err_msg, hue="red") + sys.exit(1) + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--font-path", action="append", + type=Path, required=True) + parser.add_argument("-o", "--output", default="patched", type=Path) + parser.add_argument("-d", "--docker", action="store_true") + parser.add_argument("-v", "--verbose", action="store_true") + return parser.parse_known_args() + + +def patch_single_font( + font_path: Path, output_dir: Path, fp_args: str, verbose: bool +) -> None: + display_name = f"{font_path.parent.name}/{font_path.name}" + output_path = output_dir / font_path.parent.name.replace("MonoLisa/", "") + output_path.mkdir(exist_ok=True) + + cmd = ( + "fontforge -script " + f"./bin/font-patcher {font_path} " + "--glyphdir ./src/glyphs/ " + f"-o {output_path} " + f"{fp_args}" + ) + + if verbose: + echo(f"cmd: {cmd}") + run_cmd(cmd, font_path, verbose) + else: + with Spinner(f"{color.yellow}:::{color.end} Patching {display_name}... "): + run_cmd(cmd, font_path, verbose) + + echo(f"{display_name} patched!", hue="green") + + +def collect_files_by_dir(fontfiles: List[Path]) -> Set[Path]: + return set([f.parent for f in fontfiles]) + + +def patch_font_dir_docker( + font_dir_path: Path, output_dir: Path, fp_args: str, verbose: bool +) -> None: + font_dir_path = font_dir_path.resolve() + output_path = (output_dir / font_dir_path.name).resolve() + output_path.mkdir(exist_ok=True) + + cmd = ( + "docker run --rm " + f"-v '{font_dir_path}:/in' " + f"-v '{output_path}:/out' " + f" -u '{os.getuid()}:{os.getegid()}' " + "nerdfonts/patcher " + f"{fp_args}" + ) + + # ignoring the fact that docker exits with code 1 + if verbose: + echo(f"cmd: {cmd}") + run_cmd(cmd, font_dir_path, verbose, ignore_error=True) + else: + with Spinner( + f"{color.yellow}:::{color.end} Patching fonts in {font_dir_path.name}... " + ): + run_cmd(cmd, font_dir_path, verbose, ignore_error=True) + + echo(f"{font_dir_path.name}/ fonts patched!", hue="green") + + +def echo(msg: str, header=False, hue="cyan") -> None: + if header: + print(f"==>{color.magenta} {msg} {color.end}<==") + else: + print(f"{color.__dict__[hue]}::{color.end} {msg}") + + +def main(): + + echo("MonoLisa NerdFont Patcher", header=True) + args, fp_args = get_args() + fp_args = " ".join(fp_args) + if fp_args: + echo(f"Flags passed to font-patcher: {fp_args}") + + if args.docker: + echo("==> DOCKER MODE ENABLED") + for font_dir in collect_files_by_dir(args.font_path): + patch_font_dir_docker(font_dir, args.output, fp_args, args.verbose) + else: + for fontfile in args.font_path: + patch_single_font(Path(fontfile), args.output, + fp_args, args.verbose) + + echo("fonts are patched", hue="green") + echo("Happy typing!", hue="green") + + +if __name__ == "__main__": + + color = Color() + main() diff --git a/bin/patch-monolisa-docker b/bin/patch-monolisa-docker deleted file mode 100755 index 7edfb19..0000000 --- a/bin/patch-monolisa-docker +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -# set -e -# docker exits with code 1? - -exts=(otf ttf woff woff2) -ext=$1 -shift -fp_args=$@ - -if [[ -z $ext ]]; then - echo "please provide an extension" - exit 1 -fi - -if [[ ! " ${exts[@]} " =~ " ${ext} " ]]; then - echo "$ext is not a valid extension" - echo "Please choose one of the below:" - printf '%s\n' "${exts[@]}" - exit 1 -fi - -echo "Patching all fonts with ext -> $ext" -echo "Using the following arguments for font-patcher $fp_args" - -docker run \ - --rm \ - -v "$(pwd)/MonoLisa/$ext:/in" \ - -v "$(pwd)/patched/$ext:/out" \ - -u "$(id -u):$(id -g)" \ - nerdfonts/patcher \ - $fp_arg - -exit 0