libxlsxwriter/src/worksheet.c in fast_excel-0.2.6 vs libxlsxwriter/src/worksheet.c in fast_excel-0.3.0

- old
+ new

@@ -1,11 +1,11 @@ /***************************************************************************** * worksheet - A library for creating Excel XLSX worksheet 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 <ctype.h> @@ -15,12 +15,10 @@ #include "xlsxwriter/utility.h" #include "xlsxwriter/relationships.h" #define LXW_STR_MAX 32767 #define LXW_BUFFER_SIZE 4096 -#define LXW_PORTRAIT 1 -#define LXW_LANDSCAPE 0 #define LXW_PRINT_ACROSS 1 #define LXW_VALIDATION_MAX_TITLE_LENGTH 32 #define LXW_VALIDATION_MAX_STRING_LENGTH 255 /* @@ -269,14 +267,15 @@ { if (!image) return; free(image->filename); - free(image->short_name); + free(image->description); free(image->extension); free(image->url); free(image->tip); + free(image->image_buffer); free(image); } /* * Free a worksheet data_validation. @@ -447,10 +446,11 @@ free(worksheet->hbreaks); free(worksheet->vbreaks); free(worksheet->name); free(worksheet->quoted_name); + free(worksheet->vba_codename); free(worksheet); worksheet = NULL; } @@ -537,10 +537,30 @@ return cell; } /* + * Create a new worksheet inline_string cell object for rich strings. + */ +STATIC lxw_cell * +_new_inline_rich_string_cell(lxw_row_t row_num, + lxw_col_t col_num, char *string, + lxw_format *format) +{ + lxw_cell *cell = calloc(1, sizeof(lxw_cell)); + RETURN_ON_MEM_ERROR(cell, cell); + + cell->row_num = row_num; + cell->col_num = col_num; + cell->type = INLINE_RICH_STRING_CELL; + cell->format = format; + cell->u.string = string; + + return cell; +} + +/* * Create a new worksheet formula cell object. */ STATIC lxw_cell * _new_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula, lxw_format *format) @@ -840,42 +860,10 @@ return -1; return 0; } /* - * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz - * of OpenOffice. - */ -STATIC uint16_t -_hash_password(const char *password) -{ - size_t count; - uint8_t i; - uint16_t hash = 0x0000; - - count = strlen(password); - - for (i = 0; i < count; i++) { - uint32_t low_15; - uint32_t high_15; - uint32_t letter = password[i] << (i + 1); - - low_15 = letter & 0x7fff; - high_15 = letter & (0x7fff << 15); - high_15 = high_15 >> 15; - letter = low_15 | high_15; - - hash ^= letter; - } - - hash ^= count; - hash ^= 0xCE4B; - - return hash; -} - -/* * Simple replacement for libgen.h basename() for compatibility with MSVC. It * handles forward and back slashes. It doesn't copy exactly the return * format of basename(). */ char * @@ -1751,10 +1739,13 @@ col_opt = NULL; } } if (col_opt) { + if (col_opt->hidden) + return 0; + width = col_opt->width; /* Convert to pixels. */ if (width == 0) { pixels = 0; @@ -1786,10 +1777,13 @@ double height; row = lxw_worksheet_find_row(self, row_num); if (row) { + if (row->hidden) + return 0; + height = row->height; if (height == 0) pixels = 0; else @@ -1912,27 +1906,34 @@ } y_abs += y1; /* Adjust start col for offsets that are greater than the col width. */ - while (x1 >= _worksheet_size_col(self, col_start)) { - x1 -= _worksheet_size_col(self, col_start); - col_start++; + if (_worksheet_size_col(self, col_start) > 0) { + while (x1 >= _worksheet_size_col(self, col_start)) { + x1 -= _worksheet_size_col(self, col_start); + col_start++; + } } /* Adjust start row for offsets that are greater than the row height. */ - while (y1 >= _worksheet_size_row(self, row_start)) { - y1 -= _worksheet_size_row(self, row_start); - row_start++; + if (_worksheet_size_row(self, row_start) > 0) { + while (y1 >= _worksheet_size_row(self, row_start)) { + y1 -= _worksheet_size_row(self, row_start); + row_start++; + } } /* Initialize end cell to the same as the start cell. */ col_end = col_start; row_end = row_start; - width = width + x1; - height = height + y1; + /* Only offset the image in the cell if the row/col isn't hidden. */ + if (_worksheet_size_col(self, col_start) > 0) + width = width + x1; + if (_worksheet_size_row(self, row_start) > 0) + height = height + y1; /* Subtract the underlying cell widths to find the end cell. */ while (width >= _worksheet_size_col(self, col_end)) { width -= _worksheet_size_col(self, col_end); col_end++; @@ -1990,11 +1991,11 @@ /* * Set up image/drawings. */ 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; lxw_rel_tuple *relationship; double width; @@ -2025,11 +2026,11 @@ drawing_object = calloc(1, sizeof(lxw_drawing_object)); RETURN_VOID_ON_MEM_ERROR(drawing_object); drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE; drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL; - drawing_object->description = lxw_strdup(image_data->short_name); + drawing_object->description = lxw_strdup(image_data->description); /* Scale to user scale. */ width = image_data->width * image_data->x_scale; height = image_data->height * image_data->y_scale; @@ -2077,24 +2078,33 @@ /* * Set up chart/drawings. */ void lxw_worksheet_prepare_chart(lxw_worksheet *self, - uint16_t chart_ref_id, uint16_t drawing_id, - lxw_image_options *image_data) + uint32_t chart_ref_id, + uint32_t drawing_id, + lxw_image_options *image_data, + uint8_t is_chartsheet) { lxw_drawing_object *drawing_object; lxw_rel_tuple *relationship; double width; double height; char filename[LXW_FILENAME_LENGTH]; 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; + self->drawing->orientation = self->orientation; + } + else { + self->drawing->embedded = LXW_TRUE; + } + relationship = calloc(1, sizeof(lxw_rel_tuple)); GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); relationship->type = lxw_strdup("/drawing"); GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); @@ -2257,12 +2267,11 @@ return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " - "no size data found in file: %s.", - image_options->filename); + "no size data found in: %s.", image_options->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* @@ -2384,12 +2393,11 @@ return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " - "no size data found in file: %s.", - image_options->filename); + "no size data found in: %s.", image_options->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* @@ -2434,12 +2442,11 @@ return LXW_NO_ERROR; file_error: LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " - "no size data found in file: %s.", - image_options->filename); + "no size data found in: %s.", image_options->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } /* @@ -2452,11 +2459,11 @@ unsigned char signature[4]; /* Read 4 bytes to look for the file header/signature. */ if (fread(signature, 1, 4, image_options->stream) < 4) { LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " - "couldn't read file type for file: %s.", + "couldn't read image type for: %s.", image_options->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } if (memcmp(&signature[1], "PNG", 3) == 0) { @@ -2471,11 +2478,11 @@ if (_process_bmp(image_options) != LXW_NO_ERROR) return LXW_ERROR_IMAGE_DIMENSIONS; } else { LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " - "unsupported image format for file: %s.", + "unsupported image format for: %s.", image_options->filename); return LXW_ERROR_IMAGE_DIMENSIONS; } return LXW_NO_ERROR; @@ -2575,10 +2582,45 @@ free(string); } /* + * Write out an inline rich string. Doesn't use the xml functions as an + * optimization in the inner cell writing loop. + */ +STATIC void +_write_inline_rich_string_cell(lxw_worksheet *self, char *range, + int32_t style_index, lxw_cell *cell) +{ + char *string = cell->u.string; + + /* Add attribute to preserve leading or trailing whitespace. */ + if (isspace((unsigned char) string[0]) + || isspace((unsigned char) string[strlen(string) - 1])) { + + if (style_index) + fprintf(self->file, + "<c r=\"%s\" s=\"%d\" t=\"inlineStr\"><is>%s</is></c>", + range, style_index, string); + else + fprintf(self->file, + "<c r=\"%s\" t=\"inlineStr\"><is>%s</is></c>", + range, string); + } + else { + if (style_index) + fprintf(self->file, + "<c r=\"%s\" s=\"%d\" t=\"inlineStr\">" + "<is>%s</is></c>", range, style_index, string); + else + fprintf(self->file, + "<c r=\"%s\" t=\"inlineStr\">" + "<is>%s</is></c>", range, string); + } +} + +/* * Write out a formula worksheet cell with a numeric result. */ STATIC void _write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) { @@ -2710,10 +2752,15 @@ if (cell->type == INLINE_STRING_CELL) { _write_inline_string_cell(self, range, style_index, cell); return; } + if (cell->type == INLINE_RICH_STRING_CELL) { + _write_inline_rich_string_cell(self, range, style_index, cell); + return; + } + /* For other cell types use the general functions. */ LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("r", range); if (style_index) @@ -3091,18 +3138,19 @@ struct xml_attribute *attribute; if (!self->fit_page && !self->filter_on && self->tab_color == LXW_COLOR_UNSET - && !self->outline_changed && !self->vba_codename) { + && !self->outline_changed + && !self->vba_codename && !self->is_chartsheet) { return; } 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"); if (self->fit_page || self->tab_color != LXW_COLOR_UNSET @@ -3362,17 +3410,16 @@ /* * Write the <sheetProtection> element. */ STATIC void -_worksheet_write_sheet_protection(lxw_worksheet *self) +_worksheet_write_sheet_protection(lxw_worksheet *self, + lxw_protection *protect) { struct xml_attribute_list attributes; struct xml_attribute *attribute; - struct lxw_protection *protect = &self->protection; - if (!protect->is_configured) return; LXW_INIT_ATTRIBUTES(); @@ -3380,11 +3427,11 @@ LXW_PUSH_ATTRIBUTES_STR("password", protect->hash); if (!protect->no_sheet) LXW_PUSH_ATTRIBUTES_INT("sheet", 1); - if (protect->content) + if (!protect->no_content) LXW_PUSH_ATTRIBUTES_INT("content", 1); if (!protect->objects) LXW_PUSH_ATTRIBUTES_INT("objects", 1); @@ -3437,11 +3484,11 @@ /* * Write the <drawing> element. */ STATIC void -_write_drawing(lxw_worksheet *self, uint16_t id) +_worksheet_write_drawing(lxw_worksheet *self, uint16_t id) { struct xml_attribute_list attributes; struct xml_attribute *attribute; char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; @@ -3459,18 +3506,18 @@ /* * Write the <drawing> elements. */ STATIC void -_write_drawings(lxw_worksheet *self) +_worksheet_write_drawings(lxw_worksheet *self) { if (!self->drawing) return; self->rel_count++; - _write_drawing(self, self->rel_count); + _worksheet_write_drawing(self, self->rel_count); } /* * Write the <formula1> element for numbers. */ @@ -3688,10 +3735,56 @@ LXW_FREE_ATTRIBUTES(); } /* + * External functions to call intern XML methods shared with chartsheet. + */ +void +lxw_worksheet_write_sheet_views(lxw_worksheet *self) +{ + _worksheet_write_sheet_views(self); +} + +void +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); +} + +void +lxw_worksheet_write_sheet_protection(lxw_worksheet *self, + lxw_protection *protect) +{ + _worksheet_write_sheet_protection(self, protect); +} + +void +lxw_worksheet_write_sheet_pr(lxw_worksheet *self) +{ + _worksheet_write_sheet_pr(self); +} + +void +lxw_worksheet_write_page_setup(lxw_worksheet *self) +{ + _worksheet_write_page_setup(self); +} + +void +lxw_worksheet_write_header_footer(lxw_worksheet *self) +{ + _worksheet_write_header_footer(self); +} + +/* * Assemble and write the XML file. */ void lxw_worksheet_assemble_xml_file(lxw_worksheet *self) { @@ -3721,11 +3814,11 @@ _worksheet_write_sheet_data(self); else _worksheet_write_optimized_sheet_data(self); /* Write the sheetProtection element. */ - _worksheet_write_sheet_protection(self); + _worksheet_write_sheet_protection(self, &self->protection); /* Write the autoFilter element. */ _worksheet_write_auto_filter(self); /* Write the mergeCells element. */ @@ -3754,11 +3847,11 @@ /* Write the colBreaks element. */ _worksheet_write_col_breaks(self); /* Write the drawing element. */ - _write_drawings(self); + _worksheet_write_drawings(self); /* Close the worksheet tag. */ lxw_xml_end_tag(self->file, "worksheet"); } @@ -3821,11 +3914,11 @@ if (lxw_utf8_strlen(string) > LXW_STR_MAX) return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; if (!self->optimize) { /* Get the SST element and string id. */ - sst_element = lxw_get_sst_index(self->sst, string); + sst_element = lxw_get_sst_index(self->sst, string, LXW_FALSE); if (!sst_element) return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; string_id = sst_element->index; @@ -4297,10 +4390,160 @@ return worksheet_write_url_opt(self, row_num, col_num, url, format, NULL, NULL); } /* + * Write a rich string to an Excel file. + * + * Rather than duplicate several of the styles.c font xml methods of styles.c + * and write the data to a memory buffer this function creates a temporary + * styles object and uses it to write the data to a file. It then reads that + * data back into memory and closes the file. + */ +lxw_error +worksheet_write_rich_string(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, + lxw_rich_string_tuple *rich_strings[], + lxw_format *format) +{ + lxw_cell *cell; + int32_t string_id; + struct sst_element *sst_element; + lxw_error err; + uint8_t i; + long file_size; + char *rich_string = NULL; + char *string_copy = NULL; + lxw_styles *styles = NULL; + lxw_format *default_format = NULL; + lxw_rich_string_tuple *rich_string_tuple = NULL; + FILE *tmpfile; + + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Iterate through rich string fragments to check for input errors. */ + i = 0; + err = LXW_NO_ERROR; + while ((rich_string_tuple = rich_strings[i++]) != NULL) { + + /* Check for NULL or empty strings. */ + if (!rich_string_tuple->string || !*rich_string_tuple->string) { + err = LXW_ERROR_PARAMETER_VALIDATION; + } + } + + /* If there are less than 2 fragments it isn't a rich string. */ + if (i <= 2) + err = LXW_ERROR_PARAMETER_VALIDATION; + + if (err) + return err; + + /* Create a tmp file for the styles object. */ + tmpfile = lxw_tmpfile(self->tmpdir); + if (!tmpfile) + return LXW_ERROR_CREATING_TMPFILE; + + /* Create a temp styles object for writing the font data. */ + styles = lxw_styles_new(); + GOTO_LABEL_ON_MEM_ERROR(styles, mem_error); + styles->file = tmpfile; + + /* Create a default format for non-formatted text. */ + default_format = lxw_format_new(); + GOTO_LABEL_ON_MEM_ERROR(default_format, mem_error); + + /* Iterate through the rich string fragments and write each one out. */ + i = 0; + while ((rich_string_tuple = rich_strings[i++]) != NULL) { + lxw_xml_start_tag(tmpfile, "r", NULL); + + if (rich_string_tuple->format) { + /* Write the user defined font format. */ + lxw_styles_write_rich_font(styles, rich_string_tuple->format); + } + else { + /* Write a default font format. Except for the first fragment. */ + if (i > 1) + lxw_styles_write_rich_font(styles, default_format); + } + + lxw_styles_write_string_fragment(styles, rich_string_tuple->string); + lxw_xml_end_tag(tmpfile, "r"); + } + + /* Free the temp objects. */ + lxw_styles_free(styles); + lxw_format_free(default_format); + + /* Flush the file and read the size to calculate the required memory. */ + fflush(tmpfile); + file_size = ftell(tmpfile); + + /* Allocate a buffer for the rich string xml data. */ + rich_string = calloc(file_size + 1, 1); + GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error); + + /* Rewind the file and read the data into the memory buffer. */ + rewind(tmpfile); + if (fread(rich_string, file_size, 1, tmpfile) < 1) { + fclose(tmpfile); + free(rich_string); + return LXW_ERROR_READING_TMPFILE; + } + + /* Close the temp file. */ + fclose(tmpfile); + + if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) { + free(rich_string); + return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; + } + + if (!self->optimize) { + /* Get the SST element and string id. */ + sst_element = lxw_get_sst_index(self->sst, rich_string, LXW_TRUE); + free(rich_string); + + if (!sst_element) + return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; + + string_id = sst_element->index; + cell = _new_string_cell(row_num, col_num, string_id, + sst_element->string, format); + } + else { + /* Look for and escape control chars in the string. */ + if (strpbrk(rich_string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C" + "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16" + "\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) { + string_copy = lxw_escape_control_characters(rich_string); + free(rich_string); + } + else { + string_copy = rich_string; + } + cell = _new_inline_rich_string_cell(row_num, col_num, string_copy, + format); + } + + _insert_cell(self, row_num, col_num, cell); + + return LXW_NO_ERROR; + +mem_error: + lxw_styles_free(styles); + lxw_format_free(default_format); + fclose(tmpfile); + + return LXW_ERROR_MEMORY_MALLOC_FAILED; +} + +/* * Set the properties of a single column or a range of columns with options. */ lxw_error worksheet_set_column_opt(lxw_worksheet *self, lxw_col_t firstcol, @@ -5232,14 +5475,15 @@ protect->scenarios = options->scenarios; protect->objects = options->objects; } if (password) { - uint16_t hash = _hash_password(password); + uint16_t hash = lxw_hash_password(password); lxw_snprintf(protect->hash, 5, "%X", hash); } + protect->no_content = LXW_TRUE; protect->is_configured = LXW_TRUE; } /* * Set the worksheet properties for outlines and grouping. @@ -5287,11 +5531,11 @@ lxw_row_t row_num, lxw_col_t col_num, const char *filename, lxw_image_options *user_options) { FILE *image_stream; - char *short_name; + char *description; lxw_image_options *options; if (!filename) { LXW_WARN("worksheet_insert_image()/_opt(): " "filename must be specified."); @@ -5305,13 +5549,13 @@ "file doesn't exist or can't be opened: %s.", filename); return LXW_ERROR_PARAMETER_VALIDATION; } - /* Get the filename from the full path to add to the Drawing object. */ - short_name = lxw_basename(filename); - if (!short_name) { + /* Use the filename as the default description, like Excel. */ + description = lxw_basename(filename); + if (!description) { LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " "couldn't get basename for file: %s.", filename); fclose(image_stream); return LXW_ERROR_PARAMETER_VALIDATION; } @@ -5330,11 +5574,11 @@ options->y_scale = user_options->y_scale; } /* Copy other options or set defaults. */ options->filename = lxw_strdup(filename); - options->short_name = lxw_strdup(short_name); + options->description = lxw_strdup(description); options->stream = image_stream; options->row = row_num; options->col = col_num; if (!options->x_scale) @@ -5347,11 +5591,11 @@ STAILQ_INSERT_TAIL(self->image_data, options, list_pointers); fclose(image_stream); return LXW_NO_ERROR; } else { - free(options); + _free_image_options(options); fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } } @@ -5364,10 +5608,99 @@ const char *filename) { return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL); } +lxw_error +worksheet_insert_image_buffer_opt(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, + const unsigned char *image_buffer, + size_t image_size, + lxw_image_options *user_options) +{ + FILE *image_stream; + lxw_image_options *options; + + if (!image_size) { + LXW_WARN("worksheet_insert_image_buffer()/_opt(): " + "size must be non-zero."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Write the image buffer to a temporary file so we can read the + * dimensions like an ordinary file. */ + image_stream = lxw_tmpfile(self->tmpdir); + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; + + fwrite(image_buffer, 1, image_size, image_stream); + rewind(image_stream); + + /* Create a new object to hold the image options. */ + options = calloc(1, sizeof(lxw_image_options)); + if (!options) { + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + /* Store the image data in the options structure. */ + options->image_buffer = calloc(1, image_size); + if (!options->image_buffer) { + _free_image_options(options); + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + else { + memcpy(options->image_buffer, image_buffer, image_size); + options->image_buffer_size = image_size; + options->is_image_buffer = LXW_TRUE; + } + + 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; + options->description = lxw_strdup(user_options->description); + } + + /* Copy other options or set defaults. */ + options->filename = lxw_strdup("image_buffer"); + options->stream = image_stream; + options->row = row_num; + options->col = col_num; + + if (!options->x_scale) + options->x_scale = 1; + + if (!options->y_scale) + options->y_scale = 1; + + if (_get_image_properties(options) == LXW_NO_ERROR) { + STAILQ_INSERT_TAIL(self->image_data, options, list_pointers); + fclose(image_stream); + return LXW_NO_ERROR; + } + else { + _free_image_options(options); + fclose(image_stream); + return LXW_ERROR_IMAGE_DIMENSIONS; + } +} + +lxw_error +worksheet_insert_image_buffer(lxw_worksheet *self, + lxw_row_t row_num, + lxw_col_t col_num, + const unsigned char *image_buffer, + size_t image_size) +{ + return worksheet_insert_image_buffer_opt(self, row_num, col_num, + image_buffer, image_size, NULL); +} + /* * Insert an chart into the worksheet. */ lxw_error worksheet_insert_chart_opt(lxw_worksheet *self, @@ -5734,6 +6067,22 @@ worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, lxw_data_validation *validation) { 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; }