MedgeClaw cjk-viz

install
source · Clone the upstream repo
git clone https://github.com/xjtulyc/MedgeClaw
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/xjtulyc/MedgeClaw "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/cjk-viz" ~/.claude/skills/xjtulyc-medgeclaw-cjk-viz && rm -rf "$T"
manifest: skills/cjk-viz/SKILL.md
source content

CJK 可视化字体配置

何时使用

任何绘图代码中包含中文文本(标题、轴标签、图例、注释)时,必须在绘图前 执行字体检测。不要假设某个字体一定存在。

快速使用

方式一:导入 helper(推荐)

scripts/setup_cjk_font.py
复制到工作目录,或直接引用:

import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'skills/cjk-viz/scripts'))
from setup_cjk_font import setup_cjk_font

font_name = setup_cjk_font()  # 自动检测、配置、返回字体名
# 如果返回 None,说明系统无可用 CJK 字体,会打印警告

调用后

plt.rcParams
已经配置好,直接绘图即可。

方式二:内联代码片段

如果不想引入外部文件,在脚本开头加入:

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

def _setup_cjk():
    candidates = [
        'Noto Sans CJK SC', 'Noto Sans SC', 'Source Han Sans SC',
        'WenQuanYi Micro Hei', 'WenQuanYi Zen Hei',
        'Droid Sans Fallback', 'AR PL UMing CN',
        'SimHei', 'Microsoft YaHei', 'PingFang SC',
    ]
    available = {f.name for f in fm.fontManager.ttflist}
    for name in candidates:
        if name in available:
            plt.rcParams['font.sans-serif'] = [name, 'DejaVu Sans']
            plt.rcParams['axes.unicode_minus'] = False
            return name
    # 尝试从常见路径加载 .ttf
    search_paths = [
        '/usr/share/fonts', '/usr/local/share/fonts',
        os.path.expanduser('~/.local/share/fonts'),
        os.path.join(os.path.dirname(__file__), 'fonts'),
    ]
    for base in search_paths:
        for root, _, files in os.walk(base):
            for f in files:
                if f.lower().endswith('.ttf') and any(
                    k in f.lower() for k in ['noto', 'cjk', 'hei', 'han', 'wenquan', 'droid']
                ):
                    path = os.path.join(root, f)
                    fm.fontManager.addfont(path)
                    prop = fm.FontProperties(fname=path)
                    name = prop.get_name()
                    plt.rcParams['font.sans-serif'] = [name, 'DejaVu Sans']
                    plt.rcParams['axes.unicode_minus'] = False
                    return name
    print("⚠️  未找到 CJK 字体,中文可能显示为方块。")
    print("   安装建议: apt install fonts-noto-cjk 或 pip install matplotlib-cjk-fonts")
    return None

_cjk_font = _setup_cjk()

方式三:安装字体后再绘图

如果检测失败,在 Docker 容器内安装:

apt-get update && apt-get install -y fonts-noto-cjk
# 或者用 pip 安装打包好的字体
pip install matplotlib-cjk-fonts

安装后需要清除 matplotlib 字体缓存:

import matplotlib
import shutil, os
cache_dir = matplotlib.get_cachedir()
if os.path.exists(cache_dir):
    shutil.rmtree(cache_dir)
    print(f"已清除缓存: {cache_dir}")

关键陷阱:
.ttc
文件与 matplotlib

这是最常见的坑。 很多 Linux/Docker 环境安装的 CJK 字体是

.ttc
(TrueType Collection) 格式(如
NotoSansCJK-Regular.ttc
),matplotlib 能检测到但
rcParams
设置后不生效。

症状

  • setup_cjk_font()
    报告成功,但图片中文仍显示为方块 □□□
  • findfont()
    能找到字体文件,但渲染时不使用

解决方案:FontProperties 模式

.ttc
文件,必须用
FontProperties(fname=path)
显式传给每个文本元素:

from matplotlib.font_manager import FontProperties

# 全局 FontProperties 对象
CJK_FP = FontProperties(fname='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')

# 用法: 每个含中文的文本元素都要传 fontproperties=CJK_FP
ax.set_xlabel('中文标签', fontproperties=CJK_FP)
ax.set_ylabel('中文标签', fontproperties=CJK_FP)
ax.set_title('中文标题', fontproperties=CJK_FP)
ax.set_yticklabels(chinese_labels, fontproperties=CJK_FP)

# legend 需要特殊处理: prop= 设置条目字体, title 需要单独设置
ax.legend(title='中文图例标题', prop=CJK_FP)
ax.get_legend().get_title().set_fontproperties(CJK_FP)

# suptitle 同理
plt.suptitle('中文总标题', fontproperties=CJK_FP)

优先级策略

  1. 优先找
    .ttf
    文件
    → 可以用
    rcParams
    全局设置,最省事
  2. 只有
    .ttc
    文件
    → 必须用
    FontProperties(fname=)
    逐个传参
  3. 都没有 → 安装字体或用内嵌
    .ttf

helper 脚本已内置此逻辑

scripts/setup_cjk_font.py
setup_cjk_font()
会优先找
.ttf
, 找不到时返回
.ttc
路径。调用
get_cjk_fp()
获取
FontProperties
对象。

字体优先级

按以下顺序尝试(覆盖大多数 Linux / Docker / macOS 环境):

优先级字体名常见来源
1Noto Sans CJK SC
fonts-noto-cjk
(Debian/Ubuntu)
2Noto Sans SCGoogle Fonts
3Source Han Sans SCAdobe 思源黑体
4WenQuanYi Micro Hei
fonts-wqy-microhei
5WenQuanYi Zen Hei
fonts-wqy-zenhei
6Droid Sans FallbackAndroid / 旧版 Docker 镜像
7AR PL UMing CN
fonts-arphic-uming
8SimHeiWindows
9Microsoft YaHeiWindows
10PingFang SCmacOS

与其他 skill 配合

  • 使用
    scientific-visualization
    matplotlib
    skill 时,先执行本 skill 的字体配置
  • 使用
    plotly
    生成静态图片(
    write_image
    )时同样需要配置字体
  • biomed-dispatch
    的 prompt 中可以加入:"绘图前先运行 cjk-viz 字体检测"

验证

绘图后可以用以下代码快速验证中文是否正常渲染:

fig, ax = plt.subplots(figsize=(4, 2))
ax.text(0.5, 0.5, '中文测试 Chinese Test 123',
        ha='center', va='center', fontsize=16, transform=ax.transAxes)
ax.set_title('字体验证')
fig.savefig('/workspace/outputs/cjk_font_test.png', dpi=100, bbox_inches='tight')
print("✅ 验证图已保存,请检查中文是否正常显示")