#!/usr/bin/env bash # # scripts/subset-fonts.sh — Plan 01-12 Wave 1 Task 1. # # One-off subsetting recipe for the Mokosh design-integration font bundle: # # Lora variable (normal + italic, axis wght 400-700) — display family per # R2 designer reply 2026-05-19 (substitutes Newsreader for Cyrillic # coverage; Cyreal foundry, OFL-1.1) # IBM Plex Sans (Regular, Medium, SemiBold, Bold) — UI body family # IBM Plex Mono (Regular, Medium) — diagnostic / timer family # # All faces are subsetted to Latin (U+0020-007E + U+00A0-00FF) + Cyrillic # basic (U+0400-045F + supplemental code points) per RESEARCH §1 + §6. # Total bundled artifact target: ~250-300 KB per R2 substitution adjustment. # # Upstream sources: # Lora-Cyrillic: https://github.com/cyrealtype/Lora-Cyrillic (main branch) # IBM Plex: https://github.com/IBM/plex (master branch) # # Output: src/shared/fonts/*.woff2 (7 or 8 files depending on Lora italic # upstream layout — A5 in RESEARCH Assumptions Log). # # Re-run trigger: when the upstream Lora-Cyrillic or IBM/plex releases # bump a major version with bug fixes; or when the UNICODES range needs # expansion (Cyrillic supplement, extra punctuation). The committed # WOFF2 files are the source of truth — running this script overwrites # them only on intentional regeneration. # # References: # - fontTools pyftsubset: # https://fonttools.readthedocs.io/en/latest/subset/index.html # - WOFF2 (RFC 8081): https://www.rfc-editor.org/rfc/rfc8081 # - OFL-1.1 best practices: https://scripts.sil.org/OFL_web # # Google bash style applies; see https://google.github.io/styleguide/shellguide.html set -euo pipefail readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" readonly FONTS_OUT_DIR="${REPO_ROOT}/src/shared/fonts" # Subset coverage per RESEARCH §1 + §6: # Latin core U+0020-007E # Latin supplement U+00A0-00FF (covers Cyrillic context: nbsp, ©, ®, ™ etc.) # Latin extended bits U+0131 (dotless i), U+0152-0153 (Œœ), U+0301 (combining acute) # Cyrillic basic U+0400-045F (full RU + UA basic alphabet) # Cyrillic supplement U+0490-0491 (Ґґ), U+04B0-04B1 (Ұұ — Kazakh) # Numero sign U+2116 (№ — common Russian punctuation) readonly UNICODES='U+0020-007E,U+00A0-00FF,U+0131,U+0152-0153,U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116' # Common pyftsubset flags shared across faces. readonly -a PYFTSUBSET_FLAGS=( "--unicodes=${UNICODES}" '--flavor=woff2' "--layout-features=*" '--no-hinting' '--desubroutinize' '--name-IDs=*' '--name-legacy' '--name-languages=*' ) # subset_face subset_face() { local -r input_ttf="$1" local -r output_woff2="$2" if [[ ! -f "${input_ttf}" ]]; then echo "ERROR: input TTF not found: ${input_ttf}" >&2 return 1 fi pyftsubset "${input_ttf}" \ "${PYFTSUBSET_FLAGS[@]}" \ "--output-file=${output_woff2}" local -r bytes="$(stat -c%s "${output_woff2}")" echo " subsetted: ${output_woff2} (${bytes} bytes)" } main() { local -r scratch="${1:-}" if [[ -z "${scratch}" || ! -d "${scratch}" ]]; then cat >&2 < must contain the upstream TTFs: Lora-VariableFont_wght.ttf Lora-Italic-VariableFont_wght.ttf IBMPlexSans-Regular.ttf, -Medium.ttf, -SemiBold.ttf, -Bold.ttf IBMPlexMono-Regular.ttf, -Medium.ttf Fetch recipe: SCRATCH=\$(mktemp -d) curl -L -o "\${SCRATCH}/Lora-VariableFont_wght.ttf" \\ "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/variable/Lora%5Bwght%5D.ttf" curl -L -o "\${SCRATCH}/Lora-Italic-VariableFont_wght.ttf" \\ "https://raw.githubusercontent.com/cyrealtype/Lora-Cyrillic/main/fonts/variable/Lora-Italic%5Bwght%5D.ttf" for v in Regular Medium SemiBold Bold; do curl -L -o "\${SCRATCH}/IBMPlexSans-\${v}.ttf" \\ "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-sans/fonts/complete/ttf/IBMPlexSans-\${v}.ttf" done for v in Regular Medium; do curl -L -o "\${SCRATCH}/IBMPlexMono-\${v}.ttf" \\ "https://raw.githubusercontent.com/IBM/plex/master/packages/plex-mono/fonts/complete/ttf/IBMPlexMono-\${v}.ttf" done EOF return 2 fi mkdir -p "${FONTS_OUT_DIR}" echo "==> Subsetting Lora (variable, Cyreal foundry; R2 substitute for Newsreader)" subset_face "${scratch}/Lora-VariableFont_wght.ttf" "${FONTS_OUT_DIR}/Lora-VariableFont.woff2" subset_face "${scratch}/Lora-Italic-VariableFont_wght.ttf" "${FONTS_OUT_DIR}/Lora-Italic-VariableFont.woff2" echo "==> Subsetting IBM Plex Sans (4 weights)" local variant for variant in Regular Medium SemiBold Bold; do subset_face "${scratch}/IBMPlexSans-${variant}.ttf" "${FONTS_OUT_DIR}/IBMPlexSans-${variant}.woff2" done echo "==> Subsetting IBM Plex Mono (2 weights)" for variant in Regular Medium; do subset_face "${scratch}/IBMPlexMono-${variant}.ttf" "${FONTS_OUT_DIR}/IBMPlexMono-${variant}.woff2" done echo echo "==> Bundle summary:" du -ch "${FONTS_OUT_DIR}"/*.woff2 | tail -1 echo echo "==> Done. Files emitted under ${FONTS_OUT_DIR}." } main "$@"