1use crate::oxml::chart::{
27 Chart as OxmlChart, ChartData, ChartSeries, ChartType, DataLabels, SeriesFormat,
28};
29use crate::oxml::shape::{Graphic as OxmlGraphic, GraphicFrame as OxmlFrame};
30use crate::shape::base::Shape;
31use crate::units::Emu;
32
33#[derive(Clone, Debug, Default)]
45pub struct ChartShape {
46 pub(crate) frame: OxmlFrame,
48}
49
50impl ChartShape {
51 pub fn new(chart_type: ChartType, data: ChartData) -> Self {
57 let chart = OxmlChart::new(chart_type, data);
58 let frame = OxmlFrame {
59 graphic: OxmlGraphic::Chart(chart),
60 ..Default::default()
61 };
62 ChartShape { frame }
63 }
64
65 pub fn from_frame(frame: OxmlFrame) -> Self {
67 ChartShape { frame }
68 }
69
70 pub fn chart(&self) -> Option<&OxmlChart> {
74 match &self.frame.graphic {
75 OxmlGraphic::Chart(c) => Some(c),
76 _ => None,
77 }
78 }
79
80 pub fn chart_mut(&mut self) -> Option<&mut OxmlChart> {
82 match &mut self.frame.graphic {
83 OxmlGraphic::Chart(c) => Some(c),
84 _ => None,
85 }
86 }
87
88 pub fn chart_type(&self) -> ChartType {
92 self.chart()
93 .map(|c| c.chart_type)
94 .unwrap_or(ChartType::Column)
95 }
96
97 pub fn set_chart_type(&mut self, chart_type: ChartType) {
99 if let Some(c) = self.chart_mut() {
100 c.chart_type = chart_type;
101 }
102 }
103
104 pub fn data(&self) -> Option<&ChartData> {
106 self.chart().map(|c| &c.data)
107 }
108
109 pub fn data_mut(&mut self) -> Option<&mut ChartData> {
111 self.chart_mut().map(|c| &mut c.data)
112 }
113
114 pub fn rid(&self) -> &str {
119 match &self.frame.graphic {
120 OxmlGraphic::Chart(c) => &c.rid,
121 _ => "",
122 }
123 }
124
125 pub fn set_rid(&mut self, rid: impl Into<String>) {
128 if let Some(c) = self.chart_mut() {
129 c.rid = rid.into();
130 }
131 }
132
133 pub fn set_placeholder(&mut self, ph_idx: u32, ph_type: Option<&str>) {
143 self.frame.is_placeholder = true;
144 self.frame.ph_idx = Some(ph_idx);
145 self.frame.ph_type = ph_type.map(|s| s.to_string());
146 }
147
148 pub fn clear_placeholder(&mut self) {
150 self.frame.is_placeholder = false;
151 self.frame.ph_idx = None;
152 self.frame.ph_type = None;
153 }
154
155 pub fn is_placeholder(&self) -> bool {
157 self.frame.is_placeholder
158 }
159
160 pub fn ph_idx(&self) -> Option<u32> {
162 self.frame.ph_idx
163 }
164
165 pub fn ph_type(&self) -> Option<&str> {
167 self.frame.ph_type.as_deref()
168 }
169
170 pub fn data_labels(&self) -> Option<&DataLabels> {
177 self.chart().and_then(|c| c.data.data_labels.as_ref())
178 }
179
180 pub fn set_data_labels(&mut self, labels: Option<DataLabels>) {
187 if let Some(c) = self.chart_mut() {
188 c.data.data_labels = labels;
189 }
190 }
191
192 pub fn series_data_labels(&self, series_idx: usize) -> Option<&DataLabels> {
199 self.chart()
200 .and_then(|c| c.data.series.get(series_idx))
201 .and_then(|s| s.data_labels.as_ref())
202 }
203
204 pub fn set_series_data_labels(&mut self, series_idx: usize, labels: Option<DataLabels>) {
210 if let Some(c) = self.chart_mut() {
211 if let Some(s) = c.data.series.get_mut(series_idx) {
212 s.data_labels = labels;
213 }
214 }
215 }
216
217 pub fn series_format(&self, series_idx: usize) -> Option<&SeriesFormat> {
226 self.chart()
227 .and_then(|c| c.data.series.get(series_idx))
228 .and_then(|s| s.format.as_ref())
229 }
230
231 pub fn set_series_format(&mut self, series_idx: usize, format: Option<SeriesFormat>) {
259 if let Some(c) = self.chart_mut() {
260 if let Some(s) = c.data.series.get_mut(series_idx) {
261 s.format = format;
262 }
263 }
264 }
265
266 pub fn is_series_secondary(&self, series_idx: usize) -> bool {
277 self.chart()
278 .and_then(|c| c.data.series.get(series_idx))
279 .map(|s| s.secondary_axis)
280 .unwrap_or(false)
281 }
282
283 pub fn set_series_secondary(&mut self, series_idx: usize, secondary: bool) {
296 if let Some(c) = self.chart_mut() {
297 if let Some(s) = c.data.series.get_mut(series_idx) {
298 s.secondary_axis = secondary;
299 }
300 }
301 }
302
303 pub fn secondary_chart_type(&self) -> Option<ChartType> {
312 self.chart().and_then(|c| c.data.secondary_chart_type)
313 }
314
315 pub fn set_secondary_chart_type(&mut self, chart_type: Option<ChartType>) {
346 if let Some(c) = self.chart_mut() {
347 c.data.secondary_chart_type = chart_type;
348 }
349 }
350
351 pub fn push_series(&mut self, series: ChartSeries) -> Option<usize> {
355 if let Some(c) = self.chart_mut() {
356 c.data.series.push(series);
357 Some(c.data.series.len() - 1)
358 } else {
359 None
360 }
361 }
362}
363
364impl Shape for ChartShape {
365 fn id(&self) -> u32 {
366 self.frame.id
367 }
368 fn set_id(&mut self, id: u32) {
369 self.frame.id = id;
370 }
371 fn name(&self) -> &str {
372 &self.frame.name
373 }
374 fn set_name(&mut self, name: String) {
375 self.frame.name = name;
376 }
377 fn shape_type(&self) -> &'static str {
378 "chart"
379 }
380
381 fn left(&self) -> Emu {
382 self.frame.properties.xfrm.off_x.unwrap_or_default()
383 }
384 fn set_left(&mut self, emu: Emu) {
385 self.frame.properties.xfrm.off_x = Some(emu);
386 }
387 fn top(&self) -> Emu {
388 self.frame.properties.xfrm.off_y.unwrap_or_default()
389 }
390 fn set_top(&mut self, emu: Emu) {
391 self.frame.properties.xfrm.off_y = Some(emu);
392 }
393 fn width(&self) -> Emu {
394 self.frame.properties.xfrm.ext_cx.unwrap_or_default()
395 }
396 fn set_width(&mut self, emu: Emu) {
397 self.frame.properties.xfrm.ext_cx = Some(emu);
398 }
399 fn height(&self) -> Emu {
400 self.frame.properties.xfrm.ext_cy.unwrap_or_default()
401 }
402 fn set_height(&mut self, emu: Emu) {
403 self.frame.properties.xfrm.ext_cy = Some(emu);
404 }
405
406 fn rotation(&self) -> f64 {
408 0.0
409 }
410 fn set_rotation(&mut self, _deg: f64) {}
411}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416 use crate::oxml::chart::{ChartCategory, ChartSeries};
417
418 #[test]
420 fn new_chart_shape_basics() {
421 let data = ChartData {
422 categories: vec![ChartCategory::new("A"), ChartCategory::new("B")],
423 series: vec![ChartSeries::new("S", vec![1.0, 2.0])],
424 title: Some("T".to_string()),
425 data_labels: None,
426 ..Default::default()
427 };
428 let mut s = ChartShape::new(ChartType::Column, data);
429 assert_eq!(s.chart_type(), ChartType::Column);
430 assert_eq!(s.chart().unwrap().data.categories.len(), 2);
431 assert_eq!(s.chart().unwrap().data.series.len(), 1);
432 assert_eq!(s.rid(), "");
433 s.set_chart_type(ChartType::Line);
435 assert_eq!(s.chart_type(), ChartType::Line);
436 assert_eq!(s.chart().unwrap().data.series[0].values, vec![1.0, 2.0]);
437 }
438
439 #[test]
441 fn set_rid_propagates() {
442 let mut s = ChartShape::new(ChartType::Pie, ChartData::default());
443 s.set_rid("rIdChart1");
444 assert_eq!(s.rid(), "rIdChart1");
445 }
446
447 #[test]
449 fn shape_trait_geometry() {
450 let mut s = ChartShape::new(ChartType::Bar, ChartData::default());
451 s.set_left(Emu(100));
452 s.set_top(Emu(200));
453 s.set_width(Emu(300));
454 s.set_height(Emu(400));
455 assert_eq!(s.left(), Emu(100));
456 assert_eq!(s.top(), Emu(200));
457 assert_eq!(s.width(), Emu(300));
458 assert_eq!(s.height(), Emu(400));
459 s.set_rotation(45.0);
461 assert_eq!(s.rotation(), 0.0);
462 }
463
464 #[test]
466 fn frame_xml_contains_chart_reference() {
467 let mut s = ChartShape::new(ChartType::Column, ChartData::default());
468 s.set_rid("rIdChart7");
469 let mut w = crate::oxml::writer::XmlWriter::new();
470 s.frame.write_xml(&mut w);
471 let xml = w.into_string();
472 assert!(xml.contains("<p:graphicFrame>"), "xml: {}", xml);
473 assert!(xml.contains("<c:chart"), "xml: {}", xml);
474 assert!(xml.contains("r:id=\"rIdChart7\""), "xml: {}", xml);
475 }
476
477 #[test]
481 fn data_labels_setter_and_getter() {
482 let mut s = ChartShape::new(ChartType::Column, ChartData::default());
483 assert!(s.data_labels().is_none());
484 s.set_data_labels(Some(DataLabels::show_values()));
486 assert_eq!(s.data_labels().unwrap().show_val, Some(true));
487 s.set_data_labels(None);
489 assert!(s.data_labels().is_none());
490 }
491
492 #[test]
494 fn series_data_labels_by_index() {
495 let data = ChartData {
496 categories: vec![ChartCategory::new("A")],
497 series: vec![
498 ChartSeries::new("S1", vec![1.0]),
499 ChartSeries::new("S2", vec![2.0]),
500 ],
501 title: None,
502 data_labels: None,
503 ..Default::default()
504 };
505 let mut s = ChartShape::new(ChartType::Column, data);
506 assert!(s.series_data_labels(0).is_none());
508 s.set_series_data_labels(0, Some(DataLabels::show_values()));
510 assert_eq!(s.series_data_labels(0).unwrap().show_val, Some(true));
511 assert!(s.series_data_labels(1).is_none());
513 assert!(s.series_data_labels(99).is_none());
515 s.set_series_data_labels(99, Some(DataLabels::show_values()));
517 }
518
519 #[test]
523 fn series_format_setter_and_getter() {
524 use crate::oxml::color::Color;
525 use crate::units::RGBColor;
526 let data = ChartData {
527 categories: vec![ChartCategory::new("A")],
528 series: vec![ChartSeries::new("S1", vec![1.0])],
529 ..Default::default()
530 };
531 let mut s = ChartShape::new(ChartType::Column, data);
532 assert!(s.series_format(0).is_none());
534 s.set_series_format(
536 0,
537 Some(SeriesFormat::solid_fill(Color::RGB(RGBColor(
538 0xFF, 0x00, 0x00,
539 )))),
540 );
541 assert!(s.series_format(0).is_some());
542 s.set_series_format(0, None);
544 assert!(s.series_format(0).is_none());
545 s.set_series_format(99, Some(SeriesFormat::default()));
547 assert!(s.series_format(99).is_none());
548 }
549
550 #[test]
554 fn series_secondary_axis_setter_and_getter() {
555 let data = ChartData {
556 categories: vec![ChartCategory::new("A")],
557 series: vec![
558 ChartSeries::new("S1", vec![1.0]),
559 ChartSeries::new("S2", vec![2.0]),
560 ],
561 title: None,
562 data_labels: None,
563 ..Default::default()
564 };
565 let mut s = ChartShape::new(ChartType::Column, data);
566 assert!(!s.is_series_secondary(0));
568 assert!(!s.is_series_secondary(1));
569 s.set_series_secondary(1, true);
571 assert!(!s.is_series_secondary(0));
572 assert!(s.is_series_secondary(1));
573 assert!(!s.is_series_secondary(99));
575 s.set_series_secondary(99, true);
577 }
578
579 #[test]
581 fn push_series_appends_to_end() {
582 let data = ChartData {
583 categories: vec![ChartCategory::new("A")],
584 series: vec![ChartSeries::new("S1", vec![1.0])],
585 title: None,
586 data_labels: None,
587 ..Default::default()
588 };
589 let mut s = ChartShape::new(ChartType::Column, data);
590 assert_eq!(s.data().unwrap().series.len(), 1);
591 let idx = s.push_series(ChartSeries::new("S2", vec![2.0]));
592 assert_eq!(idx, Some(1));
593 assert_eq!(s.data().unwrap().series.len(), 2);
594 assert_eq!(s.data().unwrap().series[1].name, "S2");
595 }
596}