diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 2773401c..a2aaa5a1 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -1331,6 +1331,18 @@ typedef struct lxw_chart_options { * See @ref working_with_object_positioning.*/ uint8_t object_position; + /** Optional description or "Alt text" for the chart. This field can be + * used to provide a text description of the chart to help + * accessibility. Defaults to the image filename as in Excel. Set to NULL + * to ignore the description field. */ + char *description; + + /** Optional parameter to help accessibility. It is used to mark the chart + * as decorative, and thus uninformative, for automated screen + * readers. As in Excel, if this parameter is in use the `description` + * field isn't written. */ + uint8_t decorative; + } lxw_chart_options; /* Internal struct to represent lxw_image_options and lxw_chart_options diff --git a/src/drawing.c b/src/drawing.c index f2fbfcac..6b9714a2 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -648,12 +648,13 @@ _drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index) +_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index, + lxw_drawing_object *drawing_object) { lxw_xml_start_tag(self->file, "xdr:nvGraphicFramePr", NULL); /* Write the xdr:cNvPr element. */ - _drawing_write_c_nv_pr(self, "Chart", index, NULL); + _drawing_write_c_nv_pr(self, "Chart", index, drawing_object); /* Write the xdr:cNvGraphicFramePr element. */ _drawing_write_c_nv_graphic_frame_pr(self); @@ -781,7 +782,8 @@ _drawing_write_a_graphic(lxw_drawing *self, uint32_t index) */ STATIC void _drawing_write_graphic_frame(lxw_drawing *self, uint32_t index, - uint32_t rel_index) + uint32_t rel_index, + lxw_drawing_object *drawing_object) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -792,7 +794,7 @@ _drawing_write_graphic_frame(lxw_drawing *self, uint32_t index, lxw_xml_start_tag(self->file, "xdr:graphicFrame", &attributes); /* Write the xdr:nvGraphicFramePr element. */ - _drawing_write_nv_graphic_frame_pr(self, index); + _drawing_write_nv_graphic_frame_pr(self, index, drawing_object); /* Write the xdr:xfrm element. */ _drawing_write_xfrm(self); @@ -829,7 +831,8 @@ _drawing_write_two_cell_anchor(lxw_drawing *self, uint32_t index, if (drawing_object->type == LXW_DRAWING_CHART) { /* Write the xdr:graphicFrame element for charts. */ - _drawing_write_graphic_frame(self, index, drawing_object->rel_index); + _drawing_write_graphic_frame(self, index, drawing_object->rel_index, + drawing_object); } else if (drawing_object->type == LXW_DRAWING_IMAGE) { /* Write the xdr:pic element. */ @@ -908,7 +911,7 @@ _drawing_write_absolute_anchor(lxw_drawing *self, uint32_t frame_index) _drawing_write_ext(self, 6162675, 6124575); } - _drawing_write_graphic_frame(self, frame_index, frame_index); + _drawing_write_graphic_frame(self, frame_index, frame_index, NULL); /* Write the xdr:clientData element. */ _drawing_write_client_data(self); diff --git a/src/worksheet.c b/src/worksheet.c index 6806c954..385d6cd4 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -2954,10 +2954,11 @@ lxw_worksheet_prepare_chart(lxw_worksheet *self, drawing_object->anchor = object_props->object_position; drawing_object->type = LXW_DRAWING_CHART; - drawing_object->description = lxw_strdup("TODO_DESC"); + drawing_object->description = lxw_strdup(object_props->description); drawing_object->tip = NULL; drawing_object->rel_index = _get_drawing_rel_index(self, NULL); drawing_object->url_rel_index = 0; + drawing_object->decorative = object_props->decorative; /* Scale to user scale. */ width = object_props->width * object_props->x_scale; @@ -9254,6 +9255,8 @@ worksheet_insert_chart_opt(lxw_worksheet *self, object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; object_props->object_position = user_options->object_position; + object_props->description = lxw_strdup(user_options->description); + object_props->decorative = user_options->decorative; } /* Copy other options or set defaults. */ diff --git a/test/functional/src/test_chart_area05.c b/test/functional/src/test_chart_area05.c new file mode 100644 index 00000000..2221ded7 --- /dev/null +++ b/test/functional/src/test_chart_area05.c @@ -0,0 +1,49 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_chart_area05.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_AREA); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 60957824; + chart->axis_id_2 = 60959360; + + uint8_t data[5][3] = { + {1, 8, 3}, + {2, 7, 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, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + lxw_chart_options chart_options = {.description = "Some alternative text"}; + worksheet_insert_chart_opt(worksheet, CELL("E9"), chart, &chart_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_area06.c b/test/functional/src/test_chart_area06.c new file mode 100644 index 00000000..c6cce3dc --- /dev/null +++ b/test/functional/src/test_chart_area06.c @@ -0,0 +1,49 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_chart_area06.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_AREA); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 60957824; + chart->axis_id_2 = 60959360; + + uint8_t data[5][3] = { + {1, 8, 3}, + {2, 7, 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, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + lxw_chart_options chart_options = {.description = "Some alternative text", .decorative = LXW_TRUE}; + worksheet_insert_chart_opt(worksheet, CELL("E9"), chart, &chart_options); + + return workbook_close(workbook); +} diff --git a/test/functional/test_chart_area.py b/test/functional/test_chart_area.py index cb9ef674..f48fa807 100644 --- a/test/functional/test_chart_area.py +++ b/test/functional/test_chart_area.py @@ -21,3 +21,9 @@ class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): def test_chart_area03(self): self.run_exe_test('test_chart_area03') + + def test_chart_area05(self): + self.run_exe_test('test_chart_area05') + + def test_chart_area06(self): + self.run_exe_test('test_chart_area06') diff --git a/test/functional/xlsx_files/chart_area05.xlsx b/test/functional/xlsx_files/chart_area05.xlsx new file mode 100644 index 00000000..6ded8676 Binary files /dev/null and b/test/functional/xlsx_files/chart_area05.xlsx differ diff --git a/test/functional/xlsx_files/chart_area06.xlsx b/test/functional/xlsx_files/chart_area06.xlsx new file mode 100644 index 00000000..c92a7818 Binary files /dev/null and b/test/functional/xlsx_files/chart_area06.xlsx differ