}
}
}
+
+#[derive(Default)]
+pub struct Footnotes(Vec<Arc<Footnote>>);
+
+impl Footnotes {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn push(&mut self, footnote: Footnote) -> Arc<Footnote> {
+ let footnote = Arc::new(footnote.with_index(self.0.len()));
+ self.0.push(footnote.clone());
+ footnote
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
pub struct PivotTableBuilder {
look: Arc<Look>,
title: Box<Value>,
axes: EnumMap<Axis3, Axis>,
dimensions: Vec<Dimension>,
cells: HashMap<usize, Value>,
+ footnotes: Footnotes,
}
impl PivotTableBuilder {
axes,
dimensions,
cells: HashMap::new(),
+ footnotes: Footnotes::new(),
}
}
pub fn with_look(mut self, look: Arc<Look>) -> Self {
value,
);
}
+ pub fn with_footnotes(mut self, footnotes: Footnotes) -> Self {
+ debug_assert!(self.footnotes.is_empty());
+ self.footnotes = footnotes;
+ self
+ }
pub fn build(self) -> PivotTable {
let mut table = PivotTable::new(self.title, self.look.clone());
table.dimensions = self.dimensions;
self
}
+ fn with_corner_text(mut self, corner_text: Value) -> Self {
+ self.corner_text = Some(Box::new(corner_text));
+ self
+ }
+
fn with_show_title(mut self, show_title: bool) -> Self {
self.show_title = show_title;
self
}
}
-pub struct Layers {}
-
#[derive(Clone, Debug)]
pub struct Footnote {
index: usize,
- content: Value,
- marker: Option<Value>,
+ content: Box<Value>,
+ marker: Option<Box<Value>>,
show: bool,
}
impl Footnote {
+ pub fn new(content: impl Into<Value>) -> Self {
+ Self {
+ index: 0,
+ content: Box::new(content.into()),
+ marker: None,
+ show: true,
+ }
+ }
+ pub fn with_marker(mut self, marker: impl Into<Value>) -> Self {
+ self.marker = Some(Box::new(marker.into()));
+ self
+ }
+ pub fn with_show(mut self, show: bool) -> Self {
+ self.show = show;
+ self
+ }
+ pub fn with_index(mut self, index: usize) -> Self {
+ self.index = index;
+ self
+ }
pub fn display_marker(&self, options: impl IntoValueOptions) -> DisplayMarker<'_> {
DisplayMarker {
footnote: self,
id: s.clone(),
})
}
+ pub fn with_footnote(mut self, footnote: &Arc<Footnote>) -> Self {
+ let footnotes = &mut self.styling.get_or_insert_default().footnotes;
+ footnotes.push(footnote.clone());
+ footnotes.sort_by_key(|f| f.index);
+ self
+ }
}
impl From<&str> for Value {
}
}
- pub fn with_styling(self, styling: &'a ValueStyle) -> Self {
- Self {
- markup: styling.style.font_style.markup,
- subscripts: styling.subscripts.as_slice(),
- footnotes: styling.footnotes.as_slice(),
- ..self
+ pub fn with_styling(mut self, styling: &'a ValueStyle) -> Self {
+ if let Some(area_style) = &styling.style {
+ self.markup = area_style.font_style.markup;
}
+ self.subscripts = styling.subscripts.as_slice();
+ self.footnotes = styling.footnotes.as_slice();
+ self
}
pub fn with_font_style(self, font_style: &FontStyle) -> Self {
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default)]
pub struct ValueStyle {
- pub style: AreaStyle,
+ pub style: Option<AreaStyle>,
pub subscripts: Vec<String>,
pub footnotes: Vec<Arc<Footnote>>,
}
use enum_map::EnumMap;
use crate::output::pivot::{
- Area, Axis2, Border, BorderStyle, Class, Color, Dimension, Group, HeadingRegion, LabelPosition,
- Look, PivotTable, RowColBorder, Stroke,
+ Area, Axis2, Border, BorderStyle, Class, Color, Dimension, Footnote, Footnotes, Group,
+ HeadingRegion, LabelPosition, Look, PivotTable, RowColBorder, Stroke,
};
use super::{Axis3, DimensionBuilder, GroupBuilder, PivotTableBuilder, Value};
);
}
+#[test]
+fn footnotes() {
+ let mut footnotes = Footnotes::new();
+ let f0 = footnotes.push(Footnote::new("First footnote").with_marker("*"));
+ let f1 = footnotes.push(Footnote::new("Second footnote"));
+ let a = Dimension::builder(
+ Axis3::X,
+ Group::builder(Value::new_text("A").with_footnote(&f0))
+ .with_label_shown()
+ .with(Value::new_text("B").with_footnote(&f1))
+ .with(Value::new_text("C").with_footnote(&f0).with_footnote(&f1)),
+ );
+ let d = Dimension::builder(
+ Axis3::Y,
+ Group::builder(Value::new_text("D").with_footnote(&f1))
+ .with_label_shown()
+ .with(Value::new_text("E").with_footnote(&f0))
+ .with(Value::new_text("F").with_footnote(&f1).with_footnote(&f0)),
+ );
+ let look = test_look().with_row_label_position(LabelPosition::Nested);
+ let mut pt = PivotTable::builder(
+ Value::new_text("Pivot Table with Alphabetic Subscript Footnotes").with_footnote(&f0),
+ vec![a, d],
+ );
+ pt.insert(&[0, 0], Value::new_number(Some(0.0)));
+ pt.insert(&[1, 0], Value::new_number(Some(1.0)).with_footnote(&f0));
+ pt.insert(&[0, 1], Value::new_number(Some(2.0)).with_footnote(&f1));
+ pt.insert(
+ &[1, 1],
+ Value::new_number(Some(3.0))
+ .with_footnote(&f0)
+ .with_footnote(&f1),
+ );
+ let pt = pt
+ .with_look(Arc::new(look))
+ .build()
+ .with_caption(Value::new_text("Caption").with_footnote(&f0))
+ .with_corner_text(
+ Value::new_text("Corner")
+ .with_footnote(&f0)
+ .with_footnote(&f1),
+ );
+ assert_rendering(
+ &pt,
+ "\
+Pivot Table with Alphabetic Subscript Footnotes[*]
+╭────────────┬──────────────────╮
+│ │ A[*] │
+│ ├───────┬──────────┤
+│Corner[*][b]│ B[b] │ C[*][b] │
+├────────────┼───────┼──────────┤
+│D[b] E[*] │ .00│ 1.00[*]│
+│ F[*][b]│2.00[b]│3.00[*][b]│
+╰────────────┴───────┴──────────╯
+Caption[*]
+*. First footnote
+b. Second footnote
+",
+ );
+}
+
#[test]
fn no_dimension() {
let pivot_table = PivotTableBuilder::new(Value::new_text("No Dimensions"), vec![])
impl<'a> DrawCell<'a> {
fn new(inner: &'a CellInner, table: &'a Table) -> Self {
- let (style, subscripts, footnotes) = if let Some(styling) = inner.value.styling.as_ref() {
+ let default_area_style = &table.areas[inner.area];
+ let (style, subscripts, footnotes) = if let Some(styling) = &inner.value.styling {
(
- &styling.style,
+ styling.style.as_ref().unwrap_or(default_area_style),
styling.subscripts.as_slice(),
styling.footnotes.as_slice(),
)
} else {
- (&table.areas[inner.area], [].as_slice(), [].as_slice())
+ (default_area_style, [].as_slice(), [].as_slice())
};
Self {
rotate: inner.rotate,
}
// Distribute widths of spanned columns.
+ dbg!(&unspanned_columns);
let mut columns = unspanned_columns.clone();
for cell in table.cells().filter(|cell| cell.col_span() > 1) {
let rect = cell.rect();
);
}
}
+ dbg!(&columns);
// In pathological cases, spans can cause the minimum width of a column
// to exceed the maximum width. This bollixes our interpolation