Polices libres Noto Sans / Noto Sans Mono, vérifiées et installées par l'assistant

Segoe UI et Consolas (polices Microsoft, non redistribuables) provoquaient des avertissements Typst sous Linux et macOS. Le gabarit utilise désormais Noto Sans et Noto Sans Mono. L'assistant vérifie leur présence (quarto typst fonts) et sinon les installe : paquets système sous Linux, ou téléchargement depuis le dépôt officiel Noto dans le dossier de polices de l'utilisateur (Windows, macOS, et secours Linux).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Artemia
2026-10-18 17:00:53 +02:00
co-authored by Claude Opus 5.5
parent 5b312f7459
commit 503aa66dc0
3 changed files with 87 additions and 7 deletions
+80 -3
View File
@@ -159,7 +159,7 @@ def installer_quarto_arch():
def verifier_quarto():
titre("1/3 Quarto (outil de mise en page)")
titre("1/4 Quarto (outil de mise en page)")
quarto = trouver_quarto()
if quarto:
print(f" Quarto est installé : {quarto}")
@@ -179,6 +179,82 @@ def verifier_quarto():
return quarto
# --- Polices -----------------------------------------------------------------------
DEPOT_NOTO = "https://github.com/notofonts/notofonts.github.io/raw/main/fonts"
POLICES = { # famille attendue par _quarto.yml -> fichiers à installer si elle manque
"Noto Sans": [f"NotoSans/hinted/ttf/NotoSans-{s}.ttf" for s in
("Regular", "Italic", "SemiBold", "SemiBoldItalic", "Bold", "BoldItalic")],
"Noto Sans Mono": [f"NotoSansMono/hinted/ttf/NotoSansMono-{s}.ttf" for s in ("Regular", "Bold")],
}
PAQUETS_POLICES = { # gestionnaire Linux -> commande d'installation des deux familles
"apt-get": ["sudo", "apt-get", "install", "-y", "fonts-noto-core", "fonts-noto-mono"],
"pacman": ["sudo", "pacman", "-S", "--needed", "noto-fonts"],
"dnf": ["sudo", "dnf", "install", "-y", "google-noto-sans-fonts", "google-noto-sans-mono-fonts"],
"zypper": ["sudo", "zypper", "install", "-y", "noto-sans-fonts", "noto-sans-mono-fonts"],
}
def polices_manquantes(quarto):
"""Familles absentes, d'après la liste des polices réellement visibles par Typst."""
resultat = subprocess.run([quarto, "typst", "fonts"], capture_output=True, text=True,
encoding="utf-8", errors="replace")
sortie = resultat.stdout + resultat.stderr # Quarto écrit la liste sur la sortie d'erreur
familles = {ligne.strip() for ligne in sortie.splitlines()}
return [f for f in POLICES if f not in familles]
def dossier_polices_utilisateur():
if SYSTEME == "Windows":
return Path(os.environ["LOCALAPPDATA"]) / "Microsoft/Windows/Fonts"
if SYSTEME == "Darwin":
return Path.home() / "Library/Fonts"
return Path.home() / ".local/share/fonts"
def telecharger_polices(familles):
"""Installe les polices pour l'utilisateur courant (aucun droit administrateur nécessaire)."""
dossier = dossier_polices_utilisateur()
dossier.mkdir(parents=True, exist_ok=True)
for famille in familles:
for chemin in POLICES[famille]:
cible = dossier / Path(chemin).name
print(f" Téléchargement de {cible.name}...")
urllib.request.urlretrieve(f"{DEPOT_NOTO}/{chemin}", cible)
if SYSTEME == "Windows": # déclarer la police pour l'utilisateur (comme « Installer » dans l'Explorateur)
import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows NT\CurrentVersion\Fonts", 0,
winreg.KEY_SET_VALUE) as cle:
winreg.SetValueEx(cle, f"{cible.stem} (TrueType)", 0, winreg.REG_SZ, str(cible))
if SYSTEME == "Linux" and shutil.which("fc-cache"):
subprocess.run(["fc-cache", "-f", str(dossier)], stdout=subprocess.DEVNULL)
def verifier_polices(quarto):
titre("2/4 Polices (Noto Sans et Noto Sans Mono)")
manquantes = polices_manquantes(quarto)
if not manquantes:
print(" Les polices sont installées.")
return
print(f" Polices manquantes : {', '.join(manquantes)}.")
print(" Sans elles, les documents seraient produits avec une police de remplacement.")
if not oui_non(" Les installer maintenant ?"):
return
gestionnaire = next((g for g in PAQUETS_POLICES if SYSTEME == "Linux" and shutil.which(g)), None)
try:
if gestionnaire:
print(" Votre mot de passe peut être demandé (installation système).")
subprocess.run(PAQUETS_POLICES[gestionnaire])
if not gestionnaire or polices_manquantes(quarto):
telecharger_polices(polices_manquantes(quarto))
except Exception as erreur:
print(f" L'installation des polices a échoué : {erreur}")
reste = polices_manquantes(quarto)
print(" Polices installées." if not reste else
f" Toujours introuvables : {', '.join(reste)} (les documents seront produits quand même).")
# --- Environnement Python des schémas ----------------------------------------------
def python_du_venv():
@@ -186,7 +262,7 @@ def python_du_venv():
def preparer_venv():
titre("2/3 Environnement Python (schémas)")
titre("3/4 Environnement Python (schémas)")
empreinte = hashlib.sha256(EXIGENCES.read_bytes()).hexdigest()
temoin = VENV / ".empreinte-requirements"
if python_du_venv().exists() and temoin.exists() and temoin.read_text().strip() == empreinte:
@@ -231,7 +307,7 @@ def ouvrir_dossier(dossier):
def generer(quarto, venv_ok):
titre("3/3 Génération des documents")
titre("4/4 Génération des documents")
i_format = choisir("Quel format ?", [f[0] for f in FORMATS])
libelle, cible, extension = FORMATS[i_format]
supports = lister_supports()
@@ -268,6 +344,7 @@ def main():
print("Python 3.9 ou plus récent est nécessaire.")
raise SystemExit(1)
quarto = verifier_quarto()
verifier_polices(quarto)
venv_ok = preparer_venv()
while True:
generer(quarto, venv_ok)