From 6e3dc7807b9e32e9158e4883969d4f0c73adb3d5 Mon Sep 17 00:00:00 2001 From: chenxiangtong Date: Thu, 26 Mar 2026 18:07:53 +0800 Subject: [PATCH] add fonts --- src/render/pillow/font_manager.py | 46 +++++++++++++++++++++++++++++-- src/render/pillow/fonts/.gitkeep | 0 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/render/pillow/fonts/.gitkeep diff --git a/src/render/pillow/font_manager.py b/src/render/pillow/font_manager.py index e16067a..a795a8d 100644 --- a/src/render/pillow/font_manager.py +++ b/src/render/pillow/font_manager.py @@ -8,6 +8,10 @@ _regular_path: str | None = None _bold_path: str | None = None _initialized: bool = False +# 项目内置字体目录:src/render/pillow/fonts/ +# 支持 .ttf / .ttc / .otf,文件名含 "bold" 的优先用作粗体 +_LOCAL_FONTS_DIR = Path(__file__).parent / "fonts" + _CANDIDATES: dict[str, dict[bool, list[str]]] = { "win32": { False: [ @@ -49,18 +53,50 @@ _CANDIDATES: dict[str, dict[bool, list[str]]] = { } +def _scan_local_fonts() -> tuple[str | None, str | None]: + """ + 扫描 src/render/pillow/fonts/ 目录,返回 (regular_path, bold_path)。 + 规则: + - 文件名包含 "bold"(不区分大小写)→ 优先作为粗体 + - 其余文件 → 作为常规字体 + - 若只找到一个文件,常规和粗体均使用它 + """ + if not _LOCAL_FONTS_DIR.is_dir(): + return None, None + + exts = {".ttf", ".ttc", ".otf"} + all_fonts = [p for p in _LOCAL_FONTS_DIR.iterdir() if p.suffix.lower() in exts] + if not all_fonts: + return None, None + + bold_fonts = [p for p in all_fonts if "bold" in p.stem.lower()] + regular_fonts = [p for p in all_fonts if p not in bold_fonts] + + regular = str(regular_fonts[0]) if regular_fonts else str(all_fonts[0]) + bold = str(bold_fonts[0]) if bold_fonts else regular + return regular, bold + + def _init() -> None: global _regular_path, _bold_path, _initialized if _initialized: return + # 1. 优先使用项目内 fonts/ 目录 + local_regular, local_bold = _scan_local_fonts() + if local_regular: + _regular_path = local_regular + _bold_path = local_bold + _initialized = True + return + + # 2. 回退到系统字体 platform = sys.platform if sys.platform in _CANDIDATES else "linux" def _find(bold: bool) -> str | None: for p in _CANDIDATES[platform][bold]: if Path(p).exists(): return p - # Cross-platform fallback: try all platforms' regular fonts for plat_cands in _CANDIDATES.values(): for p in plat_cands[False]: if Path(p).exists(): @@ -72,7 +108,9 @@ def _init() -> None: _initialized = True -def get_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: +def get_font( + size: int, bold: bool = False +) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: """Return a CJK-compatible font at the requested size.""" _init() key = (bold, size) @@ -90,7 +128,9 @@ def get_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFon # Ultimate fallback: PIL built-in bitmap font try: - fb: ImageFont.FreeTypeFont | ImageFont.ImageFont = ImageFont.load_default(size=size) + fb: ImageFont.FreeTypeFont | ImageFont.ImageFont = ImageFont.load_default( + size=size + ) except TypeError: fb = ImageFont.load_default() _cache[key] = fb diff --git a/src/render/pillow/fonts/.gitkeep b/src/render/pillow/fonts/.gitkeep new file mode 100644 index 0000000..e69de29