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
parent
ecfd57c504
commit
e77c2e3e10
@ -1,3 +1,4 @@
|
|||||||
[submodule "themes/docsy"]
|
[submodule "themes/docsy"]
|
||||||
path = themes/docsy
|
path = themes/docsy
|
||||||
url = https://github.com/google/docsy.git
|
url = https://github.com/matrix-org/docsy.git
|
||||||
|
branch = master
|
||||||
|
@ -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)
|
@ -0,0 +1 @@
|
|||||||
|
requests
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
Subproject commit b7562e9cb99c652ebb5ade75d2203db08ad81ede
|
Subproject commit 5023a2914528e012ecf3ec85a56028c00ee97dd2
|
Loading…
Reference in New Issue