use crate::{
config::{ChromeConfig, Margins, Orientation, PageSize, PdfConfig},
error::Result,
input::InputSource,
renderer::{PdfRenderer, ResolvedInput},
};
#[cfg(feature = "chrome")]
use crate::renderer::chrome::ChromeRenderer;
pub struct PdfBuilder {
input: Option<InputSource>,
config: PdfConfig,
chrome_config: ChromeConfig,
}
impl Default for PdfBuilder {
fn default() -> Self {
Self::new()
}
}
impl PdfBuilder {
pub fn new() -> Self {
Self {
input: None,
config: PdfConfig::default(),
chrome_config: ChromeConfig::default(),
}
}
pub fn html<S: Into<String>>(mut self, content: S) -> Self {
self.input = Some(InputSource::html(content));
self
}
pub fn file<S: Into<std::path::PathBuf>>(mut self, path: S) -> Self {
self.input = Some(InputSource::file(path));
self
}
pub fn url<S: Into<String>>(mut self, url: S) -> Self {
self.input = Some(InputSource::url(url));
self
}
#[cfg(feature = "templates")]
pub fn template<S, D>(mut self, template: S, data: D) -> Result<Self>
where
S: Into<String>,
D: serde::Serialize,
{
self.input = Some(InputSource::template(template, data)?);
Ok(self)
}
#[cfg(feature = "templates")]
pub fn template_file<P, D>(mut self, path: P, data: D) -> Result<Self>
where
P: Into<std::path::PathBuf>,
D: serde::Serialize,
{
self.input = Some(InputSource::template_file(path, data)?);
Ok(self)
}
pub fn page_size(mut self, size: PageSize) -> Self {
self.config.page_size = size;
self
}
pub fn custom_page_size(mut self, width: f64, height: f64) -> Self {
self.config.page_size = PageSize::Custom { width, height };
self
}
pub fn orientation(mut self, orientation: Orientation) -> Self {
self.config.orientation = orientation;
self
}
pub fn margin_all(mut self, margin: f64) -> Self {
self.config.margins = Margins::uniform(margin);
self
}
pub fn margins(mut self, top: f64, right: f64, bottom: f64, left: f64) -> Self {
self.config.margins = Margins {
top,
bottom,
left,
right,
};
self
}
pub fn scale(mut self, scale: f64) -> Self {
self.config.scale = scale;
self
}
pub fn print_background(mut self, enabled: bool) -> Self {
self.config.print_background = enabled;
self
}
pub fn header<S: Into<String>>(mut self, template: S) -> Self {
self.config.header_template = Some(template.into());
self.config.display_header_footer = true;
self
}
pub fn footer<S: Into<String>>(mut self, template: S) -> Self {
self.config.footer_template = Some(template.into());
self.config.display_header_footer = true;
self
}
pub fn timeout(mut self, seconds: u64) -> Self {
self.config.timeout_seconds = seconds;
self
}
pub fn chrome_path<S: Into<String>>(mut self, path: S) -> Self {
self.chrome_config.binary_path = Some(path.into());
self
}
pub fn no_sandbox(mut self) -> Self {
self.chrome_config.no_sandbox = true;
self
}
pub fn chrome_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.chrome_config
.extra_args
.extend(args.into_iter().map(|s| s.into()));
self
}
#[cfg(feature = "chrome")]
pub async fn build(self) -> Result<Vec<u8>> {
let input = self
.input
.ok_or_else(|| crate::Error::InvalidConfig("No input source specified".to_string()))?;
self.config.validate()?;
let resolved = match input {
InputSource::Url(url) => ResolvedInput::Url(url),
other => ResolvedInput::Html(other.resolve().await?),
};
let renderer = ChromeRenderer::new(self.chrome_config).await?;
renderer.render(resolved, self.config).await
}
pub async fn build_with<R: PdfRenderer>(
self,
renderer: &R,
) -> Result<Vec<u8>> {
let input = self.input.ok_or_else(||
crate::Error::InvalidConfig("No input source specified".to_string())
)?;
self.config.validate()?;
let resolved = match input {
InputSource::Url(url) => ResolvedInput::Url(url),
other => ResolvedInput::Html(other.resolve().await?),
};
renderer.render(resolved, self.config).await
}
}