fix(ppt): matplotlib CJK fallback covers container Noto CJK JP variant
All checks were successful
CD Pipeline / deploy (push) Successful in 2m19s
All checks were successful
CD Pipeline / deploy (push) Successful in 2m19s
實戰驗證在 188 容器內發現:matplotlib font_manager 從 fonts-noto-cjk ttc 檔只載入 'Noto Sans CJK JP' / 'Noto Serif CJK JP' 兩個變體(TC/SC/HK/KR 名稱未列入 ttflist),導致原本 fallback 清單比對失敗 → font.family 退到 'sans-serif'(DejaVu Sans)→ 中文 glyph 全部缺失印出 UserWarning。 修正: - _mpl_setup() fallback 清單加入 'Noto Sans CJK JP' / 'Noto Serif CJK JP' - 最終 fallback 加 substring match(涵蓋未列入但名稱含 CJK/PingFang/ JhengHei/YaHei/Source Han/WenQuanYi/Hiragino 的字型) - _mpl_horiz_bar_png 內重複的 cjk_candidates 邏輯移除,改呼叫 _mpl_setup 避免兩處清單漂移 註:Noto Sans CJK JP ttc 檔本身含完整 CJK Unified Ideographs,可正常 渲染中文(漢字共用),只是字型名稱叫 JP 而已。 bump 模板版本(舊圖表中文方塊版本快取應失效重生): - daily v3.0 → v3.0.1 - weekly v3.0 → v3.0.1 - monthly v3.1 → v3.1.1 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,9 +41,9 @@ REPORTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
# 路由層會把版本號併入快取 key,舊快取自然 miss → 重新生成。
|
||||
# Bump 規則:major 設計改版 +0.1;微調文案不需 bump。
|
||||
TEMPLATE_VERSIONS = {
|
||||
'daily': 'v3.0', # 2026-05-02 暖紙封面 + matplotlib 折線 + 4 卡指標 + 附錄頁
|
||||
'weekly': 'v3.0', # 2026-05-02 暖紙封面 + matplotlib 折線 + 區間結論帶 + 附錄頁
|
||||
'monthly': 'v3.1', # 2026-05-02 v3.1 升級:封面 elevator + KPI △% + 趨勢折線 + 帕雷托 + 商品 △/🆕 + 附錄頁
|
||||
'daily': 'v3.0.1', # 2026-05-02 (CJK fix) matplotlib fallback 涵蓋容器 Noto CJK JP
|
||||
'weekly': 'v3.0.1', # 2026-05-02 (CJK fix) 同上
|
||||
'monthly': 'v3.1.1', # 2026-05-02 (CJK fix) v3.1 + 容器 CJK 字型 fallback 修正
|
||||
'strategy': 'v3.0', # 2026-05-02 AI 頁去黑改暖紙 + 附錄頁
|
||||
'competitor': 'v3.0', # 2026-05-02 AI 頁去黑改暖紙 + 附錄頁
|
||||
'promo': 'v3.0', # 2026-05-02 AI 頁去黑改暖紙 + 附錄頁
|
||||
@@ -484,18 +484,8 @@ def _mpl_horiz_bar_png(categories, values, total_width_cm=18.5, total_height_cm=
|
||||
if not categories or not values:
|
||||
return None
|
||||
|
||||
# 嘗試找一個能顯示中文的字型(macOS / Linux 容器都適用)
|
||||
cjk_candidates = [
|
||||
"PingFang TC", "PingFang SC", "Heiti TC", "Microsoft JhengHei",
|
||||
"Noto Sans CJK TC", "Noto Sans TC", "Noto Sans CJK",
|
||||
"Source Han Sans TC", "Source Han Sans", "WenQuanYi Zen Hei",
|
||||
"Hiragino Sans GB", "Arial Unicode MS",
|
||||
]
|
||||
available = {f.name for f in fm.fontManager.ttflist}
|
||||
chosen_cjk = next((f for f in cjk_candidates if f in available), None)
|
||||
if chosen_cjk:
|
||||
plt.rcParams["font.family"] = [chosen_cjk, "DejaVu Sans"]
|
||||
plt.rcParams["axes.unicode_minus"] = False
|
||||
# 共用 _mpl_setup 的 fallback 邏輯(含容器 Noto CJK JP 變體)
|
||||
_mpl_setup()
|
||||
|
||||
fig = None
|
||||
try:
|
||||
@@ -581,7 +571,13 @@ def _add_image_from_buf(slide, buf, l, t, w, h):
|
||||
|
||||
|
||||
def _mpl_setup():
|
||||
"""共用:設定 CJK 字型並回傳 (matplotlib, plt)"""
|
||||
"""共用:設定 CJK 字型並回傳 (matplotlib, plt)。
|
||||
|
||||
fallback 策略:先試精確名稱(macOS/Windows 環境),再試容器常見的 Noto CJK
|
||||
JP 變體(fonts-noto-cjk 套件 ttc 檔,matplotlib ttflist 只載入 JP 命名,但
|
||||
字型本身含完整 CJK Unified Ideographs,可正常顯示中文)。
|
||||
最終都找不到時用 substring match(覆蓋未列入但名稱含 CJK/Hei 的字型)。
|
||||
"""
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
@@ -589,14 +585,30 @@ def _mpl_setup():
|
||||
import matplotlib.font_manager as fm
|
||||
except ImportError:
|
||||
return None, None
|
||||
|
||||
cjk_candidates = [
|
||||
"PingFang TC", "PingFang SC", "Heiti TC", "Microsoft JhengHei",
|
||||
"Noto Sans CJK TC", "Noto Sans TC", "Noto Sans CJK",
|
||||
"Source Han Sans TC", "Source Han Sans", "WenQuanYi Zen Hei",
|
||||
"Hiragino Sans GB", "Arial Unicode MS",
|
||||
# macOS
|
||||
"PingFang TC", "PingFang SC", "Heiti TC", "Hiragino Sans GB",
|
||||
# Windows
|
||||
"Microsoft JhengHei", "Microsoft YaHei",
|
||||
# Linux/Docker(noto-cjk 套件 ttc 檔,matplotlib ttflist 只認 JP 變體
|
||||
# 但實際渲染中文 OK,因為 ttc 內共用同一漢字字型表)
|
||||
"Noto Sans CJK JP", "Noto Serif CJK JP",
|
||||
"Noto Sans CJK TC", "Noto Sans CJK SC", "Noto Sans CJK",
|
||||
"Noto Sans TC", "Noto Sans SC",
|
||||
"Source Han Sans TC", "Source Han Sans SC", "Source Han Sans",
|
||||
"WenQuanYi Zen Hei", "Arial Unicode MS",
|
||||
]
|
||||
available = {f.name for f in fm.fontManager.ttflist}
|
||||
chosen_cjk = next((f for f in cjk_candidates if f in available), None)
|
||||
if not chosen_cjk:
|
||||
# 最後手段:substring match
|
||||
for name in available:
|
||||
if any(k in name for k in ("CJK", "PingFang", "JhengHei", "YaHei",
|
||||
"Source Han", "WenQuanYi", "Hiragino")):
|
||||
chosen_cjk = name
|
||||
break
|
||||
|
||||
if chosen_cjk:
|
||||
plt.rcParams["font.family"] = [chosen_cjk, "DejaVu Sans"]
|
||||
plt.rcParams["axes.unicode_minus"] = False
|
||||
|
||||
Reference in New Issue
Block a user