跪拜 Guibai
← All articles
Frontend · JavaScript · Vue.js

WeChat Mini Programs Break Custom Chinese Fonts in Five Different Ways

By 书中枫叶 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Any Mini Program that needs a custom Chinese typeface will hit the same silent failures on real hardware. The developer tools paper over illegal URL schemes, missing font weights, and Canvas API mismatches, so a project that looks finished in the simulator ships broken to users.

Summary

A WeChat Mini Program called JuShi needed three custom Chinese fonts for its UI and poster export. The developer tools showed everything working, but real devices rendered system Heiti everywhere and threw a cascade of opaque errors. The root cause is a collision of five constraints: a 2MB main package limit, `loadFontFace` rejecting local paths, separate APIs for page text and Canvas text, CSS font-weight fallback behavior, and subpackage resource isolation. The only stable architecture splits the problem into two pipelines: Data URL or HTTPS for page rendering via `loadFontFace` with `webview` scope, and local files written to `USER_DATA` for Canvas 2D poster rendering via `canvas.loadFont`. A font subset encoded as base64 JSON in the main package provides immediate first-screen rendering, while a full woff downloaded over HTTPS later overwrites it. Every failure path needs an explicit fallback so the app never breaks, just degrades to system fonts.

Takeaways
`wx.loadFontFace` accepts only HTTPS URLs and Data URLs; local paths like `/static/` or `wxfile://` fail on real devices.
Pairing Data URL with `scopes: ['native']` in a single `loadFontFace` call causes `network error` and kills the entire font load.
Page text and Canvas poster text require completely separate font-loading APIs: `loadFontFace` for webview, `canvas.loadFont` with a local file for Canvas 2D.
Setting `font-weight: 700` on a Chinese handwriting or Fangsong font that only has a Regular weight forces the engine to fall back to system Heiti.
Native `<input>` components do not inherit `page { font-family }`; overlaying a `<text>` on top creates a double-stacked ghosting effect.
Font subsets encoded as base64 JSON in the main package give immediate first-screen rendering; a full woff downloaded later over HTTPS overwrites the subset.
`canvas.loadFont` can return the font's internal family string, which must be used directly in `ctx.font`—CSS aliases and fallback stacks are ignored.
Putting font JSON in a subpackage and loading it with `require.async` breaks the path at build time; brand fonts needed on the homepage must live in the main package.
Conclusions

The WeChat developer tools are dangerously permissive with local font paths, creating a systematic gap between simulator success and real-device failure that the official documentation does not warn about.

Chinese font loading in Mini Programs is not a single technical problem but a collision of five independent constraints—package size, URL scheme, dual APIs, CSS weight fallback, and subpackage isolation—that only surface together on hardware.

The architecture of splitting page rendering and poster rendering into two completely separate font pipelines is unintuitive but necessary because WeChat provides no unified custom font API across webview and Canvas contexts.

Free Chinese handwriting fonts almost never include a Bold weight, so the common CSS pattern of `font-weight: 700` on titles silently destroys the custom font without any console error.

Concepts & terms
wx.loadFontFace
WeChat Mini Program API for loading a custom font into page rendering. Accepts only HTTPS URLs or Data URLs in the `source` parameter. The `scopes` field controls whether the font applies to webview elements or native components.
Canvas 2D loadFont
The Canvas 2D API method for registering a custom font for poster or image generation in Mini Programs. Unlike `loadFontFace`, it requires a local file path in `USER_DATA` and returns the font's internal family name.
font subsetting
Using a tool like `fontTools` to extract only the specific characters a project needs from a full Chinese font file, reducing size from several megabytes to tens of kilobytes so it fits within the 2MB main package limit.
USER_DATA_PATH
The writable local file storage directory in WeChat Mini Programs (`wx.env.USER_DATA_PATH`). Required for storing font files that Canvas 2D's `loadFont` can read, since it cannot access package-internal paths.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗