Fix the live tofu in jax_intro, without trading it for subscript tofu#78
Merged
Conversation
The PRNG key-splitting figure sets a Chinese axis title ("PRNG 密钥拆分树"),
but this lecture never registers a CJK font. matplotlib therefore falls
back to DejaVu Sans, which has no CJK coverage, and the title renders as
blank boxes ("tofu"). Nothing errors and a valid PNG is written, so the
defect is silent -- it only shows up in the published output.
Adds this edition's standard font block to the existing import cell,
immediately after the matplotlib import, so it runs before any figure is
drawn. The block is byte-for-byte identical to the one already carried by
debugging.md, numba.md and the other 15 lectures in this repo, and the
font path resolves to the tracked asset lectures/_fonts/SourceHanSerifSC-SemiBold.otf.
See #77
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nother
jax_intro's figure labels the split tree with key₀, key₁, subkey₁ ... and a
vertical ellipsis. Source Han Serif SC follows Adobe-GB1 and contains none of
U+2080-2083 or U+22EE, so registering it as the sole font.family entry would
have fixed the title (5 CJK characters) while breaking 8 label glyphs that
render correctly today — a wash at best, and silent either way.
Measured on matplotlib 3.11.1 against this repo's own font:
font.family = ['Source Han Serif SC'] -> 4 missing-glyph warnings
font.family = ['Source Han Serif SC', 'DejaVu Sans'] -> 0
Deliberately deviates from the single-entry dialect used elsewhere here.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes CJK “tofu” (blank-box) rendering in lectures/jax_intro.md by restoring matplotlib font registration and configuring font.family with an explicit fallback so both Chinese titles and non-CJK glyphs (e.g., subscripts, vertical ellipsis, Greek symbol forms) render correctly in generated figures.
Changes:
- Register the bundled Source Han Serif SC font via
matplotlib.font_managerin the lecture’s import cell (_fonts/SourceHanSerifSC-SemiBold.otf). - Set
mpl.rcParams['font.family']to['Source Han Serif SC', 'DejaVu Sans']to allow fallback for glyphs not covered by Source Han Serif SC.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Restores the font block in
jax_intro.md, whose figure titlePRNG 密钥拆分树has been rendering as five blank boxes live on the published site for roughly three months — the block was stripped in4120371(2026-04-12), the title was translated to Chinese two days later, and this edition has published three times since.The fallback is load-bearing here rather than precautionary. This lecture's figure labels the split tree with
key₀,key₁,subkey₁,key₂,subkey₂,key₃,subkey₃and a vertical ellipsis — eight glyphs that Source Han Serif SC does not contain. Registering it as the solefont.familyentry would have fixed the 5-character title and broken those 8 labels, which render correctly today. That is a wash at best, and silent in both directions.Note this edition uses
_fonts/where the other two usefonts/, and the block carries# i18nmarkers. Both are preserved here; standardising the two paths is a separate item in the program review.Chinese figure labels in these lectures render as tofu — blank rectangles — because the notebook never registers a CJK font. matplotlib falls back to DejaVu Sans, which has no CJK coverage, emits a
UserWarning, substitutes boxes, and writes a perfectly valid PNG. Nothing fails.Two changes, and the second is the interesting one
1. Register the font where it was missing, following this edition's existing convention and path.
2. Give
font.familya fallback. This is a deliberate deviation from the single-entry form used elsewhere in the estate, and it is a defect fix rather than a style choice.Setting
font.familyto a single entry removes matplotlib's per-glyph fallback. Source Han Serif SC follows Adobe-GB1, which omits subscript digits (U+2080–U+2083), the vertical ellipsis (U+22EE) and the symbol Greek forms (U+03F5 ϵ, U+03D5 ϕ) — characters these lectures genuinely use in labels. So the naive fix would have swapped one set of blank boxes for another.Measured on matplotlib 3.11.1 against this repo's own font file, rendering a label containing 密钥拆分树,
key₀,⋮,ϵandϕ:font.family['Source Han Serif SC']['Source Han Serif SC', 'DejaVu Sans']CJK still comes from Source Han Serif; everything outside Adobe-GB1 now falls back instead of vanishing.
Why this was invisible
The missing-glyph warning is raised inside the Jupyter kernel subprocess. Sphinx's
-Wonly escalates Sphinx-logger warnings, so CI never sees it — myst-nb renders it into the published page as stderr output instead. No CI log in the estate has ever containedfindfontorGlyph … missing.A detector now exists (
bin/check-tofuinproject-translation), validated against the live sites. It found pages that source-level analysis missed, because it reads what was actually rendered.Background
Full history, the argument against reverting to
text.usetex, and the recommendation to replace per-lecture injection with a build-level mechanism: project-translation reports/2026-07-24-cjk-font-rendering-review.md. Engine side: action-translation#182 (resync strips these blocks; the guardrail is prompt prose).🤖 Generated with Claude Code