use std::path::{Path, PathBuf};
use easypdf_core::{
Orientation, PageSize, PdfFont, PdfImage, PdfMetadata, PdfTable, PdfText, PdfWriteHandler,
Result, Rotation,
};
use easypdf_reader::{PdfManipulator, PdfReader, ReadStrategy};
#[must_use]
pub struct PdfCreateBuilder {
pub(crate) path: PathBuf,
pub(crate) title: String,
pub(crate) page_size: PageSize,
pub(crate) orientation: Orientation,
pub(crate) metadata: PdfMetadata,
#[allow(dead_code)]
pub(crate) fonts: Vec<PdfFont>,
pub(crate) handlers: Vec<Box<dyn PdfWriteHandler>>,
}
impl PdfCreateBuilder {
pub(crate) fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
title: String::from("Untitled"),
page_size: PageSize::A4,
orientation: Orientation::default(),
metadata: PdfMetadata::default(),
fonts: Vec::new(),
handlers: Vec::new(),
}
}
#[must_use = "builder method"]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
#[must_use = "builder method"]
pub const fn page_size(mut self, size: PageSize) -> Self {
self.page_size = size;
self
}
#[must_use = "builder method"]
pub const fn orientation(mut self, orientation: Orientation) -> Self {
self.orientation = orientation;
self
}
#[must_use = "builder method"]
pub fn metadata(mut self, metadata: PdfMetadata) -> Self {
self.metadata = metadata;
self
}
#[must_use = "builder method"]
pub fn register_handler(mut self, handler: Box<dyn PdfWriteHandler>) -> Self {
self.handlers.push(handler);
self
}
pub fn add_text(self, content: impl Into<String>) -> PdfTextBuilder<Self> {
PdfTextBuilder {
parent: self,
text: PdfText::new(content),
}
}
#[must_use = "builder method"]
pub fn add_table(self, table: &PdfTable) -> PdfTableBuilder {
PdfTableBuilder {
parent: self,
table: table.clone(),
x: 72.0,
y: 700.0,
col_widths: Vec::new(),
row_height: 20.0,
font: PdfFont::helvetica(10.0),
}
}
#[must_use = "builder method"]
pub fn add_image(self, image: &PdfImage) -> PdfImageBuilder {
PdfImageBuilder {
parent: self,
image: image.clone(),
x: 72.0,
y: 700.0,
w: 0.0,
h: 0.0,
}
}
pub fn build(self) -> Result<easypdf_writer::PdfWriter> {
let mut writer = easypdf_writer::PdfWriter::new(&self.title);
writer = writer.metadata(self.metadata);
for handler in self.handlers {
writer = writer.register_handler(handler);
}
Ok(writer)
}
pub fn do_write(self) -> Result<PathBuf> {
let path = self.path.clone();
let page_size = self.page_size;
let orientation = self.orientation;
let mut writer = self.build()?;
writer.add_page(page_size, orientation)?;
writer.finish(&path)?;
Ok(path)
}
}
#[must_use]
pub struct PdfTextBuilder<P> {
pub(crate) parent: P,
pub(crate) text: PdfText,
}
impl PdfTextBuilder<PdfCreateBuilder> {
#[must_use = "builder method"]
pub fn font(mut self, font: PdfFont) -> Self {
self.text = self.text.font(font);
self
}
#[must_use = "builder method"]
pub fn position(self, x: f64, y: f64) -> PdfPositionedTextBuilder {
PdfPositionedTextBuilder {
parent: self.parent,
text: self.text,
x,
y,
}
}
pub fn do_write(self) -> Result<PathBuf> {
let path = self.parent.path.clone();
let page_size = self.parent.page_size;
let orientation = self.parent.orientation;
let mut writer = self.parent.build()?;
writer.add_page(page_size, orientation)?;
writer.write_text(&self.text, 100.0, 700.0)?;
writer.finish(&path)?;
Ok(path)
}
}
#[must_use]
pub struct PdfTableBuilder {
parent: PdfCreateBuilder,
table: PdfTable,
x: f64,
y: f64,
col_widths: Vec<f64>,
row_height: f64,
font: PdfFont,
}
impl PdfTableBuilder {
#[must_use = "builder method"]
pub fn position(mut self, x: f64, y: f64) -> Self {
self.x = x;
self.y = y;
self
}
#[must_use = "builder method"]
pub fn column_widths(mut self, widths: Vec<f64>) -> Self {
self.col_widths = widths;
self
}
#[must_use = "builder method"]
pub fn row_height(mut self, height: f64) -> Self {
self.row_height = height;
self
}
#[must_use = "builder method"]
pub fn font(mut self, font: PdfFont) -> Self {
self.font = font;
self
}
pub fn do_write(self) -> easypdf_core::Result<PathBuf> {
let path = self.parent.path.clone();
let page_size = self.parent.page_size;
let orientation = self.parent.orientation;
let mut writer = self.parent.build()?;
writer.add_page(page_size, orientation)?;
crate::write_table(
&mut writer,
&self.table,
self.x,
self.y,
&self.col_widths,
self.row_height,
&self.font,
)?;
writer.finish(&path)?;
Ok(path)
}
}
#[must_use]
pub struct PdfImageBuilder {
parent: PdfCreateBuilder,
image: PdfImage,
x: f64,
y: f64,
w: f64,
h: f64,
}
impl PdfImageBuilder {
#[must_use = "builder method"]
pub fn position(mut self, x: f64, y: f64) -> Self {
self.x = x;
self.y = y;
self
}
#[must_use = "builder method"]
pub fn size(mut self, w: f64, h: f64) -> Self {
self.w = w;
self.h = h;
self
}
pub fn do_write(self) -> easypdf_core::Result<PathBuf> {
let path = self.parent.path.clone();
let page_size = self.parent.page_size;
let orientation = self.parent.orientation;
let mut writer = self.parent.build()?;
writer.add_page(page_size, orientation)?;
writer.write_image(&self.image, self.x, self.y, self.w, self.h)?;
writer.finish(&path)?;
Ok(path)
}
}
#[must_use]
pub struct PdfPositionedTextBuilder {
parent: PdfCreateBuilder,
text: PdfText,
x: f64,
y: f64,
}
impl PdfPositionedTextBuilder {
pub fn do_write(self) -> Result<PathBuf> {
let path = self.parent.path.clone();
let page_size = self.parent.page_size;
let orientation = self.parent.orientation;
let mut writer = self.parent.build()?;
writer.add_page(page_size, orientation)?;
writer.write_text(&self.text, self.x, self.y)?;
writer.finish(&path)?;
Ok(path)
}
}
#[must_use]
pub struct PdfReadBuilder {
path: PathBuf,
pages: Option<std::ops::Range<usize>>,
strategy: Option<ReadStrategy>,
}
impl PdfReadBuilder {
pub(crate) fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
pages: None,
strategy: None,
}
}
#[must_use = "builder method"]
pub fn pages(mut self, range: std::ops::Range<usize>) -> Self {
self.pages = Some(range);
self
}
#[must_use = "builder method"]
pub const fn strategy(mut self, strategy: ReadStrategy) -> Self {
self.strategy = Some(strategy);
self
}
pub fn open(self) -> Result<PdfReader> {
let mut reader = match self.strategy {
Some(strategy) => PdfReader::open_with_strategy(&self.path, strategy)?,
None => PdfReader::open(&self.path)?,
};
if let Some(range) = self.pages {
reader = reader.try_pages(range)?;
}
Ok(reader)
}
pub fn extract_text(self) -> Result<String> {
self.open()?.extract_text()
}
pub fn metadata(self) -> Result<PdfMetadata> {
self.open()?.extract_metadata()
}
pub fn page_count(self) -> Result<usize> {
self.open()?.page_count()
}
}
#[must_use]
pub struct PdfSplitBuilder {
path: PathBuf,
pages_per_file: usize,
}
impl PdfSplitBuilder {
pub(crate) fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
pages_per_file: 1,
}
}
#[must_use = "builder method"]
pub const fn every_n_pages(mut self, n: usize) -> Self {
self.pages_per_file = n;
self
}
pub fn save_to_dir(self, output_dir: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
let manipulator = PdfManipulator::open(&self.path)?;
let total_pages = manipulator.page_count();
let output_dir = output_dir.as_ref();
std::fs::create_dir_all(output_dir)?;
let mut output_paths = Vec::new();
let mut start = 0;
while start < total_pages {
let end = std::cmp::min(start + self.pages_per_file, total_pages);
let mut chunk = manipulator.extract_pages(start..end)?;
let filename = format!("page_{:03}.pdf", start / self.pages_per_file + 1);
let output_path = output_dir.join(&filename);
chunk.save(&output_path)?;
output_paths.push(output_path);
start = end;
}
Ok(output_paths)
}
}
#[must_use]
pub struct PdfManipulateBuilder {
path: PathBuf,
rotations: Vec<(usize, Rotation)>,
order: Option<Vec<usize>>,
}
impl PdfManipulateBuilder {
pub(crate) fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
rotations: Vec::new(),
order: None,
}
}
#[must_use = "builder method"]
pub fn rotate_page(mut self, page_number: usize, rotation: Rotation) -> Self {
self.rotations.push((page_number, rotation));
self
}
#[must_use = "builder method"]
pub fn rotate_all(self, rotation: Rotation) -> Self {
self.rotate(rotation)
}
#[must_use = "builder method"]
pub fn rotate(mut self, rotation: Rotation) -> Self {
self.rotations.push((0, rotation)); self
}
#[must_use = "builder method"]
pub fn reorder_pages(mut self, order: &[usize]) -> Self {
self.order = Some(order.to_vec());
self
}
pub fn save(self, output: impl AsRef<Path>) -> Result<()> {
let mut manipulator = PdfManipulator::open(&self.path)?;
for (page_num, rotation) in &self.rotations {
if *page_num == 0 {
let count = manipulator.page_count();
for p in 1..=count {
manipulator.rotate_page(p, *rotation)?;
}
} else {
manipulator.rotate_page(*page_num, *rotation)?;
}
}
if let Some(order) = &self.order {
manipulator.reorder_pages(order)?;
}
manipulator.save(output)
}
}