diff --git a/.indent.pro b/.indent.pro index 87bb6c21..87771944 100644 --- a/.indent.pro +++ b/.indent.pro @@ -53,6 +53,7 @@ -T lxw_chart_axis_label_position -T lxw_chart_axis_tick_mark -T lxw_chart_axis_tick_position +-T lxw_chart_blank -T lxw_chart_fill -T lxw_chart_font -T lxw_chart_gridline diff --git a/include/xlsxwriter/chart.h b/include/xlsxwriter/chart.h index 36fe3bcb..0ec73839 100644 --- a/include/xlsxwriter/chart.h +++ b/include/xlsxwriter/chart.h @@ -514,6 +514,21 @@ typedef enum lxw_chart_axis_tick_mark { LXW_CHART_AXIS_TICK_MARK_CROSSING } lxw_chart_tick_mark; +/** + * @brief Define how blank values are displayed in a chart. + */ +typedef enum lxw_chart_blank { + + /** Show empty chart cells as gaps in the data. The default. */ + LXW_CHART_BLANKS_AS_GAP, + + /** Show empty chart cells as zeros. */ + LXW_CHART_BLANKS_AS_ZERO, + + /** Show empty chart cells as connected. Only for charts with lines. */ + LXW_CHART_BLANKS_AS_CONNECTED +} lxw_chart_blank; + enum lxw_chart_position { LXW_CHART_AXIS_RIGHT, LXW_CHART_AXIS_LEFT, @@ -884,6 +899,9 @@ typedef struct lxw_chart { uint8_t has_table_legend_keys; lxw_chart_font *table_font; + uint8_t show_blanks_as; + uint8_t show_hidden_data; + STAILQ_ENTRY (lxw_chart) ordered_list_pointers; STAILQ_ENTRY (lxw_chart) list_pointers; @@ -2545,6 +2563,42 @@ void chart_set_series_overlap(lxw_chart *chart, int8_t overlap); */ void chart_set_series_gap(lxw_chart *chart, uint16_t gap); +/** + * @brief Set the option for displaying blank data in a chart. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * @param option The display option. A #lxw_chart_blank option. + * + * The `%chart_show_blanks_as()` function controls how blank data is displayed + * in a chart: + * + * @code + * chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_CONNECTED); + * @endcode + * + * The `option` parameter can have one of the following values: + * + * - #LXW_CHART_BLANKS_AS_GAP: Show empty chart cells as gaps in the data. + * This is the default option for Excel charts. + * - #LXW_CHART_BLANKS_AS_ZERO: Show empty chart cells as zeros. + * - #LXW_CHART_BLANKS_AS_CONNECTED: Show empty chart cells as connected. + * Only for charts with lines. + */ +void chart_show_blanks_as(lxw_chart *chart, uint8_t option); + +/** + * @brief Display data on charts from hidden rows or columns. + * + * @param chart Pointer to a lxw_chart instance to be configured. + * + * Display data that is in hidden rows or columns on the chart: + * + * @code + * chart_show_hidden_data(chart); + * @endcode + */ +void chart_show_hidden_data(lxw_chart *chart); + /** * @brief Set the Pie/Doughnut chart rotation. * diff --git a/src/chart.c b/src/chart.c index f69b3aed..d9fad151 100644 --- a/src/chart.c +++ b/src/chart.c @@ -2934,6 +2934,9 @@ _chart_write_plot_vis_only(lxw_chart *self) struct xml_attribute_list attributes; struct xml_attribute *attribute; + if (self->show_hidden_data) + return; + LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("val", "1"); @@ -3043,6 +3046,31 @@ _chart_write_gap_width(lxw_chart *self, uint16_t gap) LXW_FREE_ATTRIBUTES(); } +/* + * Write the element. + */ +STATIC void +_chart_write_disp_blanks_as(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (self->show_blanks_as != LXW_CHART_BLANKS_AS_ZERO + && self->show_blanks_as != LXW_CHART_BLANKS_AS_CONNECTED) + return; + + LXW_INIT_ATTRIBUTES(); + + if (self->show_blanks_as == LXW_CHART_BLANKS_AS_ZERO) + LXW_PUSH_ATTRIBUTES_STR("val", "zero"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "span"); + + lxw_xml_empty_tag(self->file, "c:dispBlanksAs", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + /* * Write the element. */ @@ -3841,6 +3869,9 @@ _chart_write_chart(lxw_chart *self) /* Write the c:plotVisOnly element. */ _chart_write_plot_vis_only(self); + /* Write the c:dispBlanksAs element. */ + _chart_write_disp_blanks_as(self); + lxw_xml_end_tag(self->file, "c:chart"); } @@ -5129,6 +5160,24 @@ chart_set_series_overlap(lxw_chart *self, int8_t overlap) overlap); } +/* + * Set the option for displaying blank data in a chart. + */ +void +chart_show_blanks_as(lxw_chart *self, uint8_t option) +{ + self->show_blanks_as = option; +} + +/* + * Display data on charts from hidden rows or columns. + */ +void +chart_show_hidden_data(lxw_chart *self) +{ + self->show_hidden_data = LXW_TRUE; +} + /* * Set the Bar/Column gap for all data series. */ diff --git a/test/functional/src/test_chart_blank01.c b/test/functional/src/test_chart_blank01.c new file mode 100644 index 00000000..4b7c6dbc --- /dev/null +++ b/test/functional/src/test_chart_blank01.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_blank01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 45705856; + chart->axis_id_2 = 45843584; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + + chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_GAP); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_blank02.c b/test/functional/src/test_chart_blank02.c new file mode 100644 index 00000000..731292a7 --- /dev/null +++ b/test/functional/src/test_chart_blank02.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_blank02.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 44253568; + chart->axis_id_2 = 44269952; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + + chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_ZERO); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_blank03.c b/test/functional/src/test_chart_blank03.c new file mode 100644 index 00000000..538ecbfc --- /dev/null +++ b/test/functional/src/test_chart_blank03.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_blank03.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 44253568; + chart->axis_id_2 = 44269952; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + + chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_CONNECTED); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_blank04.c b/test/functional/src/test_chart_blank04.c new file mode 100644 index 00000000..1530aa43 --- /dev/null +++ b/test/functional/src/test_chart_blank04.c @@ -0,0 +1,53 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_blank04.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 42268928; + chart->axis_id_2 = 42990208; + + /* Leave some blanks in the default test data. */ + + worksheet_write_number(worksheet, 0, 0, 1, NULL); + worksheet_write_number(worksheet, 1, 0, 2, NULL); + + worksheet_write_number(worksheet, 3, 0, 4, NULL); + worksheet_write_number(worksheet, 4, 0, 5, NULL); + + worksheet_write_number(worksheet, 0, 1, 2, NULL); + worksheet_write_number(worksheet, 1, 1, 4, NULL); + + worksheet_write_number(worksheet, 3, 1, 8, NULL); + worksheet_write_number(worksheet, 4, 1, 10, NULL); + + worksheet_write_number(worksheet, 0, 2, 3, NULL); + worksheet_write_number(worksheet, 1, 2, 6, NULL); + + worksheet_write_number(worksheet, 3, 2, 12, NULL); + worksheet_write_number(worksheet, 4, 2, 15, NULL); + + + + chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + + chart_show_blanks_as(chart, LXW_CHART_BLANKS_AS_CONNECTED); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_blank06.c b/test/functional/src/test_chart_blank06.c new file mode 100644 index 00000000..44f37e47 --- /dev/null +++ b/test/functional/src/test_chart_blank06.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_blank06.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 42270080; + chart->axis_id_2 = 42990208; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + chart_add_series(chart, NULL, "=Sheet1!$A$1:$A$5"); + chart_add_series(chart, NULL, "=Sheet1!$B$1:$B$5"); + chart_add_series(chart, NULL, "=Sheet1!$C$1:$C$5"); + + chart_show_hidden_data(chart); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/test_chart_blank.py b/test/functional/test_chart_blank.py new file mode 100644 index 00000000..ce222eb5 --- /dev/null +++ b/test/functional/test_chart_blank.py @@ -0,0 +1,33 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# Copyright 2014-2017, John McNamara, jmcnamara@cpan.org +# + +import base_test_class + +class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): + """ + Test file created with libxlsxwriter against a file created by Excel. + + """ + + def test_chart_blank01(self): + self.run_exe_test('test_chart_blank01') + + def test_chart_blank02(self): + self.run_exe_test('test_chart_blank02') + + def test_chart_blank03(self): + self.run_exe_test('test_chart_blank03') + + def test_chart_blank04(self): + self.run_exe_test('test_chart_blank04') + + # Chartsheet. + # def test_chart_blank05(self): + # self.run_exe_test('test_chart_blank05') + + def test_chart_blank06(self): + self.run_exe_test('test_chart_blank06') diff --git a/test/functional/xlsx_files/chart_blank01.xlsx b/test/functional/xlsx_files/chart_blank01.xlsx new file mode 100644 index 00000000..d59b0c1a Binary files /dev/null and b/test/functional/xlsx_files/chart_blank01.xlsx differ diff --git a/test/functional/xlsx_files/chart_blank02.xlsx b/test/functional/xlsx_files/chart_blank02.xlsx new file mode 100644 index 00000000..8760b393 Binary files /dev/null and b/test/functional/xlsx_files/chart_blank02.xlsx differ diff --git a/test/functional/xlsx_files/chart_blank03.xlsx b/test/functional/xlsx_files/chart_blank03.xlsx new file mode 100644 index 00000000..1c0d41a6 Binary files /dev/null and b/test/functional/xlsx_files/chart_blank03.xlsx differ diff --git a/test/functional/xlsx_files/chart_blank04.xlsx b/test/functional/xlsx_files/chart_blank04.xlsx new file mode 100644 index 00000000..70f0b1e4 Binary files /dev/null and b/test/functional/xlsx_files/chart_blank04.xlsx differ diff --git a/test/functional/xlsx_files/chart_blank06.xlsx b/test/functional/xlsx_files/chart_blank06.xlsx new file mode 100644 index 00000000..2c6f5e4c Binary files /dev/null and b/test/functional/xlsx_files/chart_blank06.xlsx differ