Skip to content

Commit

Permalink
feat: add Range::row_attributes
Browse files Browse the repository at this point in the history
This currently only supports the "outlineLevel" attribute.
  • Loading branch information
bonsairobo committed Mar 26, 2024
1 parent 953d80e commit bde6613
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 15 deletions.
20 changes: 19 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@ pub struct Range<T> {
start: (u32, u32),
end: (u32, u32),
inner: Vec<T>,
row_attributes: Option<Vec<RowAttributes>>,
}

#[allow(missing_docs)]
#[derive(Clone, Debug, Default)]
pub struct RowAttributes {
pub outline_level: Option<u8>,
}

impl<T: CellType> Range<T> {
Expand All @@ -361,6 +368,7 @@ impl<T: CellType> Range<T> {
start,
end,
inner: vec![T::default(); ((end.0 - start.0 + 1) * (end.1 - start.1 + 1)) as usize],
row_attributes: None,
}
}

Expand All @@ -371,6 +379,7 @@ impl<T: CellType> Range<T> {
start: (0, 0),
end: (0, 0),
inner: Vec::new(),
row_attributes: None,
}
}

Expand Down Expand Up @@ -437,7 +446,10 @@ impl<T: CellType> Range<T> {
///
/// panics when a `Cell` row is lower than the first `Cell` row or
/// bigger than the last `Cell` row.
pub fn from_sparse(cells: Vec<Cell<T>>) -> Range<T> {
pub fn from_sparse(
cells: Vec<Cell<T>>,
row_attributes: Option<Vec<RowAttributes>>,
) -> Range<T> {
if cells.is_empty() {
Range::empty()
} else {
Expand Down Expand Up @@ -469,6 +481,7 @@ impl<T: CellType> Range<T> {
start: (row_start, col_start),
end: (row_end, col_end),
inner: v,
row_attributes,
}
}
}
Expand Down Expand Up @@ -618,6 +631,11 @@ impl<T: CellType> Range<T> {
}
}

#[allow(missing_docs)]
pub fn row_attributes(&self) -> Option<&[RowAttributes]> {
self.row_attributes.as_deref()
}

/// Get an iterator over used cells only
pub fn used_cells(&self) -> UsedCells<'_, T> {
UsedCells {
Expand Down
3 changes: 3 additions & 0 deletions src/ods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,13 @@ fn get_range<T: Default + Clone + PartialEq>(
}
let row_min = row_min + first_empty_rows_repeated;
let row_max = row_max + first_empty_rows_repeated;
let num_rows = row_max - row_min;
Range {
start: (row_min as u32, col_min as u32),
end: (row_max as u32, col_max as u32),
inner: cells,
// TODO: ODS support
row_attributes: None,
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/xls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,9 @@ impl<RS: Read + Seek> Xls<RS> {
_ => (),
}
}
let range = Range::from_sparse(cells);
let formula = Range::from_sparse(formulas);
// TODO: XLS support for row attributes
let range = Range::from_sparse(cells, None);
let formula = Range::from_sparse(formulas, None);
sheets.insert(name, (range, formula));
}

Expand Down
5 changes: 3 additions & 2 deletions src/xlsb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,8 @@ impl<RS: Read + Seek> Reader<RS> for Xlsb<RS> {
cells.push(Cell::new(cell.pos, Data::from(cell.val)));
}
}
Ok(Range::from_sparse(cells))
// TODO: XLSB support for row attributes
Ok(Range::from_sparse(cells, None))
}

/// MS-XLSB 2.1.7.62
Expand All @@ -495,7 +496,7 @@ impl<RS: Read + Seek> Reader<RS> for Xlsb<RS> {
cells.push(cell);
}
}
Ok(Range::from_sparse(cells))
Ok(Range::from_sparse(cells, None))
}

/// MS-XLSB 2.1.7.62
Expand Down
18 changes: 16 additions & 2 deletions src/xlsx/cells_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use quick_xml::{
};

use super::{
get_attribute, get_dimension, get_row, get_row_column, read_string, Dimensions, XlReader,
get_attribute, get_dimension, get_outline_level, get_row, get_row_column, read_string,
Dimensions, XlReader,
};
use crate::{
datatype::DataRef,
formats::{format_excel_f64_ref, CellFormat},
Cell, XlsxError,
Cell, RowAttributes, XlsxError,
};

/// An xlsx Cell Iterator
Expand All @@ -21,6 +22,7 @@ pub struct XlsxCellReader<'a> {
dimensions: Dimensions,
row_index: u32,
col_index: u32,
row_attributes: RowAttributes,
buf: Vec<u8>,
cell_buf: Vec<u8>,
}
Expand Down Expand Up @@ -66,6 +68,7 @@ impl<'a> XlsxCellReader<'a> {
dimensions,
row_index: 0,
col_index: 0,
row_attributes: Default::default(),
buf: Vec::with_capacity(1024),
cell_buf: Vec::with_capacity(1024),
})
Expand All @@ -75,6 +78,10 @@ impl<'a> XlsxCellReader<'a> {
self.dimensions
}

pub(crate) fn row_attributes(&self) -> RowAttributes {
self.row_attributes.clone()
}

pub fn next_cell(&mut self) -> Result<Option<Cell<DataRef<'a>>>, XlsxError> {
loop {
self.buf.clear();
Expand All @@ -87,6 +94,13 @@ impl<'a> XlsxCellReader<'a> {
let row = get_row(range)?;
self.row_index = row;
}
let attribute =
get_attribute(row_element.attributes(), QName(b"outlineLevel"))?;
if let Some(range) = attribute {
self.row_attributes.outline_level = Some(get_outline_level(range)?);
} else {
self.row_attributes.outline_level = None;
}
}
Ok(Event::End(ref row_element)) if row_element.local_name().as_ref() == b"row" => {
self.row_index += 1;
Expand Down
46 changes: 38 additions & 8 deletions src/xlsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,21 +809,39 @@ impl<RS: Read + Seek> Xlsx<RS> {
let mut cell_reader = self.worksheet_cells_reader(name)?;
let len = cell_reader.dimensions().len();
let mut cells = Vec::new();
let mut row_attributes = Vec::new();
if len < 100_000 {
cells.reserve(len as usize);
}

// HACK: track when row changes. It would be cleaner to give the cell
// reader a "next_row" API.
let mut current_row = None;

loop {
match cell_reader.next_cell() {
Ok(Some(Cell {
val: DataRef::Empty,
pos,
..
})) => (),
Ok(Some(cell)) => cells.push(cell),
})) => {
if Some(pos.0) != current_row {
current_row = Some(pos.0);
row_attributes.push(cell_reader.row_attributes());
}
}
Ok(Some(cell)) => {
if Some(cell.pos.0) != current_row {
current_row = Some(cell.pos.0);
row_attributes.push(cell_reader.row_attributes());
}
cells.push(cell);
}
Ok(None) => break,
Err(e) => return Err(e),
}
}
Ok(Range::from_sparse(cells))
Ok(Range::from_sparse(cells, Some(row_attributes)))
}
}

Expand Down Expand Up @@ -870,12 +888,18 @@ impl<RS: Read + Seek> Reader<RS> for Xlsx<RS> {
}

fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, XlsxError> {
let rge = self.worksheet_range_ref(name)?;
let inner = rge.inner.into_iter().map(|v| v.into()).collect();
let Range {
start,
end,
inner,
row_attributes,
} = self.worksheet_range_ref(name)?;
let inner = inner.into_iter().map(|v| v.into()).collect();
Ok(Range {
start: rge.start,
end: rge.end,
start,
end,
inner,
row_attributes,
})
}

Expand All @@ -891,7 +915,7 @@ impl<RS: Read + Seek> Reader<RS> for Xlsx<RS> {
cells.push(cell);
}
}
Ok(Range::from_sparse(cells))
Ok(Range::from_sparse(cells, None))
}

fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
Expand Down Expand Up @@ -1047,6 +1071,12 @@ fn get_row_and_optional_column(range: &[u8]) -> Result<(u32, Option<u32>), XlsxE
Ok((row, col.checked_sub(1)))
}

fn get_outline_level(range: &[u8]) -> Result<u8, XlsxError> {
let s = std::str::from_utf8(range)
.map_err(|e| XlsxError::Xml(quick_xml::Error::NonDecodable(Some(e))))?;
Ok(s.parse()?)
}

/// attempts to read either a simple or richtext string
pub(crate) fn read_string(
xml: &mut XlReader<'_>,
Expand Down

0 comments on commit bde6613

Please sign in to comment.