Skip to content

Instantly share code, notes, and snippets.

@Arlorean
Created July 23, 2024 14:26
Show Gist options
  • Save Arlorean/a27f3a29bb7b48bedf0289a66331851b to your computer and use it in GitHub Desktop.
Save Arlorean/a27f3a29bb7b48bedf0289a66331851b to your computer and use it in GitHub Desktop.
FontsToCss: Convert a font or zip of fonts to a .css with embedded @font-face base64 data
# FONTS TO CSS
#
# Convert a font or zip of fonts to a .css with embedded @font-face base64 data.
#
# Install Python: https://www.python.org/downloads/
# NOTE: Check "Add Python to environment variables" during Windows install
# Using Command Line:
# pip install fonttools
# python convert.py <path_to_font_or_zip_of_fonts>
import base64
import os
import zipfile
import sys
from fontTools.ttLib import TTFont, woff2
def font_to_base64(font_path):
with open(font_path, "rb") as font_file:
encoded_string = base64.b64encode(font_file.read()).decode('utf-8')
return encoded_string
def get_font_properties(font_path):
font = TTFont(font_path)
name = font['name']
family_name = name.getName(1, 3, 1, 1033).toStr()
subfamily_name = name.getName(2, 3, 1, 1033).toStr()
font_weight = "normal"
font_style = "normal"
# Extract font weight
os2 = font['OS/2']
if os2.usWeightClass:
font_weight = os2.usWeightClass
# Extract font style
if "Italic" in subfamily_name or "Oblique" in subfamily_name:
font_style = "italic"
return family_name, font_weight, font_style
def convert_woff_to_ttf(font_path):
ttf_path = font_path.replace('.woff', '.ttf').replace('.woff2', '.ttf')
woff2.decompress(font_path, ttf_path)
return ttf_path
def create_css(font_files, output_css):
with open(output_css, "w") as css_file:
for font_path in font_files:
print(f"Adding @font-face for '{os.path.basename(font_path)}'")
# Convert all fonts to TTF to get consistent properties for family name, style and weight
if font_path.endswith(('.woff', '.woff2')):
font_path = convert_woff_to_ttf(font_path)
family_name, font_weight, font_style = get_font_properties(font_path)
base64_string = font_to_base64(font_path)
css_file.write(f"@font-face {{\n")
css_file.write(f" font-family: '{family_name}';\n")
css_file.write(f" src: url('data:font/ttf;charset=utf-8;base64,{base64_string}') format('truetype');\n")
css_file.write(f" font-weight: {font_weight};\n")
css_file.write(f" font-style: {font_style};\n")
css_file.write(f" font-display: swap;\n")
css_file.write(f"}}\n\n")
def is_font_file(filename):
return filename.lower().endswith(('.ttf', '.otf', '.woff', '.woff2'))
def is_zip_file(filename):
return filename.lower().endswith('.zip')
def process_zip(zip_path, output_css):
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall("temp_fonts")
font_files = []
for root, _, files in os.walk("temp_fonts"):
for filename in files:
if is_font_file(filename):
font_files.append(os.path.join(root, filename))
create_css(font_files, output_css)
def process_font(filename, output_css):
if is_font_file(filename):
create_css([filename], output_css)
if __name__ == "__main__":
if len(sys.argv) == 2:
filename = sys.argv[1]
output_css = "output.css"
if (is_zip_file(filename)):
process_zip(filename, output_css)
elif (is_font_file(filename)):
process_font(filename, output_css)
else:
print(f"'{filename}' is not a .zip file or a font file")
sys.exit(1)
else:
print("Usage: python fonts_to_css.py <path_to_zip_file>")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment