From 24b364d64cae58dd8b73bef07160604e945b319f Mon Sep 17 00:00:00 2001 From: Jay Two Date: Sat, 29 Jun 2019 21:04:39 +0900 Subject: [PATCH] update libxlsx to v0.8.7 --- libxlsxwriter/examples/constant_memory.c | 3 +- libxlsxwriter/examples/macro.c | 25 +++ libxlsxwriter/include/xlsxwriter.h | 2 +- libxlsxwriter/include/xlsxwriter/common.h | 23 ++- .../include/xlsxwriter/content_types.h | 1 + libxlsxwriter/include/xlsxwriter/packager.h | 16 +- libxlsxwriter/include/xlsxwriter/utility.h | 8 + libxlsxwriter/include/xlsxwriter/workbook.h | 169 ++++++++++++++---- libxlsxwriter/include/xlsxwriter/worksheet.h | 34 +++- libxlsxwriter/src/content_types.c | 2 - libxlsxwriter/src/drawing.c | 24 +-- libxlsxwriter/src/packager.c | 96 +++++++--- libxlsxwriter/src/utility.c | 13 +- libxlsxwriter/src/workbook.c | 131 ++++++++++++-- libxlsxwriter/src/worksheet.c | 37 +++- 15 files changed, 472 insertions(+), 112 deletions(-) create mode 100644 libxlsxwriter/examples/macro.c diff --git a/libxlsxwriter/examples/constant_memory.c b/libxlsxwriter/examples/constant_memory.c index e628c2d..da055a5 100644 --- a/libxlsxwriter/examples/constant_memory.c +++ b/libxlsxwriter/examples/constant_memory.c @@ -17,7 +17,8 @@ int main() { /* Set the worksheet options. */ lxw_workbook_options options = {.constant_memory = LXW_TRUE, - .tmpdir = NULL}; + .tmpdir = NULL, + .use_zip64 = LXW_FALSE}; /* Create a new workbook with options. */ lxw_workbook *workbook = workbook_new_opt("constant_memory.xlsx", &options); diff --git a/libxlsxwriter/examples/macro.c b/libxlsxwriter/examples/macro.c new file mode 100644 index 0000000..0c5114d --- /dev/null +++ b/libxlsxwriter/examples/macro.c @@ -0,0 +1,25 @@ +/***************************************************************************** + * + * An example of adding macros to a libxlsxwriter file using a VBA project + * file extracted from an existing Excel .xlsm file. + * + * The vba_extract.py utility from the libxlsxwriter examples directory can be + * used to extract the vbaProject.bin file. + * + * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("macro.xlsm"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + /* Add a macro that will execute when the file is opened. */ + workbook_add_vba_project(workbook, "vbaProject.bin"); + + worksheet_write_string(worksheet, 0, 0, "Overwrite this", NULL); + + return workbook_close(workbook); +} diff --git a/libxlsxwriter/include/xlsxwriter.h b/libxlsxwriter/include/xlsxwriter.h index 95f29a5..6072896 100644 --- a/libxlsxwriter/include/xlsxwriter.h +++ b/libxlsxwriter/include/xlsxwriter.h @@ -18,6 +18,6 @@ #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" -#define LXW_VERSION "0.8.6" +#define LXW_VERSION "0.8.7" #endif /* __LXW_XLSXWRITER_H__ */ diff --git a/libxlsxwriter/include/xlsxwriter/common.h b/libxlsxwriter/include/xlsxwriter/common.h index 9524683..c88287b 100644 --- a/libxlsxwriter/include/xlsxwriter/common.h +++ b/libxlsxwriter/include/xlsxwriter/common.h @@ -68,13 +68,22 @@ typedef enum lxw_error { /** Error reading a tmpfile. */ LXW_ERROR_READING_TMPFILE, - /** Zlib error with a file operation while creating xlsx file. */ + /** Zip generic error ZIP_ERRNO while creating the xlsx file. */ LXW_ERROR_ZIP_FILE_OPERATION, - /** Zlib error when adding sub file to xlsx file. */ + /** Zip error ZIP_PARAMERROR while creating the xlsx file. */ + LXW_ERROR_ZIP_PARAMETER_ERROR, + + /** Zip error ZIP_BADZIPFILE (use_zip64 option may be required). */ + LXW_ERROR_ZIP_BAD_ZIP_FILE, + + /** Zip error ZIP_INTERNALERROR while creating the xlsx file. */ + LXW_ERROR_ZIP_INTERNAL_ERROR, + + /** File error or unknown zip error when adding sub file to xlsx file. */ LXW_ERROR_ZIP_FILE_ADD, - /** Zlib error when closing xlsx file. */ + /** Unknown zip error when closing xlsx file. */ LXW_ERROR_ZIP_CLOSE, /** NULL function parameter ignored. */ @@ -86,12 +95,18 @@ typedef enum lxw_error { /** Worksheet name exceeds Excel's limit of 31 characters. */ LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED, - /** Worksheet name contains invalid Excel character: '[]:*?/\\' */ + /** Worksheet name cannot contain invalid characters: '[ ] : * ? / \\' */ LXW_ERROR_INVALID_SHEETNAME_CHARACTER, + /** Worksheet name cannot start or end with an apostrophe. */ + LXW_ERROR_SHEETNAME_START_END_APOSTROPHE, + /** Worksheet name is already in use. */ LXW_ERROR_SHEETNAME_ALREADY_USED, + /** Worksheet name 'History' is reserved by Excel. */ + LXW_ERROR_SHEETNAME_RESERVED, + /** Parameter exceeds Excel's limit of 32 characters. */ LXW_ERROR_32_STRING_LENGTH_EXCEEDED, diff --git a/libxlsxwriter/include/xlsxwriter/content_types.h b/libxlsxwriter/include/xlsxwriter/content_types.h index b99c0e1..5881c7e 100644 --- a/libxlsxwriter/include/xlsxwriter/content_types.h +++ b/libxlsxwriter/include/xlsxwriter/content_types.h @@ -17,6 +17,7 @@ #define LXW_APP_PACKAGE "application/vnd.openxmlformats-package." #define LXW_APP_DOCUMENT "application/vnd.openxmlformats-officedocument." +#define LXW_APP_MSEXCEL "application/vnd.ms-excel." /* * Struct to represent a content_types. diff --git a/libxlsxwriter/include/xlsxwriter/packager.h b/libxlsxwriter/include/xlsxwriter/packager.h index 17004ef..a563570 100644 --- a/libxlsxwriter/include/xlsxwriter/packager.h +++ b/libxlsxwriter/include/xlsxwriter/packager.h @@ -32,11 +32,17 @@ #define LXW_ZIP_BUFFER_SIZE (16384) -/* If zlib returns Z_ERRNO then errno is set and we can trap that. Otherwise - * return a default libxlsxwriter error. */ +/* If zip returns a ZIP_XXX error then errno is set and we can trap that in + * workbook.c. Otherwise return a default libxlsxwriter error. */ #define RETURN_ON_ZIP_ERROR(err, default_err) \ - if (err == Z_ERRNO) \ + if (err == ZIP_ERRNO) \ return LXW_ERROR_ZIP_FILE_OPERATION; \ + else if (err == ZIP_PARAMERROR) \ + return LXW_ERROR_ZIP_PARAMETER_ERROR; \ + else if (err == ZIP_BADZIPFILE) \ + return LXW_ERROR_ZIP_BAD_ZIP_FILE; \ + else if (err == ZIP_INTERNALERROR) \ + return LXW_ERROR_ZIP_INTERNAL_ERROR; \ else \ return default_err; @@ -54,6 +60,7 @@ typedef struct lxw_packager { char *filename; char *buffer; char *tmpdir; + uint8_t use_zip64; } lxw_packager; @@ -64,7 +71,8 @@ extern "C" { #endif /* *INDENT-ON* */ -lxw_packager *lxw_packager_new(const char *filename, char *tmpdir); +lxw_packager *lxw_packager_new(const char *filename, char *tmpdir, + uint8_t use_zip64); void lxw_packager_free(lxw_packager *packager); lxw_error lxw_create_package(lxw_packager *self); diff --git a/libxlsxwriter/include/xlsxwriter/utility.h b/libxlsxwriter/include/xlsxwriter/utility.h index a24ac6d..5b4b365 100644 --- a/libxlsxwriter/include/xlsxwriter/utility.h +++ b/libxlsxwriter/include/xlsxwriter/utility.h @@ -17,6 +17,7 @@ #define __LXW_UTILITY_H__ #include +#include #include "common.h" #include "xmlwriter.h" @@ -166,6 +167,13 @@ size_t lxw_utf8_strlen(const char *str); void lxw_str_tolower(char *str); +/* Define a portable version of strcasecmp(). */ +#ifdef _MSC_VER +#define lxw_strcasecmp _stricmp +#else +#define lxw_strcasecmp strcasecmp +#endif + FILE *lxw_tmpfile(char *tmpdir); /* Use a user defined function to format doubles in sprintf or else a simple diff --git a/libxlsxwriter/include/xlsxwriter/workbook.h b/libxlsxwriter/include/xlsxwriter/workbook.h index d91176b..b72edcc 100644 --- a/libxlsxwriter/include/xlsxwriter/workbook.h +++ b/libxlsxwriter/include/xlsxwriter/workbook.h @@ -203,27 +203,40 @@ typedef struct lxw_doc_properties { * * The following properties are supported: * - * - `constant_memory`: Reduces the amount of data stored in memory so that - * large files can be written efficiently. + * - `constant_memory`: This option reduces the amount of data stored in + * memory so that large files can be written efficiently. This option is off + * by default. See the note below for limitations when this mode is on. * - * @note In this mode a row of data is written and then discarded when a - * cell in a new row is added via one of the `worksheet_write_*()` - * functions. Therefore, once this option is active, data should be written in - * sequential row order. For this reason the `worksheet_merge_range()` - * doesn't work in this mode. See also @ref ww_mem_constant. - * - * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior - * to assembling the final XLSX file. The temporary files are created in the + * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior to + * assembling the final XLSX file. The temporary files are created in the * system's temp directory. If the default temporary directory isn't * accessible to your application, or doesn't contain enough space, you can * specify an alternative location using the `tmpdir` option. + * + * - `use_zip64`: Make the zip library use ZIP64 extensions when writing very + * large xlsx files to allow the zip container, or individual XML files + * within it, to be greater than 4 GB. See [ZIP64 on Wikipedia][zip64_wiki] + * for more information. This option is off by default. + * + * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 + * + * @note In `constant_memory` mode a row of data is written and then discarded + * when a cell in a new row is added via one of the `worksheet_write_*()` + * functions. Therefore, once this option is active, data should be written in + * sequential row order. For this reason the `worksheet_merge_range()` doesn't + * work in this mode. See also @ref ww_mem_constant. + * */ typedef struct lxw_workbook_options { - /** Optimize the workbook to use constant memory for worksheets */ + /** Optimize the workbook to use constant memory for worksheets. */ uint8_t constant_memory; /** Directory to use for the temporary files created by libxlsxwriter. */ char *tmpdir; + + /** Allow ZIP64 extensions when creating the xlsx file zip container. */ + uint8_t use_zip64; + } lxw_workbook_options; /** @@ -272,6 +285,9 @@ typedef struct lxw_workbook { lxw_hash_table *used_xf_formats; + char *vba_project; + char *vba_codename; + } lxw_workbook; @@ -313,30 +329,37 @@ lxw_workbook *workbook_new(const char *filename); * additional options to be set. * * @code - * lxw_workbook_options options = {.constant_memory = 1, - * .tmpdir = "C:\\Temp"}; + * lxw_workbook_options options = {.constant_memory = LXW_TRUE, + * .tmpdir = "C:\\Temp", + * .use_zip64 = LXW_FALSE}; * * lxw_workbook *workbook = workbook_new_opt("filename.xlsx", &options); * @endcode * * The options that can be set via #lxw_workbook_options are: * - * - `constant_memory`: Reduces the amount of data stored in memory so that - * large files can be written efficiently. - * - * @note In this mode a row of data is written and then discarded when a - * cell in a new row is added via one of the `worksheet_write_*()` - * functions. Therefore, once this option is active, data should be written in - * sequential row order. For this reason the `worksheet_merge_range()` - * doesn't work in this mode. See also @ref ww_mem_constant. + * - `constant_memory`: This option reduces the amount of data stored in + * memory so that large files can be written efficiently. This option is off + * by default. See the note below for limitations when this mode is on. * - * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior - * to assembling the final XLSX file. The temporary files are created in the + * - `tmpdir`: libxlsxwriter stores workbook data in temporary files prior to + * assembling the final XLSX file. The temporary files are created in the * system's temp directory. If the default temporary directory isn't * accessible to your application, or doesn't contain enough space, you can - * specify an alternative location using the `tmpdir` option.* + * specify an alternative location using the `tmpdir` option. + * + * - `use_zip64`: Make the zip library use ZIP64 extensions when writing very + * large xlsx files to allow the zip container, or individual XML files + * within it, to be greater than 4 GB. See [ZIP64 on Wikipedia][zip64_wiki] + * for more information. This option is off by default. + * + * [zip64_wiki]: https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64 * - * See @ref working_with_memory for more details. + * @note In `constant_memory` mode a row of data is written and then discarded + * when a cell in a new row is added via one of the `worksheet_write_*()` + * functions. Therefore, once this option is active, data should be written in + * sequential row order. For this reason the `worksheet_merge_range()` doesn't + * work in this mode. See also @ref ww_mem_constant. * */ lxw_workbook *workbook_new_opt(const char *filename, @@ -376,14 +399,17 @@ lxw_workbook *new_workbook_opt(const char *filename, * * @image html workbook02.png * - * The worksheet name must be a valid Excel worksheet name, i.e. it must be - * less than 32 character and it cannot contain any of the characters: + * The worksheet name must be a valid Excel worksheet name, i.e: * - * / \ [ ] : * ? - * - * In addition, you cannot use the same, case insensitive, `sheetname` for more - * than one worksheet, or chartsheet. + * - The name is less than or equal to 31 UTF-8 characters. + * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` + * - The name doesn't start or end with an apostrophe. + * - The name isn't "History", which is reserved by Excel. (Case insensitive). + * - The name isn't already in use. (Case insensitive). * + * If any of these errors are encountered the function will return NULL. + * You can check for valid name using the `workbook_validate_sheet_name()` + * function. */ lxw_worksheet *workbook_add_worksheet(lxw_workbook *workbook, const char *sheetname); @@ -412,13 +438,17 @@ lxw_worksheet *workbook_add_worksheet(lxw_workbook *workbook, * * @endcode * - * The chartsheet name must be a valid Excel worksheet name, i.e. it must be - * less than 32 character and it cannot contain any of the characters: + * The chartsheet name must be a valid Excel worksheet name, i.e.: * - * / \ [ ] : * ? + * - The name is less than or equal to 31 UTF-8 characters. + * - The name doesn't contain any of the characters: ` [ ] : * ? / \ ` + * - The name doesn't start or end with an apostrophe. + * - The name isn't "History", which is reserved by Excel. (Case insensitive). + * - The name isn't already in use. (Case insensitive). * - * In addition, you cannot use the same, case insensitive, `sheetname` for more - * than one chartsheet, or worksheet. + * If any of these errors are encountered the function will return NULL. + * You can check for valid name using the `workbook_validate_sheet_name()` + * function. * * At least one worksheet should be added to a new workbook when creating a * chartsheet in order to provide data for the chart. The @ref worksheet.h @@ -797,7 +827,9 @@ lxw_chartsheet *workbook_get_chartsheet_by_name(lxw_workbook *workbook, * * - 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. + * - The name doesn't start or end with an apostrophe. + * - The name isn't "History", which is reserved by Excel. (Case insensitive). + * - The name isn't already in use. (Case insensitive, see the note below). * * @code * lxw_error err = workbook_validate_sheet_name(workbook, "Foglio"); @@ -807,10 +839,73 @@ lxw_chartsheet *workbook_get_chartsheet_by_name(lxw_workbook *workbook, * `workbook_add_chartsheet()` but it can be explicitly called by the user * beforehand to ensure that the sheet name is valid. * + * @note This function does an ASCII lowercase string comparison to determine + * if the sheet name is already in use. It doesn't take UTF-8 characters into + * account. Thus it would flag "Café" and "café" as a duplicate (just like + * Excel) but it wouldn't catch "CAFÉ". If you need a full UTF-8 case + * insensitive check you should use a third party library to implement it. + * */ lxw_error workbook_validate_sheet_name(lxw_workbook *workbook, const char *sheetname); +/** + * @brief Add a vbaProject binary to the Excel workbook. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param filename The path/filename of the vbaProject.bin file. + * + * The `%workbook_add_vba_project()` function can be used to add macros or + * functions to a workbook using a binary VBA project file that has been + * extracted from an existing Excel xlsm file: + * + * @code + * workbook_add_vba_project(workbook, "vbaProject.bin"); + * @endcode + * + * Only one `vbaProject.bin file` can be added per workbook. The name doesn't + * have to be `vbaProject.bin`. Any suitable path/name for an existing VBA bin + * file will do. + * + * Once you add a VBA project had been add to an libxlsxwriter workbook you + * should ensure that the file extension is `.xlsm` to prevent Excel from + * giving a warning when it opens the file: + * + * @code + * lxw_workbook *workbook = new_workbook("macro.xlsm"); + * @endcode + * + * See also @ref working_with_macros + * + * @return A #lxw_error. + */ +lxw_error workbook_add_vba_project(lxw_workbook *workbook, + const char *filename); + +/** + * @brief Set the VBA name for the workbook. + * + * @param workbook Pointer to a lxw_workbook instance. + * @param name Name of the workbook used by VBA. + * + * The `workbook_set_vba_name()` function can be used to set the VBA name for + * the workbook. This is sometimes required when a vbaProject macro included + * via `workbook_add_vba_project()` refers to the workbook by a name other + * than `ThisWorkbook`. + * + * @code + * workbook_set_vba_name(workbook, "MyWorkbook"); + * @endcode + * + * If an Excel VBA name for the workbook isn't specified then libxlsxwriter + * will use `ThisWorkbook`. + * + * See also @ref working_with_macros + * + * @return A #lxw_error. + */ +lxw_error workbook_set_vba_name(lxw_workbook *workbook, const char *name); + void lxw_workbook_free(lxw_workbook *workbook); void lxw_workbook_assemble_xml_file(lxw_workbook *workbook); void lxw_workbook_set_default_xf_indices(lxw_workbook *workbook); diff --git a/libxlsxwriter/include/xlsxwriter/worksheet.h b/libxlsxwriter/include/xlsxwriter/worksheet.h index 305b29c..5f2c6e2 100644 --- a/libxlsxwriter/include/xlsxwriter/worksheet.h +++ b/libxlsxwriter/include/xlsxwriter/worksheet.h @@ -741,10 +741,10 @@ typedef struct lxw_worksheet { uint8_t right_to_left; uint8_t screen_gridlines; uint8_t show_zeros; - uint8_t vba_codename; uint8_t vcenter; uint8_t zoom_scale_normal; uint8_t num_validations; + char *vba_codename; lxw_color_t tab_color; @@ -1592,7 +1592,7 @@ lxw_error worksheet_set_row_opt(lxw_worksheet *worksheet, * format_set_bold(bold); * * // Set the first column to bold. - * worksheet_set_column(worksheet, 0, 0, LXW_DEF_COL_HEIGHT, bold); + * worksheet_set_column(worksheet, 0, 0, LXW_DEF_COL_WIDTH, bold); * @endcode * * The `format` parameter will be applied to any cells in the column that @@ -3235,17 +3235,43 @@ void worksheet_outline_settings(lxw_worksheet *worksheet, uint8_t visible, void worksheet_set_default_row(lxw_worksheet *worksheet, double height, uint8_t hide_unused_rows); +/** + * @brief Set the VBA name for the worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance. + * @param name Name of the worksheet used by VBA. + * + * The `worksheet_set_vba_name()` function can be used to set the VBA name for + * the worksheet. This is sometimes required when a vbaProject macro included + * via `workbook_add_vba_project()` refers to the worksheet by a name other + * than the worksheet name: + * + * @code + * workbook_set_vba_name (workbook, "MyWorkbook"); + * worksheet_set_vba_name(worksheet, "MySheet1"); + * @endcode + * + * In general Excel uses the worksheet name such as "Sheet1" as the VBA name. + * However, this can be changed in the VBA environment or if the the macro was + * extracted from a foreign language version of Excel. + * + * See also @ref working_with_macros + * + * @return A #lxw_error. + */ +lxw_error worksheet_set_vba_name(lxw_worksheet *worksheet, const char *name); + lxw_worksheet *lxw_worksheet_new(lxw_worksheet_init_data *init_data); void lxw_worksheet_free(lxw_worksheet *worksheet); void lxw_worksheet_assemble_xml_file(lxw_worksheet *worksheet); void lxw_worksheet_write_single_row(lxw_worksheet *worksheet); void lxw_worksheet_prepare_image(lxw_worksheet *worksheet, - uint16_t image_ref_id, uint16_t drawing_id, + uint32_t image_ref_id, uint32_t drawing_id, lxw_image_options *image_data); void lxw_worksheet_prepare_chart(lxw_worksheet *worksheet, - uint16_t chart_ref_id, uint16_t drawing_id, + uint32_t chart_ref_id, uint32_t drawing_id, lxw_image_options *image_data, uint8_t is_chartsheet); diff --git a/libxlsxwriter/src/content_types.c b/libxlsxwriter/src/content_types.c index b5a920b..fcbdf52 100644 --- a/libxlsxwriter/src/content_types.c +++ b/libxlsxwriter/src/content_types.c @@ -50,8 +50,6 @@ lxw_content_types_new(void) LXW_APP_DOCUMENT "spreadsheetml.styles+xml"); lxw_ct_add_override(content_types, "/xl/theme/theme1.xml", LXW_APP_DOCUMENT "theme+xml"); - lxw_ct_add_override(content_types, "/xl/workbook.xml", - LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); return content_types; diff --git a/libxlsxwriter/src/drawing.c b/libxlsxwriter/src/drawing.c index 8b94c34..555a394 100644 --- a/libxlsxwriter/src/drawing.c +++ b/libxlsxwriter/src/drawing.c @@ -225,7 +225,7 @@ _drawing_write_to(lxw_drawing *self, lxw_drawing_coords *coords) * Write the element. */ STATIC void -_drawing_write_c_nv_pr(lxw_drawing *self, char *object_name, uint16_t index, +_drawing_write_c_nv_pr(lxw_drawing *self, char *object_name, uint32_t index, lxw_drawing_object *drawing_object) { struct xml_attribute_list attributes; @@ -282,7 +282,7 @@ _drawing_write_c_nv_pic_pr(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_nv_pic_pr(lxw_drawing *self, uint16_t index, +_drawing_write_nv_pic_pr(lxw_drawing *self, uint32_t index, lxw_drawing_object *drawing_object) { lxw_xml_start_tag(self->file, "xdr:nvPicPr", NULL); @@ -300,7 +300,7 @@ _drawing_write_nv_pic_pr(lxw_drawing *self, uint16_t index, * Write the element. */ STATIC void -_drawing_write_a_blip(lxw_drawing *self, uint16_t index) +_drawing_write_a_blip(lxw_drawing *self, uint32_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -345,7 +345,7 @@ _drawing_write_a_stretch(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_blip_fill(lxw_drawing *self, uint16_t index) +_drawing_write_blip_fill(lxw_drawing *self, uint32_t index) { lxw_xml_start_tag(self->file, "xdr:blipFill", NULL); @@ -463,7 +463,7 @@ _drawing_write_sp_pr(lxw_drawing *self, lxw_drawing_object *drawing_object) * Write the element. */ STATIC void -_drawing_write_pic(lxw_drawing *self, uint16_t index, +_drawing_write_pic(lxw_drawing *self, uint32_t index, lxw_drawing_object *drawing_object) { lxw_xml_start_tag(self->file, "xdr:pic", NULL); @@ -529,7 +529,7 @@ _drawing_write_c_nv_graphic_frame_pr(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint16_t index) +_drawing_write_nv_graphic_frame_pr(lxw_drawing *self, uint32_t index) { lxw_xml_start_tag(self->file, "xdr:nvGraphicFramePr", NULL); @@ -599,7 +599,7 @@ _drawing_write_xfrm(lxw_drawing *self) * Write the element. */ STATIC void -_drawing_write_chart(lxw_drawing *self, uint16_t index) +_drawing_write_chart(lxw_drawing *self, uint32_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -623,7 +623,7 @@ _drawing_write_chart(lxw_drawing *self, uint16_t index) * Write the element. */ STATIC void -_drawing_write_a_graphic_data(lxw_drawing *self, uint16_t index) +_drawing_write_a_graphic_data(lxw_drawing *self, uint32_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -646,7 +646,7 @@ _drawing_write_a_graphic_data(lxw_drawing *self, uint16_t index) * Write the element. */ STATIC void -_drawing_write_a_graphic(lxw_drawing *self, uint16_t index) +_drawing_write_a_graphic(lxw_drawing *self, uint32_t index) { lxw_xml_start_tag(self->file, "a:graphic", NULL); @@ -661,7 +661,7 @@ _drawing_write_a_graphic(lxw_drawing *self, uint16_t index) * Write the element. */ STATIC void -_drawing_write_graphic_frame(lxw_drawing *self, uint16_t index) +_drawing_write_graphic_frame(lxw_drawing *self, uint32_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -689,7 +689,7 @@ _drawing_write_graphic_frame(lxw_drawing *self, uint16_t index) * Write the element. */ STATIC void -_drawing_write_two_cell_anchor(lxw_drawing *self, uint16_t index, +_drawing_write_two_cell_anchor(lxw_drawing *self, uint32_t index, lxw_drawing_object *drawing_object) { struct xml_attribute_list attributes; @@ -811,7 +811,7 @@ _drawing_write_absolute_anchor(lxw_drawing *self) void lxw_drawing_assemble_xml_file(lxw_drawing *self) { - uint16_t index; + uint32_t index; lxw_drawing_object *drawing_object; /* Write the XML declaration. */ diff --git a/libxlsxwriter/src/packager.c b/libxlsxwriter/src/packager.c index 8637e9d..841f978 100644 --- a/libxlsxwriter/src/packager.c +++ b/libxlsxwriter/src/packager.c @@ -76,7 +76,7 @@ _open_zipfile_win32(const char *filename) * Create a new packager object. */ lxw_packager * -lxw_packager_new(const char *filename, char *tmpdir) +lxw_packager_new(const char *filename, char *tmpdir, uint8_t use_zip64) { lxw_packager *packager = calloc(1, sizeof(lxw_packager)); GOTO_LABEL_ON_MEM_ERROR(packager, mem_error); @@ -89,6 +89,7 @@ lxw_packager_new(const char *filename, char *tmpdir) GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error); packager->buffer_size = LXW_ZIP_BUFFER_SIZE; + packager->use_zip64 = use_zip64; /* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */ packager->zipfile_info.tmz_date.tm_sec = 0; @@ -170,7 +171,7 @@ _write_worksheet_files(lxw_packager *self) lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -210,7 +211,7 @@ _write_chartsheet_files(lxw_packager *self) lxw_sheet *sheet; lxw_chartsheet *chartsheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -251,7 +252,7 @@ _write_image_files(lxw_packager *self) FILE *image_stream; char filename[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) @@ -293,6 +294,35 @@ _write_image_files(lxw_packager *self) return LXW_NO_ERROR; } +/* + * Write the xl/vbaProject.bin file. + */ +STATIC lxw_error +_add_vba_project(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_error err; + FILE *image_stream; + + if (!workbook->vba_project) + return LXW_NO_ERROR; + + /* Check that the image file exists and can be opened. */ + image_stream = fopen(workbook->vba_project, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("Error adding vbaProject.bin to xlsx file: " + "file doesn't exist or can't be opened: %s.", + workbook->vba_project); + return LXW_ERROR_CREATING_TMPFILE; + } + + err = _add_file_to_zip(self, image_stream, "xl/vbaProject.bin"); + fclose(image_stream); + RETURN_ON_ERROR(err); + + return LXW_NO_ERROR; +} + /* * Write the chart files. */ @@ -302,7 +332,7 @@ _write_chart_files(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_chart *chart; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; lxw_error err; STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) { @@ -328,12 +358,12 @@ _write_chart_files(lxw_packager *self) /* * Count the chart files. */ -uint16_t +uint32_t _get_chart_count(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_chart *chart; - uint16_t chart_count = 0; + uint32_t chart_count = 0; STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) { chart_count++; @@ -353,7 +383,7 @@ _write_drawing_files(lxw_packager *self) lxw_worksheet *worksheet; lxw_drawing *drawing; char filename[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -386,14 +416,14 @@ _write_drawing_files(lxw_packager *self) /* * Count the drawing files. */ -uint16_t +uint32_t _get_drawing_count(lxw_packager *self) { lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; lxw_worksheet *worksheet; lxw_drawing *drawing; - uint16_t drawing_count = 0; + uint32_t drawing_count = 0; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) @@ -449,7 +479,7 @@ _write_app_file(lxw_packager *self) lxw_chartsheet *chartsheet; lxw_defined_name *defined_name; lxw_app *app; - uint16_t named_range_count = 0; + uint32_t named_range_count = 0; char *autofilter; char *has_range; char number[LXW_ATTR_32] = { 0 }; @@ -694,11 +724,11 @@ _write_content_types_file(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 }; - uint16_t index = 1; - uint16_t worksheet_index = 1; - uint16_t chartsheet_index = 1; - uint16_t drawing_count = _get_drawing_count(self); - uint16_t chart_count = _get_chart_count(self); + uint32_t index = 1; + uint32_t worksheet_index = 1; + uint32_t chartsheet_index = 1; + uint32_t drawing_count = _get_drawing_count(self); + uint32_t chart_count = _get_chart_count(self); lxw_error err = LXW_NO_ERROR; if (!content_types) { @@ -721,6 +751,17 @@ _write_content_types_file(lxw_packager *self) if (workbook->has_bmp) lxw_ct_add_default(content_types, "bmp", "image/bmp"); + if (workbook->vba_project) + lxw_ct_add_default(content_types, "bin", + "application/vnd.ms-office.vbaProject"); + + if (workbook->vba_project) + lxw_ct_add_override(content_types, "/xl/workbook.xml", + LXW_APP_MSEXCEL "sheet.macroEnabled.main+xml"); + else + lxw_ct_add_override(content_types, "/xl/workbook.xml", + LXW_APP_DOCUMENT "spreadsheetml.sheet.main+xml"); + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { if (sheet->is_chartsheet) { lxw_snprintf(filename, LXW_FILENAME_LENGTH, @@ -774,8 +815,8 @@ _write_workbook_rels_file(lxw_packager *self) lxw_workbook *workbook = self->workbook; lxw_sheet *sheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t worksheet_index = 1; - uint16_t chartsheet_index = 1; + uint32_t worksheet_index = 1; + uint32_t chartsheet_index = 1; lxw_error err = LXW_NO_ERROR; if (!rels) { @@ -811,6 +852,10 @@ _write_workbook_rels_file(lxw_packager *self) lxw_add_document_relationship(rels, "/sharedStrings", "sharedStrings.xml"); + if (workbook->vba_project) + lxw_add_ms_package_relationship(rels, "/vbaProject", + "vbaProject.bin"); + lxw_relationships_assemble_xml_file(rels); err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels"); @@ -836,7 +881,7 @@ _write_worksheet_rels_file(lxw_packager *self) lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 0; + uint32_t index = 0; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -898,7 +943,7 @@ _write_chartsheet_rels_file(lxw_packager *self) lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 0; + uint32_t index = 0; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -960,7 +1005,7 @@ _write_drawing_rels_file(lxw_packager *self) lxw_sheet *sheet; lxw_worksheet *worksheet; char sheetname[LXW_FILENAME_LENGTH] = { 0 }; - uint16_t index = 1; + uint32_t index = 1; lxw_error err; STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { @@ -1066,7 +1111,8 @@ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename) NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0); + Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, + self->use_zip64); if (error != ZIP_OK) { LXW_ERROR("Error adding member to zipfile"); @@ -1119,7 +1165,8 @@ _add_buffer_to_zip(lxw_packager *self, unsigned char *buffer, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0); + Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, + self->use_zip64); if (error != ZIP_OK) { LXW_ERROR("Error adding member to zipfile"); @@ -1200,6 +1247,9 @@ lxw_create_package(lxw_packager *self) error = _write_image_files(self); RETURN_ON_ERROR(error); + error = _add_vba_project(self); + RETURN_ON_ERROR(error); + error = _write_core_file(self); RETURN_ON_ERROR(error); diff --git a/libxlsxwriter/src/utility.c b/libxlsxwriter/src/utility.c index 75a2453..e16e481 100644 --- a/libxlsxwriter/src/utility.c +++ b/libxlsxwriter/src/utility.c @@ -21,14 +21,19 @@ char *error_strings[LXW_MAX_ERRNO + 1] = { "Error creating output xlsx file. Usually a permissions error.", "Error encountered when creating a tmpfile during file assembly.", "Error reading a tmpfile.", - "Zlib error with a file operation while creating xlsx file.", - "Zlib error when adding sub file to xlsx file.", - "Zlib error when closing xlsx file.", + "Zip generic error ZIP_ERRNO while creating the xlsx file.", + "Zip error ZIP_PARAMERROR while creating the xlsx file.", + "Zip error ZIP_BADZIPFILE (use_zip64 option may be required).", + "Zip error ZIP_INTERNALERROR while creating the xlsx file.", + "File error or unknown zip error when adding sub file to xlsx file.", + "Unknown zip error when closing xlsx file.", "NULL function parameter ignored.", "Function parameter validation error.", "Worksheet name exceeds Excel's limit of 31 characters.", - "Worksheet name contains invalid Excel character: '[]:*?/\\'", + "Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'", + "Worksheet name cannot start or end with an apostrophe.", "Worksheet name is already in use.", + "Worksheet name 'History' is reserved by Excel.", "Parameter exceeds Excel's limit of 32 characters.", "Parameter exceeds Excel's limit of 128 characters.", "Parameter exceeds Excel's limit of 255 characters.", diff --git a/libxlsxwriter/src/workbook.c b/libxlsxwriter/src/workbook.c index b5cfb16..0888ab5 100644 --- a/libxlsxwriter/src/workbook.c +++ b/libxlsxwriter/src/workbook.c @@ -40,13 +40,13 @@ LXW_RB_GENERATE_CHARTSHEET_NAMES(lxw_chartsheet_names, lxw_chartsheet_name, STATIC int _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 strcmp(name1->name, name2->name); + return lxw_strcasecmp(name1->name, name2->name); } /* @@ -208,6 +208,8 @@ lxw_workbook_free(lxw_workbook *workbook) lxw_sst_free(workbook->sst); free(workbook->options.tmpdir); free(workbook->ordered_charts); + free(workbook->vba_project); + free(workbook->vba_codename); free(workbook); } @@ -885,9 +887,9 @@ _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(sheet, self->sheets, list_pointers) { @@ -1135,6 +1137,10 @@ _write_file_version(lxw_workbook *self) 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(); @@ -1150,6 +1156,10 @@ _write_workbook_pr(lxw_workbook *self) 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); @@ -1470,6 +1480,7 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options) 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; @@ -1717,6 +1728,22 @@ workbook_close(lxw_workbook *self) 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); @@ -1727,13 +1754,15 @@ workbook_close(lxw_workbook *self) _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; @@ -1749,26 +1778,47 @@ workbook_close(lxw_workbook *self) 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: @@ -2114,6 +2164,14 @@ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname) 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; @@ -2124,3 +2182,48 @@ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname) 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; +} diff --git a/libxlsxwriter/src/worksheet.c b/libxlsxwriter/src/worksheet.c index 78a0092..a9438d7 100644 --- a/libxlsxwriter/src/worksheet.c +++ b/libxlsxwriter/src/worksheet.c @@ -448,6 +448,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet) free(worksheet->vbreaks); free(worksheet->name); free(worksheet->quoted_name); + free(worksheet->vba_codename); free(worksheet); worksheet = NULL; @@ -1507,7 +1508,8 @@ _worksheet_write_optimized_sheet_data(lxw_worksheet *self) while (read_size) { read_size = fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); - fwrite(buffer, 1, read_size, self->file); + /* Ignore return value. There is no easy way to raise error. */ + (void) fwrite(buffer, 1, read_size, self->file); } fclose(self->optimize_tmpfile); @@ -1992,7 +1994,7 @@ _worksheet_position_object_emus(lxw_worksheet *self, */ void lxw_worksheet_prepare_image(lxw_worksheet *self, - uint16_t image_ref_id, uint16_t drawing_id, + uint32_t image_ref_id, uint32_t drawing_id, lxw_image_options *image_data) { lxw_drawing_object *drawing_object; @@ -2079,8 +2081,8 @@ lxw_worksheet_prepare_image(lxw_worksheet *self, */ void lxw_worksheet_prepare_chart(lxw_worksheet *self, - uint16_t chart_ref_id, - uint16_t drawing_id, + uint32_t chart_ref_id, + uint32_t drawing_id, lxw_image_options *image_data, uint8_t is_chartsheet) { @@ -3147,7 +3149,7 @@ _worksheet_write_sheet_pr(lxw_worksheet *self) LXW_INIT_ATTRIBUTES(); if (self->vba_codename) - LXW_PUSH_ATTRIBUTES_INT("codeName", self->vba_codename); + LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename); if (self->filter_on) LXW_PUSH_ATTRIBUTES_STR("filterMode", "1"); @@ -5629,7 +5631,14 @@ worksheet_insert_image_buffer_opt(lxw_worksheet *self, /* Write the image buffer to a temporary file so we can read the * dimensions like an ordinary file. */ image_stream = lxw_tmpfile(self->tmpdir); - fwrite(image_buffer, 1, image_size, image_stream); + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; + + if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { + fclose(image_stream); + return LXW_ERROR_CREATING_TMPFILE; + } + rewind(image_stream); /* Create a new object to hold the image options. */ @@ -6066,3 +6075,19 @@ worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row, return worksheet_data_validation_range(self, row, col, row, col, validation); } + +/* + * Set the VBA name for the worksheet. + */ +lxw_error +worksheet_set_vba_name(lxw_worksheet *self, const char *name) +{ + if (!name) { + LXW_WARN("worksheet_set_vba_name(): " "name must be specified."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + self->vba_codename = lxw_strdup(name); + + return LXW_NO_ERROR; +}