Skip to content

Instantly share code, notes, and snippets.

@flodolo
Created January 5, 2025 06:54
Show Gist options
  • Save flodolo/ba0bd409bc1612945b8e436f175abc1f to your computer and use it in GitHub Desktop.
Save flodolo/ba0bd409bc1612945b8e436f175abc1f to your computer and use it in GitHub Desktop.
Pontoon: minimize Font Awesome
#! /usr/bin/env python
"""
The script looks for TTF fonts in FontAwesome folder. It doesn't use WOFF2
directly, because glyph names seem to be unreliable.
Optimized fonts are saved in `/pontoon/base/static/fonts/` as WOFF2.
The list of possible icons is done searching for `fa-*` in specific files (HTML,
JS). This is not ideal, but there is a lot of dynamic code inserting icons, so
searching just for the class syntax `fa[brs] fa-iconname` does't work.
Requirement: fonttools with woff2 support
pip install fonttools brotli
"""
from fontTools.ttLib import TTFont
from fontTools.subset import Subsetter, Options
from pathlib import Path
import argparse
import os
import re
import sys
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--source",
required=True,
help="Path to folder with Pontoon source files",
)
parser.add_argument(
"--fa",
required=True,
help="Path to Font Awesome folder",
)
args = parser.parse_args()
# Get list of files in Pontoon folder
root_folder = Path(args.source)
file_extensions = (
".css",
".js",
".html",
".tsx",
)
excluded_folders = (".git", ".venv", "venv", "node_modules")
excluded_files = "fontawesome-all.css"
files = [
file.relative_to(root_folder)
for file in root_folder.rglob("*")
if file.is_file()
and file.suffix in file_extensions
and file.name not in excluded_files
and not any(
excluded_folder in file.parts for excluded_folder in excluded_folders
)
]
# Exclude more folders, convert to absolute paths
excluded_roots = (
"docs/",
"static/",
"translate/coverage/",
"translate/dist/",
)
files = [
os.path.join(root_folder, f)
for f in files
if not str(f).startswith(excluded_roots)
]
files.sort()
# Find icons in HTML and JS searching for fa-SOMETHING
fa_icon = re.compile(r"[\s'\"]*(fa-\w[-\w]*)[\s'\"]*")
# Match all non-ASCII characters (non-7-bit)
special_characters = re.compile(r"[^\x00-\x7F]")
ignored_matches = (
# Misc false positives
"fa-arab-ir",
# Font Awesome directives
"fa-fw",
"fa-w",
"fa-2x",
"fa-lg",
"fa-sm",
"fa-w" "fa-stack",
"fa-stack-1x",
"fa-stack-2x",
# Non-glyph directive
"fa-spin",
)
icons = []
code_points = []
for file in files:
with open(file) as f:
if file.endswith(".css"):
# Check for hard-coded special characters in CSS files
for line in f:
matches = special_characters.findall(line)
for char in matches:
char_code = ord(char)
# Check if the character is in the PUA range
if (
(0xE000 <= char_code <= 0xF8FF)
or (0xF0000 <= char_code <= 0xFFFFD)
or (0x100000 <= char_code <= 0x10FFFD)
):
code_points.append(char_code)
else:
for line in f:
for match in fa_icon.findall(line):
if match not in ignored_matches:
if match not in icons:
# Drop "fa-" from the name
icons.append(match[3:])
code_points = list(set(code_points))
code_points.sort()
icons = list(set(icons))
icons.sort()
# Search for font files
pontoon_font_folder = os.path.join(
root_folder, "pontoon", "base", "static", "fonts"
)
font_folder = Path(args.fa)
font_files = [
file
for file in font_folder.rglob("fa-*.ttf")
if file.is_file() and "v4compatibility" not in file.name
]
# Icon name mapping to glyphs
mapping = {
"arrow-circle-left": "circle-arrow-left",
"arrow-circle-right": "circle-arrow-right",
"calendar-alt": "calendar-days",
"cloud-download-alt": "cloud-arrow-down",
"cloud-upload-alt": "cloud-arrow-up",
"cog": "gear",
"cogs": "gears",
"dot-circle": "circle-dot",
"exclamation-circle": "circle-exclamation",
"expand-arrows-alt": "maximize",
"pencil-alt": "pencil",
"search": "magnifying-glass",
"sign-in-alt": "right-to-bracket",
"sign-out-alt": "right-from-bracket",
"sync": "arrows-rotate",
"tasks": "list-check",
"times": "xmark",
"trash-alt": "trash-can",
}
found = []
for font_file in font_files:
print(f"\n-----\nAnalizing: {font_file.name}\n")
# Load the font
font = TTFont(font_file)
# Mapping for glyph names and Unicode
cmap = font["cmap"].getBestCmap() # Unicode-to-glyph mapping
reverse_cmap = {v: k for k, v in cmap.items()} # Glyph-to-Unicode mapping
# Select known glyph names
selected_glyphs = set()
for icon in icons:
glyph_name = mapping.get(icon, icon)
if glyph_name in font.getGlyphOrder():
selected_glyphs.add(glyph_name)
found.append(icon)
else:
print(f"Glyph not found: {glyph_name}")
# Store alternative code points for selected glyphs
alt_code_points = set()
for glyph_name in selected_glyphs:
code_point = reverse_cmap.get(glyph_name)
if code_point:
alt_code_points.add(code_point)
# Select glyphs by Unicode code points
for code_point in set(code_points + list(alt_code_points)):
glyph_name = cmap.get(code_point)
if glyph_name:
selected_glyphs.add(glyph_name)
found.append(f"0x{code_point:x}")
else:
print(f"Code point not found: 0x{code_point:x}")
# Print all selected glyphs
print("\nSelected glyphs:")
for glyph_name in selected_glyphs:
print(
f" {glyph_name} (Unicode: 0x{reverse_cmap.get(glyph_name, 'N/A'):04x})"
)
print(f"Total: {len(selected_glyphs)}")
# Subset the font
options = Options()
options.glyph_names = True
subsetter = Subsetter(options=options)
subsetter.populate(glyphs=selected_glyphs)
subsetter.subset(font)
# Save as .woff2
new_filename = f"{font_file.stem}.woff2"
font.flavor = "woff2"
font.save(os.path.join(pontoon_font_folder, new_filename))
print(f"\nGenerated new file: {new_filename}\n-----\n")
font.close()
missing = list(set(icons) - set(found))
if missing:
missing.sort()
print(f"Missing glyphs: {', '.join(missing)}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment