mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Charts: a missing varyColors varies single-series bar colours by point (#143)
This commit is contained in:
@@ -150,9 +150,8 @@ pub(crate) fn chart_from(root: &El) -> Option<Chart> {
|
||||
"surfaceChart" | "surface3DChart" => ChartType::Surface,
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(v) = g.child_val("varyColors") {
|
||||
c.vary_colors = v == "1" || v == "true";
|
||||
}
|
||||
// A missing <c:varyColors> means "vary by point" (the schema default is true), as PowerPoint draws it.
|
||||
c.vary_colors = g.child_val("varyColors").is_none_or(|v| v == "1" || v == "true");
|
||||
if let Some(v) = g.child_val("gapWidth").and_then(|v| v.parse::<f64>().ok()) {
|
||||
c.gap_width = (v / 100.0).clamp(0.0, 5.0);
|
||||
}
|
||||
@@ -225,3 +224,27 @@ pub(crate) fn chart_from(root: &El) -> Option<Chart> {
|
||||
}
|
||||
Some(c)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::read_chart_xml;
|
||||
|
||||
const NS: &str = "http://schemas.openxmlformats.org/drawingml/2006/chart";
|
||||
|
||||
fn xml(vary: &str) -> String {
|
||||
let ser = r#"<c:ser><c:idx val="0"/><c:order val="0"/></c:ser>"#;
|
||||
let bar = format!(r#"<c:barChart><c:barDir val="col"/><c:grouping val="clustered"/>{vary}{ser}</c:barChart>"#);
|
||||
format!(r#"<c:chartSpace xmlns:c="{NS}"><c:chart><c:plotArea>{bar}</c:plotArea></c:chart></c:chartSpace>"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_vary_colors_means_vary_by_point() {
|
||||
assert!(read_chart_xml(xml("").as_bytes()).is_some_and(|c| c.vary_colors));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_vary_colors_is_respected() {
|
||||
assert!(read_chart_xml(xml(r#"<c:varyColors val="0"/>"#).as_bytes()).is_some_and(|c| !c.vary_colors));
|
||||
assert!(read_chart_xml(xml(r#"<c:varyColors val="1"/>"#).as_bytes()).is_some_and(|c| c.vary_colors));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,18 @@ fn series_fill(ctx: &Ctx, c: &Chart, i: usize, s: Option<&deckcraft_model::chart
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill of one bar: a `<c:dPt>` fill wins, and a single-series chart that varies colours by point uses one accent per point
|
||||
/// (not waterfalls, which are never read from a `<c:barChart>`).
|
||||
fn bar_fill(ctx: &Ctx, c: &Chart, si: usize, pi: usize, s: &deckcraft_model::chart::Series) -> Rgba {
|
||||
if let Some((_, Fill::Solid { color: cc })) = s.point_fills.iter().find(|(k, _)| *k == pi) {
|
||||
return ctx.color(cc, None);
|
||||
}
|
||||
if c.vary_colors && c.series.len() == 1 && s.fill.is_none() && c.kind != ChartType::Waterfall {
|
||||
return series_color(ctx, c, pi);
|
||||
}
|
||||
series_fill(ctx, c, si, Some(s))
|
||||
}
|
||||
|
||||
fn label(ctx: &mut RenderContext, rctx: &Ctx, m: Affine, text: &str, r: Rect, size: f64, align: Align, anchor: Anchor, col: Rgba, bold: bool) {
|
||||
let mut body = TextBody::from_text(text);
|
||||
body.body.inset_l = Some(0.0);
|
||||
@@ -291,7 +303,7 @@ pub fn draw(ctx: &mut RenderContext, rctx: &Ctx, c: &Chart, m: Affine, w: f64, h
|
||||
Rect::new(plot.x0 + start, to_v(b), plot.x0 + start + bar, to_v(a))
|
||||
};
|
||||
ctx.set_transform(m);
|
||||
ctx.set_paint(color(series_fill(rctx, c, si, Some(s)), 1.0));
|
||||
ctx.set_paint(color(bar_fill(rctx, c, si, i, s), 1.0));
|
||||
ctx.fill_rect(&r.abs());
|
||||
if c.data_labels {
|
||||
let t = fmt_num(s.values.get(i).copied().flatten().unwrap_or(0.0));
|
||||
|
||||
@@ -523,3 +523,20 @@ fn hyperlinks_draw_in_the_hyperlink_colour_underlined() {
|
||||
let own = px(&run_deck(RunProps { link, fill: red.clone(), ..Default::default() }));
|
||||
assert_eq!(diff(&own, &px(&run_deck(RunProps { fill: red, underline: Some("sng".into()), ..Default::default() }))), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_series_column_chart_varies_colours_by_point() {
|
||||
let render = |vary: bool| {
|
||||
let mut c = Chart::sample(ChartType::Column);
|
||||
c.series.truncate(1);
|
||||
c.vary_colors = vary;
|
||||
let sh = Shape {
|
||||
id: ShapeId(800),
|
||||
xfrm: Some(Xfrm::new(50.0, 50.0, 400.0, 300.0)),
|
||||
kind: ShapeKind::Chart(Box::new(c)),
|
||||
..Default::default()
|
||||
};
|
||||
render_slide(&deck_with(vec![sh]), 0, &RenderOpts { scale: 1.0, ..Default::default() }).pixels
|
||||
};
|
||||
assert_ne!(render(true), render(false));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user