oxidize-pdf 2.4.2

A pure Rust PDF generation and manipulation library with zero external dependencies
Documentation
//! Example demonstrating adjusted table column widths for portrait orientation
//!
//! This example shows the same tables as advanced_tables_example.rs but with
//! column widths adjusted to fit properly within portrait margins:
//! - A4 Portrait: 595 x 842 points (451 points usable width)
//! - All tables adjusted to ~440 points total width
//! - Demonstrates responsive table design for narrow layouts

use oxidize_pdf::advanced_tables::{
    AdvancedTableBuilder, AdvancedTableExt, BorderStyle, CellAlignment, CellStyle, HeaderBuilder,
    Padding, TableRenderer,
};
use oxidize_pdf::coordinate_system::CoordinateSystem;
use oxidize_pdf::graphics::Color;
use oxidize_pdf::page::{LayoutManager, Page};
use oxidize_pdf::text::Font;
use oxidize_pdf::Document;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    println!("Creating advanced tables with adjusted widths example PDF...");

    // Create a new document
    let mut doc = Document::new();

    // Page 1: Financial Report Table (adjusted widths)
    let mut page1 = Page::a4(); // Standard portrait orientation
    let mut layout_manager =
        LayoutManager::new(&page1, CoordinateSystem::PdfStandard).with_element_spacing(40.0);
    create_financial_table(&mut page1, &mut layout_manager)?;
    doc.add_page(page1);

    // Page 2: Product Inventory Table (adjusted widths)
    let mut page2 = Page::a4();
    let mut layout_manager =
        LayoutManager::new(&page2, CoordinateSystem::PdfStandard).with_element_spacing(40.0);
    create_inventory_table(&mut page2, &mut layout_manager)?;
    doc.add_page(page2);

    // Page 3: Schedule/Timetable (adjusted widths)
    let mut page3 = Page::a4();
    let mut layout_manager =
        LayoutManager::new(&page3, CoordinateSystem::PdfStandard).with_element_spacing(40.0);
    create_schedule_table(&mut page3, &mut layout_manager)?;
    doc.add_page(page3);

    // Save the document
    let output_path = "examples/results/advanced_tables_adjusted_example.pdf";
    doc.save(output_path)?;
    println!("PDF saved to: {}", output_path);
    println!("Portrait dimensions: 595 x 842 points (451 points usable width)");

    Ok(())
}

fn create_financial_table(
    page: &mut Page,
    layout_manager: &mut LayoutManager,
) -> Result<(), Box<dyn Error>> {
    println!("Creating financial report table with adjusted widths...");

    // Define cell styles
    let header_style = CellStyle::new()
        .background_color(Color::rgb(0.2, 0.4, 0.8))
        .text_color(Color::white())
        .font(Font::HelveticaBold)
        .font_size(12.0)
        .alignment(CellAlignment::Center)
        .padding(Padding::uniform(8.0))
        .border(BorderStyle::Solid, 2.0, Color::rgb(0.1, 0.2, 0.4));

    let data_style = CellStyle::new()
        .font(Font::Helvetica)
        .font_size(10.0)
        .padding(Padding::uniform(6.0))
        .border(BorderStyle::Solid, 1.0, Color::gray(0.5));

    let total_style = CellStyle::new()
        .font(Font::HelveticaBold)
        .font_size(11.0)
        .background_color(Color::rgb(0.95, 0.95, 0.95))
        .padding(Padding::uniform(6.0))
        .border(BorderStyle::Double, 2.0, Color::black());

    // Create the table - widths adjusted from 570 to 440 points
    let table = AdvancedTableBuilder::new()
        .title("Quarterly Financial Report - Q3 2024 (Adjusted)")
        .columns(vec![
            ("Department", 110.0), // Reduced from 150
            ("Q1 Revenue", 85.0),  // Reduced from 100
            ("Q2 Revenue", 85.0),  // Reduced from 100
            ("Q3 Revenue", 85.0),  // Reduced from 100
            ("Total", 75.0),       // Reduced from 120
        ]) // Total: 440 points (fits in 451 available)
        .header_style(header_style)
        .data_style(data_style)
        .zebra_stripes(true, Color::rgb(0.98, 0.98, 0.98))
        .add_row(vec![
            "Sales",
            "$1,250,000",
            "$1,380,000",
            "$1,520,000",
            "$4,150,000",
        ])
        .add_row(vec![
            "Marketing",
            "$850,000",
            "$920,000",
            "$980,000",
            "$2,750,000",
        ])
        .add_row(vec![
            "Engineering",
            "$2,100,000",
            "$2,200,000",
            "$2,350,000",
            "$6,650,000",
        ])
        .add_row(vec![
            "Support",
            "$450,000",
            "$480,000",
            "$510,000",
            "$1,440,000",
        ])
        .add_row(vec![
            "Operations",
            "$780,000",
            "$820,000",
            "$890,000",
            "$2,490,000",
        ])
        .add_row_with_style(
            vec![
                "TOTAL",
                "$5,430,000",
                "$5,800,000",
                "$6,250,000",
                "$17,480,000",
            ],
            total_style,
        )
        .build()?;

    // Calculate table height for intelligent positioning
    let renderer = TableRenderer::new();
    let table_height = renderer.calculate_table_height(&table);

    println!("Financial table width: {} points", table.calculate_width());

    // Position table using layout manager
    if let Some(y_position) = layout_manager.add_element(table_height) {
        let x_position = layout_manager.center_x(table.calculate_width());
        page.add_advanced_table(&table, x_position, y_position)?;
    } else {
        return Err("Table does not fit on page".into());
    }

    Ok(())
}

fn create_inventory_table(
    page: &mut Page,
    layout_manager: &mut LayoutManager,
) -> Result<(), Box<dyn Error>> {
    println!("Creating inventory table with adjusted widths...");

    // Create a table with complex headers and properly sized columns
    let header_builder = HeaderBuilder::new(7)
        .add_level(vec![
            ("Product Information", 3), // Spans 3 columns
            ("Stock Levels", 2),        // Spans 2 columns
            ("Pricing", 2),             // Spans 2 columns
        ])
        .add_level(vec![
            ("SKU", 1),
            ("Name", 1),
            ("Category", 1),
            ("In Stock", 1),
            ("Reserved", 1),
            ("Cost", 1),
            ("Retail", 1),
        ]);

    let table = AdvancedTableBuilder::new()
        .title("Product Inventory Report (Adjusted)")
        // Columns adjusted to fit in 440 points total
        .columns(vec![
            ("SKU", 50.0),      // Product codes are short
            ("Name", 90.0),     // Product names need more space
            ("Category", 70.0), // Category names are medium
            ("In Stock", 50.0), // Numbers are short
            ("Reserved", 50.0), // Numbers are short
            ("Cost", 65.0),     // Currency values need space
            ("Retail", 65.0),   // Currency values need space
        ]) // Total: 440 points (fits in 451 available)
        .complex_header(header_builder)
        .add_row(vec![
            "PRD-001",
            "Laptop Pro",
            "Electronics",
            "45",
            "12",
            "$899",
            "$1,299",
        ])
        .add_row(vec![
            "PRD-002",
            "Wireless Mouse",
            "Accessories",
            "128",
            "15",
            "$15",
            "$39",
        ])
        .add_row(vec![
            "PRD-003",
            "USB-C Hub",
            "Accessories",
            "87",
            "8",
            "$25",
            "$59",
        ])
        .add_row(vec![
            "PRD-004",
            "Monitor 27\"",
            "Electronics",
            "23",
            "5",
            "$180",
            "$399",
        ])
        .add_row(vec![
            "PRD-005",
            "Keyboard Mech",
            "Accessories",
            "67",
            "10",
            "$45",
            "$129",
        ])
        .build()?;

    // Calculate table height for intelligent positioning
    let renderer = TableRenderer::new();
    let table_height = renderer.calculate_table_height(&table);

    println!("Inventory table width: {} points", table.calculate_width());

    // Position table using layout manager
    if let Some(y_position) = layout_manager.add_element(table_height) {
        let x_position = layout_manager.center_x(table.calculate_width());
        page.add_advanced_table(&table, x_position, y_position)?;
    } else {
        return Err("Table does not fit on page".into());
    }

    Ok(())
}

fn create_schedule_table(
    page: &mut Page,
    layout_manager: &mut LayoutManager,
) -> Result<(), Box<dyn Error>> {
    println!("Creating schedule table with adjusted widths...");

    // Create alternating styles for time slots
    let time_style = CellStyle::new()
        .background_color(Color::rgb(0.3, 0.3, 0.5))
        .text_color(Color::white())
        .font(Font::HelveticaBold)
        .font_size(10.0)
        .alignment(CellAlignment::Center);

    let event_style = CellStyle::new()
        .font(Font::Helvetica)
        .font_size(10.0)
        .padding(Padding::new(4.0, 8.0, 4.0, 8.0));

    let break_style = CellStyle::new()
        .background_color(Color::rgb(0.9, 0.95, 0.9))
        .font(Font::HelveticaOblique)
        .font_size(10.0)
        .alignment(CellAlignment::Center);

    let table = AdvancedTableBuilder::new()
        .title("Conference Schedule - Day 1 (Adjusted)")
        .columns(vec![
            ("Time", 60.0),     // Reduced from 80
            ("Track A", 127.0), // Reduced from 160
            ("Track B", 127.0), // Reduced from 160
            ("Track C", 126.0), // Reduced from 160
        ]) // Total: 440 points (fits in 451 available)
        .add_row_with_mixed_styles(vec![
            (time_style.clone(), "9:00 AM"),
            (event_style.clone(), "Keynote: Future of Technology"),
            (event_style.clone(), "Workshop: Cloud Architecture"),
            (event_style.clone(), "Tutorial: Machine Learning"),
        ])
        .add_row_with_mixed_styles(vec![
            (time_style.clone(), "10:30 AM"),
            (break_style.clone(), "Coffee Break"),
            (break_style.clone(), "Coffee Break"),
            (break_style.clone(), "Coffee Break"),
        ])
        .add_row_with_mixed_styles(vec![
            (time_style.clone(), "11:00 AM"),
            (event_style.clone(), "Panel: Industry Trends"),
            (event_style.clone(), "Demo: DevOps Tools"),
            (event_style.clone(), "Talk: Security Best Practices"),
        ])
        .add_row_with_mixed_styles(vec![
            (time_style.clone(), "12:30 PM"),
            (break_style.clone(), "Lunch"),
            (break_style.clone(), "Lunch"),
            (break_style, "Lunch"),
        ])
        .add_row_with_mixed_styles(vec![
            (time_style, "2:00 PM"),
            (event_style.clone(), "Workshop: Microservices"),
            (event_style.clone(), "Case Study: Scaling"),
            (event_style, "Hands-on: Kubernetes"),
        ])
        .build()?;

    // Calculate table height for intelligent positioning
    let renderer = TableRenderer::new();
    let table_height = renderer.calculate_table_height(&table);

    println!("Schedule table width: {} points", table.calculate_width());

    // Position table using layout manager
    if let Some(y_position) = layout_manager.add_element(table_height) {
        let x_position = layout_manager.center_x(table.calculate_width());
        page.add_advanced_table(&table, x_position, y_position)?;
    } else {
        return Err("Table does not fit on page".into());
    }

    Ok(())
}