use anyhow::{Context, Result};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use which::which;
pub fn find_browser_executable() -> Result<PathBuf> {
let candidates = [
"chromium",
"google-chrome",
"google-chrome-stable",
"chrome",
"brave",
"microsoft-edge",
];
for candidate in candidates {
if let Ok(path) = which(candidate) {
return Ok(path);
}
}
if let Some(home) = dirs::home_dir() {
let home_bin_chrome = home.join("bin/google-chrome");
if home_bin_chrome.is_file() {
return Ok(home_bin_chrome);
}
}
anyhow::bail!(
"No Chromium-compatible browser found (checked chromium, google-chrome, brave, edge).\n\
Please install chromium (e.g. 'sudo pacman -S chromium' or 'apt install chromium-browser')."
)
}
pub fn render_html_to_pdf(html_path: &Path, output_pdf: &Path) -> Result<()> {
let browser = find_browser_executable()?;
let abs_html = fs::canonicalize(html_path)
.with_context(|| format!("Failed to canonicalize HTML path: {}", html_path.display()))?;
let abs_pdf = if output_pdf.is_absolute() {
output_pdf.to_path_buf()
} else {
std::env::current_dir()?.join(output_pdf)
};
if let Some(parent) = abs_pdf.parent() {
fs::create_dir_all(parent)?;
}
let url = format!("file://{}", abs_html.display());
let pdf_arg = format!("--print-to-pdf={}", abs_pdf.display());
let output = Command::new(&browser)
.arg("--headless")
.arg("--disable-gpu")
.arg("--no-pdf-header-footer")
.arg("--run-all-compositor-stages-before-draw")
.arg("--virtual-time-budget=3000") .arg(&pdf_arg)
.arg(&url)
.output()
.with_context(|| format!("Failed to execute browser at {}", browser.display()))?;
if !output.status.success() && !abs_pdf.exists() {
let err_str = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Browser PDF rendering failed: {}", err_str);
}
if !abs_pdf.exists() {
anyhow::bail!("PDF output file was not created at {}", abs_pdf.display());
}
Ok(())
}