/***************************************************************************** * chartsheet - A library for creating Excel XLSX chartsheet files. * * Used in conjunction with the libxlsxwriter library. * * Copyright 2014-2020, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. * */ #include "xlsxwriter/xmlwriter.h" #include "xlsxwriter/chartsheet.h" #include "xlsxwriter/utility.h" /* * Forward declarations. */ /***************************************************************************** * * Private functions. * ****************************************************************************/ /* * Create a new chartsheet object. */ lxw_chartsheet * 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); /* Use an embedded worksheet instance to write XML records that are * shared with worksheet.c. */ 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; } chartsheet->worksheet->is_chartsheet = LXW_TRUE; chartsheet->worksheet->zoom_scale_normal = LXW_FALSE; chartsheet->worksheet->orientation = LXW_LANDSCAPE; return chartsheet; mem_error: lxw_chartsheet_free(chartsheet); return NULL; } /* * Free a chartsheet object. */ void lxw_chartsheet_free(lxw_chartsheet *chartsheet) { if (!chartsheet) return; lxw_worksheet_free(chartsheet->worksheet); free(chartsheet->name); free(chartsheet->quoted_name); free(chartsheet); } /***************************************************************************** * * XML functions. * ****************************************************************************/ /* * Write the XML declaration. */ STATIC void _chartsheet_xml_declaration(lxw_chartsheet *self) { lxw_xml_declaration(self->file); } /* * Write the element. */ STATIC void _chartsheet_write_chartsheet(lxw_chartsheet *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char xmlns[] = "http://schemas.openxmlformats.org/" "spreadsheetml/2006/main"; char xmlns_r[] = "http://schemas.openxmlformats.org/" "officeDocument/2006/relationships"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); lxw_xml_start_tag(self->file, "chartsheet", &attributes); LXW_FREE_ATTRIBUTES(); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_pr(lxw_chartsheet *self) { lxw_worksheet_write_sheet_pr(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_views(lxw_chartsheet *self) { lxw_worksheet_write_sheet_views(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_page_margins(lxw_chartsheet *self) { lxw_worksheet_write_page_margins(self->worksheet); } /* * Write the elements. */ STATIC void _chartsheet_write_drawings(lxw_chartsheet *self) { lxw_worksheet_write_drawings(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_sheet_protection(lxw_chartsheet *self) { lxw_worksheet_write_sheet_protection(self->worksheet, &self->protection); } /* * Write the element. */ STATIC void _chartsheet_write_page_setup(lxw_chartsheet *self) { lxw_worksheet_write_page_setup(self->worksheet); } /* * Write the element. */ STATIC void _chartsheet_write_header_footer(lxw_chartsheet *self) { lxw_worksheet_write_header_footer(self->worksheet); } /***************************************************************************** * * XML file assembly functions. * ****************************************************************************/ /* * Assemble and write the XML file. */ void lxw_chartsheet_assemble_xml_file(lxw_chartsheet *self) { /* Set the embedded worksheet filehandle to the same as the chartsheet. */ self->worksheet->file = self->file; /* Write the XML declaration. */ _chartsheet_xml_declaration(self); /* Write the chartsheet element. */ _chartsheet_write_chartsheet(self); /* Write the sheetPr element. */ _chartsheet_write_sheet_pr(self); /* Write the sheetViews element. */ _chartsheet_write_sheet_views(self); /* Write the sheetProtection element. */ _chartsheet_write_sheet_protection(self); /* Write the pageMargins element. */ _chartsheet_write_page_margins(self); /* Write the chartsheet page setup. */ _chartsheet_write_page_setup(self); /* Write the headerFooter element. */ _chartsheet_write_header_footer(self); /* Write the drawing element. */ _chartsheet_write_drawings(self); lxw_xml_end_tag(self->file, "chartsheet"); } /***************************************************************************** * * Public functions. * ****************************************************************************/ /* * Set a chartsheet chart, with options. */ lxw_error chartsheet_set_chart_opt(lxw_chartsheet *self, lxw_chart *chart, lxw_chart_options *user_options) { lxw_object_properties *object_props; 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 properties. */ object_props = calloc(1, sizeof(lxw_object_properties)); RETURN_ON_MEM_ERROR(object_props, LXW_ERROR_MEMORY_MALLOC_FAILED); if (user_options) { object_props->x_offset = user_options->x_offset; object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; } /* TODO. Read defaults from chart. */ object_props->width = 480; object_props->height = 288; if (!object_props->x_scale) object_props->x_scale = 1; if (!object_props->y_scale) object_props->y_scale = 1; /* Store chart references so they can be ordered in the workbook. */ object_props->chart = chart; /* Store the chart data in the embedded worksheet. */ STAILQ_INSERT_TAIL(self->worksheet->chart_data, object_props, list_pointers); chart->in_use = LXW_TRUE; chart->is_chartsheet = LXW_TRUE; chart->is_protected = self->is_protected; self->chart = chart; 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; } /* * Set the color of the chartsheet tab. */ void chartsheet_set_tab_color(lxw_chartsheet *self, lxw_color_t color) { self->worksheet->tab_color = color; } /* * Set the chartsheet protection flags to prevent modification of chartsheet * objects. */ void chartsheet_protect(lxw_chartsheet *self, const char *password, lxw_protection *options) { struct lxw_protection_obj *protect = &self->protection; /* Copy any user parameters to the internal structure. */ if (options) { protect->objects = options->no_objects; protect->no_content = options->no_content; } else { protect->objects = LXW_FALSE; protect->no_content = LXW_FALSE; } if (password) { uint16_t hash = lxw_hash_password(password); lxw_snprintf(protect->hash, 5, "%X", hash); } else { if (protect->objects && protect->no_content) return; } protect->no_sheet = LXW_TRUE; protect->scenarios = LXW_TRUE; protect->is_configured = LXW_TRUE; if (self->chart) self->chart->is_protected = LXW_TRUE; else self->is_protected = LXW_TRUE; } /* * Set the chartsheet zoom factor. */ void chartsheet_set_zoom(lxw_chartsheet *self, uint16_t scale) { /* Confine the scale to Excel"s range */ if (scale < 10 || scale > 400) { LXW_WARN("chartsheet_set_zoom(): " "Zoom factor scale outside range: 10 <= zoom <= 400."); return; } self->worksheet->zoom = scale; } /* * Set the page orientation as portrait. */ void chartsheet_set_portrait(lxw_chartsheet *self) { worksheet_set_portrait(self->worksheet); } /* * Set the page orientation as landscape. */ void chartsheet_set_landscape(lxw_chartsheet *self) { worksheet_set_landscape(self->worksheet); } /* * Set the paper type. Example. 1 = US Letter, 9 = A4 */ void chartsheet_set_paper(lxw_chartsheet *self, uint8_t paper_size) { worksheet_set_paper(self->worksheet, paper_size); } /* * Set all the page margins in inches. */ void chartsheet_set_margins(lxw_chartsheet *self, double left, double right, double top, double bottom) { worksheet_set_margins(self->worksheet, left, right, top, bottom); } /* * Set the page header caption and options. */ lxw_error chartsheet_set_header_opt(lxw_chartsheet *self, const char *string, lxw_header_footer_options *options) { return worksheet_set_header_opt(self->worksheet, string, options); } /* * Set the page footer caption and options. */ lxw_error chartsheet_set_footer_opt(lxw_chartsheet *self, const char *string, lxw_header_footer_options *options) { return worksheet_set_footer_opt(self->worksheet, string, options); } /* * Set the page header caption. */ lxw_error chartsheet_set_header(lxw_chartsheet *self, const char *string) { return chartsheet_set_header_opt(self, string, NULL); } /* * Set the page footer caption. */ lxw_error chartsheet_set_footer(lxw_chartsheet *self, const char *string) { return chartsheet_set_footer_opt(self, string, NULL); }