From: Ben Pfaff Date: Sun, 9 Mar 2025 19:51:51 +0000 (-0700) Subject: work on tablelooks X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0edfa9790351bcedb31395e370eee8dfe7cee3de;p=pspp work on tablelooks --- diff --git a/rust/pspp/src/output/pivot/mod.rs b/rust/pspp/src/output/pivot/mod.rs index b08d83d8dc..6dcded30cd 100644 --- a/rust/pspp/src/output/pivot/mod.rs +++ b/rust/pspp/src/output/pivot/mod.rs @@ -510,6 +510,7 @@ impl CategoryTrait for Category { pub struct Look { pub name: Option, + /// Whether to hide rows or columns whose cells are all empty. pub omit_empty: bool, pub row_label_position: RowLabelPosition, @@ -588,16 +589,18 @@ pub enum 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, general_properties: GeneralProperties, @@ -607,6 +610,60 @@ mod look_xml { printing_properties: PrintingProperties, } + impl From 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")] @@ -625,7 +682,7 @@ mod look_xml { minimum_row_width: i64, #[serde(rename = "@rowDimensionLabels")] - row_dimension_labels: RowLabelPosition, + row_label_position: RowLabelPosition, } #[derive(Deserialize, Debug)] @@ -635,7 +692,7 @@ mod look_xml { marker_position: FootnoteMarkerPosition, #[serde(rename = "@numberFormat")] - number_format: FootnoteMarkerType, + marker_type: FootnoteMarkerType, } #[derive(Deserialize, Debug)] @@ -694,7 +751,51 @@ mod look_xml { 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] @@ -702,7 +803,7 @@ mod look_xml { Italic, } - #[derive(Deserialize, Debug, Default)] + #[derive(Deserialize, Debug, Default, PartialEq, Eq)] #[serde(rename_all = "camelCase")] enum FontWeight { #[default] @@ -710,7 +811,7 @@ mod look_xml { Bold, } - #[derive(Deserialize, Debug, Default)] + #[derive(Deserialize, Debug, Default, PartialEq, Eq)] #[serde(rename_all = "camelCase")] enum FontUnderline { #[default] @@ -804,12 +905,27 @@ mod look_xml { 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)