from os import environ, path, remove import platform import shutil import subprocess from urllib.request import Request, urlopen from zipfile import ZipFile from fontTools.ttLib import TTFont from glyphsLib import GSFont def is_ci(): ci_envs = [ "JENKINS_HOME", "TRAVIS", "CIRCLECI", "GITHUB_ACTIONS", "GITLAB_CI", "TF_BUILD", ] for env in ci_envs: if environ.get(env): return True return False def run(command, extra_args=None, log=not is_ci()): """ Run a command line interface (CLI) command. """ if extra_args is None: extra_args = [] if isinstance(command, str): command = command.split() subprocess.run(command + extra_args, stdout=subprocess.DEVNULL if not log else None) def set_font_name(font: TTFont, name: str, id: int): font["name"].setName(name, nameID=id, platformID=1, platEncID=0, langID=0x0) font["name"].setName(name, nameID=id, platformID=3, platEncID=1, langID=0x409) def get_font_name(font: TTFont, id: int) -> str: return ( font["name"] .getName(nameID=id, platformID=3, platEncID=1, langID=0x409) .__str__() ) def del_font_name(font: TTFont, id: int): font["name"].removeNames(nameID=id) def joinPaths(*args: list[str]) -> str: return "/".join(args) def get_font_forge_bin(): WIN_FONTFORGE_PATH = "C:/Program Files (x86)/FontForgeBuilds/bin/fontforge.exe" MAC_FONTFORGE_PATH = ( "/Applications/FontForge.app/Contents/Resources/opt/local/bin/fontforge" ) LINUX_FONTFORGE_PATH = "/usr/bin/fontforge" system_name = platform.uname()[0] result = "" if "Darwin" in system_name: result = MAC_FONTFORGE_PATH elif "Windows" in system_name: result = WIN_FONTFORGE_PATH else: result = LINUX_FONTFORGE_PATH if not path.exists(result): result = shutil.which("fontforge") return result def parse_github_mirror(github_mirror: str) -> str: github = environ.get("GITHUB") # custom github mirror, for CN users if not github: github = github_mirror return f"https://{github}" def download_zip_and_extract( name: str, url: str, zip_path: str, output_dir: str, remove_zip: bool = False ) -> bool: try: if not path.exists(zip_path): try: print(f"{name} does not exist, download from {url}") user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" req = Request(url, headers={"User-Agent": user_agent}) with urlopen(req) as response, open(zip_path, "wb") as out_file: total_size = int(response.getheader("Content-Length").strip()) downloaded_size = 0 block_size = 8192 while True: buffer = response.read(block_size) if not buffer: break out_file.write(buffer) if not is_ci(): downloaded_size += len(buffer) percent_downloaded = (downloaded_size / total_size) * 100 print( f"Downloading {name}: [{percent_downloaded:.2f}%] {downloaded_size} / {total_size}", end="\r", ) except Exception as e: print( f"\nFail to download {name}. Please check your internet connection or download it manually from {url}, then put downloaded zip into project's root and run this script again. \n Error: {e}" ) return False with ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall(output_dir) if remove_zip: remove(zip_path) return True except Exception as e: print(f"Download zip and extract failed, error: {e}") return False def check_font_patcher( version: str, github_mirror: str = "github.com", target_dir: str = "FontPatcher" ) -> bool: bin_path = f"{target_dir}/font-patcher" if path.exists(target_dir): with open(bin_path, "r", encoding="utf-8") as f: if f"# Nerd Fonts Version: {version}" in f.read(): return True else: print("FontPatcher version not match, delete it") shutil.rmtree("FontPatcher", ignore_errors=True) zip_path = "FontPatcher.zip" url = f"{parse_github_mirror(github_mirror)}/ryanoasis/nerd-fonts/releases/download/v{version}/{zip_path}" if not download_zip_and_extract( name="Nerd Font Patcher", url=url, zip_path=zip_path, output_dir=target_dir ): return False with open(bin_path, "r", encoding="utf-8") as f: if f"# Nerd Fonts Version: {version}" in f.read(): return True print(f"FontPatcher version is not {version}, please download it from {url}") return False def download_cn_base_font( tag: str, zip_path: str, target_dir: str, github_mirror: str = "github.com" ) -> bool: url = f"{parse_github_mirror(github_mirror)}/subframe7536/maple-font/releases/download/{tag}/{zip_path}" return download_zip_and_extract( name=f"{'Static' if 'static' in zip_path else 'Variable'} CN Base Font", url=url, zip_path=zip_path, output_dir=target_dir, ) def match_unicode_names(file_path: str) -> dict[str, str]: font = GSFont(file_path) result = {} for glyph in font.glyphs: glyph_name = glyph.name unicode_values = glyph.unicode if glyph_name and unicode_values: unicode_str = f"uni{''.join(unicode_values).upper().zfill(4)}" result[unicode_str]=glyph_name return result