Created
July 23, 2024 14:26
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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