use anyhow::{Context, Result};
use printpdf::{
ColorBits, ColorSpace, Image, ImageTransform, ImageXObject, Mm, PdfDocument, Px,
};
use std::fs::File;
use std::io::BufWriter;
use std::path::{Path, PathBuf};
pub fn build_pdf(frames: &[PathBuf], output: &Path) -> Result<()> {
const LONG_EDGE_MM: f32 = 297.0;
let (doc, first_page, first_layer) = {
let (w_mm, h_mm) = page_size_for(&frames[0], LONG_EDGE_MM)?;
let (doc, page, layer) =
PdfDocument::new("captube", Mm(w_mm), Mm(h_mm), "Layer 1");
add_frame_to_page(&doc, page, layer, &frames[0], w_mm, h_mm)?;
(doc, page, layer)
};
let _ = (first_page, first_layer);
for frame in frames.iter().skip(1) {
let (w_mm, h_mm) = page_size_for(frame, LONG_EDGE_MM)?;
let (page, layer) = doc.add_page(Mm(w_mm), Mm(h_mm), "Layer 1");
add_frame_to_page(&doc, page, layer, frame, w_mm, h_mm)?;
}
let mut out = BufWriter::new(File::create(output).context("create pdf file")?);
doc.save(&mut out).context("save pdf")?;
Ok(())
}
fn page_size_for(path: &Path, long_edge_mm: f32) -> Result<(f32, f32)> {
let dims = image::image_dimensions(path)
.with_context(|| format!("read dimensions of {}", path.display()))?;
let (w, h) = (dims.0 as f32, dims.1 as f32);
let (w_mm, h_mm) = if w >= h {
(long_edge_mm, long_edge_mm * h / w)
} else {
(long_edge_mm * w / h, long_edge_mm)
};
Ok((w_mm, h_mm))
}
fn add_frame_to_page(
doc: &printpdf::PdfDocumentReference,
page: printpdf::PdfPageIndex,
layer: printpdf::PdfLayerIndex,
path: &Path,
page_w_mm: f32,
page_h_mm: f32,
) -> Result<()> {
let dyn_img = image::open(path)
.with_context(|| format!("decode image {}", path.display()))?
.to_rgb8();
let (w, h) = (dyn_img.width(), dyn_img.height());
let data = dyn_img.into_raw();
let xobj = ImageXObject {
width: Px(w as usize),
height: Px(h as usize),
color_space: ColorSpace::Rgb,
bits_per_component: ColorBits::Bit8,
interpolate: true,
image_data: data,
image_filter: None,
clipping_bbox: None,
smask: None,
};
let img = Image::from(xobj);
let dpi_x = w as f32 / (page_w_mm / 25.4);
let dpi_y = h as f32 / (page_h_mm / 25.4);
let dpi = dpi_x.min(dpi_y);
let layer_ref = doc.get_page(page).get_layer(layer);
img.add_to_layer(
layer_ref,
ImageTransform {
translate_x: Some(Mm(0.0)),
translate_y: Some(Mm(0.0)),
rotate: None,
scale_x: None,
scale_y: None,
dpi: Some(dpi),
},
);
Ok(())
}