libxlsxwriter/src/workbook.c in fast_excel-0.2.6 vs libxlsxwriter/src/workbook.c in fast_excel-0.3.0
- old
+ new
@@ -1,24 +1,29 @@
/*****************************************************************************
* workbook - A library for creating Excel XLSX workbook files.
*
* Used in conjunction with the libxlsxwriter library.
*
- * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
+ * Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
*
*/
#include "xlsxwriter/xmlwriter.h"
#include "xlsxwriter/workbook.h"
#include "xlsxwriter/utility.h"
#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
/*
* Forward declarations.
*/
@@ -28,18 +33,24 @@
* Private functions.
*
****************************************************************************/
/*
- * 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);
+ return lxw_strcasecmp(name1->name, name2->name);
}
+STATIC int
+_chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2)
+{
+ return lxw_strcasecmp(name1->name, name2->name);
+}
+
/*
* Free workbook properties.
*/
STATIC void
_free_doc_properties(lxw_doc_properties *properties)
@@ -79,13 +90,15 @@
* Free a workbook object.
*/
void
lxw_workbook_free(lxw_workbook *workbook)
{
- lxw_worksheet *worksheet;
+ 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;
lxw_defined_name *defined_name_tmp;
lxw_custom_property *custom_property;
@@ -95,21 +108,30 @@
_free_doc_properties(workbook->properties);
free(workbook->filename);
- /* Free the worksheets in the workbook. */
- if (workbook->worksheets) {
- while (!STAILQ_EMPTY(workbook->worksheets)) {
- worksheet = STAILQ_FIRST(workbook->worksheets);
- STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers);
- lxw_worksheet_free(worksheet);
- }
- free(workbook->worksheets);
+ /* Free the sheets in the workbook. */
+ if (workbook->sheets) {
+ while (!STAILQ_EMPTY(workbook->sheets)) {
+ sheet = STAILQ_FIRST(workbook->sheets);
+ if (sheet->is_chartsheet)
+ lxw_chartsheet_free(sheet->u.chartsheet);
+ else
+ lxw_worksheet_free(sheet->u.worksheet);
+
+ STAILQ_REMOVE_HEAD(workbook->sheets, list_pointers);
+ free(sheet);
+ }
+ free(workbook->sheets);
}
+ /* 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) {
while (!STAILQ_EMPTY(workbook->charts)) {
chart = STAILQ_FIRST(workbook->charts);
STAILQ_REMOVE_HEAD(workbook->charts, list_pointers);
@@ -151,26 +173,45 @@
}
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);
free(workbook->ordered_charts);
+ free(workbook->vba_project);
+ free(workbook->vba_codename);
free(workbook);
}
/*
* Set the default index for each format. This is only used for testing.
@@ -430,11 +471,11 @@
else {
/* This is a new num_format. */
num_format_index = calloc(1, sizeof(uint16_t));
*num_format_index = index;
format->num_format_index = index;
- lxw_insert_hash_element(num_formats, num_format,
+ lxw_insert_hash_element(num_formats, format->num_format,
num_format_index,
LXW_FORMAT_FIELD_LEN);
index++;
num_format_count++;
}
@@ -494,10 +535,11 @@
STATIC lxw_error
_store_defined_name(lxw_workbook *self, const char *name,
const char *app_name, const char *formula, int16_t index,
uint8_t hidden)
{
+ lxw_sheet *sheet;
lxw_worksheet *worksheet;
lxw_defined_name *defined_name;
lxw_defined_name *list_defined_name;
char name_copy[LXW_DEFINED_NAME_LENGTH];
char *tmp_str;
@@ -544,11 +586,16 @@
worksheet_name++;
if (worksheet_name[strlen(worksheet_name) - 1] == '\'')
worksheet_name[strlen(worksheet_name) - 1] = '\0';
/* Search for worksheet name to get the equivalent worksheet index. */
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ if (sheet->is_chartsheet)
+ continue;
+ else
+ worksheet = sheet->u.worksheet;
+
if (strcmp(worksheet_name, worksheet->name) == 0) {
defined_name->index = worksheet->index;
lxw_strcpy(defined_name->normalised_sheetname,
worksheet_name);
}
@@ -835,28 +882,38 @@
* Iterate through the worksheets and set up any chart or image drawings.
*/
STATIC void
_prepare_drawings(lxw_workbook *self)
{
+ lxw_sheet *sheet;
lxw_worksheet *worksheet;
lxw_image_options *image_options;
- uint16_t chart_ref_id = 0;
- uint16_t image_ref_id = 0;
- uint16_t drawing_id = 0;
+ uint32_t chart_ref_id = 0;
+ uint32_t image_ref_id = 0;
+ uint32_t drawing_id = 0;
+ uint8_t is_chartsheet;
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ 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))
continue;
drawing_id++;
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);
}
@@ -887,18 +944,22 @@
*/
STATIC void
_prepare_defined_names(lxw_workbook *self)
{
lxw_worksheet *worksheet;
+ lxw_sheet *sheet;
char app_name[LXW_DEFINED_NAME_LENGTH];
char range[LXW_DEFINED_NAME_LENGTH];
char area[LXW_MAX_CELL_RANGE_LENGTH];
char first_col[8];
char last_col[8];
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
-
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ if (sheet->is_chartsheet)
+ continue;
+ else
+ worksheet = sheet->u.worksheet;
/*
* Check for autofilter settings and store them.
*/
if (worksheet->autofilter.in_use) {
@@ -1074,10 +1135,14 @@
LXW_PUSH_ATTRIBUTES_STR("appName", "xl");
LXW_PUSH_ATTRIBUTES_STR("lastEdited", "4");
LXW_PUSH_ATTRIBUTES_STR("lowestEdited", "4");
LXW_PUSH_ATTRIBUTES_STR("rupBuild", "4505");
+ if (self->vba_project)
+ LXW_PUSH_ATTRIBUTES_STR("codeName",
+ "{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}");
+
lxw_xml_empty_tag(self->file, "fileVersion", &attributes);
LXW_FREE_ATTRIBUTES();
}
@@ -1089,10 +1154,14 @@
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
+
+ if (self->vba_codename)
+ LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename);
+
LXW_PUSH_ATTRIBUTES_STR("defaultThemeVersion", "124226");
lxw_xml_empty_tag(self->file, "workbookPr", &attributes);
LXW_FREE_ATTRIBUTES();
@@ -1168,17 +1237,27 @@
* Write the <sheets> element.
*/
STATIC void
_write_sheets(lxw_workbook *self)
{
+ lxw_sheet *sheet;
lxw_worksheet *worksheet;
+ lxw_chartsheet *chartsheet;
lxw_xml_start_tag(self->file, "sheets", NULL);
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
- _write_sheet(self, worksheet->name, worksheet->index + 1,
- worksheet->hidden);
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ 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);
+ }
}
lxw_xml_end_tag(self->file, "sheets");
}
@@ -1222,13 +1301,10 @@
&attributes);
LXW_FREE_ATTRIBUTES();
}
-/*
- * Write the <definedNames> element.
- */
STATIC void
_write_defined_names(lxw_workbook *self)
{
lxw_defined_name *defined_name;
@@ -1328,20 +1404,36 @@
/* Create the workbook object. */
workbook = calloc(1, sizeof(lxw_workbook));
GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error);
workbook->filename = lxw_strdup(filename);
+ /* Add the sheets list. */
+ workbook->sheets = calloc(1, sizeof(struct lxw_sheets));
+ GOTO_LABEL_ON_MEM_ERROR(workbook->sheets, mem_error);
+ STAILQ_INIT(workbook->sheets);
+
/* Add the worksheets list. */
workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets));
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);
STAILQ_INIT(workbook->charts);
@@ -1386,10 +1478,11 @@
lxw_format_get_xf_index(format);
if (options) {
workbook->options.constant_memory = options->constant_memory;
workbook->options.tmpdir = lxw_strdup(options->tmpdir);
+ workbook->options.use_zip64 = options->use_zip64;
}
return workbook;
mem_error:
@@ -1402,11 +1495,12 @@
* Add a new worksheet to the Excel workbook.
*/
lxw_worksheet *
workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
{
- lxw_worksheet *worksheet;
+ lxw_sheet *sheet = NULL;
+ lxw_worksheet *worksheet = NULL;
lxw_worksheet_name *worksheet_name = NULL;
lxw_error error;
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
char *new_name = NULL;
@@ -1419,17 +1513,17 @@
/* 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, "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));
goto mem_error;
}
@@ -1449,13 +1543,23 @@
/* Create a new worksheet object. */
worksheet = lxw_worksheet_new(&init_data);
GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
- self->num_sheets++;
+ /* Add it to the worksheet list. */
+ self->num_worksheets++;
STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers);
+ /* Create a new sheet object. */
+ sheet = calloc(1, sizeof(lxw_sheet));
+ GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error);
+ sheet->u.worksheet = worksheet;
+
+ /* Add it to the worksheet list. */
+ self->num_sheets++;
+ STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers);
+
/* Store the worksheet so we can look it up by name. */
worksheet_name->name = init_data.name;
worksheet_name->worksheet = worksheet;
RB_INSERT(lxw_worksheet_names, self->worksheet_names, worksheet_name);
@@ -1463,14 +1567,99 @@
mem_error:
free(init_data.name);
free(init_data.quoted_name);
free(worksheet_name);
+ free(worksheet);
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.
*/
lxw_chart *
workbook_add_chart(lxw_workbook *self, uint8_t type)
{
@@ -1526,48 +1715,75 @@
* Call finalization code and close file.
*/
lxw_error
workbook_close(lxw_workbook *self)
{
+ lxw_sheet *sheet = NULL;
lxw_worksheet *worksheet = NULL;
lxw_packager *packager = NULL;
lxw_error error = LXW_NO_ERROR;
/* Add a default worksheet if non have been added. */
if (!self->num_sheets)
workbook_add_worksheet(self, NULL);
/* Ensure that at least one worksheet has been selected. */
if (self->active_sheet == 0) {
- worksheet = STAILQ_FIRST(self->worksheets);
- worksheet->selected = 1;
- worksheet->hidden = 0;
+ sheet = STAILQ_FIRST(self->sheets);
+ if (!sheet->is_chartsheet) {
+ worksheet = sheet->u.worksheet;
+ worksheet->selected = 1;
+ worksheet->hidden = 0;
+ }
}
/* Set the active sheet. */
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ if (sheet->is_chartsheet)
+ continue;
+ else
+ worksheet = sheet->u.worksheet;
+
if (worksheet->index == self->active_sheet)
worksheet->active = 1;
}
+ /* Set workbook and worksheet VBA codenames if a macro has been added. */
+ if (self->vba_project) {
+ if (!self->vba_codename)
+ workbook_set_vba_name(self, "ThisWorkbook");
+
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
+ if (sheet->is_chartsheet)
+ continue;
+ else
+ worksheet = sheet->u.worksheet;
+
+ if (!worksheet->vba_codename)
+ worksheet_set_vba_name(worksheet, worksheet->name);
+ }
+ }
+
/* Set the defined names for the worksheets such as Print Titles. */
_prepare_defined_names(self);
/* Prepare the drawings, charts and images. */
_prepare_drawings(self);
/* Add cached data to charts. */
_add_chart_cache_data(self);
/* Create a packager object to assemble sub-elements into a zip file. */
- packager = lxw_packager_new(self->filename, self->options.tmpdir);
+ packager = lxw_packager_new(self->filename,
+ self->options.tmpdir,
+ self->options.use_zip64);
/* If the packager fails it is generally due to a zip permission error. */
if (packager == NULL) {
fprintf(stderr, "[ERROR] workbook_close(): "
"Error creating '%s'. "
- "Error = %s\n", self->filename, strerror(errno));
+ "System error = %s\n", self->filename, strerror(errno));
error = LXW_ERROR_CREATING_XLSX_FILE;
goto mem_error;
}
@@ -1579,30 +1795,51 @@
/* Error and non-error conditions fall through to the cleanup code. */
if (error == LXW_ERROR_CREATING_TMPFILE) {
fprintf(stderr, "[ERROR] workbook_close(): "
"Error creating tmpfile(s) to assemble '%s'. "
- "Error = %s\n", self->filename, strerror(errno));
+ "System error = %s\n", self->filename, strerror(errno));
}
- /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zlib. */
+ /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zip. */
if (error == LXW_ERROR_ZIP_FILE_OPERATION) {
fprintf(stderr, "[ERROR] workbook_close(): "
- "Zlib error while creating xlsx file '%s'. "
- "Error = %s\n", self->filename, strerror(errno));
+ "Zip ZIP_ERRNO error while creating xlsx file '%s'. "
+ "System error = %s\n", self->filename, strerror(errno));
}
+ /* If LXW_ERROR_ZIP_PARAMETER_ERROR then errno is set by zip. */
+ if (error == LXW_ERROR_ZIP_PARAMETER_ERROR) {
+ fprintf(stderr, "[ERROR] workbook_close(): "
+ "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. "
+ "System error = %s\n", self->filename, strerror(errno));
+ }
+
+ /* If LXW_ERROR_ZIP_BAD_ZIP_FILE then errno is set by zip. */
+ if (error == LXW_ERROR_ZIP_BAD_ZIP_FILE) {
+ fprintf(stderr, "[ERROR] workbook_close(): "
+ "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. "
+ "This may require the use_zip64 option for large files. "
+ "System error = %s\n", self->filename, strerror(errno));
+ }
+
+ /* If LXW_ERROR_ZIP_INTERNAL_ERROR then errno is set by zip. */
+ if (error == LXW_ERROR_ZIP_INTERNAL_ERROR) {
+ fprintf(stderr, "[ERROR] workbook_close(): "
+ "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. "
+ "System error = %s\n", self->filename, strerror(errno));
+ }
+
/* The next 2 error conditions don't set errno. */
if (error == LXW_ERROR_ZIP_FILE_ADD) {
fprintf(stderr, "[ERROR] workbook_close(): "
- "Zlib error adding file to xlsx file '%s'.\n",
- self->filename);
+ "Zip error adding file to xlsx file '%s'.\n", self->filename);
}
if (error == LXW_ERROR_ZIP_CLOSE) {
fprintf(stderr, "[ERROR] workbook_close(): "
- "Zlib error closing xlsx file '%s'.\n", self->filename);
+ "Zip error closing xlsx file '%s'.\n", self->filename);
}
mem_error:
lxw_packager_free(packager);
lxw_workbook_free(self);
@@ -1909,24 +2146,103 @@
else
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)
return LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED;
/* Check that the worksheet name doesn't contain invalid characters. */
if (strpbrk(sheetname, "[]:*?/\\"))
return LXW_ERROR_INVALID_SHEETNAME_CHARACTER;
+ /* Check that the worksheet doesn't start or end with an apostrophe. */
+ if (sheetname[0] == '\'' || sheetname[strlen(sheetname) - 1] == '\'')
+ return LXW_ERROR_SHEETNAME_START_END_APOSTROPHE;
+
+ /* Check that the worksheet name isn't the reserved work "History". */
+ if (lxw_strcasecmp(sheetname, "history") == 0)
+ return LXW_ERROR_SHEETNAME_RESERVED;
+
/* Check if the worksheet name is already in use. */
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;
+}
+
+/*
+ * Add a vbaProject binary to the Excel workbook.
+ */
+lxw_error
+workbook_add_vba_project(lxw_workbook *self, const char *filename)
+{
+ FILE *filehandle;
+
+ if (!filename) {
+ LXW_WARN("workbook_add_vba_project(): "
+ "filename must be specified.");
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
+ }
+
+ /* Check that the vbaProject file exists and can be opened. */
+ filehandle = fopen(filename, "rb");
+ if (!filehandle) {
+ LXW_WARN_FORMAT1("workbook_add_vba_project(): "
+ "file doesn't exist or can't be opened: %s.",
+ filename);
+ return LXW_ERROR_PARAMETER_VALIDATION;
+ }
+ fclose(filehandle);
+
+ self->vba_project = lxw_strdup(filename);
+
+ return LXW_NO_ERROR;
+}
+
+/*
+ * Set the VBA name for the workbook.
+ */
+lxw_error
+workbook_set_vba_name(lxw_workbook *self, const char *name)
+{
+ if (!name) {
+ LXW_WARN("workbook_set_vba_name(): " "name must be specified.");
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
+ }
+
+ self->vba_codename = lxw_strdup(name);
return LXW_NO_ERROR;
}