///|
pub(all) struct TableCell {
index : Int
paragraphs : Array[Paragraph]
} derive(Eq, Debug, ToJson)
///|
pub(all) struct TableRow {
index : Int
cells : Array[TableCell]
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Table {
region : Region
rows : Array[TableRow]
} derive(Eq, Debug, ToJson)
///|
pub fn TableCell::text(
self : TableCell,
include_hidden? : Bool = false,
) -> String {
self.paragraphs.map(p => p.text(include_hidden~)).join("\n")
}
///|
pub fn Table::column_count(self : Table) -> Int {
let mut maximum = 0
for row in self.rows {
maximum = maximum.max(row.cells.length())
}
maximum
}
///|
pub fn Table::is_rectangular(self : Table) -> Bool {
if self.rows.is_empty() {
return true
}
let count = self.rows[0].cells.length()
self.rows.all(row => row.cells.length() == count)
}
///|
/// Group contiguous table paragraphs. No inference of merged cells is made.
pub fn Document::tables(self : Document) -> Array[Table] {
let tables : Array[Table] = []
let rows : Array[TableRow] = []
let cells : Array[TableCell] = []
let paragraphs : Array[Paragraph] = []
let mut region = Body
let mut row_id : Int? = None
let mut cell_id : Int? = None
for paragraph in self.paragraphs {
let boundary = paragraph.table_row is None || paragraph.region != region
if boundary || paragraph.table_row != row_id {
if cell_id is Some(id) {
cells.push({ index: id, paragraphs: paragraphs.copy(), })
paragraphs.clear()
cell_id = None
}
if row_id is Some(id) {
rows.push({ index: id, cells: cells.copy(), })
cells.clear()
row_id = None
}
}
if boundary && !rows.is_empty() {
tables.push({ region, rows: rows.copy(), })
rows.clear()
}
if paragraph.table_row is Some(row) && paragraph.table_cell is Some(cell) {
region = paragraph.region
row_id = Some(row)
if cell_id != Some(cell) {
if cell_id is Some(id) {
cells.push({ index: id, paragraphs: paragraphs.copy(), })
paragraphs.clear()
}
cell_id = Some(cell)
}
paragraphs.push(paragraph)
}
}
if cell_id is Some(id) {
cells.push({ index: id, paragraphs: paragraphs.copy(), })
}
if row_id is Some(id) {
rows.push({ index: id, cells: cells.copy(), })
}
if !rows.is_empty() {
tables.push({ region, rows: rows.copy(), })
}
tables
}
///|
fn csv_field(value : String, delimiter : Char) -> String {
let mut quote = false
for c in value.iter() {
if c == delimiter || c == '"' || c == '\n' || c == '\r' {
quote = true
}
}
if !quote {
return value
}
let out = StringBuilder()
out.write_char('"')
for c in value.iter() {
if c == '"' {
out.write_char('"')
}
out.write_char(c)
}
out.write_char('"')
out.to_string()
}
///|
pub fn Table::to_csv(
self : Table,
delimiter? : Char = ',',
include_hidden? : Bool = false,
) -> String {
let out = StringBuilder()
for row in self.rows {
for i in 0.. 0 {
out.write_char(delimiter)
}
out.write_string(csv_field(row.cells[i].text(include_hidden~), delimiter))
}
out.write_string("\r\n")
}
out.to_string()
}
///|
pub fn Table::cell(self : Table, row : Int, column : Int) -> TableCell? {
if row < 0 || row >= self.rows.length() {
return None
}
self.rows[row].cells.get(column)
}