#!/usr/bin/env -S uv run --script # /// script # dependencies = ["httpx"] # /// import argparse import logging import re from pathlib import Path import httpx logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") logger = logging.getLogger(__name__) HEADERS = { "sec-ch-ua": '"Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"', "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36", } def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("url", help="Google Fonts CSS URL") parser.add_argument("-o", "--output", type=Path, default=Path.cwd(), help="Output directory") parser.add_argument("--subsets", default="latin,latin-ext", help="Comma-separated unicode subsets to include") return parser.parse_args() def filter_css_by_subsets(css_content: str, subsets: list[str]) -> str: """Filter @font-face rules to only include specified unicode subsets.""" font_faces = re.findall(r"@font-face\s*{[^}]+}", css_content, re.DOTALL) filtered_faces = [] for face in font_faces: if any(subset in face for subset in subsets): filtered_faces.append(face) return "\n".join(filtered_faces) def parse_googlefont_stylesheet(css_content: str) -> list[dict]: """Parse Google Fonts CSS and return list of font entries with metadata.""" comments = re.findall(r"/\*\s*([^*]+)\s*\*/", css_content) families = re.findall(r"font-family:\s*['\"]([^'\"]+)['\"]", css_content) styles = re.findall(r"font-style:\s*(\w+)", css_content) weights = re.findall(r"font-weight:\s*([^;]+)", css_content) displays = re.findall(r"font-display:\s*(\w+)", css_content) urls = re.findall(r"url\(([^)]+)\)", css_content) ranges = re.findall(r"unicode-range:\s*([^;]+)", css_content) fonts = [] for comment, family, style, weight, display, url, unicode_range in zip(comments, families, styles, weights, displays, urls, ranges): entry = { "subset": comment.strip().lower(), "family": family, "style": style, "weight": weight.strip(), "display": display, "url": url.strip("'\""), "unicode_range": unicode_range.strip(), } fonts.append(entry) return fonts def download_google_fonts(url: str, output_dir: Path, subsets: list[str]) -> None: client = httpx.Client(headers=HEADERS) output_dir = output_dir.resolve() output_dir.mkdir(parents=True, exist_ok=True) fonts_dir = output_dir / "fonts" fonts_dir.mkdir(parents=True, exist_ok=True) logger.info(f"Downloading from {url}") response = client.get(url) response.raise_for_status() css_content = response.text fonts = parse_googlefont_stylesheet(css_content=css_content) filtered_fonts = [] for font in fonts: if font.get("subset") in subsets: filtered_fonts.append(font) logger.info(f"Found {len(filtered_fonts)} font entries for subsets: {', '.join(subsets)}") for font in filtered_fonts: font_url = font["url"] logger.info(f"Downloading {font_url}") font_response = client.get(url=font_url) font_response.raise_for_status() font_filename = Path(font_url).name font_path = fonts_dir / font_filename font_path.write_bytes(font_response.content) logger.info(f"Saved to {font_path}") css_lines = [] for font in filtered_fonts: css_lines.append(f"/* {font['subset']} */") css_lines.append("@font-face {") css_lines.append(f" font-family: '{font['family']}';") css_lines.append(f" font-style: {font['style']};") css_lines.append(f" font-weight: {font['weight']};") css_lines.append(f" font-display: {font['display']};") font_filename = Path(font["url"]).name css_lines.append(f" src: url(fonts/{font_filename}) format('woff2');") css_lines.append(f" unicode-range: {font['unicode_range']};") css_lines.append("}") local_css_content = "\n".join(css_lines) css_file = output_dir / "fonts.css" css_file.write_text(local_css_content) logger.info(f"CSS file saved to {css_file}") def main() -> None: args = parse_args() subset_list = [s.strip() for s in args.subsets.split(",")] download_google_fonts( url=args.url, output_dir=args.output, subsets=subset_list, ) if __name__ == "__main__": main()