Skip to content

Commit c677059

Browse files
authored
Merge pull request #99 from pelson/feature/mathjax3
Add mathjax v3 support
2 parents c1a4a3b + 1334ab6 commit c677059

69 files changed

Lines changed: 11351 additions & 6182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

xkcd-script/font/xkcd-script.otf

26.7 KB
Binary file not shown.

xkcd-script/font/xkcd-script.sfd

Lines changed: 7837 additions & 6064 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

xkcd-script/font/xkcd-script.ttf

11.6 KB
Binary file not shown.

xkcd-script/font/xkcd-script.woff

7.73 KB
Binary file not shown.

xkcd-script/generator/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# xkcd-script font generation pipeline
2+
3+
Each script runs in order inside the `fontbuilder` Docker image; `run.sh` orchestrates them and accepts an optional starting step (`./run.sh 5` skips pt1–pt4).
4+
5+
## Stages
6+
7+
| # | Script | What it does |
8+
|---|---|---|
9+
| 1 | `pt1_character_extraction.py` | Extract character strokes from `handwriting_minimal.png` (scikit-image). |
10+
| 2 | `pt2_character_classification.py` | Cluster strokes into lines (k-means, fixed seed). |
11+
| 3 | `pt3_ppm_to_svg.py` | Convert per-character PPM → SVG via `potrace`. |
12+
| 4 | `pt4_additional_sources.py` | Trace extra glyphs from comic panels and `extras/`. |
13+
| 5 | `pt5_svg_to_font.py` | Import SVG glyphs into a FontForge SFD; apply stroke normalisation, weight nudges, math-symbol imports. |
14+
| 6 | `pt6_derived_chars.py` | Build derived/composed glyphs: diacritics, ligatures, Greek, IPA, combining marks, math cmap aliases (U+1D400 block via altuni). |
15+
| 7 | `pt7_font_properties.py` | Apply kerning, GPOS anchors, pin CFF hints and OS/2 metrics. Output is `xkcd-script-pt7.sfd` — the **base** font used for everything downstream. |
16+
| 8 | `pt8_derivatives.py` | Orchestrator. Runs each `pt8X_*.py` derivative step in turn. |
17+
| 9 | `pt9_gen_reprod_font.py` | Scrub the SFD for reproducibility, freeze CFF charstrings, generate committed binaries (otf/ttf/woff). |
18+
19+
## Derivatives (pt8)
20+
21+
`pt7` produces a single kitchen-sink base SFD with everything — Latin, Greek, math symbols and aliases, ligatures, combining marks. Each `pt8X_<name>.py` reads that base and either writes its own derivative SFD or extracts data from it to splice elsewhere; `pt8_derivatives.py` runs them with `runpy`.
22+
23+
Today there is no live derivative font: the sole entry, `pt8a_mathjax3.py`, only extracts extensible-glyph outline data into `../xkcd-mathjax3.js`. The display-sized large operators that used to live in a separate mathjax3 WOFF are now stylistic alternates in the base font (`ss01`).
24+
25+
Because derivatives can only subtract or overlay what pt7 already has, **everything plausibly useful belongs in pt7**. Don't pre-strip pt7 for size — that loses the subtractive option.
11 KB
Loading
6.8 KB
Loading
7.18 KB
Loading
9.69 KB
Loading

xkcd-script/generator/pt4_additional_sources.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,23 @@ def _clean_potrace_svg(raw_svg_path, clean_svg_path):
7272
os.remove(clean_svg_path + '.sfd')
7373

7474

75-
def extract_symbol(arr, y0, y1, x0, x1, name, exclude=None):
75+
def extract_symbol(arr, y0, y1, x0, x1, name, exclude=None, pad=0):
7676
"""Crop glyph region, upsample, binarise, run potrace, clean, save SVG.
7777
7878
exclude: optional list of (y0, y1, x0, x1) regions in full-image coordinates
7979
to blank out (set to background) before potrace, for removing
8080
artefacts that cannot be separated by tightening the main crop.
81+
pad: white-pixel border added around the crop before upsampling. Use
82+
when the source PNG is tightly cropped and ink touches the edge —
83+
potrace otherwise produces edge artefacts where contours run into
84+
the canvas boundary.
8185
"""
8286
crop = arr[y0:y1, x0:x1].copy()
8387
if exclude:
8488
for ey0, ey1, ex0, ex1 in exclude:
8589
crop[ey0 - y0:ey1 - y0, ex0 - x0:ex1 - x0] = 255
90+
if pad:
91+
crop = np.pad(crop, pad, mode='constant', constant_values=255)
8692
upsample = SPECIALUPSAMPLE.get(name, UPSAMPLE)
8793
big = Image.fromarray(crop).resize(
8894
(crop.shape[1] * upsample, crop.shape[0] * upsample),
@@ -175,14 +181,18 @@ def extract_symbol(arr, y0, y1, x0, x1, name, exclude=None):
175181
('right_half_arrow', '2343_mathematical_symbol_fight_2x__right_half_arrow'), # ⇀ U+21C0 source
176182
('right_lim_arrow', '2343_mathematical_symbol_fight_2x__right_lim_arrow'), # → U+2192 source
177183
('triangle', '2343_mathematical_symbol_fight_2x__triangle'), # △ U+25B3 source
184+
('circled_times', '2034_equations_2x__circled_times'), # ⊗ U+2297 source
185+
('sqrt_vertical', 'sqrt_vertical'), # √ tall surd, unencoded glyph `radical.tall`
186+
('braceleft_tall', '2435_geothmetic_meandian_2x__brace__shortened'), # { tall brace for MathJax stretchy assembly
187+
('parenleft_tall', '2059_modified_bayes_theorem_2x__lparen'), # ( tall paren for MathJax stretchy assembly (\binom, \left(, pmatrix); right paren is mirrored in pt5
178188
]
179189

180190
print('Extracting hand-drawn extras...')
181191
for name, filename in EXTRAS:
182192
src_path = os.path.join(EXTRAS_DIR, f'{filename}.png')
183193
arr_extra = np.array(Image.open(src_path).convert('L'))
184194
h, w = arr_extra.shape
185-
extract_symbol(arr_extra, 0, h, 0, w, name)
195+
extract_symbol(arr_extra, 0, h, 0, w, name, pad=10)
186196

187197

188198
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)