| 1 | #!/usr/bin/env python3 |
| 2 | """Split ShannonSans-Variable.woff2 into a Latin face and an "extended" face. |
| 3 | |
| 4 | The full variable font is ~531 KB. Nearly every page only needs Latin, so the |
| 5 | site preloads the Latin subset and lets the browser fetch the extended subset |
| 6 | only when a page actually contains a glyph in its unicode-range (latin-ext, |
| 7 | Greek, Cyrillic, Devanagari, ...). Both subsets keep the wght axis and every |
| 8 | OpenType layout feature. |
| 9 | |
| 10 | The pinned full font stays in public/brand/fonts/ as the source of truth |
| 11 | (see lib/public-auth-routes.test.ts); rerun this after replacing it: |
| 12 | |
| 13 | python3 web/scripts/subset-shannon-sans.py |
| 14 | |
| 15 | Needs fontTools with the brotli module (pip install fonttools brotli). The |
| 16 | ranges printed at the end must match the `unicode-range` declarations in |
| 17 | app/[locale]/layout.tsx. |
| 18 | """ |
| 19 | from __future__ import annotations |
| 20 | |
| 21 | from pathlib import Path |
| 22 | |
| 23 | from fontTools import subset |
| 24 | from fontTools.ttLib import TTFont |
| 25 | |
| 26 | FONTS = Path(__file__).resolve().parent.parent / "public" / "brand" / "fonts" |
| 27 | SOURCE = FONTS / "ShannonSans-Variable.woff2" |
| 28 | |
| 29 | # Google Fonts' "latin" range plus the arrows and minus/division signs the |
| 30 | # site's copy uses, so a plain English page never needs the second file. |
| 31 | LATIN = ( |
| 32 | [(0x0000, 0x00FF), (0x0131, 0x0131), (0x0152, 0x0153), (0x02BB, 0x02BC)] |
| 33 | + [(0x02C6, 0x02C6), (0x02DA, 0x02DA), (0x02DC, 0x02DC), (0x0304, 0x0304)] |
| 34 | + [(0x0308, 0x0308), (0x0329, 0x0329), (0x2000, 0x206F), (0x20AC, 0x20AC)] |
| 35 | + [(0x2122, 0x2122), (0x2190, 0x2199), (0x2212, 0x2215), (0xFEFF, 0xFEFF)] |
| 36 | + [(0xFFFD, 0xFFFD)] |
| 37 | ) |
| 38 | |
| 39 | |
| 40 | def in_ranges(cp: int, ranges: list[tuple[int, int]]) -> bool: |
| 41 | return any(lo <= cp <= hi for lo, hi in ranges) |
| 42 | |
| 43 | |
| 44 | def to_ranges(cps: list[int]) -> list[tuple[int, int]]: |
| 45 | out: list[tuple[int, int]] = [] |
| 46 | for cp in sorted(cps): |
| 47 | if out and cp == out[-1][1] + 1: |
| 48 | out[-1] = (out[-1][0], cp) |
| 49 | else: |
| 50 | out.append((cp, cp)) |
| 51 | return out |
| 52 | |
| 53 | |
| 54 | def css_range(ranges: list[tuple[int, int]]) -> str: |
| 55 | return ", ".join( |
| 56 | f"U+{lo:04X}" if lo == hi else f"U+{lo:04X}-{hi:04X}" for lo, hi in ranges |
| 57 | ) |
| 58 | |
| 59 | |
| 60 | def write_subset(unicodes: list[int], dest: Path) -> None: |
| 61 | options = subset.Options() |
| 62 | options.flavor = "woff2" |
| 63 | options.layout_features = ["*"] |
| 64 | options.name_IDs = ["*"] |
| 65 | options.name_languages = ["*"] |
| 66 | options.notdef_outline = True |
| 67 | options.glyph_names = False |
| 68 | font = TTFont(SOURCE) |
| 69 | subsetter = subset.Subsetter(options) |
| 70 | subsetter.populate(unicodes=unicodes) |
| 71 | subsetter.subset(font) |
| 72 | font.save(dest) |
| 73 | |
| 74 | |
| 75 | def main() -> None: |
| 76 | cmap = TTFont(SOURCE).getBestCmap() |
| 77 | latin = [cp for cp in cmap if in_ranges(cp, LATIN)] |
| 78 | ext = [cp for cp in cmap if not in_ranges(cp, LATIN)] |
| 79 | write_subset(latin, FONTS / "ShannonSans-Variable-latin.woff2") |
| 80 | write_subset(ext, FONTS / "ShannonSans-Variable-ext.woff2") |
| 81 | print("latin unicode-range:", css_range(LATIN)) |
| 82 | print("ext unicode-range: ", css_range(to_ranges(ext))) |
| 83 | |
| 84 | |
| 85 | if __name__ == "__main__": |
| 86 | main() |
| 87 |