use crate::pdf_generator::PageLayout;
use anyhow::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptimizationProfile {
Web,
Print,
Archive,
Ebook,
Custom(OptimizationSettings),
}
impl OptimizationProfile {
pub fn settings(&self) -> OptimizationSettings {
match self {
OptimizationProfile::Web => OptimizationSettings {
compression_level: CompressionLevel::High,
image_dpi: 150,
embed_fonts: false,
subset_fonts: true,
preserve_metadata: false,
tagged_pdf: false,
linearize: true, },
OptimizationProfile::Print => OptimizationSettings {
compression_level: CompressionLevel::Low,
image_dpi: 300,
embed_fonts: true,
subset_fonts: false,
preserve_metadata: true,
tagged_pdf: false,
linearize: false,
},
OptimizationProfile::Archive => OptimizationSettings {
compression_level: CompressionLevel::Medium,
image_dpi: 250,
embed_fonts: true,
subset_fonts: false,
preserve_metadata: true,
tagged_pdf: true,
linearize: false,
},
OptimizationProfile::Ebook => OptimizationSettings {
compression_level: CompressionLevel::Medium,
image_dpi: 180,
embed_fonts: true,
subset_fonts: false,
preserve_metadata: true,
tagged_pdf: true,
linearize: true,
},
OptimizationProfile::Custom(settings) => *settings,
}
}
pub fn web() -> Self {
OptimizationProfile::Web
}
pub fn print() -> Self {
OptimizationProfile::Print
}
pub fn archive() -> Self {
OptimizationProfile::Archive
}
pub fn ebook() -> Self {
OptimizationProfile::Ebook
}
pub fn custom(settings: OptimizationSettings) -> Self {
OptimizationProfile::Custom(settings)
}
}
impl Default for OptimizationProfile {
fn default() -> Self {
OptimizationProfile::Archive
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OptimizationSettings {
pub compression_level: CompressionLevel,
pub image_dpi: u32,
pub embed_fonts: bool,
pub subset_fonts: bool,
pub preserve_metadata: bool,
pub tagged_pdf: bool,
pub linearize: bool,
}
impl Default for OptimizationSettings {
fn default() -> Self {
OptimizationProfile::Archive.settings()
}
}
impl OptimizationSettings {
pub fn new() -> Self {
Self::default()
}
pub fn with_compression(mut self, level: CompressionLevel) -> Self {
self.compression_level = level;
self
}
pub fn with_image_dpi(mut self, dpi: u32) -> Self {
self.image_dpi = dpi;
self
}
pub fn with_embed_fonts(mut self, embed: bool) -> Self {
self.embed_fonts = embed;
self
}
pub fn with_subset_fonts(mut self, subset: bool) -> Self {
self.subset_fonts = subset;
self
}
pub fn with_preserve_metadata(mut self, preserve: bool) -> Self {
self.preserve_metadata = preserve;
self
}
pub fn with_tagged_pdf(mut self, tagged: bool) -> Self {
self.tagged_pdf = tagged;
self
}
pub fn with_linearize(mut self, linearize: bool) -> Self {
self.linearize = linearize;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionLevel {
None,
Low,
Medium,
High,
Maximum,
}
impl CompressionLevel {
pub fn deflate_level(&self) -> u8 {
match self {
CompressionLevel::None => 0,
CompressionLevel::Low => 3,
CompressionLevel::Medium => 6,
CompressionLevel::High => 9,
CompressionLevel::Maximum => 9,
}
}
pub fn none() -> Self {
CompressionLevel::None
}
pub fn low() -> Self {
CompressionLevel::Low
}
pub fn medium() -> Self {
CompressionLevel::Medium
}
pub fn high() -> Self {
CompressionLevel::High
}
pub fn maximum() -> Self {
CompressionLevel::Maximum
}
}
impl Default for CompressionLevel {
fn default() -> Self {
CompressionLevel::Medium
}
}
pub struct OptimizedPdfGenerator {
profile: OptimizationProfile,
settings: OptimizationSettings,
layout: PageLayout,
font: String,
font_size: f32,
}
impl OptimizedPdfGenerator {
pub fn new(profile: OptimizationProfile) -> Self {
let settings = profile.settings();
Self {
profile,
settings,
layout: PageLayout::portrait(),
font: "Helvetica".to_string(),
font_size: 12.0,
}
}
pub fn with_layout(mut self, layout: PageLayout) -> Self {
self.layout = layout;
self
}
pub fn with_font(mut self, font: &str) -> Self {
self.font = font.to_string();
self
}
pub fn with_font_size(mut self, size: f32) -> Self {
self.font_size = size;
self
}
pub fn generate(&self, elements: &[crate::elements::Element], output_path: &str) -> Result<()> {
crate::pdf_generator::create_pdf_from_elements_with_layout(
output_path,
elements,
&self.font,
self.font_size,
self.layout,
)
}
pub fn generate_bytes(&self, elements: &[crate::elements::Element]) -> Result<Vec<u8>> {
crate::pdf_generator::generate_pdf_bytes(
elements,
&self.font,
self.font_size,
self.layout,
)
}
pub fn settings(&self) -> OptimizationSettings {
self.settings
}
pub fn profile(&self) -> OptimizationProfile {
self.profile
}
}
impl Default for OptimizedPdfGenerator {
fn default() -> Self {
Self::new(OptimizationProfile::default())
}
}
pub fn optimize_pdf_bytes(
_pdf_data: &[u8],
_settings: OptimizationSettings,
) -> Result<Vec<u8>> {
anyhow::bail!("PDF optimization not yet implemented")
}
pub fn optimize_pdf_file(
input_path: &str,
output_path: &str,
profile: OptimizationProfile,
) -> Result<()> {
let data = std::fs::read(input_path)?;
let optimized = optimize_pdf_bytes(&data, profile.settings())?;
std::fs::write(output_path, optimized)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profile_settings() {
let web_settings = OptimizationProfile::Web.settings();
assert_eq!(web_settings.compression_level, CompressionLevel::High);
assert_eq!(web_settings.image_dpi, 150);
assert!(!web_settings.embed_fonts);
assert!(web_settings.subset_fonts);
assert!(web_settings.linearize);
let print_settings = OptimizationProfile::Print.settings();
assert_eq!(print_settings.compression_level, CompressionLevel::Low);
assert_eq!(print_settings.image_dpi, 300);
assert!(print_settings.embed_fonts);
assert!(!print_settings.subset_fonts);
assert!(!print_settings.linearize);
}
#[test]
fn test_custom_settings() {
let settings = OptimizationSettings::new()
.with_compression(CompressionLevel::High)
.with_image_dpi(200)
.with_embed_fonts(true)
.with_tagged_pdf(true);
assert_eq!(settings.compression_level, CompressionLevel::High);
assert_eq!(settings.image_dpi, 200);
assert!(settings.embed_fonts);
assert!(settings.tagged_pdf);
}
#[test]
fn test_compression_level() {
assert_eq!(CompressionLevel::None.deflate_level(), 0);
assert_eq!(CompressionLevel::Low.deflate_level(), 3);
assert_eq!(CompressionLevel::Medium.deflate_level(), 6);
assert_eq!(CompressionLevel::High.deflate_level(), 9);
assert_eq!(CompressionLevel::Maximum.deflate_level(), 9);
}
#[test]
fn test_optimized_generator() {
let generator = OptimizedPdfGenerator::new(OptimizationProfile::Web)
.with_font("Courier")
.with_font_size(10.0);
assert_eq!(generator.profile(), OptimizationProfile::Web);
assert_eq!(generator.settings().compression_level, CompressionLevel::High);
assert_eq!(generator.font, "Courier");
assert_eq!(generator.font_size, 10.0);
}
}