#!/usr/bin/env python3 import re from pathlib import Path def formula_name_to_class(formula_name: str) -> str: """Convert formula name (e.g. 'acrylic-hand', 'graham_hand') to valid Ruby PascalCase.""" # Split on hyphens and underscores, capitalize each segment, join (no separators) parts = re.split(r"[-_]+", formula_name) return "".join(p.capitalize() for p in parts if p) class HomebrewFormulaGenerator: def __init__(self, fonts_dir): self.fonts_dir = Path(fonts_dir) self.formula_dir = self.fonts_dir.parent / "Formula" self.formula_dir.mkdir(exist_ok=True) self.web_extensions = (".woff", ".woff2", ".eot", ".svg") def _font_has_files(self, font_dir: Path) -> tuple[bool, bool, bool, bool]: """Return (has_ttf, has_otf, has_web, has_other) for the font folder.""" has_ttf = any((font_dir / "ttf").glob("*.ttf")) has_otf = any((font_dir / "otf").glob("*.otf")) web_dir = font_dir / "web" has_web = any( f.suffix.lower() in self.web_extensions for f in web_dir.glob("*") if f.is_file() ) other_dir = font_dir / "other_files" has_other = any(other_dir.iterdir()) if other_dir.exists() else False return (has_ttf, has_otf, has_web, has_other) def _generate_test_block( self, formula_name: str, has_ttf: bool, has_otf: bool, has_web: bool, has_other: bool ) -> str: """Generate test block that asserts at least one install path has files for this formula.""" assertions = [] if has_ttf: assertions.append('assert (share/"fonts/truetype").glob("*.ttf").any?, "No TTF fonts installed"') if has_otf: assertions.append('assert (share/"fonts/opentype").glob("*.otf").any?, "No OTF fonts installed"') if has_web: assertions.append( 'assert (share/"fonts/webfonts").glob("*").any?, "No web fonts installed"' ) if has_other: assertions.append( f'assert_predicate share/"{formula_name}", :directory?, "Other files dir missing"' ) if not assertions: assertions.append( f'assert_predicate share/"{formula_name}", :directory?, "Formula share dir missing"' ) return "\n ".join(assertions) def generate_formula_content( self, font_name: str, formula_name: str, has_ttf: bool, has_otf: bool, has_web: bool, has_other: bool, ) -> str: """Generate the Ruby formula content for a font.""" class_name = formula_name_to_class(formula_name) extensions = [] if has_ttf: extensions.append("TTF") if has_otf: extensions.append("OTF") if has_web: extensions.append("web") ext_comment = " ".join(extensions) if extensions else "other_files only" test_block = self._generate_test_block( formula_name, has_ttf, has_otf, has_web, has_other ) return f"""# typed: false # frozen_string_literal: true # This file was generated by the font folder cleanup script # Do not edit this file directly # Installs: {ext_comment} class Font{class_name} < Formula desc "Font: {formula_name}" homepage "http://clancy.genet-godzilla.ts.net:8085/Fonts/homebrew-fonts" url "http://clancy.genet-godzilla.ts.net:8085/Fonts/homebrew-fonts/archive/main.tar.gz" version "1.0.0" def install # Create font directories (share/"fonts").mkpath (share/"fonts/truetype").mkpath (share/"fonts/opentype").mkpath (share/"fonts/webfonts").mkpath # Install TTF fonts Dir.glob("font_files/{font_name}/ttf/*.ttf").each do |font| system "cp", font, share/"fonts/truetype" end # Install OTF fonts Dir.glob("font_files/{font_name}/otf/*.otf").each do |font| system "cp", font, share/"fonts/opentype" end # Install web fonts Dir.glob("font_files/{font_name}/web/*.{{woff,woff2,eot,svg}}").each do |font| system "cp", font, share/"fonts/webfonts" end # Install documentation and other files (share/"{formula_name}").mkpath Dir.glob("font_files/{font_name}/other_files/*").each do |file| system "cp", "-r", file, share/"{formula_name}" end end def post_install user_fonts = Pathname.new(File.expand_path("~/Library/Fonts")) user_fonts.mkpath Dir.glob(share/"fonts/truetype/*.ttf").each do |f| system "cp", "-f", f, user_fonts.to_s end Dir.glob(share/"fonts/opentype/*.otf").each do |f| system "cp", "-f", f, user_fonts.to_s end end def caveats <<~EOS Fonts have been copied to ~/Library/Fonts/ and should appear in Font Book. Web fonts and other files are available in: #{{share}}/fonts/webfonts #{{share}}/{formula_name} EOS end test do {test_block} end end """ def generate_formulas(self): """Generate Homebrew formulas for all font folders.""" for font_dir in sorted(self.fonts_dir.glob("font-*")): if not font_dir.is_dir(): continue font_name = font_dir.name formula_name = font_name.replace("font-", "", 1) has_ttf, has_otf, has_web, has_other = self._font_has_files(font_dir) formula_content = self.generate_formula_content( font_name, formula_name, has_ttf, has_otf, has_web, has_other ) formula_path = self.formula_dir / f"font-{formula_name}.rb" formula_path.write_text(formula_content) print(f"Generated formula for font-{formula_name}") def main(): # Get the absolute path to the font_files directory script_dir = Path(__file__).parent fonts_dir = script_dir.parent / 'font_files' if not fonts_dir.exists(): print(f"Error: Font directory not found: {fonts_dir}") return generator = HomebrewFormulaGenerator(fonts_dir) generator.generate_formulas() if __name__ == "__main__": main()