Opt for serving new spec JS/CSS dependencies locally instead of downloading from CDNs (#3036)

This PR does two things:

* Updates the git submodule for the new spec hugo theme ([google/docsy](https://github.com/google/docsy)) to our fork ([matrix-org/docsy](https://github.com/matrix-org/docsy)) which has a couple changes to load JS from local sources instead of remote, as well as allowing sites to override the URL that font CSS files are loaded from. Note that my definition of "font CSS" files here as CSS files that contain settings and point to locations of where font files (`.woff2`) are located.
* Adds a script (and the files generated as a result of running that script) that can take a google fonts URL, download the fonts it points to and spit out those fonts as well as a font CSS file pointing to them for local distribution. We then use the resulting font CSS file in our project variables.

This brings the benefit of not serving files from a CDN which can track users across the web, as well as inadvertently pinning docsy to a specific commit. The downside is that we need to remember to update [matrix-org/docsy](https://github.com/matrix-org/docsy) when needed (and apply the patches on top, though they're quite small).
pull/977/head
Andrew Morgan 3 years ago committed by Richard van der Hoff
parent ecfd57c504
commit e77c2e3e10

3
.gitmodules vendored

@ -1,3 +1,4 @@
[submodule "themes/docsy"]
path = themes/docsy
url = https://github.com/google/docsy.git
url = https://github.com/matrix-org/docsy.git
branch = master

@ -33,5 +33,9 @@ $warning-background: #FFE0E0;
$table-row-alternate: $secondary-lightest-background;
$table-row-default: $secondary-lighter-background;
/*
Opt to serve fonts locally by overriding web-font-path to be a non-google fonts URL.
This is only possible with our modified docsy theme: https://github.com/matrix-org/docsy
*/
$web-font-path: "../css/fonts/Inter.css";
$google_font_name: "Inter";
$google_font_family: "Inter:300,300i,400,400i,700,700i";

@ -0,0 +1,168 @@
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-cyrillic-ext-normal-300.woff2);
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-cyrillic-normal-300.woff2);
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-greek-ext-normal-300.woff2);
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-greek-normal-300.woff2);
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-vietnamese-normal-300.woff2);
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-latin-ext-normal-300.woff2);
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url(../../fonts/Inter-latin-normal-300.woff2);
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-cyrillic-ext-normal-400.woff2);
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-cyrillic-normal-400.woff2);
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-greek-ext-normal-400.woff2);
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-greek-normal-400.woff2);
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-vietnamese-normal-400.woff2);
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-latin-ext-normal-400.woff2);
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url(../../fonts/Inter-latin-normal-400.woff2);
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-cyrillic-ext-normal-700.woff2);
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-cyrillic-normal-700.woff2);
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-greek-ext-normal-700.woff2);
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-greek-normal-700.woff2);
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-vietnamese-normal-700.woff2);
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-latin-ext-normal-700.woff2);
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url(../../fonts/Inter-latin-normal-700.woff2);
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@ -0,0 +1,30 @@
# Fonts
## Inter.css
`Inter.css` is a local copy of
https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i, modified to pull
font files (`.woff2`) from local sources. It was created
using `download_google_fonts_css.py`.
## download_google_fonts_css.py
`download_google_fonts_css.py` is a script that takes a google fonts CSS URL, downloads
the file and linked fonts, then saves the fonts locally along with a modified CSS file to
load them. Example call:
```sh
python3 download_google_fonts_css.py \
"https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i" \
../../fonts \
../../fonts
```
Which would pop out a `Inter.css` file that should be `@import url("Inter.css")`d
somewhere in the site's SCSS (currently in
[/assets-hugo/scss/_variables_project.scss](/assets-hugo/scss/_variables_project.scss)).
Re-running the script and committing any new files is only necessary when a desired
font updates (not very often), or we want to change the font we're using. In that case,
remove the existing font files at `/static/fonts/*.woff2` and re-run the script with a
different URL.

@ -0,0 +1,134 @@
#!/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
font_weight = 0
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-%d.woff2" % (
font_family, font_lang, font_style, font_weight
)
font_filepath = font_output_dir + filename
with open(font_filepath, "w") as f:
print("Writing font file:", font_filepath)
f.write(resp.text)
# Replace google URL with local URL
line = re.sub(r"url\(.+\)", f"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)
font_weight_match = re.match(r".*font-weight: (.+);$", line)
if font_weight_match:
font_weight = int(font_weight_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)

@ -1 +1 @@
Subproject commit b7562e9cb99c652ebb5ade75d2203db08ad81ede
Subproject commit 5023a2914528e012ecf3ec85a56028c00ee97dd2
Loading…
Cancel
Save