Skip to content

Commit 4b3b588

Browse files
committed
Address PR #99 review comments
- pt6: clarify _GREEK_UPPER / math-alias comments; remove dead skip counter and try/except from the altuni loop - pt7: remove try/except AttributeError around addLookup_setFeatureName - xkcd-mathjax3.js: remove auto @font-face injection (page's responsibility); rewrite header comment; rename mirrorBarHTML -> vinculumHTML; refactor buildExtendedSegs to accept a glyph object directly and use a unified ordered cuts:[{axis,pct}] config in place of separate cutXPct/cutYPct; add comments to _mapSegs, _extend non-crossing branch, resetOverlays, and resize debounce; drop xkcdHidden legacy flag from replaceVinculums and add a dedicated xkcdRuleTop handler in resetOverlays - samples: replace Dockerfile + run.sh with generate_math_samples.sh using the official Playwright image directly; update preview.sh - formulas.yaml: add || and \hline\hline to multiplication-table sample so double-rule rendering changes are caught by CI
1 parent 1a3aa0a commit 4b3b588

12 files changed

Lines changed: 176 additions & 223 deletions

xkcd-script/generator/pt6_derived_chars.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,8 +1314,8 @@ def _add_altuni(glyph, codepoint):
13141314

13151315

13161316
# Greek codepoint sequences for the U+1D6A8.. and U+1D6E2.. math blocks.
1317-
# Position 17 is the "capital theta symbol" slot, aliased back to plain Θ.
1318-
# 0x03A2 is a Unicode hole in the Greek block; the math block uses Σ in that slot.
1317+
# Not a simple 0x0391+i offset: position 17 is the "capital theta symbol" (maps to
1318+
# plain Θ), and U+03A2 is an unassigned hole so position 18 jumps straight to Σ (U+03A3).
13191319
_GREEK_UPPER = [
13201320
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
13211321
0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
@@ -1337,10 +1337,11 @@ def _add_altuni(glyph, codepoint):
13371337
if _src != 0x1D455:
13381338
_math_aliases[_src] = 0x0061 + _i
13391339

1340-
# TeX italic substitutes that fill the U+1D455 hole and similar
1341-
_math_aliases[0x210E] = ord('h') # ℎ PLANCK CONSTANT
1342-
_math_aliases[0x210F] = ord('h') # ℏ PLANCK CONSTANT OVER TWO PI (no ħ in math italic)
1343-
_math_aliases[0x2113] = ord('l') # ℓ SCRIPT SMALL L
1340+
# Canonical math symbols defined by Unicode to look like a specific letter glyph.
1341+
# U+1D455 is unassigned because U+210E already exists for math-italic h.
1342+
_math_aliases[0x210E] = ord('h') # ℎ PLANCK CONSTANT (math italic h by definition)
1343+
_math_aliases[0x210F] = ord('h') # ℏ PLANCK CONSTANT OVER TWO PI
1344+
_math_aliases[0x2113] = ord('l') # ℓ SCRIPT SMALL L (math italic l by definition)
13441345
_math_aliases[0x03D5] = 0x03C6 # ϕ GREEK PHI SYMBOL → φ
13451346

13461347
# Math Bold Latin: U+1D400–U+1D433
@@ -1380,17 +1381,8 @@ def _add_altuni(glyph, codepoint):
13801381
for _i, _cp in enumerate(_GREEK_LOWER):
13811382
_math_aliases[0x1D6C2 + _i] = _cp
13821383

1383-
_added = 0
1384-
_skipped = 0
13851384
for _src_cp, _dst_cp in sorted(_math_aliases.items()):
1386-
try:
1387-
_g = font[_dst_cp]
1388-
except TypeError:
1389-
_skipped += 1
1390-
continue
1391-
_add_altuni(_g, _src_cp)
1392-
_added += 1
1393-
print(f" math aliases: added {_added}, skipped {_skipped} (no source glyph)")
1385+
_add_altuni(font[_dst_cp], _src_cp)
13941386

13951387

13961388
# ---------------------------------------------------------------------------

xkcd-script/generator/pt7_font_properties.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,6 @@ def getkern(left, right):
278278
('latn', ('dflt',)),
279279
('math', ('dflt',)))),))
280280
font.addLookupSubtable('ss01-display-operators', 'ss01-display-operators-1')
281-
try:
282-
font.addLookup_setFeatureName('ss01-display-operators',
283-
'Display large operators')
284-
except AttributeError:
285-
pass
286281

287282
for _base, _disp in (('summation', 'summation.disp'),
288283
('product', 'product.disp'),

xkcd-script/generator/pt9_gen_reprod_font.py renamed to xkcd-script/generator/pt8_gen_reprod_font.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Produce reproducible binary fonts (OTF / TTF / WOFF) for xkcd-script from
33
the pt7 SFD.
44
5+
NOTE: this step runs *after* pt8_derivatives.py (which now also lives in
6+
the pt8 slot). Keeping the original pt8_ name here intentionally to
7+
minimise diff noise during the MathJax PR; a follow-up should renumber
8+
this to pt9_ and reserve pt8_ for the derivatives orchestrator.
9+
510
1. Scrub timestamps + XUID from the input SFD so FontForge can't bake
611
build-time metadata into the output.
712
2. Save the scrubbed SFD as the canonical committed copy.

xkcd-script/generator/pt8a_mathjax3.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,47 @@
2020
# ---------------------------------------------------------------------------
2121
#
2222
# The runtime renderer in xkcd-mathjax3.js takes each glyph's outline, applies
23-
# a coordinate-threshold extension (shift points past cutX by Nx, splice
24-
# jittered cubic sub-segments across the gap; same for cutY along an axis
25-
# optionally tilted by leanDeg), and produces an SVG path sized to the
26-
# requested overlay. Per-glyph parameters live here so they travel with
23+
# a coordinate-threshold extension (shift points past each cut by N, splice
24+
# jittered cubic sub-segments across the gap; each cut may optionally lean
25+
# its extension axis), and produces an SVG path sized to the requested
26+
# overlay. Per-glyph parameters live here so they travel with
2727
# the font: when the font is rebuilt this step regenerates the embedded
2828
# data and the renderer picks up the new shapes without code changes.
2929

3030
EXTENSIBLE_MARKER_BEGIN = '// ── BEGIN GENERATED GLYPH DATA ──'
3131
EXTENSIBLE_MARKER_END = '// ── END GENERATED GLYPH DATA ──'
3232
MATHJAX_JS = '../xkcd-mathjax3.js'
3333

34-
# cutXPct / cutYPct : cut threshold as % of bbox along that axis
35-
# leanDeg : Y-extension axis lean from vertical (deg)
36-
# unitsPerSeg : sub-segment density along the inserted gap
37-
# amp : jitter amplitude in font units
34+
# cuts : ordered list of {axis: 'x'|'y', pct: %, lean?: deg}. pct is
35+
# the cut threshold along axis as % of bbox; optional lean
36+
# rotates the extension axis from vertical (Y-cuts only).
37+
# unitsPerSeg : sub-segment density along the inserted gap
38+
# amp : jitter amplitude in font units
3839
EXTENSIBLE_CONFIG = {
39-
'emdash': {'cutXPct': 50, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 120, 'amp': 4},
40-
'radical': {'cutXPct': 70, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 60, 'amp': 5},
41-
'radical.tall': {'cutXPct': 56, 'cutYPct': 45, 'leanDeg': -2.0, 'unitsPerSeg': 45, 'amp': 3},
40+
'emdash': {'cuts': [{'axis': 'x', 'pct': 50}], 'unitsPerSeg': 120, 'amp': 4},
41+
'radical': {'cuts': [{'axis': 'x', 'pct': 70}, {'axis': 'y', 'pct': 50}], 'unitsPerSeg': 60, 'amp': 5},
42+
'radical.tall': {'cuts': [{'axis': 'x', 'pct': 56}, {'axis': 'y', 'pct': 45, 'lean': -2.0}], 'unitsPerSeg': 45, 'amp': 3},
4243
# tall { — two Y cuts (above and below the central joint) so stretch is
4344
# symmetric and the joint shape stays fixed. Used for any \begin{cases}
4445
# with 2+ rows; xkcd-mathjax3.js always overlays this glyph, never the
45-
# basic braceleft.
46-
'braceleft.tall': {'cutXPct': 50, 'cutYPct': [33, 67], 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3},
46+
# basic braceleft. Highest pct first so each successive cut's threshold
47+
# stays in the already-extended region.
48+
'braceleft.tall': {'cuts': [{'axis': 'y', 'pct': 67}, {'axis': 'y', 'pct': 33}], 'unitsPerSeg': 45, 'amp': 3},
4749
# tall ( — single mid-height Y cut; no joint to preserve, so the stretch
4850
# extends a uniform vertical mid-section. parenright.tall is the X-mirror
4951
# of this glyph and is produced at render time in xkcd-mathjax3.js, so it
5052
# isn't listed here.
51-
'parenleft.tall': {'cutXPct': 50, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3},
53+
'parenleft.tall': {'cuts': [{'axis': 'y', 'pct': 50}], 'unitsPerSeg': 45, 'amp': 3},
5254
# [ — the regular bracketleft (U+005B) extends fine on Y because its body
5355
# is a near-straight vertical stroke; no dedicated .tall artwork needed.
5456
# Single mid-Y cut leaves the top/bottom serifs intact and inserts uniform
5557
# jittered stroke through the middle. bracketright is X-mirrored at
5658
# render time in xkcd-mathjax3.js, so it isn't listed here.
57-
'bracketleft': {'cutXPct': 50, 'cutYPct': 32, 'leanDeg': 0.0, 'unitsPerSeg': 45, 'amp': 3},
59+
'bracketleft': {'cuts': [{'axis': 'y', 'pct': 32}], 'unitsPerSeg': 45, 'amp': 3},
5860
# → (U+2192). Cut early in the X axis (in the tail, before the head) so
5961
# extension lengthens the shaft and the arrowhead shape is preserved.
60-
# Y cut not used — \overrightarrow / \vec only ever stretches in X.
61-
'arrowright': {'cutXPct': 39, 'cutYPct': 50, 'leanDeg': 0.0, 'unitsPerSeg': 120, 'amp': 4},
62+
# Stretches only in X — \overrightarrow / \vec never need vertical growth.
63+
'arrowright': {'cuts': [{'axis': 'x', 'pct': 39}], 'unitsPerSeg': 120, 'amp': 4},
6264
}
6365

6466

@@ -120,8 +122,9 @@ def _as_js(data):
120122
lines.append(' advance: %s,' % g['advance'])
121123
lines.append(' bbox: {xmin: %s, ymin: %s, xmax: %s, ymax: %s},'
122124
% (bb['xmin'], bb['ymin'], bb['xmax'], bb['ymax']))
123-
lines.append(' config: {cutXPct: %s, cutYPct: %s, leanDeg: %s, unitsPerSeg: %s, amp: %s},'
124-
% (cfg['cutXPct'], cfg['cutYPct'], cfg['leanDeg'], cfg['unitsPerSeg'], cfg['amp']))
125+
cuts_js = ', '.join(json.dumps(c, separators=(', ', ': ')) for c in cfg['cuts'])
126+
lines.append(' config: {cuts: [%s], unitsPerSeg: %s, amp: %s},'
127+
% (cuts_js, cfg['unitsPerSeg'], cfg['amp']))
125128
lines.append(' commands: [')
126129
for cmd in g['commands']:
127130
lines.append(' ' + json.dumps(cmd) + ',')

xkcd-script/generator/run.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ set -ex
1818
[ "$FROM" -le 5 ] && $RUN_CTXT python3 pt5_svg_to_font.py
1919
[ "$FROM" -le 6 ] && $RUN_CTXT python3 pt6_derived_chars.py
2020
[ "$FROM" -le 7 ] && $RUN_CTXT python3 pt7_font_properties.py
21+
# TODO: pt8_gen_reprod_font.py should logically be pt9 since it runs after
22+
# pt8_derivatives.py; left as pt8_ to minimise diff noise for the MathJax PR.
2123
[ "$FROM" -le 8 ] && $RUN_CTXT python3 pt8_derivatives.py
22-
[ "$FROM" -le 9 ] && $RUN_CTXT python3 pt9_gen_reprod_font.py
24+
[ "$FROM" -le 8 ] && $RUN_CTXT python3 pt8_gen_reprod_font.py

xkcd-script/samples/mathjax3/Dockerfile

Lines changed: 0 additions & 29 deletions
This file was deleted.

xkcd-script/samples/mathjax3/formulas.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ column-vector:
115115
# via the font's GPOS mark anchors. The final \widehat{xyz} row is the wide-base
116116
# case which legitimately needs MathJax's stretchy extender — it must keep working.
117117
# Exercises: digit '1' padding (many 1s in the body and totals); vertical
118-
# rules `|`; multiple `\hline`s separating header / body / totals; mixed
119-
# text + numeric cells; ragged column widths (1 vs 12 vs 225).
120-
# Note: MathJax 3 silently collapses `||` and `\hline\hline` to single
121-
# rules, so this sample sticks to single rules everywhere.
118+
# rules `|` and `||`; `\hline` and `\hline\hline`; mixed text + numeric
119+
# cells; ragged column widths (1 vs 12 vs 225).
120+
# The `||` column separator and `\hline\hline` are intentionally included so
121+
# any change in how MathJax renders double rules shows up as a sample diff.
122122
multiplication-table:
123123
formulas:
124-
- tex: '$$\begin{array}{c|c|c|c|c|c|c} \times & 1 & 2 & 3 & 4 & 5 & \text{sum} \\ \hline 1 & 1 & 2 & 3 & 4 & 5 & 15 \\ 2 & 2 & 4 & 6 & 8 & 10 & 30 \\ 3 & 3 & 6 & 9 & 12 & 15 & 45 \\ 4 & 4 & 8 & 12 & 16 & 20 & 60 \\ 5 & 5 & 10 & 15 & 20 & 25 & 75 \\ \hline \text{sum} & 15 & 30 & 45 & 60 & 75 & 225 \\ \end{array}$$'
124+
- tex: '$$\begin{array}{c||c|c|c|c|c|c} \times & 1 & 2 & 3 & 4 & 5 & \text{sum} \\ \hline\hline 1 & 1 & 2 & 3 & 4 & 5 & 15 \\ 2 & 2 & 4 & 6 & 8 & 10 & 30 \\ 3 & 3 & 6 & 9 & 12 & 15 & 45 \\ 4 & 4 & 8 & 12 & 16 & 20 & 60 \\ 5 & 5 & 10 & 15 & 20 & 25 & 75 \\ \hline \text{sum} & 15 & 30 & 45 & 60 & 75 & 225 \\ \end{array}$$'
125125

126126
accents-zoo:
127127
cellHeight: 80

xkcd-script/samples/mathjax3/generate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ function head(group) {
6060
return `<!DOCTYPE html>
6161
<html><head><meta charset="utf-8">
6262
<style>
63+
@font-face { font-family:'xkcd-script'; src:url('/xkcd-script/font/xkcd-script.woff') format('woff'); }
6364
html, body { margin:0; padding:0; background:#fff;
6465
font-family:'xkcd-script', sans-serif;
6566
font-size:22px; line-height:1.6; color:#111; }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
# Regenerate the MathJax sample PNGs using the pinned official Playwright image.
3+
# `git diff` / `git status` after running shows what changed; CI runs this
4+
# script and then `git diff --exit-code` over the PNG paths.
5+
#
6+
# The repo root is bind-mounted so generate.js can serve the live
7+
# xkcd-mathjax3.js and woff files from the working tree.
8+
#
9+
# The Playwright image version must stay in sync with the `playwright` package
10+
# version in package.json.
11+
12+
set -euo pipefail
13+
14+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
15+
REPO_ROOT="$( cd "${DIR}/../../.." && pwd )"
16+
PLAYWRIGHT_VERSION="v1.49.1-noble"
17+
18+
docker run --rm \
19+
-u "$(id -u):$(id -g)" \
20+
-v "${REPO_ROOT}:/work" \
21+
-w /work/xkcd-script/samples/mathjax3 \
22+
"mcr.microsoft.com/playwright:${PLAYWRIGHT_VERSION}" \
23+
bash -c 'npm ci && node generate.js "$@"' -- "$@"

xkcd-script/samples/mathjax3/run.sh

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)