main.rs
129 lignes · 4753 octets
//! Génère un PDF par langue à partir de `book/<lang>/print.html`. //! //! Backend : `wkhtmltopdf` (léger, stable, gère le JS via QtWebKit). //! Fallback optionnel : `chromium --headless --print-to-pdf`. //! //! Usage : //! cargo run --release --manifest-path scripts/build-pdf/Cargo.toml //! cargo run --release --manifest-path scripts/build-pdf/Cargo.toml -- fr en //! PDF_BACKEND=chromium cargo run --release --manifest-path scripts/build-pdf/Cargo.toml -- fr //! //! Sortie : `book/<lang>/documentation-<lang>.pdf` use std::{env, fs, path::PathBuf, process::Command}; const ALL_LANGS: &[&str] = &["fr", "en", "de", "es", "pt", "it"]; enum Backend { Wkhtmltopdf(String), Chromium(String), } fn pick_backend() -> Option<Backend> { let forced = env::var("PDF_BACKEND").unwrap_or_default(); let try_wk = forced.is_empty() || forced == "wkhtmltopdf"; let try_cr = forced.is_empty() || forced == "chromium"; if try_wk { if Command::new("wkhtmltopdf").arg("--version").output().is_ok() { return Some(Backend::Wkhtmltopdf("wkhtmltopdf".into())); } } if try_cr { for bin in ["chromium-browser", "chromium", "google-chrome"] { if Command::new(bin).arg("--version").output().is_ok() { return Some(Backend::Chromium(bin.into())); } } } None } fn render_wkhtmltopdf(bin: &str, src: &PathBuf, out: &PathBuf, lang: &str) -> std::io::Result<bool> { let url = format!("file://{}", src.display()); // Note : les switches --print-media-type, --header-*, --footer-* ne sont // pas supportés par le wkhtmltopdf non-patché (distro standard). Désactivés // ici pour éviter le bruit. Si tu veux en-tête/pied de page, installer la // version avec Qt patché (wkhtmltopdf.org) et réactiver les flags. let _ = lang; let status = Command::new(bin) .args([ "--enable-local-file-access", "--enable-javascript", "--javascript-delay", "3000", // laisse 3s à Mermaid "--encoding", "UTF-8", "--margin-top", "15mm", "--margin-bottom", "15mm", "--margin-left", "15mm", "--margin-right", "15mm", "--quiet", &url, out.to_str().unwrap(), ]) .status()?; Ok(status.success()) } fn render_chromium(bin: &str, src: &PathBuf, out: &PathBuf, _lang: &str) -> std::io::Result<bool> { let url = format!("file://{}", src.display()); let status = Command::new(bin) .args([ "--headless=new", "--disable-gpu", "--no-sandbox", "--hide-scrollbars", "--disable-background-networking", "--disable-sync", "--no-first-run", "--disable-extensions", "--no-default-browser-check", "--virtual-time-budget=10000", "--run-all-compositor-stages-before-draw", &format!("--print-to-pdf={}", out.display()), "--no-pdf-header-footer", &url, ]) .status()?; Ok(status.success()) } fn main() -> Result<(), Box<dyn std::error::Error>> { let backend = pick_backend() .ok_or("Ni wkhtmltopdf ni chromium trouvés. Installer : apt install wkhtmltopdf")?; let backend_name = match &backend { Backend::Wkhtmltopdf(b) => format!("wkhtmltopdf ({b})"), Backend::Chromium(b) => format!("chromium ({b})"), }; println!("→ Backend : {backend_name}"); let args: Vec<String> = env::args().skip(1).collect(); let langs: Vec<&str> = if args.is_empty() { ALL_LANGS.to_vec() } else { args.iter().map(String::as_str).collect() }; let book = env::current_dir()?.join("book"); let pdf_dir = env::current_dir()?.join("pdf"); fs::create_dir_all(&pdf_dir)?; for lang in langs { let src = book.join(lang).join("print.html"); if !src.exists() { eprintln!("✗ {} introuvable — lance build-all-langs.sh d'abord", src.display()); continue; } let src_abs = fs::canonicalize(&src)?; let out = pdf_dir.join(format!("documentation-{lang}.pdf")); println!("→ {lang} : {} → {}", src_abs.display(), out.display()); let ok = match &backend { Backend::Wkhtmltopdf(b) => render_wkhtmltopdf(b, &src_abs, &out, lang)?, Backend::Chromium(b) => render_chromium(b, &src_abs, &out, lang)?, }; if ok { let kb = out.metadata()?.len() / 1024; println!(" ✓ {} ({} KB)", out.display(), kb); } else { eprintln!(" ✗ backend a échoué pour {lang}"); } } println!("\n✓ Terminé."); Ok(()) }
GitRust