use oxidize_pdf::document::Document;
use oxidize_pdf::objects::{Dictionary, Object, ObjectId};
use oxidize_pdf::text::Font;
use oxidize_pdf::writer::{ObjectStreamConfig, ObjectStreamWriter, PdfWriter, WriterConfig};
use oxidize_pdf::Page;
use std::fs::{self, File};
use std::io::BufWriter;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Object Streams Demo ===\n");
let output_dir = "examples/results/object_streams_demo";
fs::create_dir_all(output_dir)?;
let mut doc = Document::new();
let mut page = Page::new(595.0, 842.0);
page.text()
.set_font(Font::Helvetica, 16.0)
.at(100.0, 750.0)
.write("Object Streams Demo - File Size Comparison")?;
page.text()
.set_font(Font::Helvetica, 12.0)
.at(100.0, 720.0)
.write("This PDF demonstrates object stream compression")?;
for i in 0..20 {
page.text()
.set_font(Font::Helvetica, 10.0)
.at(100.0, 680.0 - (i as f64 * 20.0))
.write(&format!(
"Line {}: Object streams compress multiple objects together",
i + 1
))?;
}
doc.add_page(page);
println!("1. Writing PDF WITHOUT object streams (traditional)...");
let traditional_path = format!("{}/traditional.pdf", output_dir);
let traditional_config = WriterConfig {
use_xref_streams: false,
use_object_streams: false,
pdf_version: "1.4".to_string(),
compress_streams: true,
incremental_update: false,
};
let file = File::create(&traditional_path)?;
let mut writer = PdfWriter::with_config(BufWriter::new(file), traditional_config);
writer.write_document(&mut doc)?;
let traditional_size = fs::metadata(&traditional_path)?.len();
println!(
" ✓ Written: {} ({} bytes)",
traditional_path, traditional_size
);
println!("\n2. Writing PDF WITH object streams (modern PDF 1.5+)...");
let modern_path = format!("{}/with_object_streams.pdf", output_dir);
let modern_config = WriterConfig {
use_xref_streams: false, use_object_streams: false,
pdf_version: "1.5".to_string(),
compress_streams: true,
incremental_update: false,
};
println!(" Creating object stream writer...");
let obj_stream_config = ObjectStreamConfig {
max_objects_per_stream: 100,
compression_level: 6,
enabled: true,
};
let mut obj_stream_writer = ObjectStreamWriter::new(obj_stream_config);
println!(" Simulating object compression...");
for i in 1..=50 {
let mut dict = Dictionary::new();
dict.set("Type", Object::Name("Example".to_string()));
dict.set("Index", Object::Integer(i));
dict.set("Data", Object::String(format!("Sample data {}", i)));
let dict_bytes = format!(
"<< /Type /Example /Index {} /Data (Sample data {}) >>",
i, i
)
.into_bytes();
obj_stream_writer.add_object(ObjectId::new(i as u32, 0), dict_bytes)?;
}
let streams = obj_stream_writer.finalize()?;
println!(" ✓ Created {} object streams", streams.len());
let total_objects: usize = streams.iter().map(|s| s.objects.len()).sum();
println!(" ✓ Compressed {} objects total", total_objects);
let mut total_uncompressed = 0;
let mut total_compressed = 0;
for stream in &mut streams.clone() {
for (_, data) in &stream.objects {
total_uncompressed += data.len();
}
if let Ok(compressed) = stream.generate_stream_data(6) {
total_compressed += compressed.len();
}
}
let compression_ratio = if total_uncompressed > 0 {
(1.0 - (total_compressed as f64 / total_uncompressed as f64)) * 100.0
} else {
0.0
};
println!("\n3. Compression Statistics:");
println!(" Uncompressed objects: {} bytes", total_uncompressed);
println!(" Compressed streams: {} bytes", total_compressed);
println!(" Compression ratio: {:.1}%", compression_ratio);
let file = File::create(&modern_path)?;
let mut writer = PdfWriter::with_config(BufWriter::new(file), modern_config);
writer.write_document(&mut doc)?;
let modern_size = fs::metadata(&modern_path)?.len();
println!(" ✓ Written: {} ({} bytes)", modern_path, modern_size);
println!("\n4. File Size Comparison:");
println!(" Traditional (PDF 1.4): {} bytes", traditional_size);
println!(" Modern (PDF 1.5): {} bytes", modern_size);
if modern_size < traditional_size {
let reduction = ((traditional_size - modern_size) as f64 / traditional_size as f64) * 100.0;
println!(" ✓ Size reduction: {:.1}%", reduction);
} else {
println!(" Note: Full object stream integration pending");
}
println!("\n5. Object Stream Details:");
for (idx, stream) in streams.iter().enumerate() {
println!(
" Stream {}: {} objects (ID: {})",
idx + 1,
stream.objects.len(),
stream.stream_id
);
let dict = stream.generate_dictionary(&[]);
println!(" Type: {:?}", dict.get("Type"));
println!(" N: {:?}", dict.get("N"));
println!(" First: {:?}", dict.get("First"));
println!(" Filter: {:?}", dict.get("Filter"));
}
println!("\n✓ Demo complete!");
println!(" Output files: {}/", output_dir);
println!("\nNext steps:");
println!(" - Integrate ObjectStreamWriter with PdfWriter");
println!(" - Implement automatic object stream generation");
println!(" - Add Feature 2.2.2: Cross-Reference Streams");
println!(" - Expected combined reduction: 11-61%");
Ok(())
}