output::{
driver::Driver,
pivot::{
- Axis2, CellStyle, Color, FontStyle, HeadingRegion, HorzAlign, PivotTable, StringValue,
- TemplateValue, TextValue, Value, ValueInner, ValueStyle, VariableValue, VertAlign,
+ Area, AreaStyle, Axis2, Axis3, Border, BorderStyle, BoxBorder, CellStyle, Color,
+ FontStyle, Footnote, Footnotes, HeadingRegion, HorzAlign, PivotTable, RowColBorder,
+ Stroke, Value, ValueInner, ValueStyle, VertAlign,
},
Item,
},
let mut content = Vec::new();
let mut cursor = Cursor::new(&mut content);
- Header::new(pivot_table).write_le(&mut cursor).unwrap();
+ pivot_table.write_be(&mut cursor).unwrap();
self.writer
.start_file(light_table_name(table_id), SimpleFileOptions::default())
}
}
+impl BinWrite for PivotTable {
+ type Args<'a> = ();
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ _endian: Endian,
+ _args: (),
+ ) -> binrw::BinResult<()> {
+ // Header.
+ Header::new(self).write_le(writer)?;
+
+ // Titles.
+ (
+ self.title(),
+ self.subtype(),
+ Optional(Some(self.title())),
+ Optional(self.corner_text.as_ref()),
+ Optional(self.caption.as_ref()),
+ )
+ .write_le(writer)?;
+
+ // Footnotes.
+ self.footnotes.write_le(writer)?;
+
+ // Areas.
+ static SPV_AREAS: [Area; 8] = [
+ Area::Title,
+ Area::Caption,
+ Area::Footer,
+ Area::Corner,
+ Area::Labels(Axis2::X),
+ Area::Labels(Axis2::Y),
+ Area::Data,
+ Area::Layers,
+ ];
+ for (index, area) in SPV_AREAS.into_iter().enumerate() {
+ self.look.areas[area].write_le_args(writer, index)?;
+ }
+
+ // Borders.
+ static SPV_BORDERS: [Border; 19] = [
+ Border::Title,
+ Border::OuterFrame(BoxBorder::Left),
+ Border::OuterFrame(BoxBorder::Top),
+ Border::OuterFrame(BoxBorder::Right),
+ Border::OuterFrame(BoxBorder::Bottom),
+ Border::InnerFrame(BoxBorder::Left),
+ Border::InnerFrame(BoxBorder::Top),
+ Border::InnerFrame(BoxBorder::Right),
+ Border::InnerFrame(BoxBorder::Bottom),
+ Border::DataLeft,
+ Border::DataTop,
+ Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X)),
+ Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::Y)),
+ Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::X)),
+ Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)),
+ Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X)),
+ Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::Y)),
+ Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::X)),
+ Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)),
+ ];
+ let borders_start = Count::new(writer)?;
+ (1, SPV_BORDERS.len() as u32).write_be(writer)?;
+ for (index, border) in SPV_BORDERS.into_iter().enumerate() {
+ self.look.borders[border].write_be_args(writer, index)?;
+ }
+ (SpvBool(self.show_grid_lines), 0u8, 0u16).write_le(writer)?;
+ borders_start.finish_le32(writer)?;
+
+ // Print Settings.
+ let ps_start = Count::new(writer)?;
+ (
+ 1u32,
+ SpvBool(self.look.print_all_layers),
+ SpvBool(self.look.paginate_layers),
+ SpvBool(self.look.shrink_to_fit[Axis2::X]),
+ SpvBool(self.look.shrink_to_fit[Axis2::Y]),
+ SpvBool(self.look.top_continuation),
+ SpvBool(self.look.bottom_continuation),
+ self.look.n_orphan_lines as u32,
+ SpvString(self.look.continuation.as_ref().map_or("", |s| s.as_str())),
+ )
+ .write_be(writer)?;
+ ps_start.finish_le32(writer)?;
+
+ // Table Settings.
+ let ts_start = Count::new(writer)?;
+ (1u32, 4u32, self.spv_layer() as u32).write_be(writer)?;
+ ts_start.finish_le32(writer)?;
+
+ Ok(())
+ }
+}
+
+impl PivotTable {
+ fn spv_layer(&self) -> usize {
+ let mut layer = 0;
+ for (dimension, layer_value) in self
+ .axis_dimensions(Axis3::Z)
+ .zip(self.current_layer.iter().copied())
+ .rev()
+ {
+ layer = layer * dimension.len() + layer_value;
+ }
+ layer
+ }
+}
+
impl<W> Driver for SpvWriter<W>
where
W: Write + Seek,
match &item.details {
super::Details::Chart => todo!(),
super::Details::Image => todo!(),
- super::Details::Group(items) => todo!(),
- super::Details::Message(diagnostic) => todo!(),
+ super::Details::Group(_items) => todo!(),
+ super::Details::Message(_diagnostic) => todo!(),
super::Details::PageBreak => {
self.needs_page_break = true;
return;
}
super::Details::Table(pivot_table) => self.write_table(&*item, pivot_table),
- super::Details::Text(text) => todo!(),
+ super::Details::Text(_text) => todo!(),
};
todo!()
}
#[derive(Serialize)]
struct TableStructure;
-struct Bool(bool);
-impl BinWrite for Bool {
+impl BinWrite for Footnote {
+ type Args<'a> = ();
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ endian: Endian,
+ args: Self::Args<'_>,
+ ) -> binrw::BinResult<()> {
+ (
+ &self.content,
+ Optional(self.marker.as_ref()),
+ if self.show { 1i32 } else { -1 },
+ )
+ .write_options(writer, endian, args)
+ }
+}
+
+impl BinWrite for Footnotes {
+ type Args<'a> = ();
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ endian: Endian,
+ args: Self::Args<'_>,
+ ) -> binrw::BinResult<()> {
+ (self.0.len() as u32).write_options(writer, endian, args)?;
+ for footnote in &self.0 {
+ footnote.write_options(writer, endian, args)?;
+ }
+ Ok(())
+ }
+}
+
+impl BinWrite for AreaStyle {
+ type Args<'a> = usize;
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ endian: Endian,
+ index: usize,
+ ) -> binrw::BinResult<()> {
+ let typeface = if self.font_style.font.is_empty() {
+ "SansSerif"
+ } else {
+ self.font_style.font.as_str()
+ };
+ (
+ (index + 1) as u8,
+ 0x31u8,
+ SpvString(typeface),
+ self.font_style.size as f32 * 1.33,
+ self.font_style.bold as u32 + 2 * self.font_style.italic as u32,
+ SpvBool(self.font_style.underline),
+ self.cell_style
+ .horz_align
+ .map_or(64173, |horz_align| horz_align.as_spv(61453)),
+ self.cell_style.vert_align.as_spv(),
+ self.font_style.fg[0],
+ self.font_style.bg[0],
+ )
+ .write_options(writer, endian, ())?;
+
+ if self.font_style.fg[0] != self.font_style.fg[1]
+ || self.font_style.bg[0] != self.font_style.bg[1]
+ {
+ (SpvBool(true), self.font_style.fg[1], self.font_style.bg[1]).write_options(
+ writer,
+ endian,
+ (),
+ )?;
+ } else {
+ (SpvBool(false), SpvString(""), SpvString("")).write_options(writer, endian, ())?;
+ }
+
+ (
+ self.cell_style.margins[Axis2::X][0],
+ self.cell_style.margins[Axis2::X][1],
+ self.cell_style.margins[Axis2::Y][0],
+ self.cell_style.margins[Axis2::Y][1],
+ )
+ .write_options(writer, endian, ())
+ }
+}
+
+impl Stroke {
+ fn as_spv(&self) -> u32 {
+ match self {
+ Stroke::None => 0,
+ Stroke::Solid => 1,
+ Stroke::Dashed => 2,
+ Stroke::Thick => 3,
+ Stroke::Thin => 4,
+ Stroke::Double => 5,
+ }
+ }
+}
+
+impl Color {
+ fn as_spv(&self) -> u32 {
+ ((self.alpha as u32) << 24)
+ | ((self.r as u32) << 16)
+ | ((self.g as u32) << 8)
+ | (self.b as u32)
+ }
+}
+
+impl BinWrite for BorderStyle {
+ type Args<'a> = usize;
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ _endian: Endian,
+ index: usize,
+ ) -> binrw::BinResult<()> {
+ (index as u32, self.stroke.as_spv(), self.color.as_spv()).write_be(writer)
+ }
+}
+
+struct SpvBool(bool);
+impl BinWrite for SpvBool {
type Args<'a> = ();
fn write_options<W: Write + Seek>(
#[bw(magic(1u16))]
version: u8,
- x0: Bool,
- x1: Bool,
- rotate_inner_column_labels: Bool,
- rotate_outer_row_labels: Bool,
- x2: Bool,
+ x0: SpvBool,
+ x1: SpvBool,
+ rotate_inner_column_labels: SpvBool,
+ rotate_outer_row_labels: SpvBool,
+ x2: SpvBool,
x3: u32,
min_col_heading_width: i32,
max_col_heading_width: i32,
fn new(pivot_table: &PivotTable) -> Self {
Self {
version: 3,
- x0: Bool(true),
- x1: Bool(false),
- rotate_inner_column_labels: Bool(pivot_table.rotate_inner_column_labels),
- rotate_outer_row_labels: Bool(pivot_table.rotate_outer_row_labels),
- x2: Bool(true),
+ x0: SpvBool(true),
+ x1: SpvBool(false),
+ rotate_inner_column_labels: SpvBool(pivot_table.rotate_inner_column_labels),
+ rotate_outer_row_labels: SpvBool(pivot_table.rotate_outer_row_labels),
+ x2: SpvBool(true),
x3: 0x15,
min_col_heading_width: *pivot_table.look.heading_widths[HeadingRegion::Columns].start()
as i32,
self.font.as_str()
};
(
- Bool(self.bold),
- Bool(self.italic),
- Bool(self.underline),
- Bool(true),
+ SpvBool(self.bold),
+ SpvBool(self.italic),
+ SpvBool(self.underline),
+ SpvBool(true),
self.fg[0],
self.bg[0],
SpvString(typeface),
endian: Endian,
args: Self::Args<'_>,
) -> binrw::BinResult<()> {
- if let Some(font_style) = self.font_style {
- (0x31u8, font_style).write_options(writer, endian, args)?;
- } else {
- 0x58u8.write_options(writer, endian, args)?;
- }
+ (
+ Optional(self.font_style.as_ref()),
+ Optional(self.cell_style.as_ref()),
+ )
+ .write_options(writer, endian, args)
+ }
+}
- if let Some(cell_style) = self.cell_style {
- (0x31u8, cell_style).write_options(writer, endian, args)?;
- } else {
- 0x58u8.write_options(writer, endian, args)?;
- }
+struct Optional<T>(Option<T>);
- Ok(())
+impl<T> BinWrite for Optional<T>
+where
+ T: BinWrite,
+{
+ type Args<'a> = T::Args<'a>;
+
+ fn write_options<W: Write + Seek>(
+ &self,
+ writer: &mut W,
+ endian: Endian,
+ args: Self::Args<'_>,
+ ) -> binrw::BinResult<()> {
+ match &self.0 {
+ Some(value) => {
+ 0x31u8.write_le(writer)?;
+ value.write_options(writer, endian, args)
+ }
+ None => 0x58u8.write_le(writer),
+ }
}
}
template: Option<&'a str>,
}
+impl<'a> OptionalStyle<'a> {
+ fn new(value: &'a Value) -> Self {
+ Self {
+ style: &value.styling,
+ template: None,
+ }
+ }
+}
+
impl<'a> Default for OptionalStyle<'a> {
fn default() -> Self {
Self {
match &self.inner {
ValueInner::Number(number) => {
if number.var_name.is_some() || number.value_label.is_some() {
- 2u8.write_options(writer, endian, args)?;
- //write_optional_style(self.styling.as_ref(),writer, endian, args)?;
(
+ 2u8,
+ OptionalStyle::new(self),
SpvFormat {
format: number.format,
honor_small: number.honor_small,
)
.write_options(writer, endian, args)?;
} else {
- 1u8.write_options(writer, endian, args)?;
- //write_optional_style(self.styling.as_ref(),writer, endian, args)?;
- number
- .value
- .unwrap_or(-f64::MAX)
+ (
+ 1u8,
+ OptionalStyle::new(self),
+ number.value.unwrap_or(-f64::MAX),
+ Show::as_spv(&number.show),
+ )
.write_options(writer, endian, args)?;
- Show::as_spv(&number.show).write_options(writer, endian, args)?;
}
}
- ValueInner::String(_string) => todo!(),
- ValueInner::Variable(_variable) => todo!(),
- ValueInner::Text(_text) => todo!(),
- ValueInner::Template(_template) => todo!(),
+ ValueInner::String(string) => {
+ (
+ 4u8,
+ OptionalStyle::new(self),
+ SpvFormat {
+ format: if string.hex {
+ Format::new(Type::AHex, (string.s.len() * 2) as u16, 0).unwrap()
+ } else {
+ Format::new(Type::A, (string.s.len()) as u16, 0).unwrap()
+ },
+ honor_small: false,
+ },
+ SpvString::optional(&string.value_label),
+ SpvString::optional(&string.var_name),
+ Show::as_spv(&string.show),
+ SpvString(&string.s),
+ )
+ .write_options(writer, endian, args)?;
+ }
+ ValueInner::Variable(variable) => {
+ (
+ 5u8,
+ OptionalStyle::new(self),
+ SpvString(&variable.var_name),
+ SpvString::optional(&variable.variable_label),
+ Show::as_spv(&variable.show),
+ )
+ .write_options(writer, endian, args)?;
+ }
+ ValueInner::Text(text) => {
+ (
+ 3u8,
+ SpvString(&text.local),
+ OptionalStyle::new(self),
+ SpvString(&text.id),
+ SpvString(&text.c),
+ SpvBool(true),
+ )
+ .write_options(writer, endian, args)?;
+ }
+ ValueInner::Template(template) => {
+ (
+ 0u8,
+ OptionalStyle::new(self),
+ SpvString(&template.local),
+ template.args.len() as u32,
+ )
+ .write_options(writer, endian, args)?;
+ for arg in &template.args {
+ if arg.len() > 1 {
+ (arg.len() as u32, 0u32).write_options(writer, endian, args)?;
+ for (index, value) in arg.iter().enumerate() {
+ if index > 0 {
+ 0u32.write_le(writer)?;
+ }
+ value.write_options(writer, endian, args)?;
+ }
+ } else {
+ (0u32, arg).write_options(writer, endian, args)?;
+ }
+ }
+ }
ValueInner::Empty => {
(
3u8,
OptionalStyle::default(),
SpvString(""),
SpvString(""),
- Bool(true),
+ SpvBool(true),
)
.write_options(writer, endian, args)?;
}