Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CSS fonts to cover every symbol used in a deck name so far #11711

Merged
merged 2 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions maintenance/fonts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import os
from itertools import chain
from os.path import basename

Check warning on line 3 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L1-L3

Added lines #L1 - L3 were not covered by tests

from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode
from fontTools import subset

Check warning on line 7 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L5-L7

Added lines #L5 - L7 were not covered by tests

from decksite.database import db

Check warning on line 9 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L9

Added line #L9 was not covered by tests


def ad_hoc() -> None:
print('Subsetting fonts')

Check warning on line 13 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L12-L13

Added lines #L12 - L13 were not covered by tests
# Some symbols we use outside of deck names
base_chars = {'①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑯', 'Ⓣ', '⇅', '⊕', '⸺', '▪', '🐞', '🚫', '🏆', '📰', '💻', '▾', '△', '🛈', '✅', '☐', '☑'}

Check warning on line 15 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L15

Added line #L15 was not covered by tests
# And all the non-latin1 chars in deck names
all_chars = base_chars | deck_name_chars()
print('Looking for', ''.join(all_chars))
remaining_chars = all_chars.copy()
map: dict[str, list[str]] = {}

Check warning on line 20 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L17-L20

Added lines #L17 - L20 were not covered by tests
for path, is_base_font in get_font_paths().items():
font = TTFont(path, 0, allowVID=0, ignoreDecompileErrors=True, fontNumber=-1)
found_chars = find_chars(font, all_chars)

Check warning on line 23 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L22-L23

Added lines #L22 - L23 were not covered by tests
for c in found_chars:
map[c] = map.get(c, []) + [path]
needed_found_chars = found_chars & remaining_chars
remaining_chars -= needed_found_chars

Check warning on line 27 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L25-L27

Added lines #L25 - L27 were not covered by tests
if found_chars:
print(f'Found {len(found_chars)} chars ({len(needed_found_chars)} needed): {found_chars} in {path} ({len(remaining_chars)} remaining)')

Check warning on line 29 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L29

Added line #L29 was not covered by tests
if needed_found_chars and not is_base_font:
subset_font(path, needed_found_chars)

Check warning on line 31 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L31

Added line #L31 was not covered by tests
if remaining_chars:
print('Could not find all chars:', remaining_chars)

Check warning on line 33 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L33

Added line #L33 was not covered by tests
for c, paths in map.items():
if len(paths) > 1:
print(f'Char {c} found in multiple fonts: {paths}')
print("""

Check warning on line 37 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L36-L37

Added lines #L36 - L37 were not covered by tests
Done. Now you have a bunch of woff2 files in the current directory. You need to convert them to CSS-friendly base64
strings and put them in the CSS file. You can do this with https://hellogreg.github.io/woff2base/
If you end up with a font we've never used before you need to change all the lists of fonts in the CSS file to
include it. Funnily enough merging the fonts in something like FontLab actually increases their size by 20KB.
""")

def deck_name_chars() -> set[str]:
sql = 'SELECT name FROM deck WHERE name <> CONVERT(name USING latin2)'
names = db().values(sql)
return set(''.join(names))

Check warning on line 47 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L44-L47

Added lines #L44 - L47 were not covered by tests

# This is just a giant hack because there's 3G+ of fonts we want to look in (!) so I don't want to add them to the repo.
def get_font_paths() -> dict[str, bool]:
paths = {

Check warning on line 51 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L50-L51

Added lines #L50 - L51 were not covered by tests
'/Users/bakert/Downloads/main-text.ttf': True,
'/Users/bakert/Downloads/Noto_Emoji/static/NotoEmoji-Regular.ttf': False,
'/Users/bakert/Downloads/symbola/Symbola.ttf': False,
# I got the TTF for this from https://github.com/indigofeather/fonts/tree/master - it's not in the noto-cjk repo
'/Users/bakert/Downloads/NotoSansCJKtc-Regular.ttf': False,
}
paths.update({path: False for path in find_ttfs('/Users/bakert/notofonts.github.io/')})
return paths

Check warning on line 59 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L59

Added line #L59 was not covered by tests

def find_ttfs(path: str) -> list[str]:
ttf_files = []

Check warning on line 62 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L61-L62

Added lines #L61 - L62 were not covered by tests
for root, _dirs, files in os.walk(path):
for file in files:
if file.endswith('.ttf') and 'Regular' in file and 'Serif' not in file:
ttf_files.append(os.path.join(root, file))
return ttf_files

Check warning on line 67 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L66-L67

Added lines #L66 - L67 were not covered by tests

def find_chars(font: TTFont, to_find: set[str]) -> set[str]:

Check warning on line 69 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L69

Added line #L69 was not covered by tests
chars = chain.from_iterable([y + (Unicode[y[0]],) for y in x.cmap.items()] for x in font['cmap'].tables)
points = [char[0] for char in chars]
return {c for c in to_find if ord(c) in points}

def subset_font(path: str, chars: set[str]) -> str:
text = ','.join(chars)
new_path = f'{basename(path)}.subset.woff2'
args = [

Check warning on line 77 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L74-L77

Added lines #L74 - L77 were not covered by tests
path,
f'--text={text}',
'--no-layout-closure',
f'--output-file={new_path}',
'--flavor=woff2',
]
subset.main(args)
return new_path

Check warning on line 85 in maintenance/fonts.py

View check run for this annotation

Codecov / codecov/patch

maintenance/fonts.py#L84-L85

Added lines #L84 - L85 were not covered by tests
Loading