Skip to content

jax_intro renders its Chinese figure title as tofu — live to readers for three months #77

Description

@mmcky

jax_intro.md:544 sets a Chinese figure title — ax.set_title("PRNG 密钥拆分树", fontsize=13, pad=10) — and the lecture carries no matplotlib font configuration. matplotlib falls back to DejaVu Sans, which has no CJK coverage, so the title renders as tofu: blank rectangles in an otherwise valid PNG.

This one is live. The font block was stripped in 4120371 (2026-04-12), the title was re-translated to Chinese two days later, and the block was never restored. This edition has published three times since, so readers have been seeing a boxed-out figure title for roughly three months. It is the only instance in the estate that has actually reached readers — the intermediate and intro editions have the same defect but have not published since it appeared.

Fix

Restore the block above first plotting use:

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

Check the path against this repo before committing — the estate currently has two conventions for a byte-identical asset (lectures/fonts/ in 136 lectures across the other editions, lectures/_fonts/ in 17 here and in the engine's output), and standardising on one is a separate item in the program review.

Why CI did not catch it

The missing-glyph warning is a UserWarning raised inside the Jupyter kernel subprocess. Sphinx's -W only escalates Sphinx-logger warnings, so it 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.

Separately and more seriously for this repo: the -n -W --keep-going on the sphinx-tojupyter step is inert — sphinx_tojupyter/builders/jupyter.py:297 calls exit(self.execution_status_code), initialised to 0 and never set, so that step swallows all execution errors. That also bounds what #76 achieves.

Context

Root cause, the recommendation against reverting to usetex, and a proposed CI tofu detector: project-translation reports/2026-07-24-cjk-font-rendering-review.md.

Activity

  1. mmcky commented on Jul 24, 2026

    @mmcky
    ContributorAuthor

    A runtime scan of the published site finds two more broken pages, and they are a different defect from this one.

    bin/check-tofu in project-translation reads the missing-glyph warnings where they actually land — myst-nb renders the kernel's UserWarning into the published HTML as stderr output. Pointing it at https://quantecon.github.io/lecture-python-programming.zh-cn:

    Page Characters lost
    jax_intro 密钥拆分树 — this issue, fixed in #78
    matplotlib 样式: — missing from Liberation Sans, not DejaVu
    pandas_panel 业人份低元况分占口国均实家就工平年最欧比洲状百美资际()

    Both new pages already carry the font block, so they are not the same defect.

    matplotlib.md — the block is set and then clobbered

    The font block sits at line 83. The lecture then demonstrates style sheets: plt.style.use(style) at line 307, plt.style.use('default') at 395 and 451, and plt.rcParams.update(parameters) at 422. style.use() resets rcParams to the style's own values, which wipes font.family — hence glyphs missing from Liberation Sans, a style sheet's font rather than the fallback default.

    Fix options: re-apply font.family after each style.use(), wrap the demonstrations in plt.rc_context(), or leave the demonstration figures' labels in English since the lecture is about styling rather than about the data.

    pandas_panel.md — not yet explained

    Its block was added 2026-03-20 (93dfab0), before both publish-2026may14 and publish-2026jun19, so the block was live at publish and the labels still lost their glyphs. That needs a targeted look; I have not diagnosed it. Worth checking for a later rcParams reset, an execution failure that skipped the block's cell, or a figure drawn before the block runs.

    Method note

    Source-level analysis found only jax_intro in this repo; the runtime scan found three. The two approaches are complementary — static analysis catches unpublished defects, runtime catches what readers actually see plus anything built dynamically. Both are worth keeping.

  2. mmcky commented on Aug 10, 2026

    @mmcky
    ContributorAuthor

    Verified fixed on the live site — closing.

    What landed

    Page Fix Shipped in
    jax_intro font block restored #78
    matplotlib re-apply font.family inside draw_graphs after each plt.style.use(style) #87 (0153f28)
    pandas_panel font family passed through sns.set_theme(rc=...) #87 (0153f28)

    matplotlib and pandas_panel were the two extra pages found by the runtime scan in the comment above, and they were a different defect from the original one here — not a missing font block, but plt.style.use() and sns.set_theme() resetting font.family after the i18n cell had set it.

    Verification

    The publish workflow completed successfully against 0153f28 on 2026-08-07, so the fix is live rather than only on main. Fetching the three published pages and grepping for the missing-glyph warnings where they actually land — myst-nb renders the kernel's UserWarning into the page as stderr output, which is what bin/check-tofu keys on — returns clean on all three:

    • jax_intro.html — HTTP 200, no findfont / missing from font occurrences
    • matplotlib.html — HTTP 200, clean
    • pandas_panel.html — HTTP 200, clean

    Scope note: this checks the three pages named in this issue, not the full estate. A repo-wide sweep would be worth running separately if the goal is to confirm no fourth page is affected.

    The two structural gaps this issue documented are unchanged and are not closed by this: Sphinx's -W still cannot see kernel-side UserWarnings, and the sphinx-tojupyter step still swallows execution errors via the exit(self.execution_status_code) path. Those want the CI tofu detector proposed in the linked report rather than being tracked here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions