mod cell;
pub use cell::{Cell, CellBorder, CellBorders};
use crate::units::Emu;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Column {
pub width: Emu,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Row {
pub height: Emu,
pub cells: Vec<Cell>,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, PartialEq)]
pub struct Table {
pub columns: Vec<Column>,
pub rows: Vec<Row>,
pub first_row: bool,
pub first_col: bool,
pub last_row: bool,
pub last_col: bool,
pub horz_banding: bool,
pub vert_banding: bool,
pub table_style_id: Option<String>,
}
impl Table {
#[must_use]
pub fn new(rows: usize, cols: usize, total_width: Emu, row_height: Emu) -> Self {
#[allow(clippy::cast_possible_wrap)] let col_width = Emu(total_width.0 / cols as i64);
let columns: Vec<Column> = (0..cols).map(|_| Column { width: col_width }).collect();
let table_rows: Vec<Row> = (0..rows)
.map(|_| Row {
height: row_height,
cells: (0..cols).map(|_| Cell::new()).collect(),
})
.collect();
Self {
columns,
rows: table_rows,
first_row: true,
first_col: false,
last_row: false,
last_col: false,
horz_banding: true,
vert_banding: false,
table_style_id: None,
}
}
#[must_use]
pub fn row_count(&self) -> usize {
self.rows.len()
}
#[must_use]
pub fn col_count(&self) -> usize {
self.columns.len()
}
#[must_use]
pub fn cell(&self, row_idx: usize, col_idx: usize) -> &Cell {
&self.rows[row_idx].cells[col_idx]
}
pub fn cell_mut(&mut self, row_idx: usize, col_idx: usize) -> &mut Cell {
&mut self.rows[row_idx].cells[col_idx]
}
#[must_use]
pub fn get_cell(&self, row_idx: usize, col_idx: usize) -> Option<&Cell> {
self.rows.get(row_idx)?.cells.get(col_idx)
}
pub fn get_cell_mut(&mut self, row_idx: usize, col_idx: usize) -> Option<&mut Cell> {
self.rows.get_mut(row_idx)?.cells.get_mut(col_idx)
}
pub fn iter_cells(&self) -> impl Iterator<Item = &Cell> {
self.rows.iter().flat_map(|r| r.cells.iter())
}
#[must_use]
pub fn rows(&self) -> &[Row] {
&self.rows
}
pub fn rows_mut(&mut self) -> &mut [Row] {
&mut self.rows
}
pub fn add_row(&mut self) -> &mut Row {
let height = self.rows.first().map_or(Emu(370_840), |r| r.height);
let cols = self.columns.len();
let row = Row {
height,
cells: (0..cols).map(|_| Cell::new()).collect(),
};
self.rows.push(row);
let idx = self.rows.len() - 1;
&mut self.rows[idx]
}
pub fn add_column(&mut self, width: Emu) {
self.columns.push(Column { width });
for row in &mut self.rows {
row.cells.push(Cell::new());
}
}
pub fn write_xml<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
w.write_str("<a:tbl>")?;
w.write_str("<a:tblPr")?;
if self.first_row {
w.write_str(r#" firstRow="1""#)?;
}
if self.first_col {
w.write_str(r#" firstCol="1""#)?;
}
if self.last_row {
w.write_str(r#" lastRow="1""#)?;
}
if self.last_col {
w.write_str(r#" lastCol="1""#)?;
}
if self.horz_banding {
w.write_str(r#" bandRow="1""#)?;
}
if self.vert_banding {
w.write_str(r#" bandCol="1""#)?;
}
if let Some(ref style_id) = self.table_style_id {
w.write_char('>')?;
write!(w, "<a:tblStyleId>{style_id}</a:tblStyleId>")?;
w.write_str("</a:tblPr>")?;
} else {
w.write_str("/>")?;
}
w.write_str("<a:tblGrid>")?;
for col in &self.columns {
write!(w, r#"<a:gridCol w="{}"/>"#, col.width.0)?;
}
w.write_str("</a:tblGrid>")?;
for row in &self.rows {
write!(w, r#"<a:tr h="{}">"#, row.height.0)?;
for cell in &row.cells {
cell.write_xml(w)?;
}
w.write_str("</a:tr>")?;
}
w.write_str("</a:tbl>")
}
#[must_use]
pub fn to_xml_string(&self) -> String {
let cell_count = self.rows.len() * self.columns.len();
let mut s = String::with_capacity(200 + cell_count * 200);
self.write_xml(&mut s)
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
s
}
#[must_use]
pub fn to_graphic_data_xml(&self) -> String {
let cell_count = self.rows.len() * self.columns.len();
let mut s = String::with_capacity(300 + cell_count * 200);
s.push_str(r#"<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/table">"#);
self.write_xml(&mut s)
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
s.push_str("</a:graphicData></a:graphic>");
s
}
}
#[cfg(test)]
mod tests;