refactor: Subset font to include only needed characters in SVG logo

This commit is contained in:
Paul Gauthier (aider) 2025-03-20 11:07:51 -07:00
parent de5ab102b7
commit 1b0ba71ef0

View file

@ -1,13 +1,57 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Script to generate an SVG logo for Aider with embedded font. Script to generate an SVG logo for Aider with embedded font.
Reads the Glass_TTY_VT220.ttf font and creates an SVG with the word "aider" Reads the Glass_TTY_VT220.ttf font, subsets it to only include the letters needed,
in terminal green (#14b014) on a transparent background. and creates an SVG with the word "aider" in terminal green (#14b014) on a transparent background.
""" """
import argparse import argparse
import base64 import base64
import os import os
import tempfile
from fontTools.subset import main as subset_main
from fontTools.ttLib import TTFont
def subset_font(font_path, text):
"""
Create a subset of the font containing only the characters needed for the text.
Args:
font_path (str): Path to the TTF font file
text (str): Text for which to extract characters
Returns:
bytes: The subsetted font data
"""
# Create a temporary file to store the subset font
with tempfile.NamedTemporaryFile(suffix=".ttf", delete=False) as tmp_file:
tmp_path = tmp_file.name
# Get unique characters from the text
unique_chars = set(text.lower() + text.upper())
# Create the subsetting command
subset_args = [
font_path,
"--output-file=" + tmp_path,
"--unicodes=" + ",".join([f"U+{ord(c):04X}" for c in unique_chars]),
"--name-IDs=*", # Keep all name records
"--recalc-bounds",
"--drop-tables=", # Don't drop any tables by default
]
# Run the subsetting
subset_main(subset_args)
# Read the subsetted font
with open(tmp_path, "rb") as f:
font_data = f.read()
# Clean up the temporary file
os.unlink(tmp_path)
return font_data
def generate_svg_with_embedded_font(font_path, text="aider", color="#14b014", output_path=None): def generate_svg_with_embedded_font(font_path, text="aider", color="#14b014", output_path=None):
@ -23,10 +67,10 @@ def generate_svg_with_embedded_font(font_path, text="aider", color="#14b014", ou
Returns: Returns:
str: The SVG content str: The SVG content
""" """
# Read the font file and encode it as base64 # Subset the font to only include the needed characters
with open(font_path, "rb") as f: font_data = subset_font(font_path, text)
font_data = f.read()
# Encode the font data as base64
font_base64 = base64.b64encode(font_data).decode("utf-8") font_base64 = base64.b64encode(font_data).decode("utf-8")
# Calculate SVG dimensions based on text length # Calculate SVG dimensions based on text length
@ -93,6 +137,11 @@ def main():
default="aider/website/assets/logo.svg", default="aider/website/assets/logo.svg",
help="Path to save the SVG file", help="Path to save the SVG file",
) )
parser.add_argument(
"--verbose",
action="store_true",
help="Print additional information about font subsetting"
)
args = parser.parse_args() args = parser.parse_args()
@ -108,10 +157,20 @@ def main():
os.makedirs(output_dir) os.makedirs(output_dir)
# Generate the SVG # Generate the SVG
generate_svg_with_embedded_font( if args.verbose:
print(f"Subsetting font {args.font} to include only characters for: {args.text}")
svg = generate_svg_with_embedded_font(
args.font, text=args.text, color=args.color, output_path=args.output args.font, text=args.text, color=args.color, output_path=args.output
) )
if args.verbose and args.output:
# Calculate size savings
original_size = os.path.getsize(args.font)
output_size = len(svg.encode('utf-8'))
print(f"Original font size: {original_size/1024:.2f} KB")
print(f"Output SVG size: {output_size/1024:.2f} KB")
if __name__ == "__main__": if __name__ == "__main__":
main() main()