Charts: a legend entry's paragraph text wins over its list style

Review fixes: an entry's size, weight and colour are read from its
paragraph (a:p) first and from a:lstStyle only where the paragraph
sets none, as DrawingML inherits them; the writer keeps sizes to the
reader's 1-400 pt, so a size reads back as written; and the legend
draws an entry at its own size up to 400 pt rather than up to 8 times
the legend's.
This commit is contained in:
Hamza committed 2026-11-12 14:48:11 +05:00
1 parent b5b77eb27e
commit 0e0d7f8cb2
3 files changed
+26 -5

No files matched your search

+6 -3
View File
@@ -197,12 +197,15 @@ fn chart_within(root: &El, mut budget: usize) -> Option<Chart> {
.flat_map(|l| l.children_named("legendEntry"))
.take(MAX_LEGEND_ENTRIES)
.filter_map(|e| {
// The paragraph's own run properties first, then the list style's defaults
// (`a:lstStyle` comes before `a:p`, and is the less specific of the two).
let tx = e.child("txPr");
let para = tx.and_then(|t| t.child("p"));
let entry = deckcraft_model::chart::LegendEntry {
idx: e.child("idx").and_then(|i| i.u32("val"))?,
size: text_size(tx),
bold: text_bold(tx),
color: text_color(tx),
size: text_size(para).or_else(|| text_size(tx)),
bold: text_bold(para).or_else(|| text_bold(tx)),
color: text_color(para).or_else(|| text_color(tx)),
};
(entry.size.is_some() || entry.bold.is_some() || entry.color.is_some()).then_some(entry)
})
+19 -1
View File
@@ -109,7 +109,8 @@ fn legend_entry(w: &mut W, e: &deckcraft_model::chart::LegendEntry) {
w.open0("a:pPr");
let mut a = A::new();
if let Some(sz) = e.size.filter(|v| v.is_finite() && *v > 0.0) {
a = a.a("sz", ((sz * 100.0).round().clamp(100.0, 400_000.0) as u32).to_string());
// 1 to 400 pt, as the reader takes it, so a size reads back as written.
a = a.a("sz", ((sz * 100.0).round().clamp(100.0, 40_000.0) as u32).to_string());
}
if let Some(b) = e.bold {
a = a.a("b", if b { "1" } else { "0" });
@@ -1243,6 +1244,23 @@ mod tests {
// None: nothing written.
e.legend_entries.clear();
assert!(!String::from_utf8(super::chart_xml(&e).0).unwrap().contains("legendEntry"));
// The paragraph's own properties win over the list style's, which come first in the XML.
let listed = xml.replacen(
r#"<c:legendEntry><c:idx val="0"/><c:txPr><a:bodyPr/><a:lstStyle/>"#,
r#"<c:legendEntry><c:idx val="0"/><c:txPr><a:bodyPr/><a:lstStyle><a:lvl1pPr><a:defRPr sz="1200" b="0"><a:solidFill><a:srgbClr val="0000FF"/></a:solidFill></a:defRPr></a:lvl1pPr></a:lstStyle>"#,
1,
);
assert_ne!(listed, xml, "the list style was put in");
let c = crate::read::chart::read_chart_xml(listed.as_bytes()).unwrap();
assert_eq!(c.legend_entries[0], want(0, (0xC0, 0, 0)), "the paragraph's 20 pt, bold, C00000");
// A size reads back as it was written, within the reader's 1-400 pt.
for (size, back) in [(20.5, 20.5), (400.0, 400.0), (500.0, 400.0)] {
let mut e = c.clone();
e.raw = None;
e.legend_entries = vec![deckcraft_model::chart::LegendEntry { idx: 0, size: Some(size), bold: None, color: None }];
let back_size = crate::read::chart::read_chart_xml(&super::chart_xml(&e).0).unwrap().legend_entries[0].size;
assert_eq!(back_size, Some(back), "{size} pt");
}
}
#[test]
+1 -1
View File
@@ -542,7 +542,7 @@ fn draw_legend(ctx: &mut RenderContext, draw: &LegendDraw<'_, '_>, mut area: Rec
let styles: Vec<(f64, bool, Rgba)> = (0..legend_items.len())
.map(|i| {
let e = own(i);
let size = e.and_then(|e| e.size).filter(|v| v.is_finite() && *v > 0.0).map_or(base, |v| v.min(base * 8.0));
let size = e.and_then(|e| e.size).filter(|v| v.is_finite() && *v > 0.0).map_or(base, |v| v.min(400.0));
let col = e.and_then(|e| e.color.as_ref()).map_or(text_col, |cr| rctx.color(cr, None));
(size, e.and_then(|e| e.bold).unwrap_or(false), col)
})