From: Ben Pfaff Date: Thu, 6 Mar 2025 23:42:07 +0000 (-0800) Subject: more progress on ascii output driver X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a9041a097c2fbfffaa313185d579a4353ccb12da;p=pspp more progress on ascii output driver --- diff --git a/rust/pspp/src/output/pivot/mod.rs b/rust/pspp/src/output/pivot/mod.rs index 971d9392d4..0ffa7baaee 100644 --- a/rust/pspp/src/output/pivot/mod.rs +++ b/rust/pspp/src/output/pivot/mod.rs @@ -635,7 +635,7 @@ pub enum HorzAlign { } impl HorzAlign { - fn for_mixed(var_type: VarType) -> Self { + pub fn for_mixed(var_type: VarType) -> Self { match var_type { VarType::Numeric => Self::Right, VarType::String => Self::Left, @@ -941,6 +941,12 @@ impl AsValueOptions for &PivotTable { } } +impl AsValueOptions for &ValueOptions { + fn as_value_options(self) -> ValueOptions { + *self + } +} + impl AsValueOptions for ValueOptions { fn as_value_options(self) -> ValueOptions { self @@ -1265,7 +1271,7 @@ impl<'a> DisplayValue<'a> { self.options.small } - fn var_type(&self) -> VarType { + pub fn var_type(&self) -> VarType { match self.inner { ValueInner::Number { .. } if self.show_label.is_none() => VarType::Numeric, _ => VarType::String, diff --git a/rust/pspp/src/output/pivot/output.rs b/rust/pspp/src/output/pivot/output.rs index eadf61c06c..6f8bb82ed6 100644 --- a/rust/pspp/src/output/pivot/output.rs +++ b/rust/pspp/src/output/pivot/output.rs @@ -6,8 +6,9 @@ use smallvec::{SmallVec, ToSmallVec}; use crate::output::table::{CellInner, Table}; use super::{ - Area, Axis, Axis2, Axis3, Border, BorderStyle, BoxBorder, Category, CategoryTrait, Color, - Coord2, Dimension, Footnote, PivotTable, Rect2, RowColBorder, Stroke, Value, + Area, AsValueOptions, Axis, Axis2, Axis3, Border, BorderStyle, BoxBorder, Category, + CategoryTrait, Color, Coord2, Dimension, Footnote, PivotTable, Rect2, RowColBorder, Stroke, + Value, }; /// All of the combinations of dimensions along an axis. @@ -125,6 +126,7 @@ impl PivotTable { Coord2::new(0, 0), self.look.areas.clone(), self.borders(false), + self.as_value_options(), ); for (y, row) in rows.enumerate() { table.put( @@ -161,7 +163,13 @@ impl PivotTable { self.axes[Axis3::X].label_depth, ); let n = EnumMap::from_fn(|axis| data[axis] + stub[axis]).into(); - let mut body = Table::new(n, stub, self.look.areas.clone(), self.borders(printing)); + let mut body = Table::new( + n, + stub, + self.look.areas.clone(), + self.borders(printing), + self.as_value_options(), + ); compose_headings( &mut body, &self.axes[Axis3::X], diff --git a/rust/pspp/src/output/render.rs b/rust/pspp/src/output/render.rs index 0e19ccef08..a17cd278e4 100644 --- a/rust/pspp/src/output/render.rs +++ b/rust/pspp/src/output/render.rs @@ -13,6 +13,7 @@ use crate::output::pivot::VertAlign; use super::pivot::{ AreaStyle, Axis2, BorderStyle, Coord2, Footnote, Look, PivotTable, Rect2, Stroke, ValueInner, + ValueOptions, }; use super::table::{CellInner, Content, Table}; @@ -149,6 +150,7 @@ pub struct DrawCell<'a> { pub style: &'a AreaStyle, pub subscripts: &'a [String], pub footnotes: &'a [Arc], + pub value_options: &'a ValueOptions, } impl<'a> DrawCell<'a> { @@ -168,6 +170,7 @@ impl<'a> DrawCell<'a> { style, subscripts, footnotes, + value_options: &table.value_options, } } } diff --git a/rust/pspp/src/output/table.rs b/rust/pspp/src/output/table.rs index e58e35e693..6684980ccd 100644 --- a/rust/pspp/src/output/table.rs +++ b/rust/pspp/src/output/table.rs @@ -16,7 +16,9 @@ use enum_map::{enum_map, EnumMap}; use crate::output::pivot::Coord2; -use super::pivot::{Area, AreaStyle, Axis2, Border, BorderStyle, HeadingRegion, Rect2, Value}; +use super::pivot::{ + Area, AreaStyle, Axis2, Border, BorderStyle, HeadingRegion, Rect2, Value, ValueOptions, +}; #[derive(Clone)] pub struct CellRef<'a> { @@ -180,6 +182,9 @@ pub struct Table { /// Horizontal and vertical rules. pub rules: EnumMap>, + + /// How to present values. + pub value_options: ValueOptions, } impl Table { @@ -188,6 +193,7 @@ impl Table { headers: Coord2, areas: EnumMap, borders: EnumMap, + value_options: ValueOptions, ) -> Self { Self { n, @@ -199,6 +205,7 @@ impl Table { Axis2::X => vec![Border::Title; (n.y() + 1) * n.x()], Axis2::Y => vec![Border::Title; n.y() * (n.x() + 1)], }, + value_options, } } diff --git a/rust/pspp/src/output/text.rs b/rust/pspp/src/output/text.rs index 048e045019..778f49292d 100644 --- a/rust/pspp/src/output/text.rs +++ b/rust/pspp/src/output/text.rs @@ -12,10 +12,10 @@ use unicode_width::UnicodeWidthStr; use super::{ driver::Driver, - pivot::{Axis2, BorderStyle, Coord2, PivotTable, Rect2, Stroke}, + pivot::{Axis2, BorderStyle, Coord2, DisplayValue, HorzAlign, PivotTable, Rect2, Stroke}, render::{Device, DrawCell, Pager, Params}, table::Content, - text_line::TextLine, + text_line::{clip_text, TextLine}, Details, Item, }; @@ -267,13 +267,12 @@ impl TextDriver { } } - fn cell_to_text(cell: &DrawCell) -> String { + fn display_cell<'a>(cell: &DrawCell<'a>) -> DisplayValue<'a> { cell.inner - .display(() /* XXX */) + .display(cell.value_options) .with_font_style(&cell.style.font_style) .with_subscripts(cell.subscripts) .with_footnotes(cell.footnotes) - .to_string() } fn layout_cell(&self, cell: &DrawCell, mut text: &str, bb: Rect2, clip: Rect2) -> Coord2 { @@ -284,20 +283,20 @@ impl TextDriver { use Axis2::*; let mut breaks = new_line_breaks(text, bb[X].len()); let mut size = Coord2::new(0, 0); - for (text, y) in breaks.zip(bb[Y].clone()) { + for (text, _y) in breaks.zip(bb[Y].clone()) { let width = text.width(); if width > size[X] { size[X] = width; } size[Y] += 1; + } + size + } - if !clip[Y].contains(&y) { - continue; - } - - + fn reserve(&mut self, y: usize) { + if y >= self.lines.len() { + self.lines.resize(y + 1, TextLine::new()); } - todo!() } } @@ -438,9 +437,9 @@ impl Driver for TextDriver { match &item.details { Details::Chart => todo!(), Details::Image => todo!(), - Details::Group(vec) => todo!(), + Details::Group(_) => todo!(), Details::Message(diagnostic) => todo!(), - Details::PageBreak => todo!(), + Details::PageBreak => (), Details::Table(pivot_table) => self.output_table(pivot_table), Details::Text(text) => todo!(), } @@ -453,7 +452,7 @@ impl Device for TextDriver { } fn measure_cell_width(&self, cell: &DrawCell) -> [usize; 2] { - let text = Self::cell_to_text(cell); + let text = Self::display_cell(cell).to_string(); let max_width = self.layout_cell( cell, &text, @@ -470,7 +469,7 @@ impl Device for TextDriver { } fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize { - let text = Self::cell_to_text(cell); + let text = Self::display_cell(cell).to_string(); self.layout_cell( cell, &text, @@ -490,14 +489,41 @@ impl Device for TextDriver { fn draw_cell( &mut self, - draw_cell: &DrawCell, - alternate_row: bool, + cell: &DrawCell, + _alternate_row: bool, bb: &Rect2, valign_offset: usize, spill: EnumMap, clip: &Rect2, ) { - todo!() + let display = Self::display_cell(cell); + let text = display.to_string(); + let horz_align = cell + .style + .cell_style + .horz_align + .unwrap_or_else(|| HorzAlign::for_mixed(display.var_type())); + + use Axis2::*; + let mut breaks = new_line_breaks(&text, bb[X].len()); + for (text, y) in breaks.zip(bb[Y].start + valign_offset..bb[Y].end) { + let width = text.width(); + if !clip[Y].contains(&y) { + continue; + } + + let x = match horz_align { + HorzAlign::Right | HorzAlign::Decimal { .. } => bb[X].end - width, + HorzAlign::Left => bb[X].start, + HorzAlign::Center => (bb[X].start + bb[X].end - width + 1) / 2, + }; + let Some((x, text)) = clip_text(&text, &(x..x + width), &clip[X]) else { + continue; + }; + + self.reserve(y); + self.lines[y].put(x, text); + } } fn scale(&mut self, factor: f64) { diff --git a/rust/pspp/src/output/text_line.rs b/rust/pspp/src/output/text_line.rs index 54c5f3a781..21e9e9a395 100644 --- a/rust/pspp/src/output/text_line.rs +++ b/rust/pspp/src/output/text_line.rs @@ -8,7 +8,7 @@ use unicode_width::UnicodeWidthChar; /// /// Designed to make appending text fast, and access and modification of other /// column positions possible. -#[derive(Default)] +#[derive(Clone, Default)] pub struct TextLine { /// Content. string: String, @@ -527,3 +527,34 @@ mod test { } } } + +pub fn clip_text<'a>( + text: &'a str, + bb: &Range, + clip: &Range, +) -> Option<(usize, &'a str)> { + let mut x = bb.start; + let mut width = bb.len(); + + let mut iter = text.chars(); + while x < clip.start { + let c = iter.next()?; + if let Some(w) = c.width() { + x += w; + width = width.checked_sub(w)?; + } + } + if x + width > clip.end { + if x >= clip.end { + return None; + } + + while x + width > clip.end { + let c = iter.next_back()?; + if let Some(w) = c.width() { + width = width.checked_sub(w)?; + } + } + } + Some((x, iter.as_str())) +}