use std::path::Path;
use std::process::ExitCode;
use std::time::Instant;
use pdfrum::{Document, Error, Pixmap, RenderOptions, RenderSession, VelloCpuBackend};
use rayon::prelude::*;
fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
let Some(input) = args.first() else {
eprintln!("usage: parallel-render <input.pdf> [scale]");
return ExitCode::FAILURE;
};
let scale: f64 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(2.0);
match run(Path::new(input), scale) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("error: {err}");
ExitCode::FAILURE
}
}
}
fn run(input: &Path, scale: f64) -> Result<(), Error> {
let doc = Document::open(input)?;
let options = RenderOptions::scaled(scale);
let pages: Vec<_> = doc.pages().collect();
println!(
"{} page(s) at {scale}x on {} threads",
pages.len(),
rayon::current_num_threads()
);
let backend = VelloCpuBackend::new();
let started = Instant::now();
let serial: Vec<Pixmap> = {
let mut session = RenderSession::new();
pages
.iter()
.map(|page| page.render_on(&backend, &options, &mut session))
.collect::<Result<_, _>>()?
};
let serial_time = started.elapsed();
let started = Instant::now();
let parallel: Vec<Pixmap> = pages
.par_iter()
.map_init(RenderSession::new, |session, page| {
page.render_on(&backend, &options, session)
})
.collect::<Result<_, _>>()?;
let parallel_time = started.elapsed();
println!("serial: {serial_time:>10.2?}");
println!("parallel: {parallel_time:>10.2?}");
assert_eq!(serial, parallel, "parallel output must match serial output");
println!("both runs produced identical pixels");
let total: usize = parallel
.iter()
.map(|p| p.width() as usize * p.height() as usize)
.sum();
println!("{total} pixels rendered");
Ok(())
}