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(())
}