diff --git a/.indent.pro b/.indent.pro index 14fed76b..26de6bb8 100644 --- a/.indent.pro +++ b/.indent.pro @@ -79,6 +79,7 @@ -T lxw_chart_trendline_type -T lxw_chart_type -T lxw_chartsheet +-T lxw_chartsheet_name -T lxw_col_options -T lxw_col_t -T lxw_color_t diff --git a/include/xlsxwriter/chart.h b/include/xlsxwriter/chart.h index 37ff3822..a9abb019 100644 --- a/include/xlsxwriter/chart.h +++ b/include/xlsxwriter/chart.h @@ -1069,6 +1069,7 @@ typedef struct lxw_chart { uint8_t in_use; uint8_t chart_group; uint8_t cat_has_num_fmt; + uint8_t is_chartsheet; uint8_t has_horiz_cat_axis; uint8_t has_horiz_val_axis; diff --git a/include/xlsxwriter/chartsheet.h b/include/xlsxwriter/chartsheet.h index a0a4a21e..61ec65a1 100644 --- a/include/xlsxwriter/chartsheet.h +++ b/include/xlsxwriter/chartsheet.h @@ -60,10 +60,20 @@ typedef struct lxw_chartsheet { FILE *file; lxw_worksheet *worksheet; - lxw_drawing *drawing; + char *name; + char *quoted_name; + char *tmpdir; + uint32_t index; + uint8_t active; + uint8_t selected; + uint8_t hidden; + uint16_t *active_sheet; + uint16_t *first_sheet; uint16_t rel_count; + STAILQ_ENTRY (lxw_chartsheet) list_pointers; + } lxw_chartsheet; @@ -73,6 +83,17 @@ extern "C" { #endif /* *INDENT-ON* */ +lxw_error chartsheet_set_chart_opt(lxw_chartsheet *chartsheet, + lxw_chart *chart, + lxw_image_options *user_options); + +lxw_error chartsheet_set_chart(lxw_chartsheet *chartsheet, lxw_chart *chart); + +void chartsheet_select(lxw_chartsheet *chartsheet); +void chartsheet_activate(lxw_chartsheet *chartsheet); +void chartsheet_set_first_sheet(lxw_chartsheet *chartsheet); +void chartsheet_hide(lxw_chartsheet *chartsheet); + lxw_chartsheet *lxw_chartsheet_new(); void lxw_chartsheet_free(lxw_chartsheet *chartsheet); void lxw_chartsheet_assemble_xml_file(lxw_chartsheet *self); diff --git a/include/xlsxwriter/content_types.h b/include/xlsxwriter/content_types.h index 7caa85e3..b1fe817b 100644 --- a/include/xlsxwriter/content_types.h +++ b/include/xlsxwriter/content_types.h @@ -46,6 +46,8 @@ void lxw_ct_add_override(lxw_content_types *content_types, const char *key, const char *value); void lxw_ct_add_worksheet_name(lxw_content_types *content_types, const char *name); +void lxw_ct_add_chartsheet_name(lxw_content_types *content_types, + const char *name); void lxw_ct_add_chart_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_drawing_name(lxw_content_types *content_types, diff --git a/include/xlsxwriter/drawing.h b/include/xlsxwriter/drawing.h index 0455fe04..2cd660ef 100644 --- a/include/xlsxwriter/drawing.h +++ b/include/xlsxwriter/drawing.h @@ -77,6 +77,7 @@ typedef struct lxw_drawing { FILE *file; uint8_t embedded; + uint8_t orientation; struct lxw_drawing_objects *drawing_objects; diff --git a/include/xlsxwriter/workbook.h b/include/xlsxwriter/workbook.h index 96737dad..5223b136 100644 --- a/include/xlsxwriter/workbook.h +++ b/include/xlsxwriter/workbook.h @@ -56,10 +56,12 @@ /* Define the tree.h RB structs for the red-black head types. */ RB_HEAD(lxw_worksheet_names, lxw_worksheet_name); +RB_HEAD(lxw_chartsheet_names, lxw_chartsheet_name); /* Define the queue.h structs for the workbook lists. */ STAILQ_HEAD(lxw_sheets, lxw_sheet); STAILQ_HEAD(lxw_worksheets, lxw_worksheet); +STAILQ_HEAD(lxw_chartsheets, lxw_chartsheet); STAILQ_HEAD(lxw_charts, lxw_chart); TAILQ_HEAD(lxw_defined_names, lxw_defined_name); @@ -83,18 +85,37 @@ typedef struct lxw_worksheet_name { RB_ENTRY (lxw_worksheet_name) tree_pointers; } lxw_worksheet_name; +/* Struct to represent a chartsheet name/pointer pair. */ +typedef struct lxw_chartsheet_name { + const char *name; + lxw_chartsheet *chartsheet; + + RB_ENTRY (lxw_chartsheet_name) tree_pointers; +} lxw_chartsheet_name; + /* Wrapper around RB_GENERATE_STATIC from tree.h to avoid unused function * warnings and to avoid portability issues with the _unused attribute. */ -#define LXW_RB_GENERATE_NAMES(name, type, field, cmp) \ - RB_GENERATE_INSERT_COLOR(name, type, field, static) \ - RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ - RB_GENERATE_INSERT(name, type, field, cmp, static) \ - RB_GENERATE_REMOVE(name, type, field, static) \ - RB_GENERATE_FIND(name, type, field, cmp, static) \ - RB_GENERATE_NEXT(name, type, field, static) \ - RB_GENERATE_MINMAX(name, type, field, static) \ - /* Add unused struct to allow adding a semicolon */ \ - struct lxw_rb_generate_names{int unused;} +#define LXW_RB_GENERATE_WORKSHEET_NAMES(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + RB_GENERATE_REMOVE(name, type, field, static) \ + RB_GENERATE_FIND(name, type, field, cmp, static) \ + RB_GENERATE_NEXT(name, type, field, static) \ + RB_GENERATE_MINMAX(name, type, field, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_worksheet_names{int unused;} + +#define LXW_RB_GENERATE_CHARTSHEET_NAMES(name, type, field, cmp) \ + RB_GENERATE_INSERT_COLOR(name, type, field, static) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, static) \ + RB_GENERATE_INSERT(name, type, field, cmp, static) \ + RB_GENERATE_REMOVE(name, type, field, static) \ + RB_GENERATE_FIND(name, type, field, cmp, static) \ + RB_GENERATE_NEXT(name, type, field, static) \ + RB_GENERATE_MINMAX(name, type, field, static) \ + /* Add unused struct to allow adding a semicolon */ \ + struct lxw_rb_generate_charsheet_names{int unused;} /** * @brief Macro to loop over all the worksheets in a workbook. @@ -217,7 +238,9 @@ typedef struct lxw_workbook { FILE *file; struct lxw_sheets *sheets; struct lxw_worksheets *worksheets; + struct lxw_chartsheets *chartsheets; struct lxw_worksheet_names *worksheet_names; + struct lxw_chartsheet_names *chartsheet_names; struct lxw_charts *charts; struct lxw_charts *ordered_charts; struct lxw_formats *formats; @@ -231,6 +254,7 @@ typedef struct lxw_workbook { uint16_t num_sheets; uint16_t num_worksheets; + uint16_t num_chartsheets; uint16_t first_sheet; uint16_t active_sheet; uint16_t num_xf_formats; @@ -364,6 +388,9 @@ lxw_workbook *new_workbook_opt(const char *filename, lxw_worksheet *workbook_add_worksheet(lxw_workbook *workbook, const char *sheetname); +lxw_chartsheet *workbook_add_chartsheet(lxw_workbook *chartsheet, + const char *sheetname); + /** * @brief Create a new @ref format.h "Format" object to formats cells in * worksheets. @@ -703,31 +730,49 @@ lxw_worksheet *workbook_get_worksheet_by_name(lxw_workbook *workbook, const char *name); /** - * @brief Validate a worksheet name. + * @brief Get a chartsheet object from its name. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name chartsheet name. + * + * @return A lxw_chartsheet object. + * + * This function returns a lxw_chartsheet object reference based on its name: + * + * @code + * chartsheet = workbook_get_chartsheet_by_name(workbook, "Chart1"); + * @endcode + * + */ +lxw_chartsheet *workbook_get_chartsheet_by_name(lxw_workbook *workbook, + const char *name); + +/** + * @brief Validate a worksheet or chartsheet name. * * @param workbook Pointer to a lxw_workbook instance. - * @param sheetname Worksheet name to validate. + * @param sheetname Sheet name to validate. * * @return A #lxw_error. * - * This function is used to validate a worksheet name according to the rules - * used by Excel: + * This function is used to validate a worksheet or chartsheet name according + * to the rules used by Excel: * * - The name is less than or equal to 31 UTF-8 characters. * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` * - The name isn't already in use. * * @code - * lxw_error err = workbook_validate_worksheet_name(workbook, "Foglio"); + * lxw_error err = workbook_validate_sheet_name(workbook, "Foglio"); * @endcode * - * This function is called by `workbook_add_worksheet()` but it can be - * explicitly called by the user beforehand to ensure that the worksheet - * name is valid. + * This function is called by `workbook_add_worksheet()` and + * `workbook_add_chartsheet()` but it can be explicitly called by the user + * beforehand to ensure that the sheet name is valid. * */ -lxw_error workbook_validate_worksheet_name(lxw_workbook *workbook, - const char *sheetname); +lxw_error workbook_validate_sheet_name(lxw_workbook *workbook, + const char *sheetname); void lxw_workbook_free(lxw_workbook *workbook); void lxw_workbook_assemble_xml_file(lxw_workbook *workbook); diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 9cdb33f0..d4a194cd 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -3146,7 +3146,8 @@ void lxw_worksheet_prepare_image(lxw_worksheet *worksheet, void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet, uint16_t chart_ref_id, uint16_t drawing_id, - lxw_image_options *image_data); + lxw_image_options *image_data, + uint8_t is_chartsheet); lxw_row *lxw_worksheet_find_row(lxw_worksheet *worksheet, lxw_row_t row_num); lxw_cell *lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num); @@ -3156,6 +3157,7 @@ lxw_cell *lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num); */ void lxw_worksheet_write_sheet_views(lxw_worksheet *worksheet); void lxw_worksheet_write_page_margins(lxw_worksheet *worksheet); +void lxw_worksheet_write_drawings(lxw_worksheet *worksheet); /* Declarations required for unit testing. */ #ifdef TESTING diff --git a/src/chart.c b/src/chart.c index ca2a2412..4f8becfe 100644 --- a/src/chart.c +++ b/src/chart.c @@ -4949,7 +4949,8 @@ lxw_chart_assemble_xml_file(lxw_chart *self) self->chartarea_pattern); /* Write the c:printSettings element. */ - _chart_write_print_settings(self); + if (!self->is_chartsheet) + _chart_write_print_settings(self); lxw_xml_end_tag(self->file, "c:chartSpace"); } diff --git a/src/chartsheet.c b/src/chartsheet.c index e21d3431..77450a98 100644 --- a/src/chartsheet.c +++ b/src/chartsheet.c @@ -25,7 +25,7 @@ * Create a new chartsheet object. */ lxw_chartsheet * -lxw_chartsheet_new() +lxw_chartsheet_new(lxw_worksheet_init_data *init_data) { lxw_chartsheet *chartsheet = calloc(1, sizeof(lxw_chartsheet)); GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error); @@ -35,6 +35,16 @@ lxw_chartsheet_new() chartsheet->worksheet = lxw_worksheet_new(NULL); GOTO_LABEL_ON_MEM_ERROR(chartsheet->worksheet, mem_error); + if (init_data) { + chartsheet->name = init_data->name; + chartsheet->quoted_name = init_data->quoted_name; + chartsheet->tmpdir = init_data->tmpdir; + chartsheet->index = init_data->index; + chartsheet->hidden = init_data->hidden; + chartsheet->active_sheet = init_data->active_sheet; + chartsheet->first_sheet = init_data->first_sheet; + } + return chartsheet; mem_error: @@ -52,10 +62,8 @@ lxw_chartsheet_free(lxw_chartsheet *chartsheet) return; lxw_worksheet_free(chartsheet->worksheet); - - if (chartsheet->drawing) - lxw_drawing_free(chartsheet->drawing); - + free(chartsheet->name); + free(chartsheet->quoted_name); free(chartsheet); } @@ -122,40 +130,13 @@ _chartsheet_write_page_margins(lxw_chartsheet *self) lxw_worksheet_write_page_margins(self->worksheet); } -/* - * Write the element. - */ -STATIC void -_chartsheet_write_drawing(lxw_chartsheet *self, uint16_t id) -{ - struct xml_attribute_list attributes; - struct xml_attribute *attribute; - char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; - - lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); - - LXW_INIT_ATTRIBUTES(); - - LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); - - lxw_xml_empty_tag(self->file, "drawing", &attributes); - - LXW_FREE_ATTRIBUTES(); - -} - /* * Write the elements. */ STATIC void _chartsheet_write_drawings(lxw_chartsheet *self) { - if (!self->drawing) - return; - - self->rel_count++; - - _chartsheet_write_drawing(self, self->rel_count); + lxw_worksheet_write_drawings(self->worksheet); } /***************************************************************************** @@ -199,3 +180,146 @@ lxw_chartsheet_assemble_xml_file(lxw_chartsheet *self) * Public functions. * ****************************************************************************/ +/* + * Set a chartsheet chart, with options. + */ +lxw_error +chartsheet_set_chart_opt(lxw_chartsheet *self, + lxw_chart *chart, lxw_image_options *user_options) +{ + lxw_image_options *options; + lxw_chart_series *series; + + if (!chart) { + LXW_WARN("chartsheet_set_chart()/_opt(): chart must be non-NULL."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the chart isn't being used more than once. */ + if (chart->in_use) { + LXW_WARN("chartsheet_set_chart()/_opt(): the same chart object " + "cannot be set for a chartsheet more than once."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check that the chart has a data series. */ + if (STAILQ_EMPTY(chart->series_list)) { + LXW_WARN("chartsheet_set_chart()/_opt(): chart must have a series."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check that the chart has a 'values' series. */ + STAILQ_FOREACH(series, chart->series_list, list_pointers) { + if (!series->values->formula && !series->values->sheetname) { + LXW_WARN("chartsheet_set_chart()/_opt(): chart must have a " + "'values' series."); + + return LXW_ERROR_PARAMETER_VALIDATION; + } + } + + /* Create a new object to hold the chart image options. */ + options = calloc(1, sizeof(lxw_image_options)); + RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED); + + if (user_options) { + options->x_offset = user_options->x_offset; + options->y_offset = user_options->y_offset; + options->x_scale = user_options->x_scale; + options->y_scale = user_options->y_scale; + } + + /* TODO. Read defaults from chart. */ + options->width = 480; + options->height = 288; + + if (!options->x_scale) + options->x_scale = 1; + + if (!options->y_scale) + options->y_scale = 1; + + /* Store chart references so they can be ordered in the workbook. */ + options->chart = chart; + + /* Store the chart data in the embedded worksheet. */ + STAILQ_INSERT_TAIL(self->worksheet->chart_data, options, list_pointers); + + chart->in_use = LXW_TRUE; + chart->is_chartsheet = LXW_TRUE; + + return LXW_NO_ERROR; +} + +/* + * Set a chartsheet charts. + */ +lxw_error +chartsheet_set_chart(lxw_chartsheet *self, lxw_chart *chart) +{ + return chartsheet_set_chart_opt(self, chart, NULL); +} + +/* + * Set this chartsheet as a selected worksheet, i.e. the worksheet has its tab + * highlighted. + */ +void +chartsheet_select(lxw_chartsheet *self) +{ + self->selected = LXW_TRUE; + + /* Selected worksheet can't be hidden. */ + self->hidden = LXW_FALSE; +} + +/* + * Set this chartsheet as the active worksheet, i.e. the worksheet that is + * displayed when the workbook is opened. Also set it as selected. + */ +void +chartsheet_activate(lxw_chartsheet *self) +{ + self->worksheet->selected = LXW_TRUE; + self->worksheet->active = LXW_TRUE; + + /* Active worksheet can't be hidden. */ + self->worksheet->hidden = LXW_FALSE; + + *self->active_sheet = self->index; +} + +/* + * Set this chartsheet as the first visible sheet. This is necessary + * when there are a large number of worksheets and the activated + * worksheet is not visible on the screen. + */ +void +chartsheet_set_first_sheet(lxw_chartsheet *self) +{ + /* Active worksheet can't be hidden. */ + self->hidden = LXW_FALSE; + + *self->first_sheet = self->index; +} + +/* + * Hide this chartsheet. + */ +void +chartsheet_hide(lxw_chartsheet *self) +{ + self->hidden = LXW_TRUE; + + /* A hidden worksheet shouldn't be active or selected. */ + self->selected = LXW_FALSE; + + /* If this is active_sheet or first_sheet reset the workbook value. */ + if (*self->first_sheet == self->index) + *self->first_sheet = 0; + + if (*self->active_sheet == self->index) + *self->active_sheet = 0; +} diff --git a/src/content_types.c b/src/content_types.c index 73068bd4..8f6a5cb2 100644 --- a/src/content_types.c +++ b/src/content_types.c @@ -296,6 +296,16 @@ lxw_ct_add_worksheet_name(lxw_content_types *self, const char *name) LXW_APP_DOCUMENT "spreadsheetml.worksheet+xml"); } +/* + * Add the name of a chartsheet to the ContentTypes overrides. + */ +void +lxw_ct_add_chartsheet_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, + LXW_APP_DOCUMENT "spreadsheetml.chartsheet+xml"); +} + /* * Add the name of a chart to the ContentTypes overrides. */ diff --git a/src/drawing.c b/src/drawing.c index 1a16eb1f..c15f0acd 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -489,13 +489,40 @@ _drawing_write_client_data(lxw_drawing *self) lxw_xml_empty_tag(self->file, "xdr:clientData", NULL); } +/* + * Write the element. + */ +STATIC void +_drawing_write_a_graphic_frame_locks(lxw_drawing *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("noGrp", 1); + + lxw_xml_empty_tag(self->file, "a:graphicFrameLocks", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + /* * Write the element. */ STATIC void _drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) { - lxw_xml_empty_tag(self->file, "xdr:cNvGraphicFramePr", NULL); + if (self->embedded) { + lxw_xml_empty_tag(self->file, "xdr:cNvGraphicFramePr", NULL); + } + else { + lxw_xml_start_tag(self->file, "xdr:cNvGraphicFramePr", NULL); + + /* Write the a:graphicFrameLocks element. */ + _drawing_write_a_graphic_frame_locks(self); + + lxw_xml_end_tag(self->file, "xdr:cNvGraphicFramePr"); + } } /* @@ -705,6 +732,71 @@ _drawing_write_two_cell_anchor(lxw_drawing *self, uint16_t index, LXW_FREE_ATTRIBUTES(); } +/* + * Write the element. + */ +STATIC void +_drawing_write_ext(lxw_drawing *self, uint32_t cx, uint32_t cy) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("cx", cx); + LXW_PUSH_ATTRIBUTES_INT("cy", cy); + + lxw_xml_empty_tag(self->file, "xdr:ext", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_pos(lxw_drawing *self, int32_t x, int32_t y) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("x", x); + LXW_PUSH_ATTRIBUTES_INT("y", y); + + lxw_xml_empty_tag(self->file, "xdr:pos", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_drawing_write_absolute_anchor(lxw_drawing *self) +{ + lxw_xml_start_tag(self->file, "xdr:absoluteAnchor", NULL); + + /* Horizontal == 0. Vertial == 1. */ + if (self->orientation == 0) { + /* Write the xdr:pos element. */ + _drawing_write_pos(self, 0, 0); + + /* Write the xdr:ext element. */ + _drawing_write_ext(self, 9308969, 6078325); + } + else { + /* Write the xdr:pos element. */ + _drawing_write_pos(self, 0, -47625); + } + + _drawing_write_graphic_frame(self, 1); + + /* Write the xdr:clientData element. */ + _drawing_write_client_data(self); + + lxw_xml_end_tag(self->file, "xdr:absoluteAnchor"); +} + /***************************************************************************** * * XML file assembly functions. @@ -733,7 +825,10 @@ lxw_drawing_assemble_xml_file(lxw_drawing *self) _drawing_write_two_cell_anchor(self, index, drawing_object); index++; } - + } + else { + /* Write the xdr:absoluteAnchor element. Mainly for chartsheets. */ + _drawing_write_absolute_anchor(self); } lxw_xml_end_tag(self->file, "xdr:wsDr"); diff --git a/src/packager.c b/src/packager.c index 36662f88..4362a155 100644 --- a/src/packager.c +++ b/src/packager.c @@ -200,6 +200,43 @@ _write_worksheet_files(lxw_packager *self) return LXW_NO_ERROR; } +/* + * Write the chartsheet files. + */ +STATIC lxw_error +_write_chartsheet_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_chartsheet *chartsheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 1; + lxw_error err; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + chartsheet = sheet->u.chartsheet; + else + continue; + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/chartsheets/sheet%d.xml", index++); + + chartsheet->file = lxw_tmpfile(self->tmpdir); + if (!chartsheet->file) + return LXW_ERROR_CREATING_TMPFILE; + + lxw_chartsheet_assemble_xml_file(chartsheet); + + err = _add_file_to_zip(self, chartsheet->file, sheetname); + RETURN_ON_ERROR(err); + + fclose(chartsheet->file); + } + + return LXW_NO_ERROR; +} + /* * Write the /xl/media/image?.xml files. */ @@ -306,7 +343,7 @@ _write_drawing_files(lxw_packager *self) STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) - continue; + worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; @@ -369,6 +406,7 @@ _write_app_file(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; + lxw_chartsheet *chartsheet; lxw_defined_name *defined_name; lxw_app *app; uint16_t named_range_count = 0; @@ -389,17 +427,27 @@ _write_app_file(lxw_packager *self) goto mem_error; } - lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_sheets); + if (self->workbook->num_worksheets) { + lxw_snprintf(number, LXW_ATTR_32, "%d", + self->workbook->num_worksheets); + lxw_app_add_heading_pair(app, "Worksheets", number); + } - lxw_app_add_heading_pair(app, "Worksheets", number); + if (self->workbook->num_chartsheets) { + lxw_snprintf(number, LXW_ATTR_32, "%d", + self->workbook->num_chartsheets); + lxw_app_add_heading_pair(app, "Charts", number); + } STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { - if (sheet->is_chartsheet) - continue; - else + if (sheet->is_chartsheet) { + chartsheet = sheet->u.chartsheet; + lxw_app_add_part_name(app, chartsheet->name); + } + else { worksheet = sheet->u.worksheet; - - lxw_app_add_part_name(app, worksheet->name); + lxw_app_add_part_name(app, worksheet->name); + } } /* Add the Named Ranges parts. */ @@ -604,6 +652,8 @@ _write_content_types_file(lxw_packager *self) lxw_sheet *sheet; char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; uint16_t index = 1; + uint16_t worksheet_index = 1; + uint16_t chartsheet_index = 1; lxw_error err = LXW_NO_ERROR; if (!content_types) { @@ -627,12 +677,16 @@ _write_content_types_file(lxw_packager *self) lxw_ct_add_default(content_types, "bmp", "image/bmp"); STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { - if (sheet->is_chartsheet) - continue; - - lxw_snprintf(filename, LXW_FILENAME_LENGTH, - "/xl/worksheets/sheet%d.xml", index++); - lxw_ct_add_worksheet_name(content_types, filename); + if (sheet->is_chartsheet) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/chartsheets/sheet%d.xml", chartsheet_index++); + lxw_ct_add_chartsheet_name(content_types, filename); + } + else { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/worksheets/sheet%d.xml", worksheet_index++); + lxw_ct_add_worksheet_name(content_types, filename); + } } for (index = 1; index <= self->chart_count; index++) { @@ -675,7 +729,8 @@ _write_workbook_rels_file(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint16_t worksheet_index = 1; + uint16_t chartsheet_index = 1; lxw_error err = LXW_NO_ERROR; if (!rels) { @@ -690,12 +745,18 @@ _write_workbook_rels_file(lxw_packager *self) } STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { - if (sheet->is_chartsheet) - continue; - - lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml", - index++); - lxw_add_document_relationship(rels, "/worksheet", sheetname); + if (sheet->is_chartsheet) { + lxw_snprintf(sheetname, + LXW_FILENAME_LENGTH, + "chartsheets/sheet%d.xml", chartsheet_index++); + lxw_add_document_relationship(rels, "/chartsheet", sheetname); + } + else { + lxw_snprintf(sheetname, + LXW_FILENAME_LENGTH, + "worksheets/sheet%d.xml", worksheet_index++); + lxw_add_document_relationship(rels, "/worksheet", sheetname); + } } lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml"); @@ -779,6 +840,68 @@ _write_worksheet_rels_file(lxw_packager *self) return LXW_NO_ERROR; } +/* + * Write the chartsheet .rels files for chartsheets that contain links to + * external data such as drawings. + */ +STATIC lxw_error +_write_chartsheet_rels_file(lxw_packager *self) +{ + lxw_relationships *rels; + lxw_rel_tuple *rel; + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + uint16_t index = 0; + lxw_error err; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + worksheet = sheet->u.chartsheet->worksheet; + else + continue; + + index++; + + /* TODO. This should never be empty. Put check higher up. */ + if (STAILQ_EMPTY(worksheet->external_drawing_links)) + continue; + + rels = lxw_relationships_new(); + + rels->file = lxw_tmpfile(self->tmpdir); + if (!rels->file) { + lxw_free_relationships(rels); + return LXW_ERROR_CREATING_TMPFILE; + } + + STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + + STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/chartsheets/_rels/sheet%d.xml.rels", index); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_file_to_zip(self, rels->file, sheetname); + + fclose(rels->file); + lxw_free_relationships(rels); + + RETURN_ON_ERROR(err); + } + + return LXW_NO_ERROR; +} + /* * Write the drawing .rels files for worksheets that contain charts or * drawings. @@ -797,7 +920,7 @@ _write_drawing_rels_file(lxw_packager *self) STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) - continue; + worksheet = sheet->u.chartsheet->worksheet; else worksheet = sheet->u.worksheet; @@ -997,6 +1120,9 @@ lxw_create_package(lxw_packager *self) error = _write_worksheet_files(self); RETURN_ON_ERROR(error); + error = _write_chartsheet_files(self); + RETURN_ON_ERROR(error); + error = _write_workbook_file(self); RETURN_ON_ERROR(error); @@ -1033,6 +1159,9 @@ lxw_create_package(lxw_packager *self) error = _write_worksheet_rels_file(self); RETURN_ON_ERROR(error); + error = _write_chartsheet_rels_file(self); + RETURN_ON_ERROR(error); + error = _write_drawing_rels_file(self); RETURN_ON_ERROR(error); diff --git a/src/workbook.c b/src/workbook.c index de42f5bf..2cfca6dd 100644 --- a/src/workbook.c +++ b/src/workbook.c @@ -13,10 +13,15 @@ #include "xlsxwriter/packager.h" #include "xlsxwriter/hash_table.h" -STATIC int _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2); +STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1, + lxw_worksheet_name *name2); +STATIC int _chartsheet_name_cmp(lxw_chartsheet_name *name1, + lxw_chartsheet_name *name2); #ifndef __clang_analyzer__ -LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers, - _name_cmp); +LXW_RB_GENERATE_WORKSHEET_NAMES(lxw_worksheet_names, lxw_worksheet_name, + tree_pointers, _worksheet_name_cmp); +LXW_RB_GENERATE_CHARTSHEET_NAMES(lxw_chartsheet_names, lxw_chartsheet_name, + tree_pointers, _chartsheet_name_cmp); #endif /* @@ -30,10 +35,16 @@ LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers, ****************************************************************************/ /* - * Comparator for the worksheet names structure red/black tree. + * Comparators for the sheet names structure red/black tree. */ STATIC int -_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2) +_worksheet_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2) +{ + return strcmp(name1->name, name2->name); +} + +STATIC int +_chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2) { return strcmp(name1->name, name2->name); } @@ -83,7 +94,9 @@ lxw_workbook_free(lxw_workbook *workbook) { lxw_sheet *sheet; struct lxw_worksheet_name *worksheet_name; - struct lxw_worksheet_name *next_name; + struct lxw_worksheet_name *next_worksheet_name; + struct lxw_chartsheet_name *chartsheet_name; + struct lxw_chartsheet_name *next_chartsheet_name; lxw_chart *chart; lxw_format *format; lxw_defined_name *defined_name; @@ -113,8 +126,9 @@ lxw_workbook_free(lxw_workbook *workbook) free(workbook->sheets); } - /* Free the worksheets list. The worksheet objects are freed above. */ + /* Free the sheet lists. The worksheet objects are freed above. */ free(workbook->worksheets); + free(workbook->chartsheets); /* Free the charts in the workbook. */ if (workbook->charts) { @@ -161,18 +175,35 @@ lxw_workbook_free(lxw_workbook *workbook) if (workbook->worksheet_names) { for (worksheet_name = RB_MIN(lxw_worksheet_names, workbook->worksheet_names); - worksheet_name; worksheet_name = next_name) { + worksheet_name; worksheet_name = next_worksheet_name) { - next_name = RB_NEXT(lxw_worksheet_names, - workbook->worksheet_name, worksheet_name); - RB_REMOVE(lxw_worksheet_names, - workbook->worksheet_names, worksheet_name); + next_worksheet_name = RB_NEXT(lxw_worksheet_names, + workbook->worksheet_name, + worksheet_name); + RB_REMOVE(lxw_worksheet_names, workbook->worksheet_names, + worksheet_name); free(worksheet_name); } free(workbook->worksheet_names); } + if (workbook->chartsheet_names) { + for (chartsheet_name = + RB_MIN(lxw_chartsheet_names, workbook->chartsheet_names); + chartsheet_name; chartsheet_name = next_chartsheet_name) { + + next_chartsheet_name = RB_NEXT(lxw_chartsheet_names, + workbook->chartsheet_name, + chartsheet_name); + RB_REMOVE(lxw_chartsheet_names, workbook->chartsheet_names, + chartsheet_name); + free(chartsheet_name); + } + + free(workbook->chartsheet_names); + } + lxw_hash_free(workbook->used_xf_formats); lxw_sst_free(workbook->sst); free(workbook->options.tmpdir); @@ -857,12 +888,17 @@ _prepare_drawings(lxw_workbook *self) uint16_t chart_ref_id = 0; uint16_t image_ref_id = 0; uint16_t drawing_id = 0; + uint8_t is_chartsheet; STAILQ_FOREACH(sheet, self->sheets, list_pointers) { - if (sheet->is_chartsheet) - continue; - else + if (sheet->is_chartsheet) { + worksheet = sheet->u.chartsheet->worksheet; + is_chartsheet = LXW_TRUE; + } + else { worksheet = sheet->u.worksheet; + is_chartsheet = LXW_FALSE; + } if (STAILQ_EMPTY(worksheet->image_data) && STAILQ_EMPTY(worksheet->chart_data)) @@ -873,7 +909,7 @@ _prepare_drawings(lxw_workbook *self) STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) { chart_ref_id++; lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id, - image_options); + image_options, is_chartsheet); if (image_options->chart) STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart, ordered_list_pointers); @@ -1195,17 +1231,21 @@ _write_sheets(lxw_workbook *self) { lxw_sheet *sheet; lxw_worksheet *worksheet; + lxw_chartsheet *chartsheet; lxw_xml_start_tag(self->file, "sheets", NULL); STAILQ_FOREACH(sheet, self->sheets, list_pointers) { - if (sheet->is_chartsheet) - continue; - else + if (sheet->is_chartsheet) { + chartsheet = sheet->u.chartsheet; + _write_sheet(self, chartsheet->name, chartsheet->index + 1, + chartsheet->hidden); + } + else { worksheet = sheet->u.worksheet; - - _write_sheet(self, worksheet->name, worksheet->index + 1, - worksheet->hidden); + _write_sheet(self, worksheet->name, worksheet->index + 1, + worksheet->hidden); + } } lxw_xml_end_tag(self->file, "sheets"); @@ -1253,9 +1293,6 @@ _write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name) LXW_FREE_ATTRIBUTES(); } -/* - * Write the element. - */ STATIC void _write_defined_names(lxw_workbook *self) { @@ -1369,11 +1406,22 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options) GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error); STAILQ_INIT(workbook->worksheets); + /* Add the chartsheets list. */ + workbook->chartsheets = calloc(1, sizeof(struct lxw_chartsheets)); + GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheets, mem_error); + STAILQ_INIT(workbook->chartsheets); + /* Add the worksheet names tree. */ workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names)); GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error); RB_INIT(workbook->worksheet_names); + /* Add the chartsheet names tree. */ + workbook->chartsheet_names = calloc(1, + sizeof(struct lxw_chartsheet_names)); + GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error); + RB_INIT(workbook->chartsheet_names); + /* Add the charts list. */ workbook->charts = calloc(1, sizeof(struct lxw_charts)); GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error); @@ -1456,13 +1504,13 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname) GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error); lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d", - self->num_sheets + 1); + self->num_worksheets + 1); init_data.name = new_name; init_data.quoted_name = lxw_strdup(new_name); } /* Check that the worksheet name is valid. */ - error = workbook_validate_worksheet_name(self, init_data.name); + error = workbook_validate_sheet_name(self, init_data.name); if (error) { LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has " "error: %s", init_data.name, lxw_strerror(error)); @@ -1514,6 +1562,90 @@ mem_error: return NULL; } +/* + * Add a new chartsheet to the Excel workbook. + */ +lxw_chartsheet * +workbook_add_chartsheet(lxw_workbook *self, const char *sheetname) +{ + lxw_sheet *sheet = NULL; + lxw_chartsheet *chartsheet = NULL; + lxw_chartsheet_name *chartsheet_name = NULL; + lxw_error error; + lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char *new_name = NULL; + + if (sheetname) { + /* Use the user supplied name. */ + init_data.name = lxw_strdup(sheetname); + init_data.quoted_name = lxw_quote_sheetname((char *) sheetname); + } + else { + /* Use the default SheetN name. */ + new_name = malloc(LXW_MAX_SHEETNAME_LENGTH); + GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error); + + lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Chart%d", + self->num_chartsheets + 1); + init_data.name = new_name; + init_data.quoted_name = lxw_strdup(new_name); + } + + /* Check that the chartsheet name is valid. */ + error = workbook_validate_sheet_name(self, init_data.name); + if (error) { + LXW_WARN_FORMAT2 + ("workbook_add_chartsheet(): chartsheet name '%s' has " + "error: %s", init_data.name, lxw_strerror(error)); + goto mem_error; + } + + /* Create a struct to find/store the chartsheet name/pointer. */ + chartsheet_name = calloc(1, sizeof(struct lxw_chartsheet_name)); + GOTO_LABEL_ON_MEM_ERROR(chartsheet_name, mem_error); + + /* Initialize the metadata to pass to the chartsheet. */ + init_data.hidden = 0; + init_data.index = self->num_sheets; + init_data.sst = self->sst; + init_data.optimize = self->options.constant_memory; + init_data.active_sheet = &self->active_sheet; + init_data.first_sheet = &self->first_sheet; + init_data.tmpdir = self->options.tmpdir; + + /* Create a new chartsheet object. */ + chartsheet = lxw_chartsheet_new(&init_data); + GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error); + + /* Add it to the chartsheet list. */ + self->num_chartsheets++; + STAILQ_INSERT_TAIL(self->chartsheets, chartsheet, list_pointers); + + /* Create a new sheet object. */ + sheet = calloc(1, sizeof(lxw_sheet)); + GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error); + sheet->is_chartsheet = LXW_TRUE; + sheet->u.chartsheet = chartsheet; + + /* Add it to the chartsheet list. */ + self->num_sheets++; + STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers); + + /* Store the chartsheet so we can look it up by name. */ + chartsheet_name->name = init_data.name; + chartsheet_name->chartsheet = chartsheet; + RB_INSERT(lxw_chartsheet_names, self->chartsheet_names, chartsheet_name); + + return chartsheet; + +mem_error: + free(init_data.name); + free(init_data.quoted_name); + free(chartsheet_name); + free(chartsheet); + return NULL; +} + /* * Add a new chart to the Excel workbook. */ @@ -1946,11 +2078,33 @@ workbook_get_worksheet_by_name(lxw_workbook *self, const char *name) return NULL; } +/* + * Get a chartsheet object from its name. + */ +lxw_chartsheet * +workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name) +{ + lxw_chartsheet_name chartsheet_name; + lxw_chartsheet_name *found; + + if (!name) + return NULL; + + chartsheet_name.name = name; + found = RB_FIND(lxw_chartsheet_names, + self->chartsheet_names, &chartsheet_name); + + if (found) + return found->chartsheet; + else + return NULL; +} + /* * Validate the worksheet name based on Excel's rules. */ lxw_error -workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname) +workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname) { /* Check the UTF-8 length of the worksheet name. */ if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX) @@ -1964,5 +2118,9 @@ workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname) if (workbook_get_worksheet_by_name(self, sheetname)) return LXW_ERROR_SHEETNAME_ALREADY_USED; + /* Check if the chartsheet name is already in use. */ + if (workbook_get_chartsheet_by_name(self, sheetname)) + return LXW_ERROR_SHEETNAME_ALREADY_USED; + return LXW_NO_ERROR; } diff --git a/src/worksheet.c b/src/worksheet.c index f2427a81..e542cb97 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -2080,8 +2080,10 @@ mem_error: */ void lxw_worksheet_prepare_chart(lxw_worksheet *self, - uint16_t chart_ref_id, uint16_t drawing_id, - lxw_image_options *image_data) + uint16_t chart_ref_id, + uint16_t drawing_id, + lxw_image_options *image_data, + uint8_t is_chartsheet) { lxw_drawing_object *drawing_object; lxw_rel_tuple *relationship; @@ -2091,9 +2093,13 @@ lxw_worksheet_prepare_chart(lxw_worksheet *self, if (!self->drawing) { self->drawing = lxw_drawing_new(); - self->drawing->embedded = LXW_TRUE; RETURN_VOID_ON_MEM_ERROR(self->drawing); + if (is_chartsheet) + self->drawing->embedded = LXW_FALSE; + else + self->drawing->embedded = LXW_TRUE; + relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); @@ -3702,6 +3708,12 @@ lxw_worksheet_write_page_margins(lxw_worksheet *self) _worksheet_write_page_margins(self); } +void +lxw_worksheet_write_drawings(lxw_worksheet *self) +{ + _worksheet_write_drawings(self); +} + /* * Assemble and write the XML file. */ diff --git a/test/functional/src/test_chartsheet01.c b/test/functional/src/test_chartsheet01.c new file mode 100644 index 00000000..18e9d9e0 --- /dev/null +++ b/test/functional/src/test_chartsheet01.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chartsheet01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chartsheet *chartsheet = workbook_add_chartsheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_BAR); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 79858304; + chart->axis_id_2 = 79860096; + + 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"); + + chartsheet_set_chart(chartsheet, chart); + chartsheet_activate(chartsheet); + + return workbook_close(workbook); +} diff --git a/test/functional/test_chartsheet.py b/test/functional/test_chartsheet.py new file mode 100644 index 00000000..701c9ad5 --- /dev/null +++ b/test/functional/test_chartsheet.py @@ -0,0 +1,17 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# Copyright 2014-2018, 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_chartsheet01(self): + self.run_exe_test('test_chartsheet01') diff --git a/test/functional/xlsx_files/chartsheet01.xlsx b/test/functional/xlsx_files/chartsheet01.xlsx new file mode 100644 index 00000000..a7872717 Binary files /dev/null and b/test/functional/xlsx_files/chartsheet01.xlsx differ diff --git a/test/unit/chartsheet/test_chartsheet.c b/test/unit/chartsheet/test_chartsheet.c index ea8a89d8..71a39b63 100644 --- a/test/unit/chartsheet/test_chartsheet.c +++ b/test/unit/chartsheet/test_chartsheet.c @@ -9,6 +9,7 @@ #include "../helper.h" #include "xlsxwriter/chartsheet.h" +#include "xlsxwriter/drawing.h" // Test assembling a complete Chartsheet file. CTEST(chartsheet, chartsheet) { @@ -29,7 +30,7 @@ CTEST(chartsheet, chartsheet) { lxw_chartsheet *chartsheet = lxw_chartsheet_new(NULL); chartsheet->file = testfile; - chartsheet->drawing = lxw_drawing_new(NULL); + chartsheet->worksheet->drawing = lxw_drawing_new(NULL); lxw_chartsheet_assemble_xml_file(chartsheet); diff --git a/test/unit/chartsheet/test_chartsheet_xml_declaration.c b/test/unit/chartsheet/test_chartsheet_xml_declaration.c index 212dc51c..cd75bb6d 100644 --- a/test/unit/chartsheet/test_chartsheet_xml_declaration.c +++ b/test/unit/chartsheet/test_chartsheet_xml_declaration.c @@ -17,7 +17,7 @@ CTEST(chartsheet, xml_declaration) { char exp[] = "\n"; FILE* testfile = tmpfile(); - lxw_chartsheet *chartsheet = lxw_chartsheet_new(); + lxw_chartsheet *chartsheet = lxw_chartsheet_new(NULL); chartsheet->file = testfile; _chartsheet_xml_declaration(chartsheet); diff --git a/test/unit/workbook/test_workbook_validate_worksheet_name.c b/test/unit/workbook/test_workbook_validate_worksheet_name.c index 3ed45dcc..4aefbf79 100644 --- a/test/unit/workbook/test_workbook_validate_worksheet_name.c +++ b/test/unit/workbook/test_workbook_validate_worksheet_name.c @@ -18,7 +18,7 @@ CTEST(workbook, validate_worksheet_name01) { lxw_workbook *workbook = workbook_new(NULL); lxw_error exp = LXW_NO_ERROR; - lxw_error got = workbook_validate_worksheet_name(workbook, sheetname); + lxw_error got = workbook_validate_sheet_name(workbook, sheetname); ASSERT_EQUAL(got, exp); @@ -32,7 +32,7 @@ CTEST(workbook, validate_worksheet_name02) { lxw_workbook *workbook = workbook_new(NULL); lxw_error exp = LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED; - lxw_error got = workbook_validate_worksheet_name(workbook, sheetname); + lxw_error got = workbook_validate_sheet_name(workbook, sheetname); ASSERT_EQUAL(got, exp); @@ -46,7 +46,7 @@ CTEST(workbook, validate_worksheet_name03) { lxw_workbook *workbook = workbook_new(NULL); lxw_error exp = LXW_ERROR_INVALID_SHEETNAME_CHARACTER; - lxw_error got = workbook_validate_worksheet_name(workbook, sheetname); + lxw_error got = workbook_validate_sheet_name(workbook, sheetname); ASSERT_EQUAL(got, exp); @@ -62,7 +62,7 @@ CTEST(workbook, validate_worksheet_name04) { workbook_add_worksheet(workbook, sheetname); lxw_error exp = LXW_ERROR_SHEETNAME_ALREADY_USED; - lxw_error got = workbook_validate_worksheet_name(workbook, sheetname); + lxw_error got = workbook_validate_sheet_name(workbook, sheetname); ASSERT_EQUAL(got, exp);