From 7d06d560a7b8fe4fbca0cc110512685af99ce4a6 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Fri, 30 Dec 2016 14:12:14 +0000 Subject: [PATCH] Initial working chart legend. --- .indent.pro | 2 + include/xlsxwriter/chart.h | 37 ++++++ src/chart.c | 77 ++++++++++- test/unit/chart/test_chart_write_legend.c | 150 ++++++++++++++++++++++ 4 files changed, 259 insertions(+), 7 deletions(-) create mode 100644 test/unit/chart/test_chart_write_legend.c diff --git a/.indent.pro b/.indent.pro index 8985e1e5..c5c64590 100644 --- a/.indent.pro +++ b/.indent.pro @@ -51,6 +51,8 @@ -T lxw_chart_axis -T lxw_chart_fill -T lxw_chart_font +-T lxw_chart_legend +-T lxw_chart_legend_postions -T lxw_chart_series -T lxw_chart_title -T lxw_chart_types diff --git a/include/xlsxwriter/chart.h b/include/xlsxwriter/chart.h index 3c25c604..10ccac40 100644 --- a/include/xlsxwriter/chart.h +++ b/include/xlsxwriter/chart.h @@ -149,6 +149,31 @@ typedef enum lxw_chart_types { LXW_CHART_RADAR_FILLED } lxw_chart_types; +/** Chart legend positions. */ +typedef enum lxw_chart_legend_postions { + + /** No chart legend. */ + LXW_CHART_LEGEND_NONE = 0, + + /** Chart legend positioned at right side. */ + LXW_CHART_LEGEND_RIGHT, + + /** Chart legend positioned at left side. */ + LXW_CHART_LEGEND_LEFT, + + /** Chart legend positioned at top. */ + LXW_CHART_LEGEND_TOP, + + /** Chart legend positioned at bottom. */ + LXW_CHART_LEGEND_BOTTOM, + + /** Chart legend overlaid at right side. */ + LXW_CHART_LEGEND_OVERLAY_RIGHT, + + /** Chart legend overlaid at left side. */ + LXW_CHART_LEGEND_OVERLAY_LEFT +} lxw_chart_legend_postions; + enum lxw_chart_subtypes { LXW_CHART_SUBTYPE_NONE = 0, @@ -243,6 +268,13 @@ typedef struct lxw_chart_font { } lxw_chart_font; +typedef struct lxw_chart_legend { + + lxw_chart_font *font; + uint8_t position; + +} lxw_chart_legend; + typedef struct lxw_chart_title { char *name; @@ -357,6 +389,8 @@ typedef struct lxw_chart { uint8_t cat_axis_position; uint8_t val_axis_position; + lxw_chart_legend legend; + struct lxw_chart_series_list *series_list; STAILQ_ENTRY (lxw_chart) ordered_list_pointers; @@ -750,6 +784,8 @@ void chart_title_set_name_font(lxw_chart *chart, lxw_chart_font *font); */ void chart_title_off(lxw_chart *chart); +void chart_legend_set_position(lxw_chart *chart, uint8_t position); + /** * @brief Set the chart style type. * @@ -790,6 +826,7 @@ int lxw_chart_add_data_cache(lxw_series_range *range, uint8_t *data, #ifdef TESTING STATIC void _chart_xml_declaration(lxw_chart *chart); +STATIC void _chart_write_legend(lxw_chart *chart); #endif /* TESTING */ diff --git a/src/chart.c b/src/chart.c index 15184158..cba02f1b 100644 --- a/src/chart.c +++ b/src/chart.c @@ -102,6 +102,7 @@ lxw_chart_free(lxw_chart *chart) if (!chart) return; + /* Chart series. */ if (chart->series_list) { while (!STAILQ_EMPTY(chart->series_list)) { series = STAILQ_FIRST(chart->series_list); @@ -113,6 +114,7 @@ lxw_chart_free(lxw_chart *chart) free(chart->series_list); } + /* X Axis. */ if (chart->x_axis) { _chart_free_font(chart->x_axis->title.font); _chart_free_font(chart->x_axis->num_font); @@ -121,6 +123,7 @@ lxw_chart_free(lxw_chart *chart) free(chart->x_axis); } + /* Y Axis. */ if (chart->y_axis) { _chart_free_font(chart->y_axis->title.font); _chart_free_font(chart->y_axis->num_font); @@ -129,10 +132,14 @@ lxw_chart_free(lxw_chart *chart) free(chart->y_axis); } + /* Chart title. */ _chart_free_font(chart->title.font); _chart_free_range(chart->title.range); free(chart->title.name); + /* Chart legend. */ + _chart_free_font(chart->legend.font); + free(chart); } @@ -193,6 +200,8 @@ lxw_chart_new(uint8_t type) chart->has_horiz_cat_axis = LXW_FALSE; chart->has_horiz_val_axis = LXW_TRUE; + chart->legend.position = LXW_CHART_LEGEND_RIGHT; + return chart; mem_error: @@ -1871,17 +1880,34 @@ _chart_write_cross_between(lxw_chart *self) } /* - * Write the element. + * Write the element. */ STATIC void -_chart_write_legend_pos(lxw_chart *self) +_chart_write_overlay(lxw_chart *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char val[] = "r"; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("val", val); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:overlay", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_legend_pos(lxw_chart *self, char *position) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("val", position); lxw_xml_empty_tag(self->file, "c:legendPos", &attributes); @@ -1894,10 +1920,35 @@ _chart_write_legend_pos(lxw_chart *self) STATIC void _chart_write_legend(lxw_chart *self) { + uint8_t has_overlay = LXW_FALSE; + + if (self->legend.position == LXW_CHART_LEGEND_NONE) + return; + lxw_xml_start_tag(self->file, "c:legend", NULL); /* Write the c:legendPos element. */ - _chart_write_legend_pos(self); + switch (self->legend.position) { + case LXW_CHART_LEGEND_LEFT: + _chart_write_legend_pos(self, "l"); + break; + case LXW_CHART_LEGEND_TOP: + _chart_write_legend_pos(self, "t"); + break; + case LXW_CHART_LEGEND_BOTTOM: + _chart_write_legend_pos(self, "b"); + break; + case LXW_CHART_LEGEND_OVERLAY_RIGHT: + _chart_write_legend_pos(self, "r"); + has_overlay = LXW_TRUE; + break; + case LXW_CHART_LEGEND_OVERLAY_LEFT: + _chart_write_legend_pos(self, "l"); + has_overlay = LXW_TRUE; + break; + default: + _chart_write_legend_pos(self, "r"); + } /* Write the c:layout element. */ _chart_write_layout(self); @@ -1907,6 +1958,10 @@ _chart_write_legend(lxw_chart *self) _chart_write_tx_pr_pie(self, LXW_FALSE, NULL); } + /* Write the c:overlay element. */ + if (has_overlay) + _chart_write_overlay(self); + lxw_xml_end_tag(self->file, "c:legend"); } @@ -1918,10 +1973,9 @@ _chart_write_plot_vis_only(lxw_chart *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - char val[] = "1"; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("val", val); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); lxw_xml_empty_tag(self->file, "c:plotVisOnly", &attributes); @@ -3036,6 +3090,15 @@ chart_title_off(lxw_chart *self) self->title.off = LXW_TRUE; } +/* + * Set the chart legend position. + */ +void +chart_legend_set_position(lxw_chart *self, uint8_t position) +{ + self->legend.position = position; +} + /* * Set the Pie/Doughnut chart rotation: the angle of the first slice. */ diff --git a/test/unit/chart/test_chart_write_legend.c b/test/unit/chart/test_chart_write_legend.c new file mode 100644 index 00000000..0a2af0ce --- /dev/null +++ b/test/unit/chart/test_chart_write_legend.c @@ -0,0 +1,150 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2016, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "xlsxwriter/chart.h" + +// Test the _write_legend() function. +CTEST(chart, write_legend01) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + +CTEST(chart, write_legend02) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_RIGHT); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + +CTEST(chart, write_legend03) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_TOP); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + +CTEST(chart, write_legend04) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_LEFT); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + +CTEST(chart, write_legend05) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_BOTTOM); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + + +CTEST(chart, write_legend06) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_NONE); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + + +CTEST(chart, write_legend07) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_OVERLAY_RIGHT); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +} + + +CTEST(chart, write_legend08) { + + char* got; + char exp[] = ""; + FILE* testfile = lxw_tmpfile(NULL); + + lxw_chart *chart = lxw_chart_new(LXW_CHART_AREA); + chart->file = testfile; + + chart_legend_set_position(chart, LXW_CHART_LEGEND_OVERLAY_LEFT); + _chart_write_legend(chart); + + RUN_XLSX_STREQ(exp, got); + + lxw_chart_free(chart); +}