"""Поиск системных шрифтов (в т.ч. с кириллицей).""" from __future__ import annotations import subprocess from functools import lru_cache from pathlib import Path SEARCH_DIRS = [ Path(__file__).resolve().parent / "assets" / "fonts", # шрифты, вложенные в проект Path("/usr/share/fonts"), Path("/usr/local/share/fonts"), Path.home() / ".local/share/fonts", Path("/Library/Fonts"), Path("/System/Library/Fonts"), Path("C:/Windows/Fonts"), ] ALIASES = { "serif": "DejaVuSerif", "serif-bold": "DejaVuSerif-Bold", "serif-italic": "DejaVuSerif-Italic", "serif-bolditalic": "DejaVuSerif-BoldItalic", "sans": "DejaVuSans", "sans-bold": "DejaVuSans-Bold", "sans-italic": "DejaVuSans-Oblique", "sans-bolditalic": "DejaVuSans-BoldOblique", "mono": "DejaVuSansMono", } @lru_cache(maxsize=1) def _index() -> dict[str, str]: idx: dict[str, str] = {} for d in SEARCH_DIRS: if not d.exists(): continue for f in d.rglob("*"): if f.suffix.lower() in (".ttf", ".otf", ".ttc"): idx.setdefault(f.stem.lower(), str(f)) idx.setdefault(f.name.lower(), str(f)) return idx def _fallback() -> str: idx = _index() for key in ("dejavusans", "dejavuserif", "liberationsans", "notosans", "arial", "arialunicode"): if key in idx: return idx[key] return next(iter(idx.values()), "") @lru_cache(maxsize=256) def find_font(name: str | None) -> str: """Вернуть путь к файлу шрифта по имени или алиасу.""" name = (name or "sans").strip() p = Path(name) if p.is_absolute() and p.exists(): return str(p) key = ALIASES.get(name.lower(), name).lower() idx = _index() if key in idx: return idx[key] if key + ".ttf" in idx: return idx[key + ".ttf"] # частичное совпадение matches = [v for k, v in idx.items() if key in k] if matches: return matches[0] # спросим fontconfig try: out = subprocess.run( ["fc-match", "-f", "%{file}", name], capture_output=True, text=True, timeout=10, ) path = out.stdout.strip() if path and Path(path).exists(): return path except Exception: pass return _fallback()