87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
"""Поиск системных шрифтов (в т.ч. с кириллицей)."""
|
|
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()
|