#![doc = include_str!("../README_DOCS.md")]
pub mod config;
pub mod css;
pub mod error;
pub mod parser;
pub mod renderer;
pub use config::{
FooterConfig, HeaderConfig, MarkdownOptions, PageMargins, PaperSize, PdfConfig,
};
pub use css::DEFAULT_CSS;
pub use error::{MdocError, Result};
use log::info;
use serde_json::Value;
use parser::{json_to_markdown, markdown_to_body_html};
use renderer::{
build_footer_template, build_header_template, render_html_to_pdf, wrap_body_in_html_document,
};
pub struct PdfBuilder {
config: PdfConfig,
}
impl PdfBuilder {
pub fn new() -> Self {
Self {
config: PdfConfig::default(),
}
}
pub fn with_config(config: PdfConfig) -> Self {
Self { config }
}
pub fn config(mut self, config: PdfConfig) -> Self {
self.config = config;
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.config.title = title.into();
self
}
pub fn custom_css(mut self, css: impl Into<String>) -> Self {
self.config.custom_css = Some(css.into());
self
}
pub fn extra_css(mut self, css: impl Into<String>) -> Self {
self.config.extra_css = Some(css.into());
self
}
pub fn paper_size(mut self, size: PaperSize) -> Self {
self.config.paper_size = size;
self
}
pub fn margins(mut self, margins: PageMargins) -> Self {
self.config.margins = margins;
self
}
pub fn landscape(mut self, yes: bool) -> Self {
self.config.landscape = yes;
self
}
pub fn header(mut self, header: HeaderConfig) -> Self {
self.config.header = header;
self
}
pub fn footer(mut self, footer: FooterConfig) -> Self {
self.config.footer = footer;
self
}
pub fn display_header_footer(mut self, yes: bool) -> Self {
self.config.display_header_footer = yes;
self
}
pub fn print_background(mut self, yes: bool) -> Self {
self.config.print_background = yes;
self
}
pub fn markdown_options(mut self, opts: MarkdownOptions) -> Self {
self.config.markdown_options = opts;
self
}
pub fn chrome_window_size(mut self, width: u32, height: u32) -> Self {
self.config.chrome_window_size = (width, height);
self
}
pub fn page_load_wait_secs(mut self, secs: u64) -> Self {
self.config.page_load_wait_secs = secs;
self
}
pub fn heading_numbers(mut self, yes: bool) -> Self {
self.config.heading_numbers = yes;
self
}
pub fn from_markdown(self, md: &str) -> Result<Vec<u8>> {
info!("Generating PDF from Markdown ({} chars)", md.len());
let body_html = markdown_to_body_html(md, &self.config)?;
let full_html = wrap_body_in_html_document(&body_html, &self.config);
let header = build_header_template(&self.config);
let footer = build_footer_template(&self.config);
let pdf = render_html_to_pdf(&full_html, &header, &footer, &self.config)?;
info!("PDF from Markdown: {} bytes", pdf.len());
Ok(pdf)
}
pub fn from_json(self, json_doc: &Value) -> Result<Vec<u8>> {
info!("Generating PDF from JSON");
let markdown = json_to_markdown(json_doc)?;
self.from_markdown(&markdown)
}
pub fn from_html(self, html: &str) -> Result<Vec<u8>> {
info!("Generating PDF from raw HTML ({} chars)", html.len());
let header = build_header_template(&self.config);
let footer = build_footer_template(&self.config);
let pdf = render_html_to_pdf(html, &header, &footer, &self.config)?;
info!("PDF from HTML: {} bytes", pdf.len());
Ok(pdf)
}
pub fn from_markdown_file(self, path: impl AsRef<std::path::Path>) -> Result<Vec<u8>> {
let md = std::fs::read_to_string(path.as_ref())?;
self.from_markdown(&md)
}
pub fn from_json_file(self, path: impl AsRef<std::path::Path>) -> Result<Vec<u8>> {
let content = std::fs::read_to_string(path.as_ref())?;
let json_doc: Value =
serde_json::from_str(&content).map_err(|e| MdocError::Json(e.to_string()))?;
self.from_json(&json_doc)
}
}
impl Default for PdfBuilder {
fn default() -> Self {
Self::new()
}
}