diff --git a/README.md b/README.md index 3bad7f3..553cdd1 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ correspondant à votre système : Une fenêtre s'ouvre et vous guide : -1. elle vérifie que **Python** et **Quarto** sont installés, et propose de les installer automatiquement sinon ; +1. elle vérifie que **Python**, **Quarto** et les polices **Noto Sans / Noto Sans Mono** sont installés, + et propose de les installer automatiquement sinon (sous Linux, les polices sont généralement déjà présentes) ; 2. elle prépare l'environnement Python des schémas (dossier `.venv`, une seule fois) ; 3. elle vous demande le **format** (PDF, LibreOffice .odt ou site web HTML) et les **supports** à produire (tous, ou un seul module) ; @@ -54,7 +55,9 @@ _output/ PDF générés (non versionnés) ## Produire les PDF -Prérequis : [Quarto](https://quarto.org/docs/get-started/) (inclut Pandoc et Typst). +Prérequis : [Quarto](https://quarto.org/docs/get-started/) (inclut Pandoc et Typst) et les polices libres +[Noto Sans et Noto Sans Mono](https://notofonts.github.io/) (paquets `noto-fonts` sous Arch, +`fonts-noto-core fonts-noto-mono` sous Debian/Ubuntu). ``` quarto render # tous les supports diff --git a/_quarto.yml b/_quarto.yml index ac80fc4..cf0b3de 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -17,9 +17,9 @@ format: margin: x: 2cm y: 2.2cm - mainfont: "Segoe UI" + mainfont: "Noto Sans" fontsize: 10pt - codefont: "Consolas" + codefont: "Noto Sans Mono" toc: false highlight-style: modele/code.theme include-in-header: modele/entete.typ diff --git a/scripts/assistant.py b/scripts/assistant.py index 36f55b6..e0b1bef 100644 --- a/scripts/assistant.py +++ b/scripts/assistant.py @@ -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)