Skip to content

Fix tofu in Chinese figure labels, and give font.family a fallback#206

Merged
mmcky merged 2 commits into
mainfrom
fix/restore-cjk-font-config
Jul 24, 2026
Merged

Fix tofu in Chinese figure labels, and give font.family a fallback#206
mmcky merged 2 commits into
mainfrom
fix/restore-cjk-font-config

Conversation

@mmcky

@mmcky mmcky commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Restores the font block in divergence_measures.md, kesten_processes.md and jv.md, and adds the fallback to career.md and affine_risk_prices.md, which already carried the block and were silently losing ϵ and r₁ to it.

divergence_measures is live on the published site today; the other two are queued behind the next publish. Provenance note: jv and kesten_processes lost their block to resyncs (965ad3e, da7b176), while divergence_measures never had one — its Chinese labels were hand-added after the original blanket pass.

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.family a 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.family to 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 missing-glyph warnings
['Source Han Serif SC'] 4
['Source Han Serif SC', 'DejaVu Sans'] 0

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 -W only 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 contained findfont or Glyph … missing.

A detector now exists (bin/check-tofu in project-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

mmcky added 2 commits July 24, 2026 12:06
These three lectures set Chinese text on their figures but never register a
CJK font, so matplotlib falls back to DejaVu Sans — which has no CJK coverage
— and every Chinese axis label, title and legend entry renders as blank boxes
("tofu"). Nothing errors and a valid PNG is written, so the build stays green
and the defect only shows up in the published output.

Affected labels include 密度 / KL散度 / JS散度 / 切尔诺夫熵 in
divergence_measures.md, 收益率 / 日期 / 时间 / 对数秩 / 对数规模 in
kesten_processes.md, and the subplot titles 价值函数 / $s$策略 / $\phi$策略
in jv.md.

Adds this edition's standard block to the first code cell that imports
matplotlib in each file, immediately after `import matplotlib.pyplot as plt`
so it runs before any figure is drawn:

    import matplotlib as mpl
    FONTPATH = "fonts/SourceHanSerifSC-SemiBold.otf"
    mpl.font_manager.fontManager.addfont(FONTPATH)
    plt.rcParams['font.family'] = ['Source Han Serif SC']

Matches the convention already used by the other 112 lectures here (see
aiyagari.md); the path resolves against lectures/, where
fonts/SourceHanSerifSC-SemiBold.otf is checked in. Same class of gap as the
five lectures fixed in #196 — nothing else in these files is touched.

See #205
…re lectures

Setting font.family to a single entry removes matplotlib's fallback, so any
character the CJK font lacks renders as a blank box. Source Han Serif SC follows
Adobe-GB1, which omits subscript digits (U+2080-2083), the vertical ellipsis
(U+22EE), and the lunate/symbol Greek forms (U+03F5 epsilon, U+03D5 phi) — all
of which these lectures use in figure labels.

Measured on matplotlib 3.11.1 with this repo's own font, rendering a label
containing 密钥拆分树, key₀, ⋮, ϵ and ϕ:

    font.family = ['Source Han Serif SC']                  -> 4 missing-glyph warnings
    font.family = ['Source Han Serif SC', 'DejaVu Sans']   -> 0

Two lectures already carried the block and were silently losing characters to
it: career.md sets ylabel='ϵ' (and label='ϵ'), affine_risk_prices.md builds
label=f"... (r₁ = ...)". Both are fixed by the fallback alone.

This deviates from the single-entry dialect used elsewhere in the edition. That
is deliberate: the dialect is missing a fallback, and adding one is strictly an
improvement everywhere it appears.
Copilot AI review requested due to automatic review settings July 24, 2026 02:19
@netlify

netlify Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploy Preview for astonishing-narwhal-a8fc64 ready!

Name Link
🔨 Latest commit 4310d25
🔍 Latest deploy log https://app.netlify.com/projects/astonishing-narwhal-a8fc64/deploys/6a62cbc6fbef1d000856c461
😎 Deploy Preview https://deploy-preview-206--astonishing-narwhal-a8fc64.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes missing CJK glyph rendering in several lecture notebooks by ensuring the Source Han Serif SC font is registered where needed, and by adjusting Matplotlib’s font.family setting to preserve per-glyph fallback for symbols/subscripts that Source Han Serif SC does not cover.

Changes:

  • Restores/adds the Matplotlib font-registration block (addfont) in divergence_measures.md, kesten_processes.md, and jv.md.
  • Updates font.family to include a fallback ('DejaVu Sans') in career.md and affine_risk_prices.md.
  • Standardizes the font-family list in the touched lectures to avoid “tofu”/missing-glyph substitution for non-CJK symbols.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
lectures/kesten_processes.md Reintroduces Matplotlib font registration + font family fallback to ensure CJK and symbol glyph coverage.
lectures/jv.md Restores the font registration block and adds fallback to prevent missing glyphs in figure labels.
lectures/divergence_measures.md Adds the font registration block (previously absent) with a safe fallback list for glyph coverage.
lectures/career.md Extends font.family from a single entry to a fallback list to avoid missing non-CJK glyphs.
lectures/affine_risk_prices.md Updates the existing i18n font configuration to include a fallback family, preventing missing-glyph rendering in figures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

Copy link
Copy Markdown

@github-actions
github-actions Bot temporarily deployed to pull request July 24, 2026 02:40 Inactive
@mmcky
mmcky merged commit d0e0055 into main Jul 24, 2026
8 checks passed
@mmcky
mmcky deleted the fix/restore-cjk-font-config branch July 24, 2026 04:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants