zino_extra/format/
pdf_document.rsuse printpdf::{
BuiltinFont, Error, IndirectFontRef, Mm, PdfDocumentReference, PdfLayerReference,
PdfPageReference,
};
use zino_core::{extension::JsonObjectExt, Map};
pub struct PdfDocument {
font: IndirectFontRef,
font_size: f32,
page_width: f32,
page_height: f32,
margin_left: f32,
margin_right: f32,
margin_top: f32,
margin_bottom: f32,
document: PdfDocumentReference,
current_page: PdfPageReference,
current_layer: PdfLayerReference,
current_position: (f32, f32),
page_count: usize,
layer_count: usize,
}
impl PdfDocument {
#[inline]
pub fn try_new(
document_title: impl Into<String>,
page_size: Option<(f32, f32)>,
) -> Result<Self, Error> {
let layer_name = "Page 1, Layer 1";
let (page_width, page_height) = page_size.unwrap_or((210.0, 297.0));
let document = printpdf::PdfDocument::empty(document_title);
let (page_index, layer_index) =
document.add_page(Mm(page_width), Mm(page_height), layer_name);
let current_page = document.get_page(page_index);
let current_layer = current_page.get_layer(layer_index);
let font = document.add_builtin_font(BuiltinFont::TimesRoman)?;
Ok(Self {
font,
font_size: 12.0,
page_width,
page_height,
margin_left: 12.0,
margin_right: 12.0,
margin_top: 20.0,
margin_bottom: 20.0,
document,
current_page,
current_layer,
current_position: (12.0, 20.0),
page_count: 1,
layer_count: 1,
})
}
#[inline]
pub fn set_page_size(&mut self, page_width: f32, page_height: f32) {
self.page_width = page_width;
self.page_height = page_height;
}
#[inline]
pub fn set_margin(
&mut self,
margin_left: f32,
margin_right: f32,
margin_top: f32,
margin_bottom: f32,
) {
self.margin_left = margin_left;
self.margin_right = margin_right;
self.margin_top = margin_top;
self.margin_bottom = margin_bottom;
}
#[inline]
pub fn set_font_size(&mut self, font_size: f32) {
self.font_size = font_size;
}
pub fn add_new_page(&mut self) {
let document = &self.document;
let page_count = self.page_count + 1;
let page_width = Mm(self.page_width);
let page_height = Mm(self.page_height);
let layer_name = format!("Page {page_count}, Layer 1");
let (page_index, layer_index) = document.add_page(page_width, page_height, layer_name);
let current_page = document.get_page(page_index);
let current_layer = current_page.get_layer(layer_index);
self.current_page = current_page;
self.current_layer = current_layer;
self.current_position = (self.margin_left, self.margin_top);
self.page_count = page_count;
}
pub fn add_new_layer(&mut self) {
let page_count = self.page_count;
let layer_count = self.layer_count + 1;
let layer_name = format!("Page {page_count}, Layer {layer_count}");
self.current_layer = self.current_page.add_layer(layer_name);
self.current_position = (self.margin_left, self.margin_top);
self.layer_count = layer_count;
}
pub fn add_text(&mut self, text: impl ToString, position: (f32, f32)) {
let font = &self.font;
let font_size = self.font_size;
let x = position.0;
let y = self.page_height - position.1;
self.current_layer
.use_text(text.to_string(), font_size, Mm(x), Mm(y), font);
self.current_position = (x, y + font_size);
}
pub fn add_data_table<const N: usize>(&mut self, data: Vec<&Map>, columns: [&str; N]) {
let line_height = self.font_size;
let content_width = self.page_width - self.margin_left - self.margin_right;
let span_width = content_width / (columns.len() + 1) as f32;
let (x, mut y) = self.current_position;
for (index, entry) in data.into_iter().enumerate() {
let number = index + 1;
self.add_text(number, (x, y));
let mut x = x + span_width;
for col in columns {
let value = entry.parse_string(col).unwrap_or_default();
self.add_text(value, (x, y));
x += span_width;
}
y += line_height;
}
}
#[inline]
pub fn save_to_bytes(self) -> Result<Vec<u8>, Error> {
self.document.save_to_bytes()
}
}