pub struct Look {
pub name: Option<String>,
+ /// Whether to hide rows or columns whose cells are all empty.
pub omit_empty: bool,
pub row_label_position: RowLabelPosition,
mod look_xml {
use std::{fmt::Debug, num::ParseFloatError, str::FromStr};
+ use enum_map::enum_map;
use serde::{de::Visitor, Deserialize};
use crate::output::pivot::{
- Color, FootnoteMarkerPosition, FootnoteMarkerType, RowLabelPosition, Stroke,
+ Area, AreaStyle, Axis2, Color, FootnoteMarkerPosition, FootnoteMarkerType, HeadingRegion,
+ HorzAlign, Look, RowLabelPosition, Stroke, VertAlign,
};
use thiserror::Error as ThisError;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
- struct TableProperties {
+ pub struct TableProperties {
#[serde(rename = "@name")]
name: Option<String>,
general_properties: GeneralProperties,
printing_properties: PrintingProperties,
}
+ impl From<TableProperties> for Look {
+ fn from(table_properties: TableProperties) -> Self {
+ Self {
+ name: table_properties.name,
+ omit_empty: table_properties.general_properties.hide_empty_rows,
+ row_label_position: table_properties.general_properties.row_label_position,
+ heading_widths: enum_map! {
+ HeadingRegion::ColumnHeadings => table_properties.general_properties.minimum_column_width..=table_properties.general_properties.maximum_column_width,
+ HeadingRegion::RowHeadings => table_properties.general_properties.minimum_row_width..=table_properties.general_properties.maximum_row_width,
+ }.map(|_k, r|(*r.start()).try_into().unwrap_or_default()..=(*r.end()).try_into().unwrap_or_default()),
+ footnote_marker_type: table_properties.footnote_properties.marker_type,
+ footnote_marker_position: table_properties.footnote_properties.marker_position,
+ areas: enum_map! {
+ Area::Title => table_properties.cell_format_properties.title.style.as_area_style(),
+ Area::Caption => table_properties.cell_format_properties.caption.style.as_area_style(),
+ Area::Footer => table_properties.cell_format_properties.footnotes.style.as_area_style(),
+ Area::Corner => table_properties.cell_format_properties.corner_labels.style.as_area_style(),
+ Area::ColumnLabels => table_properties.cell_format_properties.column_labels.style.as_area_style(),
+ Area::RowLabels => table_properties.cell_format_properties.row_labels.style.as_area_style(),
+ Area::Data => table_properties.cell_format_properties.data.style.as_area_style(),
+ Area::Layers => table_properties.cell_format_properties.layers.style.as_area_style(),
+ },
+ borders: todo!(),
+ print_all_layers: table_properties.printing_properties.print_all_layers,
+ paginate_layers: table_properties
+ .printing_properties
+ .print_each_layer_on_separate_page,
+ shrink_to_fit: enum_map! {
+ Axis2::X => table_properties.printing_properties.rescale_wide_table_to_fit_page,
+ Axis2::Y => table_properties.printing_properties.rescale_long_table_to_fit_page,
+ },
+ top_continuation: table_properties
+ .printing_properties
+ .continuation_text_at_top,
+ bottom_continuation: table_properties
+ .printing_properties
+ .continuation_text_at_bottom,
+ continuation: {
+ let text = table_properties.printing_properties.continuation_text;
+ if text == "" {
+ None
+ } else {
+ Some(text)
+ }
+ },
+ n_orphan_lines: table_properties
+ .printing_properties
+ .window_orphan_lines
+ .try_into()
+ .unwrap_or_default(),
+ }
+ }
+ }
+
#[derive(Deserialize, Debug)]
struct GeneralProperties {
#[serde(rename = "@hideEmptyRows")]
minimum_row_width: i64,
#[serde(rename = "@rowDimensionLabels")]
- row_dimension_labels: RowLabelPosition,
+ row_label_position: RowLabelPosition,
}
#[derive(Deserialize, Debug)]
marker_position: FootnoteMarkerPosition,
#[serde(rename = "@numberFormat")]
- number_format: FootnoteMarkerType,
+ marker_type: FootnoteMarkerType,
}
#[derive(Deserialize, Debug)]
decimal_offset: Dimension,
}
- #[derive(Deserialize, Debug, Default)]
+ impl CellStyle {
+ fn as_area_style(&self) -> AreaStyle {
+ AreaStyle {
+ cell_style: super::CellStyle {
+ horz_align: match self.text_alignment {
+ TextAlignment::Left => Some(HorzAlign::Left),
+ TextAlignment::Right => Some(HorzAlign::Right),
+ TextAlignment::Center => Some(HorzAlign::Center),
+ TextAlignment::Decimal => Some(HorzAlign::Decimal {
+ offset: self.decimal_offset.as_px_f64(),
+ c: '.',
+ }),
+ TextAlignment::Mixed => None,
+ },
+ vert_align: match self.label_location_vertical {
+ LabelLocationVertical::Positive => VertAlign::Top,
+ LabelLocationVertical::Negative => VertAlign::Bottom,
+ LabelLocationVertical::Center => VertAlign::Middle,
+ },
+ margins: enum_map! {
+ Axis2::X => [self.margin_left.as_px_i32(), self.margin_right.as_px_i32()],
+ Axis2::Y => [self.margin_top.as_px_i32(), self.margin_bottom.as_px_i32()],
+ },
+ },
+ font_style: super::FontStyle {
+ bold: self.font_weight == FontWeight::Bold,
+ italic: self.font_style == FontStyle::Italic,
+ underline: self.font_underline == FontUnderline::Underline,
+ markup: false,
+ font: self.font_family.clone(),
+ fg: [
+ self.color.unwrap_or(Color::BLACK),
+ self.alternating_text_color.unwrap_or(Color::BLACK),
+ ],
+ bg: [
+ self.color2.unwrap_or(Color::BLACK),
+ self.alternating_color.unwrap_or(Color::BLACK),
+ ],
+ size: self.font_size.as_pt_i32(),
+ },
+ }
+ }
+ }
+
+ #[derive(Deserialize, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
enum FontStyle {
#[default]
Italic,
}
- #[derive(Deserialize, Debug, Default)]
+ #[derive(Deserialize, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
enum FontWeight {
#[default]
Bold,
}
- #[derive(Deserialize, Debug, Default)]
+ #[derive(Deserialize, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
enum FontUnderline {
#[default]
continuation_text_at_top: bool,
}
- #[derive(Default, PartialEq)]
+ #[derive(Copy, Clone, Default, PartialEq)]
struct Dimension(
/// In inches.
f64,
);
+ impl Dimension {
+ fn as_px_f64(self) -> f64 {
+ self.0 * 96.0
+ }
+ fn as_px_i32(self) -> i32 {
+ num::cast(self.as_px_f64() + 0.5).unwrap_or_default()
+ }
+ fn as_pt_f64(self) -> f64 {
+ self.0 * 72.0
+ }
+ fn as_pt_i32(self) -> i32 {
+ num::cast(self.as_pt_f64() + 0.5).unwrap_or_default()
+ }
+ }
+
impl Debug for Dimension {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.2}in", self.0)