|
| 1 | +# Chinese Font Guide for StatsPAI (中文字体指南) |
| 2 | + |
| 3 | +## Quick Start |
| 4 | + |
| 5 | +```python |
| 6 | +import statspai as sp |
| 7 | +sp.use_chinese() # One-line fix, auto-detects the best Chinese font on your system |
| 8 | +``` |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## 1. Cross-Platform Chinese Font Family |
| 13 | + |
| 14 | +All platforms have "宋体" (Song/Serif CJK), but they are designed by different companies: |
| 15 | + |
| 16 | +``` |
| 17 | +宋体风格 (Serif CJK) |
| 18 | +├── Apple: Songti SC / TC macOS 预装 |
| 19 | +├── Microsoft: SimSun / NSimSun Windows 预装 |
| 20 | +├── Google: Noto Serif CJK SC 开源,全平台可安装 |
| 21 | +├── Adobe: Source Han Serif 与 Noto 同源 |
| 22 | +└── SinoType: STSong (华文宋体) macOS 预装,商业授权 |
| 23 | +``` |
| 24 | + |
| 25 | +### Platform Availability |
| 26 | + |
| 27 | +| Font | macOS | Windows | Linux | License | |
| 28 | +|------|:-----:|:-------:|:-----:|---------| |
| 29 | +| **Songti SC** (宋体-简) | Pre-installed | - | - | Apple proprietary | |
| 30 | +| **SimSun** (宋体) | - | Pre-installed | Manual install | Microsoft proprietary | |
| 31 | +| **NSimSun** (新宋体) | - | Pre-installed | Manual install | Microsoft proprietary | |
| 32 | +| **Noto Serif CJK SC** (思源宋体) | Installable | Installable | `apt install` | **Open source (OFL)** | |
| 33 | +| **STSong** (华文宋体) | Pre-installed | With Office | - | SinoType proprietary | |
| 34 | +| **AR PL UMing** | - | - | `apt install` | Open source (Arphic) | |
| 35 | + |
| 36 | +### Visual Similarity to SimSun (微软宋体) |
| 37 | + |
| 38 | +| Font | Similarity | Notes | |
| 39 | +|------|:----------:|-------| |
| 40 | +| **NSimSun** | ~99% | Monospace variant of SimSun, same designer | |
| 41 | +| **STSong** | ~85% | Slightly thicker strokes, more rounded | |
| 42 | +| **AR PL UMing** | ~75% | Open source imitation, rougher details | |
| 43 | +| **Noto Serif CJK SC** | ~65% | Modern redesign, more elegant, better typography | |
| 44 | +| **Songti SC** | ~60% | Apple design, thicker strokes | |
| 45 | + |
| 46 | +### Key Differences: Songti SC vs SimSun vs Noto Serif CJK |
| 47 | + |
| 48 | +| Feature | Songti SC (Apple) | SimSun (Microsoft) | Noto Serif CJK (Google) | |
| 49 | +|---------|:-----------------:|:------------------:|:-----------------------:| |
| 50 | +| Designer | Apple | Zhongyi (中易) | Adobe (西塚涼子) | |
| 51 | +| Design era | 2000s | 1990s | 2017 | |
| 52 | +| Strokes | Thicker, rounded | Thin, sharp | Balanced, modern | |
| 53 | +| Character count | ~30,000 | ~28,000 | ~65,000+ | |
| 54 | +| Font weights | 1 | 1 | **7** (ExtraLight to Black) | |
| 55 | +| Screen optimization | Retina displays | Low-res bitmap hints | High-res screens | |
| 56 | +| Best for | macOS screen | Windows/print legacy | Cross-platform, modern | |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## 2. StatsPAI Chinese Font Usage |
| 61 | + |
| 62 | +### Auto Mode (Recommended) |
| 63 | + |
| 64 | +```python |
| 65 | +import statspai as sp |
| 66 | + |
| 67 | +sp.use_chinese() # Auto-detect best available font |
| 68 | +sp.use_chinese('serif') # Prefer 宋体 (Songti SC / SimSun / Noto Serif CJK) |
| 69 | +sp.use_chinese('sans') # Prefer 黑体 (PingFang / SimHei / Noto Sans CJK) |
| 70 | +sp.use_chinese('SimSun') # Use a specific font |
| 71 | +``` |
| 72 | + |
| 73 | +### Font Presets for Academic Publishing |
| 74 | + |
| 75 | +```python |
| 76 | +from statspai.plots.interactive import FigureEditor |
| 77 | + |
| 78 | +editor = FigureEditor(fig=fig) |
| 79 | + |
| 80 | +# Chinese thesis standard (中文学位论文) |
| 81 | +# Auto-detects: Songti SC (Mac) / SimSun (Win) / Noto Serif CJK (Linux) |
| 82 | +editor.apply_font_preset('Chinese Thesis (中文学位论文)') |
| 83 | + |
| 84 | +# Chinese journal (中文期刊) |
| 85 | +editor.apply_font_preset('Chinese Journal (中文期刊)') |
| 86 | + |
| 87 | +# Chinese slides (中文PPT) — uses sans-serif (黑体/苹方) |
| 88 | +editor.apply_font_preset('Chinese Slide (中文PPT)') |
| 89 | +``` |
| 90 | + |
| 91 | +### What StatsPAI Does Automatically |
| 92 | + |
| 93 | +When you select a Chinese font, StatsPAI: |
| 94 | +1. Sets `font.family` to the correct family (serif/sans-serif) |
| 95 | +2. Puts the Chinese font first in the preference list |
| 96 | +3. Sets `axes.unicode_minus = False` (fixes the minus sign "−" display issue) |
| 97 | +4. Generates reproducible code that includes all these settings |
| 98 | + |
| 99 | +--- |
| 100 | + |
| 101 | +## 3. Installing Chinese Fonts |
| 102 | + |
| 103 | +### macOS |
| 104 | + |
| 105 | +Chinese fonts are **pre-installed**. No action needed: |
| 106 | +- Songti SC (宋体), PingFang SC (苹方), Heiti TC (黑体), Kaiti SC (楷体), etc. |
| 107 | + |
| 108 | +Just call `sp.use_chinese()`. |
| 109 | + |
| 110 | +### Windows |
| 111 | + |
| 112 | +SimSun (宋体) and SimHei (黑体) are **pre-installed**. No action needed. |
| 113 | + |
| 114 | +Just call `sp.use_chinese()`. |
| 115 | + |
| 116 | +### Linux (Ubuntu / Debian) |
| 117 | + |
| 118 | +```bash |
| 119 | +# Option 1: Open source Noto CJK fonts (RECOMMENDED) |
| 120 | +sudo apt install fonts-noto-cjk fonts-noto-cjk-extra |
| 121 | + |
| 122 | +# Option 2: WenQuanYi (文泉驿) |
| 123 | +sudo apt install fonts-wqy-microhei fonts-wqy-zenhei |
| 124 | + |
| 125 | +# Option 3: Arphic (文鼎) |
| 126 | +sudo apt install fonts-arphic-uming fonts-arphic-ukai |
| 127 | + |
| 128 | +# After installing, refresh font cache |
| 129 | +fc-cache -fv |
| 130 | + |
| 131 | +# Clear matplotlib cache (IMPORTANT!) |
| 132 | +python3 -c " |
| 133 | +import matplotlib, shutil, os |
| 134 | +c = matplotlib.get_cachedir() |
| 135 | +if os.path.exists(c): shutil.rmtree(c) |
| 136 | +print(f'Cleared: {c}') |
| 137 | +" |
| 138 | +``` |
| 139 | + |
| 140 | +### Linux (Fedora / RHEL) |
| 141 | + |
| 142 | +```bash |
| 143 | +sudo dnf install google-noto-serif-cjk-sc-fonts google-noto-sans-cjk-sc-fonts |
| 144 | +fc-cache -fv |
| 145 | +``` |
| 146 | + |
| 147 | +### Linux (Arch) |
| 148 | + |
| 149 | +```bash |
| 150 | +sudo pacman -S noto-fonts-cjk |
| 151 | +fc-cache -fv |
| 152 | +``` |
| 153 | + |
| 154 | +--- |
| 155 | + |
| 156 | +## 4. Installing SimSun (微软宋体) on Linux |
| 157 | + |
| 158 | +If your journal **strictly requires SimSun**, you need to install it manually. |
| 159 | + |
| 160 | +### Method 1: Copy from Windows |
| 161 | + |
| 162 | +```bash |
| 163 | +# 1. From a Windows machine, copy: C:\Windows\Fonts\simsun.ttc (17MB) |
| 164 | +# 2. Transfer to Linux server |
| 165 | + |
| 166 | +# 3. Install to user fonts directory (no sudo needed) |
| 167 | +mkdir -p ~/.local/share/fonts/ |
| 168 | +cp simsun.ttc ~/.local/share/fonts/ |
| 169 | + |
| 170 | +# 4. Refresh caches |
| 171 | +fc-cache -fv |
| 172 | + |
| 173 | +# 5. Clear matplotlib cache |
| 174 | +python3 -c " |
| 175 | +import matplotlib, shutil, os |
| 176 | +c = matplotlib.get_cachedir() |
| 177 | +if os.path.exists(c): shutil.rmtree(c) |
| 178 | +print(f'Cleared: {c}') |
| 179 | +" |
| 180 | + |
| 181 | +# 6. Verify |
| 182 | +fc-list | grep -i simsun |
| 183 | +# Expected output: /home/user/.local/share/fonts/simsun.ttc: SimSun,宋体:style=Regular |
| 184 | +``` |
| 185 | + |
| 186 | +### Method 2: Extract via Wine (no Windows machine needed) |
| 187 | + |
| 188 | +```bash |
| 189 | +sudo apt install winetricks |
| 190 | +winetricks cjkfonts |
| 191 | + |
| 192 | +# Copy the extracted font |
| 193 | +cp ~/.wine/drive_c/windows/Fonts/simsun.ttc ~/.local/share/fonts/ |
| 194 | +fc-cache -fv |
| 195 | +``` |
| 196 | + |
| 197 | +### Method 3: Download from online sources |
| 198 | + |
| 199 | +Some open font archives host SimSun. Search for "simsun.ttc download" — but verify the license terms for your use case. |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +## 5. Deploying on Render.com (Linux Server) |
| 204 | + |
| 205 | +Render servers have no `sudo` access. Three approaches: |
| 206 | + |
| 207 | +### Approach A: Bundle font file in your repo (SimSun) |
| 208 | + |
| 209 | +Project structure: |
| 210 | +``` |
| 211 | +your-project/ |
| 212 | +├── fonts/ |
| 213 | +│ └── simsun.ttc # Copy from Windows |
| 214 | +├── render_build.sh |
| 215 | +├── requirements.txt |
| 216 | +└── app.py |
| 217 | +``` |
| 218 | + |
| 219 | +`render_build.sh`: |
| 220 | +```bash |
| 221 | +#!/bin/bash |
| 222 | +set -e |
| 223 | + |
| 224 | +pip install -r requirements.txt |
| 225 | + |
| 226 | +# Install fonts to user directory (no sudo needed) |
| 227 | +mkdir -p ~/.local/share/fonts/ |
| 228 | +cp fonts/*.ttc ~/.local/share/fonts/ 2>/dev/null || true |
| 229 | +cp fonts/*.ttf ~/.local/share/fonts/ 2>/dev/null || true |
| 230 | +fc-cache -fv 2>/dev/null || true |
| 231 | + |
| 232 | +# Clear matplotlib font cache (CRITICAL — matplotlib won't find new fonts without this) |
| 233 | +python -c " |
| 234 | +import matplotlib, shutil, os |
| 235 | +c = matplotlib.get_cachedir() |
| 236 | +if os.path.exists(c): |
| 237 | + shutil.rmtree(c) |
| 238 | + print(f'Cleared matplotlib cache: {c}') |
| 239 | +" |
| 240 | +``` |
| 241 | + |
| 242 | +Render Dashboard → Settings → **Build Command**: `bash render_build.sh` |
| 243 | + |
| 244 | +### Approach B: Download Noto CJK at build time (no font file in repo) |
| 245 | + |
| 246 | +`render_build.sh`: |
| 247 | +```bash |
| 248 | +#!/bin/bash |
| 249 | +set -e |
| 250 | + |
| 251 | +pip install -r requirements.txt |
| 252 | + |
| 253 | +# Download open-source Noto Serif CJK (思源宋体) |
| 254 | +mkdir -p ~/.local/share/fonts/ |
| 255 | +FONT_URL="https://github.com/notofonts/noto-cjk/releases/download/Serif2.003/08_NotoSerifCJKsc.zip" |
| 256 | +curl -sL "$FONT_URL" -o /tmp/noto-serif.zip |
| 257 | +unzip -o /tmp/noto-serif.zip -d /tmp/noto-serif/ |
| 258 | +cp /tmp/noto-serif/*.otf ~/.local/share/fonts/ 2>/dev/null || true |
| 259 | + |
| 260 | +# Also download Noto Sans CJK (思源黑体) for sans-serif |
| 261 | +SANS_URL="https://github.com/notofonts/noto-cjk/releases/download/Sans2.004/09_NotoSansCJKsc.zip" |
| 262 | +curl -sL "$SANS_URL" -o /tmp/noto-sans.zip |
| 263 | +unzip -o /tmp/noto-sans.zip -d /tmp/noto-sans/ |
| 264 | +cp /tmp/noto-sans/*.otf ~/.local/share/fonts/ 2>/dev/null || true |
| 265 | + |
| 266 | +fc-cache -fv 2>/dev/null || true |
| 267 | + |
| 268 | +# Clear matplotlib cache |
| 269 | +python -c " |
| 270 | +import matplotlib, shutil, os |
| 271 | +c = matplotlib.get_cachedir() |
| 272 | +if os.path.exists(c): |
| 273 | + shutil.rmtree(c) |
| 274 | + print(f'Cleared matplotlib cache: {c}') |
| 275 | +" |
| 276 | + |
| 277 | +echo "Chinese fonts installed successfully" |
| 278 | +``` |
| 279 | + |
| 280 | +### Approach C: Dockerfile (if using Docker on Render) |
| 281 | + |
| 282 | +```dockerfile |
| 283 | +FROM python:3.11-slim |
| 284 | + |
| 285 | +# Install font utilities + open-source Chinese fonts |
| 286 | +RUN apt-get update && \ |
| 287 | + apt-get install -y fontconfig fonts-noto-cjk && \ |
| 288 | + rm -rf /var/lib/apt/lists/* && \ |
| 289 | + fc-cache -fv |
| 290 | + |
| 291 | +# Or use bundled SimSun: |
| 292 | +# COPY fonts/simsun.ttc /usr/share/fonts/truetype/ |
| 293 | +# RUN fc-cache -fv |
| 294 | + |
| 295 | +COPY requirements.txt . |
| 296 | +RUN pip install --no-cache-dir -r requirements.txt |
| 297 | + |
| 298 | +# Clear matplotlib cache |
| 299 | +RUN python -c "import matplotlib, shutil, os; c=matplotlib.get_cachedir(); shutil.rmtree(c, True)" |
| 300 | + |
| 301 | +COPY . /app |
| 302 | +WORKDIR /app |
| 303 | +CMD ["python", "app.py"] |
| 304 | +``` |
| 305 | + |
| 306 | +### After deployment, in your app code: |
| 307 | + |
| 308 | +```python |
| 309 | +import statspai as sp |
| 310 | + |
| 311 | +# Auto-detect the installed Chinese font |
| 312 | +font = sp.use_chinese() |
| 313 | +print(f"Using Chinese font: {font}") |
| 314 | + |
| 315 | +# Now all plots will render Chinese correctly |
| 316 | +fig, ax = plt.subplots() |
| 317 | +ax.set_title('因果推断效果分析') |
| 318 | +ax.set_xlabel('相对处理时间(年)') |
| 319 | +ax.set_ylabel('平均处理效应') |
| 320 | +``` |
| 321 | + |
| 322 | +--- |
| 323 | + |
| 324 | +## 6. Troubleshooting |
| 325 | + |
| 326 | +### Problem: Chinese text shows as boxes (□□□□) |
| 327 | + |
| 328 | +```python |
| 329 | +# Step 1: Check what fonts are available |
| 330 | +import matplotlib.font_manager as fm |
| 331 | +cn_fonts = [f.name for f in fm.fontManager.ttflist |
| 332 | + if any(kw in f.name for kw in ['Song', 'Hei', 'PingFang', 'Noto', 'Hiragino', 'SimSun'])] |
| 333 | +print(f"Chinese fonts found: {cn_fonts}") |
| 334 | + |
| 335 | +# Step 2: If empty, install fonts (see Section 3) |
| 336 | + |
| 337 | +# Step 3: If fonts are installed but still not working, clear cache |
| 338 | +import matplotlib, shutil, os |
| 339 | +cache = matplotlib.get_cachedir() |
| 340 | +shutil.rmtree(cache, ignore_errors=True) |
| 341 | +print(f"Cleared cache: {cache}") |
| 342 | +# Then restart Python/Jupyter kernel |
| 343 | +``` |
| 344 | + |
| 345 | +### Problem: Minus sign shows as "−" (square) with Chinese fonts |
| 346 | + |
| 347 | +```python |
| 348 | +import matplotlib |
| 349 | +matplotlib.rcParams['axes.unicode_minus'] = False # Use hyphen-minus instead |
| 350 | +# sp.use_chinese() does this automatically |
| 351 | +``` |
| 352 | + |
| 353 | +### Problem: Font installed but matplotlib doesn't find it |
| 354 | + |
| 355 | +```bash |
| 356 | +# Rebuild matplotlib font cache |
| 357 | +python3 -c " |
| 358 | +import matplotlib.font_manager as fm |
| 359 | +fm._load_fontmanager(try_read_cache=False) |
| 360 | +print('Font manager rebuilt') |
| 361 | +print(f'Total fonts: {len(fm.fontManager.ttflist)}') |
| 362 | +" |
| 363 | +``` |
| 364 | + |
| 365 | +--- |
| 366 | + |
| 367 | +## 7. Recommendation Summary |
| 368 | + |
| 369 | +| Scenario | Recommended Font | Why | |
| 370 | +|----------|-----------------|-----| |
| 371 | +| Personal use on Mac | `sp.use_chinese()` (auto) | Songti SC pre-installed | |
| 372 | +| Personal use on Windows | `sp.use_chinese()` (auto) | SimSun pre-installed | |
| 373 | +| Linux desktop | `Noto Serif CJK SC` | Best open-source quality | |
| 374 | +| Cloud/server deployment | `Noto Serif CJK SC` | Open source, no license issues | |
| 375 | +| Journal requires "宋体" | Install `SimSun` | Copy from Windows | |
| 376 | +| Cross-platform reproducible | `Noto Serif CJK SC` | Same font everywhere | |
| 377 | +| Presentations/slides | `sp.use_chinese('sans')` | 黑体/苹方 more readable | |
0 commit comments