graphitepdf-kit 0.4.0

A core PDF generation library for Rust: document building, text, vector graphics, images, and more.
Documentation
use crate::page::PageSize;
use crate::text::TextBuilder;
use crate::vector::{Canvas, Color};

#[cfg(feature = "tracing")]
use tracing::instrument;

#[derive(Clone, Debug, PartialEq)]
pub struct BorderStyle {
    pub color: Color,
    pub width: f64,
    pub enabled: bool,
}

impl Default for BorderStyle {
    fn default() -> Self {
        Self {
            color: Color::BLACK,
            width: 1.0,
            enabled: true,
        }
    }
}

#[derive(Clone, Debug)]
pub struct TableCell {
    pub content: Vec<u8>,
    pub width: Option<f64>,
    pub height: Option<f64>,
    pub background: Option<Color>,
    pub border: BorderStyle,
    pub colspan: u32,
    pub rowspan: u32,
}

impl Default for TableCell {
    fn default() -> Self {
        Self {
            content: Vec::new(),
            width: None,
            height: None,
            background: None,
            border: Default::default(),
            colspan: 1,
            rowspan: 1,
        }
    }
}

impl TableCell {
    pub fn text(text: TextBuilder) -> Self {
        Self {
            content: text.finish(),
            ..Default::default()
        }
    }

    pub fn canvas(canvas: Canvas) -> Self {
        Self {
            content: canvas.finish(),
            ..Default::default()
        }
    }

    pub fn background(mut self, color: Color) -> Self {
        self.background = Some(color);
        self
    }

    pub fn border(mut self, style: BorderStyle) -> Self {
        self.border = style;
        self
    }
}

#[derive(Clone, Debug, Default)]
pub struct TableRow {
    cells: Vec<TableCell>,
    height: Option<f64>,
}

impl TableRow {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn cell(mut self, cell: TableCell) -> Self {
        self.cells.push(cell);
        self
    }

    pub fn height(mut self, h: f64) -> Self {
        self.height = Some(h);
        self
    }
}

#[derive(Clone, Debug)]
pub struct TableBuilder {
    rows: Vec<TableRow>,
    x: f64,
    y: f64,
    column_widths: Vec<f64>,
}

impl TableBuilder {
    pub fn new(x: f64, y: f64) -> Self {
        Self {
            rows: Vec::new(),
            x,
            y,
            column_widths: Vec::new(),
        }
    }

    pub fn column_widths(mut self, widths: &[f64]) -> Self {
        self.column_widths = widths.to_vec();
        self
    }

    pub fn row(mut self, row: TableRow) -> Self {
        self.rows.push(row);
        self
    }

    #[cfg_attr(feature = "tracing", instrument)]
    pub fn finish(self) -> Vec<u8> {
        let mut canvas = Canvas::new();
        let mut y = self.y;

        let num_cols = self
            .rows
            .iter()
            .map(|row| row.cells.len())
            .max()
            .unwrap_or(0);
        let col_widths = if !self.column_widths.is_empty() && self.column_widths.len() == num_cols {
            self.column_widths.clone()
        } else {
            let total_width = PageSize::A4.width - 144.0;
            vec![total_width / num_cols as f64; num_cols]
        };

        for row in &self.rows {
            let row_height = row.height.unwrap_or(30.0);
            let mut x = self.x;
            for (i, cell) in row.cells.iter().enumerate() {
                let cell_width = cell.width.unwrap_or(col_widths[i]);
                if let Some(bg) = cell.background {
                    canvas = canvas.fill_color(bg);
                    canvas = canvas.rect(x, y, cell_width, row_height);
                    canvas = canvas.fill();
                }
                if cell.border.enabled {
                    canvas = canvas.stroke_color(cell.border.color);
                    canvas = canvas.line_width(cell.border.width);
                    canvas = canvas.rect(x, y, cell_width, row_height);
                    canvas = canvas.stroke();
                }
                x += cell_width;
            }
            y -= row_height;
        }
        canvas.finish()
    }
}