Files
lms-fe/scripts/generate-nanumsquare-css.py
T
chyoo 8b501266ff feat: improve agent responses and workspace UI
Add live agent progress, elapsed timing, verified code navigation and rich Markdown rendering. Preserve NanumSquare styling with missing glyph fallback, fix platform availability filtering and unify workspace scrollbar and visualization controls.
2026-10-19 17:06:18 +09:00

86 lines
3.1 KiB
Python

"""Regenerate font coverage after replacing the bundled NanumSquare WOFF2 files.
Requires fonttools[woff]. Normal app builds use the checked-in CSS, not Python.
Run: python3 scripts/generate-nanumsquare-css.py [--check]
"""
import argparse
import hashlib
import unicodedata
from pathlib import Path
from fontTools.pens.boundsPen import BoundsPen
from fontTools.ttLib import TTFont
ROOT = Path(__file__).resolve().parents[1]
FONTS = ROOT / "src/renderer/public/assets/fonts"
OUTPUT = ROOT / "src/renderer/src/styles/nanumSquare.css"
FACES = [(300, "NanumSquareL"), (400, "NanumSquare"), (700, "NanumSquareB"), (800, "NanumSquareEB")]
# Hangul fillers are intentionally invisible, like whitespace and controls.
FILLERS = {0x115F, 0x1160, 0x3164, 0xFFA0}
def coverage(font):
glyphs = font.getGlyphSet()
supported = []
for code, name in sorted(font.getBestCmap().items()):
pen = BoundsPen(glyphs)
glyphs[name].draw(pen)
intentional = chr(code).isspace() or unicodedata.category(chr(code)) in {"Cc", "Cf"} or code in FILLERS
if pen.bounds is not None or intentional:
supported.append(code)
return supported
def unicode_ranges(codes):
ranges = []
start = end = codes[0]
for code in codes[1:]:
if code == end + 1:
end = code
else:
ranges.append((start, end))
start = end = code
ranges.append((start, end))
return [f"U+{start:X}" if start == end else f"U+{start:X}-{end:X}" for start, end in ranges]
def generate():
sections = [
"/* Generated by scripts/generate-nanumsquare-css.py. Do not edit manually.\n"
" * Empty glyphs are excluded so the existing system-font fallback can render them.\n"
" * Font binaries and visible glyphs are unchanged.\n */"
]
for weight, name in FACES:
file = FONTS / f"{name}.woff2"
with TTFont(file) as font:
ranges = unicode_ranges(coverage(font))
lines = [", ".join(ranges[index:index + 10]) for index in range(0, len(ranges), 10)]
sections.append(
f"/* {file.name} sha256: {hashlib.sha256(file.read_bytes()).hexdigest()} */\n"
"@font-face {\n"
" font-family: 'NanumSquare';\n"
f" src: url('/assets/fonts/{file.name}') format('woff2');\n"
f" font-weight: {weight};\n"
" font-style: normal;\n"
" font-display: swap;\n"
" unicode-range:\n " + ",\n ".join(lines) + ";\n}"
)
return "\n\n".join(sections) + "\n"
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true")
args = parser.parse_args()
generated = generate()
if args.check:
if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != generated:
raise SystemExit("NanumSquare coverage CSS is stale; regenerate it")
print("NanumSquare coverage matches all four font files")
else:
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
OUTPUT.write_text(generated, encoding="utf-8")
print(f"Generated {OUTPUT.relative_to(ROOT)}")