}
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,
}
}
+impl AsValueOptions for &ValueOptions {
+ fn as_value_options(self) -> ValueOptions {
+ *self
+ }
+}
+
impl AsValueOptions for ValueOptions {
fn as_value_options(self) -> ValueOptions {
self
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,
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.
Coord2::new(0, 0),
self.look.areas.clone(),
self.borders(false),
+ self.as_value_options(),
);
for (y, row) in rows.enumerate() {
table.put(
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],
use super::pivot::{
AreaStyle, Axis2, BorderStyle, Coord2, Footnote, Look, PivotTable, Rect2, Stroke, ValueInner,
+ ValueOptions,
};
use super::table::{CellInner, Content, Table};
pub style: &'a AreaStyle,
pub subscripts: &'a [String],
pub footnotes: &'a [Arc<Footnote>],
+ pub value_options: &'a ValueOptions,
}
impl<'a> DrawCell<'a> {
style,
subscripts,
footnotes,
+ value_options: &table.value_options,
}
}
}
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> {
/// Horizontal and vertical rules.
pub rules: EnumMap<Axis2, Vec<Border>>,
+
+ /// How to present values.
+ pub value_options: ValueOptions,
}
impl Table {
headers: Coord2,
areas: EnumMap<Area, AreaStyle>,
borders: EnumMap<Border, BorderStyle>,
+ value_options: ValueOptions,
) -> Self {
Self {
n,
Axis2::X => vec![Border::Title; (n.y() + 1) * n.x()],
Axis2::Y => vec![Border::Title; n.y() * (n.x() + 1)],
},
+ value_options,
}
}
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,
};
}
}
- 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 {
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!()
}
}
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!(),
}
}
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,
}
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,
fn draw_cell(
&mut self,
- draw_cell: &DrawCell,
- alternate_row: bool,
+ cell: &DrawCell,
+ _alternate_row: bool,
bb: &Rect2,
valign_offset: usize,
spill: EnumMap<Axis2, [usize; 2]>,
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) {
///
/// 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,
}
}
}
+
+pub fn clip_text<'a>(
+ text: &'a str,
+ bb: &Range<usize>,
+ clip: &Range<usize>,
+) -> 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()))
+}