|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
# This script takes a google fonts CSS URL, downloads the referenced fonts locally
|
|
|
|
# and spits out a modified CSS file which now points to those local fonts.
|
|
|
|
#
|
|
|
|
# Purely for the purposes of converting a website to use local font files instead of
|
|
|
|
# making requests to Google Fonts.
|
|
|
|
|
|
|
|
import re
|
|
|
|
import requests
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# Pull the font filename to process and output directory to point at
|
|
|
|
if len(sys.argv) < 4:
|
|
|
|
print(f"""
|
|
|
|
Error: Not enough arguments.
|
|
|
|
|
|
|
|
Usage: {sys.argv[0]} google_fonts_url font_directory css_font_path
|
|
|
|
* google fonts url: A URL leading to a google font css file (i.e https://fonts.googleapis.com/css?family=Inter)
|
|
|
|
* font directory: The location that font files will be downloaded to (i.e ../../fonts)
|
|
|
|
* font path: The directory the resulting CSS will be pointing to, relative to site root (i.e /unstable/css/fonts).
|
|
|
|
This is where the browser will look for fonts.
|
|
|
|
""")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
google_fonts_url = sys.argv[1]
|
|
|
|
font_output_dir = sys.argv[2]
|
|
|
|
css_font_path = sys.argv[3]
|
|
|
|
|
|
|
|
# Get font name
|
|
|
|
if google_fonts_url.count(":") > 1:
|
|
|
|
# Account for font weights specified in the URL
|
|
|
|
# (i.e https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i)
|
|
|
|
url_match = re.match(r".*family=(.*):", google_fonts_url)
|
|
|
|
else:
|
|
|
|
url_match = re.match(r".*family=(.*)", google_fonts_url)
|
|
|
|
|
|
|
|
if not url_match:
|
|
|
|
print("Unable to extract font name, invalid google fonts URL:", google_fonts_url)
|
|
|
|
print("URL should look something like: https://fonts.googleapis.com/css?family=Inter...")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
font_name = url_match.group(1)
|
|
|
|
|
|
|
|
# Ensure font paths end with a trailing slash
|
|
|
|
if not font_output_dir.endswith("/"):
|
|
|
|
font_output_dir += "/"
|
|
|
|
if not css_font_path.endswith("/"):
|
|
|
|
css_font_path += "/"
|
|
|
|
|
|
|
|
# Download the css file and split by newline
|
|
|
|
resp = requests.get(
|
|
|
|
google_fonts_url,
|
|
|
|
# We need to set a believable user-agent, else google fonts won't give us
|
|
|
|
# all of the font weights we requested for some reason
|
|
|
|
headers={
|
|
|
|
"User-Agent": "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
if resp.status_code != 200:
|
|
|
|
print("Failed to download:", google_fonts_url, resp.status_code)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
original_contents = resp.text.split("\n")
|
|
|
|
|
|
|
|
# Download all referenced font files and write out new font file
|
|
|
|
new_css_file_lines = []
|
|
|
|
|
|
|
|
# Store metadata for helping give friendly names to each font file
|
|
|
|
font_lang = None
|
|
|
|
font_family = None
|
|
|
|
font_style = None
|
|
|
|
for line in original_contents:
|
|
|
|
# Check if this line contains a font URL
|
|
|
|
match = re.match(r".*url\((.*)\) format.*", line)
|
|
|
|
if match:
|
|
|
|
# Download the font file
|
|
|
|
font_url = match.group(1)
|
|
|
|
print("Downloading font:", font_url)
|
|
|
|
|
|
|
|
resp = requests.get(font_url)
|
|
|
|
if resp.status_code == 200:
|
|
|
|
# Save the font file
|
|
|
|
filename = "%s-%s-%s.woff2" % (
|
|
|
|
font_family, font_lang, font_style
|
|
|
|
)
|
|
|
|
font_filepath = font_output_dir + filename
|
|
|
|
with open(font_filepath, "wb") as f:
|
|
|
|
print("Writing font file:", font_filepath)
|
|
|
|
f.write(resp.content)
|
|
|
|
|
|
|
|
# Replace google URL with local URL and allow the browser to load the
|
|
|
|
# local font if it exists.
|
|
|
|
line = re.sub(r"url\(.+?\)", f"local('{font_family}'), url({css_font_path + filename})", line)
|
|
|
|
else:
|
|
|
|
print("Warning: failed to download font file:", font_url)
|
|
|
|
|
|
|
|
# Check for font metadata. If there is some, we'll note it down and use it to help
|
|
|
|
# name font files when we write them out.
|
|
|
|
# Makes for nicer font filenames than fvQtMwCp50KnMw2boKod... etc.
|
|
|
|
font_lang_match = re.match(r"^/\* (.+) \*/$", line)
|
|
|
|
if font_lang_match:
|
|
|
|
font_lang = font_lang_match.group(1)
|
|
|
|
font_family_match = re.match(r".*font-family: '(.+)';$", line)
|
|
|
|
if font_family_match:
|
|
|
|
font_family = font_family_match.group(1)
|
|
|
|
font_style_match = re.match(r".*font-style: (.+);$", line)
|
|
|
|
if font_style_match:
|
|
|
|
font_style = font_style_match.group(1)
|
|
|
|
|
|
|
|
# Append the potentially modified line to the new css file
|
|
|
|
new_css_file_lines.append(line)
|
|
|
|
|
|
|
|
# Write out the new font css file
|
|
|
|
with open(font_name + ".css", "w") as f:
|
|
|
|
new_css_file_contents = "\n".join(new_css_file_lines)
|
|
|
|
f.write(new_css_file_contents)
|