From 1dc5977e5fa012d2d694c5a3821d7f9724a58268 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Tue, 30 Jul 2024 23:30:33 +0100 Subject: [PATCH] worksheet: add embedded image options --- examples/embed_image_buffer.c | 48 +++++ include/xlsxwriter/workbook.h | 4 +- include/xlsxwriter/worksheet.h | 39 ++++ src/packager.c | 13 +- src/rich_value.c | 5 +- src/rich_value_structure.c | 32 +-- src/workbook.c | 5 +- src/worksheet.c | 200 +++++++++++++++--- test/functional/src/test_embed_image08.c | 22 ++ test/functional/src/test_embed_image09.c | 22 ++ test/functional/src/test_embed_image10.c | 22 ++ test/functional/src/test_embed_image12.c | 25 +++ test/functional/src/test_embed_image51.c | 44 ++++ test/functional/src/test_embed_image52.c | 46 ++++ test/functional/src/test_format12.c | 2 +- test/functional/src/test_landscape01.c | 2 +- test/functional/src/test_print_across01.c | 2 +- test/functional/src/test_simple03.c | 2 +- test/functional/test_embed_image.py | 23 +- test/functional/xlsx_files/embed_image08.xlsx | Bin 0 -> 8586 bytes test/functional/xlsx_files/embed_image09.xlsx | Bin 0 -> 8586 bytes test/functional/xlsx_files/embed_image10.xlsx | Bin 0 -> 9114 bytes test/functional/xlsx_files/embed_image12.xlsx | Bin 0 -> 8622 bytes 23 files changed, 493 insertions(+), 65 deletions(-) create mode 100644 examples/embed_image_buffer.c create mode 100644 test/functional/src/test_embed_image08.c create mode 100644 test/functional/src/test_embed_image09.c create mode 100644 test/functional/src/test_embed_image10.c create mode 100644 test/functional/src/test_embed_image12.c create mode 100644 test/functional/src/test_embed_image51.c create mode 100644 test/functional/src/test_embed_image52.c create mode 100644 test/functional/xlsx_files/embed_image08.xlsx create mode 100644 test/functional/xlsx_files/embed_image09.xlsx create mode 100644 test/functional/xlsx_files/embed_image10.xlsx create mode 100644 test/functional/xlsx_files/embed_image12.xlsx diff --git a/examples/embed_image_buffer.c b/examples/embed_image_buffer.c new file mode 100644 index 00000000..1fe5623f --- /dev/null +++ b/examples/embed_image_buffer.c @@ -0,0 +1,48 @@ +/* + * An example of embedding an image from a memory buffer into a worksheet + * using the libxlsxwriter library. + * + * Copyright 2014-2024 John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + + +/* Simple array with some PNG data. */ +unsigned char image_buffer[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xfc, 0x18, 0xed, 0xa3, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, + 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, + 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, + 0x00, 0x46, 0x49, 0x44, 0x41, 0x54, 0x48, 0x4b, 0x63, 0xfc, 0xcf, 0x40, + 0x63, 0x00, 0xb4, 0x80, 0xa6, 0x88, 0xb6, 0xa6, 0x83, 0x82, 0x87, 0xa6, + 0xce, 0x1f, 0xb5, 0x80, 0x98, 0xe0, 0x1d, 0x8d, 0x03, 0x82, 0xa1, 0x34, + 0x1a, 0x44, 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, + 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0xa3, 0x41, + 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0x03, 0x1f, 0x44, 0x00, + 0xaa, 0x35, 0xdd, 0x4e, 0xe6, 0xd5, 0xa1, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +unsigned int image_size = 200; + + +int main() { + + /* Create a new workbook and add a worksheet. */ + lxw_workbook *workbook = workbook_new("embed_image_buffer.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + /* Embed the image from the buffer. */ + worksheet_embed_image_buffer(worksheet, CELL("B3"), image_buffer, image_size); + + workbook_close(workbook); + + return 0; +} diff --git a/include/xlsxwriter/workbook.h b/include/xlsxwriter/workbook.h index f32ad0cf..e6350067 100644 --- a/include/xlsxwriter/workbook.h +++ b/include/xlsxwriter/workbook.h @@ -323,6 +323,7 @@ typedef struct lxw_workbook { uint16_t num_format_count; uint16_t drawing_count; uint16_t comment_count; + uint32_t num_embedded_images; uint16_t font_count; uint16_t border_count; @@ -340,8 +341,7 @@ typedef struct lxw_workbook { uint8_t has_metadata; uint8_t has_embedded_images; uint8_t has_dynamic_functions; - - uint32_t num_embedded_images; + uint8_t has_embedded_image_descriptions; lxw_hash_table *used_xf_formats; lxw_hash_table *used_dxf_formats; diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 9a28b397..793891b6 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -1738,6 +1738,9 @@ typedef struct lxw_image_options { /** Add an optional mouseover tip for a hyperlink to the image. */ const char *tip; + /** TODO */ + lxw_format *cell_format; + } lxw_image_options; /** @@ -1809,6 +1812,7 @@ typedef struct lxw_object_properties { char *md5; char *image_position; uint8_t decorative; + lxw_format *format; STAILQ_ENTRY (lxw_object_properties) list_pointers; } lxw_object_properties; @@ -2255,6 +2259,7 @@ typedef struct lxw_worksheet { uint8_t has_header_vml; uint8_t has_background_image; uint8_t has_buttons; + uint8_t storing_embedded_image; lxw_rel_tuple *external_vml_comment_link; lxw_rel_tuple *external_comment_link; lxw_rel_tuple *external_vml_header_link; @@ -3814,6 +3819,40 @@ lxw_error worksheet_embed_image_opt(lxw_worksheet *worksheet, const char *filename, lxw_image_options *options); +/** + * @brief TODO + * + * @param worksheet + * @param row + * @param col + * @param image_buffer + * @param image_size + * @return lxw_error + */ +lxw_error worksheet_embed_image_buffer(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, + const unsigned char *image_buffer, + size_t image_size); + +/** + * @brief TODO + * + * @param worksheet + * @param row + * @param col + * @param image_buffer + * @param image_size + * @param options + * @return lxw_error + */ +lxw_error worksheet_embed_image_buffer_opt(lxw_worksheet *worksheet, + lxw_row_t row, + lxw_col_t col, + const unsigned char *image_buffer, + size_t image_size, + lxw_image_options *options); + /** * @brief Set the background image for a worksheet. * diff --git a/src/packager.c b/src/packager.c index c455f653..bb0b6ce3 100644 --- a/src/packager.c +++ b/src/packager.c @@ -1120,13 +1120,13 @@ _write_rich_value_file(lxw_packager *self) return LXW_NO_ERROR; rich_value = lxw_rich_value_new(); - rich_value->workbook = self->workbook; - if (!rich_value) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } + rich_value->workbook = self->workbook; + rich_value->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rich_value->file) { @@ -1163,13 +1163,13 @@ _write_rich_value_rel_file(lxw_packager *self) return LXW_NO_ERROR; rich_value_rel = lxw_rich_value_rel_new(); - rich_value_rel->num_embedded_images = self->workbook->num_embedded_images; - if (!rich_value_rel) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } + rich_value_rel->num_embedded_images = self->workbook->num_embedded_images; + rich_value_rel->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rich_value_rel->file) { @@ -1206,7 +1206,6 @@ _write_rich_value_types_file(lxw_packager *self) return LXW_NO_ERROR; rich_value_types = lxw_rich_value_types_new(); - if (!rich_value_types) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; @@ -1248,12 +1247,14 @@ _write_rich_value_structure_file(lxw_packager *self) return LXW_NO_ERROR; rich_value_structure = lxw_rich_value_structure_new(); - if (!rich_value_structure) { err = LXW_ERROR_MEMORY_MALLOC_FAILED; goto mem_error; } + rich_value_structure->has_embedded_image_descriptions = + self->workbook->has_embedded_image_descriptions; + rich_value_structure->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); if (!rich_value_structure->file) { diff --git a/src/rich_value.c b/src/rich_value.c index f5476bad..b9da82fd 100644 --- a/src/rich_value.c +++ b/src/rich_value.c @@ -137,13 +137,14 @@ lxw_rich_value_write_images(lxw_rich_value *self) lxw_snprintf(value, LXW_UINT32_T_LENGTH, "%u", type); _rich_value_write_v(self, value); + if (object_props->description && *object_props->description) + _rich_value_write_v(self, object_props->description); + lxw_xml_end_tag(self->file, "rv"); index++; - } } - } /* diff --git a/src/rich_value_structure.c b/src/rich_value_structure.c index 43bd7b58..fc614a50 100644 --- a/src/rich_value_structure.c +++ b/src/rich_value_structure.c @@ -75,32 +75,15 @@ _rich_value_structure_xml_declaration(lxw_rich_value_structure *self) * Write the element. */ STATIC void -_rich_value_structure_write_k2(lxw_rich_value_structure *self) +_rich_value_structure_write_k(lxw_rich_value_structure *self, char *name, + char *type) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("n", "CalcOrigin"); - LXW_PUSH_ATTRIBUTES_STR("t", "i"); - - lxw_xml_empty_tag(self->file, "k", &attributes); - - LXW_FREE_ATTRIBUTES(); -} - -/* - * Write the element. - */ -STATIC void -_rich_value_structure_write_k1(lxw_rich_value_structure *self) -{ - struct xml_attribute_list attributes; - struct xml_attribute *attribute; - - LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("n", "_rvRel:LocalImageIdentifier"); - LXW_PUSH_ATTRIBUTES_STR("t", "i"); + LXW_PUSH_ATTRIBUTES_STR("n", name); + LXW_PUSH_ATTRIBUTES_STR("t", type); lxw_xml_empty_tag(self->file, "k", &attributes); @@ -122,8 +105,11 @@ _rich_value_structure_write_s(lxw_rich_value_structure *self) lxw_xml_start_tag(self->file, "s", &attributes); /* Write the k element. */ - _rich_value_structure_write_k1(self); - _rich_value_structure_write_k2(self); + _rich_value_structure_write_k(self, "_rvRel:LocalImageIdentifier", "i"); + _rich_value_structure_write_k(self, "CalcOrigin", "i"); + + if (self->has_embedded_image_descriptions) + _rich_value_structure_write_k(self, "Text", "s"); lxw_xml_end_tag(self->file, "s"); diff --git a/src/workbook.c b/src/workbook.c index dbd2f28c..bbb355c5 100644 --- a/src/workbook.c +++ b/src/workbook.c @@ -217,7 +217,6 @@ lxw_workbook_free(lxw_workbook *workbook) free(workbook->chartsheet_names); } - /* TODO add macro for these RB image frees. */ if (workbook->image_md5s) { for (image_md5 = RB_MIN(lxw_image_md5s, workbook->image_md5s); image_md5; image_md5 = next_image_md5) { @@ -1115,6 +1114,10 @@ _prepare_drawings(lxw_workbook *self) _store_image_type(self, object_props->image_type); + /* Check for images with alt-text. */ + if (object_props->description) + self->has_embedded_image_descriptions = LXW_TRUE; + /* Check for duplicate images and only store the first instance. */ if (object_props->md5) { tmp_image_md5.md5 = object_props->md5; diff --git a/src/worksheet.c b/src/worksheet.c index 44c2b28f..971a95ba 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -8523,9 +8523,13 @@ worksheet_write_url_opt(lxw_worksheet *self, else format = user_format; - err = worksheet_write_string(self, row_num, col_num, string_copy, format); - if (err) - goto mem_error; + if (!self->storing_embedded_image) { + err = + worksheet_write_string(self, row_num, col_num, string_copy, + format); + if (err) + goto mem_error; + } /* Reset default error condition. */ err = LXW_ERROR_MEMORY_MALLOC_FAILED; @@ -10487,9 +10491,9 @@ worksheet_insert_image_opt(lxw_worksheet *self, object_props->y_offset = user_options->y_offset; object_props->x_scale = user_options->x_scale; object_props->y_scale = user_options->y_scale; - object_props->object_position = user_options->object_position; object_props->url = lxw_strdup(user_options->url); object_props->tip = lxw_strdup(user_options->tip); + object_props->object_position = user_options->object_position; object_props->decorative = user_options->decorative; if (user_options->description) @@ -10653,7 +10657,6 @@ worksheet_embed_image_opt(lxw_worksheet *self, lxw_image_options *user_options) { FILE *image_stream; - const char *description; lxw_object_properties *object_props; lxw_error err; @@ -10672,15 +10675,7 @@ worksheet_embed_image_opt(lxw_worksheet *self, return LXW_ERROR_PARAMETER_VALIDATION; } - /* Use the filename as the default description, like Excel. */ - description = lxw_basename(filename); - if (!description) { - LXW_WARN_FORMAT1("worksheet_embed_image()/_opt(): " - "couldn't get basename for file: %s.", filename); - fclose(image_stream); - return LXW_ERROR_PARAMETER_VALIDATION; - } - + /* Check and store the cell dimensions. */ err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); if (err) return err; @@ -10692,23 +10687,38 @@ worksheet_embed_image_opt(lxw_worksheet *self, return LXW_ERROR_MEMORY_MALLOC_FAILED; } + /* We only copy/use a limited number of options for embedded images. */ 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; - object_props->object_position = user_options->object_position; - object_props->url = lxw_strdup(user_options->url); - object_props->tip = lxw_strdup(user_options->tip); - object_props->decorative = user_options->decorative; + if (user_options->cell_format) + object_props->format = user_options->cell_format; + + /* The url for embedded images is written as a cell url. */ + if (user_options->url) { + if (!user_options->cell_format) + object_props->format = self->default_url_format; + + self->storing_embedded_image = LXW_TRUE; + err = worksheet_write_url(self, + row_num, + col_num, + user_options->url, + object_props->format); + if (err) { + _free_object_properties(object_props); + fclose(image_stream); + return err; + } + + self->storing_embedded_image = LXW_FALSE; + } + object_props->decorative = user_options->decorative; if (user_options->description) - description = user_options->description; + object_props->description = lxw_strdup(user_options->description); } /* Copy other options or set defaults. */ object_props->filename = lxw_strdup(filename); - object_props->description = lxw_strdup(description); object_props->stream = image_stream; object_props->row = row_num; object_props->col = col_num; @@ -10731,7 +10741,6 @@ worksheet_embed_image_opt(lxw_worksheet *self, fclose(image_stream); return LXW_ERROR_IMAGE_DIMENSIONS; } - } /* @@ -10745,6 +10754,144 @@ worksheet_embed_image(lxw_worksheet *self, return worksheet_embed_image_opt(self, row_num, col_num, filename, NULL); } +/* + * Embed an image buffer, with options, into the worksheet. + */ +lxw_error +worksheet_embed_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_object_properties *object_props; + lxw_error err; + + if (!image_size) { + LXW_WARN("worksheet_embed_image_buffer()/_opt(): " + "size must be non-zero."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Write the image buffer to a file (preferably in memory) so we can read + * the dimensions like an ordinary file. For embedded images we really only + * need the image type. */ +#ifdef USE_FMEMOPEN + image_stream = fmemopen((void *) image_buffer, image_size, "rb"); + + if (!image_stream) + return LXW_ERROR_CREATING_TMPFILE; +#else + image_stream = lxw_tmpfile(self->tmpdir); + + 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); +#endif + + /* Check and store the cell dimensions. */ + err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); + if (err) + return err; + + /* Create a new object to hold the image properties. */ + object_props = calloc(1, sizeof(lxw_object_properties)); + if (!object_props) { + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + + /* Store the image data in the properties structure. */ + object_props->image_buffer = calloc(1, image_size); + if (!object_props->image_buffer) { + _free_object_properties(object_props); + fclose(image_stream); + return LXW_ERROR_MEMORY_MALLOC_FAILED; + } + else { + memcpy(object_props->image_buffer, image_buffer, image_size); + object_props->image_buffer_size = image_size; + object_props->is_image_buffer = LXW_TRUE; + } + + /* We only copy/use a limited number of options for embedded images. */ + if (user_options) { + if (user_options->cell_format) + object_props->format = user_options->cell_format; + + /* The url for embedded images is written as a cell url. */ + if (user_options->url) { + if (!user_options->cell_format) + object_props->format = self->default_url_format; + + self->storing_embedded_image = LXW_TRUE; + err = worksheet_write_url(self, + row_num, + col_num, + user_options->url, + object_props->format); + if (err) { + _free_object_properties(object_props); + fclose(image_stream); + return err; + } + + self->storing_embedded_image = LXW_FALSE; + } + + object_props->decorative = user_options->decorative; + if (user_options->description) + object_props->description = lxw_strdup(user_options->description); + } + + /* Copy other options or set defaults. */ + object_props->filename = lxw_strdup("image_buffer"); + object_props->stream = image_stream; + object_props->row = row_num; + object_props->col = col_num; + + if (object_props->x_scale == 0.0) + object_props->x_scale = 1; + + if (object_props->y_scale == 0.0) + object_props->y_scale = 1; + + if (_get_image_properties(object_props) == LXW_NO_ERROR) { + STAILQ_INSERT_TAIL(self->embedded_image_props, object_props, + list_pointers); + fclose(image_stream); + + return LXW_NO_ERROR; + } + else { + _free_object_properties(object_props); + fclose(image_stream); + return LXW_ERROR_IMAGE_DIMENSIONS; + } +} + +/* + * Insert an image buffer into the worksheet. + */ +lxw_error +worksheet_embed_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_embed_image_buffer_opt(self, row_num, col_num, + image_buffer, image_size, NULL); +} + /* * Set an image as a worksheet background. */ @@ -11586,7 +11733,8 @@ worksheet_set_error_cell(lxw_worksheet *self, lxw_row_t row_num = object_props->row; lxw_col_t col_num = object_props->col; - lxw_cell *cell = _new_error_cell(row_num, col_num, ref_id, NULL); + lxw_cell *cell = + _new_error_cell(row_num, col_num, ref_id, object_props->format); _insert_cell(self, row_num, col_num, cell); } diff --git a/test/functional/src/test_embed_image08.c b/test/functional/src/test_embed_image08.c new file mode 100644 index 00000000..2220b6ff --- /dev/null +++ b/test/functional/src/test_embed_image08.c @@ -0,0 +1,22 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image08.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_image_options image_options = {.description = "Some alt text"}; + + worksheet_embed_image_opt(worksheet, 0, 0, "images/red.png", &image_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image09.c b/test/functional/src/test_embed_image09.c new file mode 100644 index 00000000..bc4d06b9 --- /dev/null +++ b/test/functional/src/test_embed_image09.c @@ -0,0 +1,22 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image09.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_image_options image_options = {.description = "Some alt text", .decorative = LXW_TRUE}; + + worksheet_embed_image_opt(worksheet, 0, 0, "images/red.png", &image_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image10.c b/test/functional/src/test_embed_image10.c new file mode 100644 index 00000000..4afba2b7 --- /dev/null +++ b/test/functional/src/test_embed_image10.c @@ -0,0 +1,22 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image10.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_image_options image_options = {.url = "http://www.cpan.org/"}; + + worksheet_embed_image_opt(worksheet, 0, 0, "images/red.png", &image_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image12.c b/test/functional/src/test_embed_image12.c new file mode 100644 index 00000000..3a1a015d --- /dev/null +++ b/test/functional/src/test_embed_image12.c @@ -0,0 +1,25 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image12.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_format *format1 = workbook_add_format(workbook); + format_set_bg_color( format1, 0xFFFF00); + + lxw_image_options image_options = {.cell_format = format1}; + + worksheet_embed_image_opt(worksheet, 0, 0, "images/red.png", &image_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image51.c b/test/functional/src/test_embed_image51.c new file mode 100644 index 00000000..71d0e280 --- /dev/null +++ b/test/functional/src/test_embed_image51.c @@ -0,0 +1,44 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * SPDX-License-Identifier: BSD-2-Clause + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. + * + */ + +#include "xlsxwriter.h" + + +unsigned char image_buffer[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xfc, 0x18, 0xed, 0xa3, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, + 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, + 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, + 0x00, 0x46, 0x49, 0x44, 0x41, 0x54, 0x48, 0x4b, 0x63, 0xfc, 0xcf, 0x40, + 0x63, 0x00, 0xb4, 0x80, 0xa6, 0x88, 0xb6, 0xa6, 0x83, 0x82, 0x87, 0xa6, + 0xce, 0x1f, 0xb5, 0x80, 0x98, 0xe0, 0x1d, 0x8d, 0x03, 0x82, 0xa1, 0x34, + 0x1a, 0x44, 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, + 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0xa3, 0x41, + 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0x03, 0x1f, 0x44, 0x00, + 0xaa, 0x35, 0xdd, 0x4e, 0xe6, 0xd5, 0xa1, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +unsigned int image_size = 200; + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image51.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image_buffer(worksheet, 0, 0, image_buffer, image_size); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image52.c b/test/functional/src/test_embed_image52.c new file mode 100644 index 00000000..4b67cdee --- /dev/null +++ b/test/functional/src/test_embed_image52.c @@ -0,0 +1,46 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * SPDX-License-Identifier: BSD-2-Clause + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. + * + */ + +#include "xlsxwriter.h" + + +unsigned char image_buffer[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xfc, 0x18, 0xed, 0xa3, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, + 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, + 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, + 0x00, 0x46, 0x49, 0x44, 0x41, 0x54, 0x48, 0x4b, 0x63, 0xfc, 0xcf, 0x40, + 0x63, 0x00, 0xb4, 0x80, 0xa6, 0x88, 0xb6, 0xa6, 0x83, 0x82, 0x87, 0xa6, + 0xce, 0x1f, 0xb5, 0x80, 0x98, 0xe0, 0x1d, 0x8d, 0x03, 0x82, 0xa1, 0x34, + 0x1a, 0x44, 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, + 0xa3, 0x41, 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0xa3, 0x41, + 0x44, 0x30, 0x04, 0x08, 0x2a, 0x18, 0x4d, 0x45, 0x03, 0x1f, 0x44, 0x00, + 0xaa, 0x35, 0xdd, 0x4e, 0xe6, 0xd5, 0xa1, 0x22, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +unsigned int image_size = 200; + +int main() { + + lxw_workbook *workbook = workbook_new("test_embed_image52.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_image_options image_options = {.description = "Some alt text"}; + + worksheet_embed_image_buffer_opt(worksheet, 0, 0, image_buffer, image_size, &image_options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_format12.c b/test/functional/src/test_format12.c index a46f1c93..9434ccb6 100644 --- a/test/functional/src/test_format12.c +++ b/test/functional/src/test_format12.c @@ -1,7 +1,7 @@ /***************************************************************************** * Test cases for libxlsxwriter. * - * Simple test case for TODO. + * Test to compare output against Excel files. * * SPDX-License-Identifier: BSD-2-Clause * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. diff --git a/test/functional/src/test_landscape01.c b/test/functional/src/test_landscape01.c index f982aa99..760933fb 100644 --- a/test/functional/src/test_landscape01.c +++ b/test/functional/src/test_landscape01.c @@ -1,7 +1,7 @@ /***************************************************************************** * Test cases for libxlsxwriter. * - * Simple test case for TODO. + * Test to compare output against Excel files. * * SPDX-License-Identifier: BSD-2-Clause * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. diff --git a/test/functional/src/test_print_across01.c b/test/functional/src/test_print_across01.c index 6ea5d055..86893f45 100644 --- a/test/functional/src/test_print_across01.c +++ b/test/functional/src/test_print_across01.c @@ -1,7 +1,7 @@ /***************************************************************************** * Test cases for libxlsxwriter. * - * Simple test case for TODO. + * Test to compare output against Excel files. * * SPDX-License-Identifier: BSD-2-Clause * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. diff --git a/test/functional/src/test_simple03.c b/test/functional/src/test_simple03.c index 0d1f81d3..4d64eae7 100644 --- a/test/functional/src/test_simple03.c +++ b/test/functional/src/test_simple03.c @@ -1,7 +1,7 @@ /***************************************************************************** * Test cases for libxlsxwriter. * - * Simple test case for TODO. + * Test to compare output against Excel files. * * SPDX-License-Identifier: BSD-2-Clause * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. diff --git a/test/functional/test_embed_image.py b/test/functional/test_embed_image.py index 05d49933..c8536f99 100644 --- a/test/functional/test_embed_image.py +++ b/test/functional/test_embed_image.py @@ -39,9 +39,30 @@ def test_embed_image06(self): def test_embed_image07(self): self.run_exe_test('test_embed_image07') - def test_embed_image11(self): + def test_embed_image08(self): + self.run_exe_test('test_embed_image08') + + def test_embed_image09(self): + self.run_exe_test('test_embed_image09') + + def test_embed_image10(self): + # Ignore the missing "display" parameter. + self.ignore_elements = {'xl/worksheets/sheet1.xml': ['m62lPBg|M(39*EMTjv(~=%-uK#QsUo8g0RR9D07O;GP(|3-H{%)r5QGK*+yGDl zOk|y%JU~t!_w^pQfZWZvd>kG0^)La*7i@!|d+dWCPakZ+HI#kCCw_p`C97&K@DPO_ z?y}Q6YtOeM#c1i0h*D|K&w~N)}IfZpVWKN4CTn^vI)6b&bl`*7<7%R+Lxie zEoid;_<-M%SY#!|t0uLn2|nIpl%gfV6zvUz5Hos790@)$LscS4c54az#kY?%|KKHU~y^VRnN|B}P(}(UjG~&rc7<;|a>&o}d9hYq1(;8S0P!k$`nQWe30(nnw z-)BTMFul?od4k4jvCec5?Dc@gt(c}CUb%*Sld5ByaAls#oXPZ=w29XUFIy4%JEayr zL&-tSCDP}}t==@tkDIbD-e!W!VNQS+B9?pU!!~D+<1;UW(7DH5ckr~X2z~EO@vt{S zHhg5ngAm%v8K~vv?BdRC;o|Zql`|9DG}d_V*3@L1e z3`L-yZ|0>|bt{j?Be6r?@Vk3Hg2GwDYC)70A=!CY zlC(W+^8@VOQjn%<^;mB^A8Mbm5lUMN`8>5=OHB$NLE#tM4}r`y`~oy!d-v2Pwh-!K ztIQ_~qL%1Wxn#AtO4`lezJ=iea_4~V@w%IO&#{eg!WcDT{85yTiCls2knW7J6R(iY zTdIijyu@61v&{Zzi%Ro2wQ0{$i;FWpZnf>Tus;^X<9v+RHF3u$p8528D`;Jwak#G8 zuk$v}`V{Ip^%MsOr5_&+hNE*u9dT&Rx-55R&E0o4dW3XUz#fO_2_}SqT1Wr@!DRt~ z&TgPTE7)h(@m;|)to$aF<5Fj`7MFMTroOzLhZG{g0byc1LzHw!16bgGh#Mh zaoCpqMj2b?8U^0SF!pI9rBl%DLuF`Q$R9@#(mW+8=Ot$w2~^xYE?P`=TbO%)6PrtE zOBn^MSziI;LQ&;C@JD}g#Btk$NVtoY#Bv2X!FT>18W1Jk(v5|5gM()pSyznU!!_1# zA+?f~HvvIFTdBb7)Jf5ENx}iT=XM=A8KNmTnV~Pn@F9c#RV41te=#{0$( zEk^ZL&-Ey^%&;`-kx8OosQTY~+acH=4iev12=ZCYd-QqZ<+}-V`p9cv$PP>V2eR(p z#nvNzz^kg9kQlC?Fnk)DI92s%QT_t6XPzm*??6E|>ntfmy*1?gL2(Q-Iy|T(pkK>+zrI1qSfYjt0 zrjj$>AwdTx6X%tL4U0Tl8{^a7TUKgRk%13JHZ#HumU7v&V9REA#~V&1inEkpxjE1b zTLRrujrl&~7Ej74GtA||XjPe47xk3sp+OAZMwq};z9AN2m_OhuTft;@OiGCCWJa)s zhhWRcf!o{J&EC@4+5S(t8Kd2YU<|);mv{^grnX}+7A$?MswWd52OeaG3*4m=gHR2f zORVX!x-8W4FU))x@U6cs^yFc#=g=3*L}eTtPjJY{3FRH3xN+gujAdgNkie3Q4El?C zyN?up-vpLgZSOTu+HFqB6V*ey!WtsM+QX_KhYktthB#CE1Z)F_V+DCIrHyt+VOUK^ z`+0YVUJA!HJlQOGGFzR4?JL;Npl?jNy1bx$Lll3(@uZ!?_@pF&_3LSHJhl9F-Mpf1 z$)JtNDUC8I>usF8dBUCde1XK9$+IxKe6dXS%px$!ZQ1d=mYa{lkU-s;Bkg;l1-ft6 z!B#yuhLUrcM9s`}QOl$on4g;uMEh$vt}lJyKK%3u2=c`^q;`?HAX-g7U~wQ#3^0qb zav|$hsZ>$9(F=R3VbeotILhj&IQim{kUtNGEHH7N;;JBKd{)Qu2th~@f{^`35O-S; z$itoc#}D2=DwFUdm%}#2<#}wzNCc#Gr@%u`rI0YMO-b0#2pQinh#YU9N~)ac}|Mx@84YxDAK+Eg;Yg+raygzssa@NL5-(iN$LIdVA? zYI9RGT=!QI^acVYh0nFayA0SM#*j25z&8xo|E2 zgHn-Ng~z_a`T4@^D&>pSpS{Vmv_^6L-kpj&avCj_7D`VLe7${>n$3t2Zbgs?Pv4Tj z$Q%Epl67!2>a-7e*r=8d*W^0g4oC`*oaiCHUs#din%CKjJpWMwL+mCQu*fNTiT2>F z+HnE-1+s1M8&qwAZRHS&!N=(rznl{WGC{K-ge=wnIVX-F4+|>`4~svk;h3Q5JkNvQ zxtVexX4s4#9&jrvQIFVoL}hPCr`BLWEkgeysoO&O#o5<2MovygZfJM@(NWX#=~4-? zXn-PEUAj9znUJUb?$A2+aibBFy9$qst}6(aXb(lnxtGSEKCG3US|kz>jOr-kg59Mh zkRuvfj(kI`Za+QT@K{22InZDNfKp+YTEt&eFW^DT5!-8UA2Cije;Bi1EVqVM76x$V zYzyw{2xO8QXN5XyT#v_?O2?0RXouh7>9Ol~=F`$R1+UD0tesc*z%H}qj#Qll3!!Hb zFOkJeX$O8cZ)=6lW@qXPl9yG2-z4@}JXFF`_9i4X8qaEKS4$K2wXjjAtHNC1+^Ki% zd8&jdwZFcH)y~Nef#}GKX?)>8L*N(hAzvd{%Z+zSVCEvs z?7K#M6-y0^%RaX?%^|_ho)+-*F<;Dk9Li%qo=6Z$J}{;S8v4<%ZLRBQJ)?Q!KX7~} zaytkok`f*-v0%9ryu2fM^@Nc(lJ~?Rqy!=+D%_tZ%-!RG1IYbPLKY@z+fVc0bk82L zM$b-7Qd%6{aQ!R*cRMK+t`g6WLW#Ouhz^=<~8 z+(LEY)JY6cK+HH=gGw{if%j}dM1dCj8UdTAOtR*F8sCuZr+pNuAWSm$2lT5gr1B0L zb9#-3610BCG#T{pu*!&sfI^^(8+5`^2rpC@4P*wTW_{9yr!@VRV+3@{KF|w+q{y2I z!H;Ppw&Q~gr9aauinH6twlP-g4@~g(H884Cy>}hR!BggN3k64JSe2WRPxK%@S`|S% zmnCco#RCq0PT*z$7QM#HYsl%^_`0UwUh6WGkC)DUas0)oOck5cRfcDVwPNvvUNo{1 zC`&Q=Ku?Hnmft$K8rSe7oq4ag$>P3oVDOZf?}wB{?c(8<=hH9;Yc)9IxgckaWDIfI zL264*v5fGZJTVxaa+;ErY(_9ILZ*s#G0eli58CkDNxZ$&*Hqe$gIht)K}BN2WN^Y_ z%m2EYY4w$;UwZ!C6UFqA1B+GjGwS_cRF$0Aq^m{+0CXvSUWgEl%)=Js2>SW@$C9~W zq#ie{K-Rp=uFxF3@b>ygVx!s~W>|Z>ov@Z`#>5T3cN4((YF<4RLBK*4eekGiA>6lQ zl0I+*`%G|3r%rF4dv9McaoZYVW$}VU`g)|zY2z%-qwadQed&G}9YNwwfum@e9SMEp zAaZ($^;lQnwThAQ7sZoBb zm&>H@Mgy{(B&KFQQ3&(5qHymdi$^VgQM6Dt`p zy7n_PLOMiqhrNZlHv5mpvCsQn->Vwm5Osef3OKoAxXNKM^Qdp7-^VUz-tC$9tEJIt zwtEY+hV9NCx2c@-gPb|Gz7jU;+rOFG$AX*9HG$5=re>UKf(}W(EfF?9evrR%cU$z9 zrNB+@+!~(&2LAA9kp`!bR{(fAQ!5h4{-ZvO#ZJGn^V=QLhk(Vl&L_-D>biXGcjn%R zP-8vezvh!^#InDe3QZx|Oz|7#B-( z?m*iZo213!e{@Fr`0)wr@!Ea8W!&6qtihe zYKpljBs|Y=0bO!=CW{ywyJQah7__pkLQkYaz@bQr0RjL&wlaXTIxqjH+XbtQ4M^kx zm_CkfEqQCwjLc8S1BCNlBTawK@O;ga?#_@@_?%x^rpAT$gX;+yO}dr0W(ZVRnJ%d` z5+qDdEO3ETlrioW5Z$i#o%*v^)|e{j z4=4IbML+m6Ycjm%=+t+~Z9NhsG<_(e?}7h`=&eY7h*oO3Y@`m?Cr0y%#Y`F&Rm*cxN4X7@fh74%khZ_Q!11U|;B#H4|B$)6@$I@4Gy}N; znKCMl{2;a#K|jw#j=d^Br={>+-d0%5*l^wlw{uy}^`u;;e1h%KCz+j)#%`7;5y7`a zWMqX1PRRDWqSmoWLTnTsLh-e7ou2uO_Hw_NTVzj%+~!ebFg++pB{@OLx%t6-^x8wZ6XUEk*?M`tVI=1vzBWnT!`{T)c^MwsPk%fGy6ZG}h zin9{+Pg{WyZrf?SZw|b(*T3D7qj}Gwf)eRdt)DvU?~ZZ%mB0eu-HWeebr{n2@{NP; z-$pWK3_q<8cQ)16NCa0k4VyV~rzIpE9^0r}S62Wi)~ zs!0n>NYAfb?de<1rJb^fp1y+u0FeLe=|As%moGC{?||2Scf~4_f81z(`X#P}ITc#N zPHY8S6Upsx2n(KIHN#*5tfhM;35GA#+nY7sqzLR8K2kLDF?sovf~p3F_Y~cjJY`M5 zx@xU>3UfC;?(VVFD zu%bpnHwK+KZT#rhXJu{Hoa)Y{mey%(R&ZZ~w)?f}iHn3J3S&&Rt1ss^yPSBCX<67D zu!w5hv5w9$9v1id2&)M&;?x_x4p{Gyg`|CQQbH>*`$WBm;~H_YzKLGvEOd^;m`v-M zdn@kQo!D?1kzP~dU}n1e={pf~<@pieZNlEq`_|Qo1VzrG9weEG?B1=9v)YqPbm^}; zFdtpD8bi%XUxSt&xJILY_MtOTE!*Ih^08ZEc#fkorewjQ-|$)Pt;zk-^&3}Zkv$Ic z1R-R}N613^Qx-Qc&{hsHHFLXJ=^)1;CIiWpU|kl1y_S9 zEZOr-a(TL>4#g|M#=&U?jHR^~XBx%pr0reDsis`no1Y(9)vpRD_^mWTn5~DmL*$1r zOxSe0*Fi}!PDYE!PYGeC|2BcKSq#sbvsbV)XYssRgBDa8)Mou zRkRx#zIW7TrK@+&2pMuN#34U&;A{G%5$T9|&3c`x7B-LGCo}}J0A+?rnHad^I*ki4 zSPJ9out$DS^F zFQSGX%TQ+v;;%j(vM^kO`h>{L^VBv--DIDX!j;6~A(c(gV4^}x4XReaY_(Bz{3>%H zcMw?ioZhF(Hs@1(SK}#q%H!x8lA2@}d-aUYSLGW#hl@8MtP~W4gp|L^_y5Jy0Xh6h zI?y1mDhe+?>fF68$&i(qk5e*MAJ+xL&Drf}5VTJ}03hC3;vy-KVBfSoKy%oM4}1jCWbnl;IP5iQc0$ z@#+?$vI^h=gI7o0xdL#Kc&yNd!5xcak%$WR6AYc)dcGzJ(D4 zUFNfbH016Ut3g!H4m1iQd@#fxadS7;PO0Kp=hA2BqOVz_h7np1g4gF>K7Qzf#;}^` zO~yr1SATpXsmAbZ#(vccX0piBB*r+B) z9{kX>BHO_tqfr+fZ8xKKETEZKx0Bb(!-8~1i8xcO!iS>|J@oxlRrPLiGI|lxI7V=j zWAp2P%}_;+i)ab6J@g)8qte~O=jWG79yA$L_8G#&eT7- zLL7U!&7ceX1E*oEJf-1#Z)vn7X$wp!Y#~eysmF&!s~~H#r`;nPgbIQ{OU(~b`pG#w zw^#lB$$h4)8gASop%4?(kwbR2vVFc$q_NxiME?Qx$5vXt%sOjf^OvS5n}odkIwDx< z$8fO`ai&@-ya+;tChiyc8gN{*t^l{aCy97cNj+>g`ZAV^%J&&2S3HqJpP0$;L919$+rMED$@s z&Bx=gzI>ka4om0RI>BOJt5#uGD-71&x`lmfWGg5zcO)S2lRgr1@F)%%+IEm=1CnC^ zp;H!DEFCqx=kb6TBfkUKaID(V$X)jY;=W@-VJ#lN0dg|7rT+|>_|O>;Dy!N)PD~MH z1P`@HSuty|F(75Trf#+6vF~oQoD4%j_(#`}sFD9|V@HPI002NF2%sSF`@8?TzeD}G zzx!o(_bc$#4bf%b6$Jk8`=Y-?y}Guzj9P~9P5wds&l2PBV6VFKmtn~fm-j1P{oesz zHLx!ODj`;*zuDP;2YS_ny$t#h{U6Z(V#)p;`c*ISGP(=q5A>_f;@^Q@b+s;oiedi% zz3g%Q9pqI@rV# zSEr-PQ9vZWMERxM>+AhF!tcYz>5y(kx)tAD&R!#vNd{rvXY^{$PYA|et#002M((4wmAD)8%jr{4eo0#N{f+W<0vfuw`I zE7;!kk*1d;*u{w5)6P~)6CHqPiUb80z>k|JCg29rKKvIyKwc*)sxAQWLk_>O&^V~i zw;@EUX%Gs#3kMq?P@F=l)>1cK3|*Yoxl;~hOK33jIhoG7G%e_Kjw{-fBEQdXwE5)4 zZAu`pn(SVa(%1+aZ`Mmz6QGasfL0UGxr-e0`WYe15kweS>xB_l2&h&gvOHKo6d-wi zVu?Itk-kHSN?PH94+HR(L0tP=ib4Gj6U%Vce%)j32x~I&-@mJc3bbfw!ZsTRPv{ zxAH}DUUSxWvzoCmOXW4v?iV(BlI{Tf;(KxQQX{YW&gczJ(7M{+*d&c2MkuzkWvd}{ z^du@%2db4IirJ(M-M4MPkqXy$f1}x%=bbYy+P0@Qu;H)7H~K2cIIXz)1C33e9{IrZ zYE#513X{nO{Xvks7o~F%Wk0N99rF%(`}D2Vd2(ZV!{_1#?jxMcg{a+f&0M;ogR09! zFA!TiC|8~|W?jC|uquPv1Df#}AEpgkoIi=nxa33S7Mi!%I}`%pEuuvg?$g&pYHcQ&r_gM!N#?>U_+t4nfPs>mhtkT%C8d!$n4-umPbrDo}C&yTU_i~>sx+b4ALigXff-;c2C{%Xf#(*I^ANiowDBMZl3qa z)Uj#G4i1VxIUWo{Wsf{&Rh@NQ>B^jYjC*=JF_~F+l&Ea@5p9^EWTnfulS78 zHP1Ehypy2oQ%6W6rQU}~P`;Et3BN`8jIfN8gn1-D_S;F}Vv6&^+=n}u>~h=kNLH$K z#uwtlV&8$xN7|U0L|&YV z^6?2_TJghYF$q(Zev8tV=*2fBFO-E5I;T^!vM&&?`+GM_esUFFCl%rT#)SL(ku%uV zg@gV3>;I~VIr=XrHe};kQehZyF>Femc_d#E`BI3lbqODM4 zoHkZ0`qn=PN3TUcI3UAfeMy3k+&2(5(9(Bd<%GkGi4sPO%H^1k^&5_v>`*T`0SsLpZ@vl?{8NPE2gOr$7N4w{nt)C{da3 z({FYon=(RQ8H`etcyn1tmKG97>!F7ZI^!B*fm7WyV#6Y?}K{`x8WPX7|e>onpU5;TI|pC^(-ikLi(mgZef>b;O69% zN~xIn4p#2`t=$h?0R&q~vry|ip$wLcLMy_1lH=N@TYjMk;I533wmrdojdvSXW<6NC zqH`JeO$^kLD@2>^x%T{wQc zasHN>_#d$xx+N?Pv=}4g5!0Bm8hR#%fPQ02#Ab$1|F%xVc-vHB?PT!#5J|dA_p@V< z1omcE7Vlb>_#Ud~9aKVCTz#1w_H*nK7sf3-W>{NJvx{fUIHv_TLdk4{If#l z!SSg5e)Z!9r97A_``M0vVpzmP56Po~@?@vnj$Xw1PaVgf;bSu17n zuDqmMz&7on4a}1UJ$e@fpreKp7#n{NNzS2{Ql~Dog@r;O0uY33C*g?Msm7Bn7*mFL zTj=$ETA1#Mh~!Fu&IACdTsNhVyReSOm5Mc{SLYFYoO1X$Wc=g8jS{q}$- zgG4_w#7^aA9NJVGZuDbo+;%tDZ$9Uq%?(qqimWHAE z$L->5DSxolk@Ax8RVD9{$R4AsLTK{dgs4ixc}?wFN&LPVCh~M;s3VLcMcW2g$xo*K z^#inaPI?dw9$?2x@tO%8Yj2lar%@2MJB2R(n55A0*OoOeBx#(XjnFOYPgPXp5Rp;_PFc%b&2@}2L%$^D3(K&%Kd zSe(d$>2lD@uITj)BWWP%iG@=Mf=^V~KQqk5)yo#_@;f3664h;{fmmI$hfGnkQ`6*S zWwi2?7^oq3Sl%IxC#Likua=Biy1-d}&gl#uz%P9+*mY?^L!~)TYS-fc0nu(-sRpvAs4n?SZ z^eNM6V4)S^j{yZB1!u^F9Uo4J1`5~+Lc#R36Gv|PJ?jYgjAfv=x;k0fh!1u`6}}S} zs4KoiB`eHg6Vpmpr8O|Y*;h}eME=2PAR9-X)j7l}BHgUah-9J%{?p2Es<}*lQwR=d za4DXH7F75aC$~PkbMx!ER$Hy(Odd`e$K}Zvy;4O?HYW+-3{&~yDUD!614xo|)Jv0( zdY0Qfs0v&6G>u`esL|w+en8NakoU*rMfIZL<`>gYTXQ8C-32dOjc7DM>Oo3#c98`C zo-~0KEcq-sGsy^VUVvB;<#HIvy${}W+fBH)+t*mqhJ{^D!b(nPL9cVlXvzJyi+=5m zpif$!_Ni>z$bre4@j1o*FRY4AY*AMs0{|MNKQBb^M&@b>wgdls{cXwI)KiWfmLYEX z#v;=cwDA7sCj!0N9tLPzn>D|hQ~JbhpY93J2POBO@<335f|k{&Vgby%c#cMNxd5*n(*@PYQYBQ6UgyJ_NEY2EcDgC6TWw!UDd;S?9k>{gzJ&=d?QPo>vYVC} zGf4>H9-&Qi_E(HsD+_he)2YUi!5WF=J4p>^)3s|2?T z<_vrAvv2huk7Hi+y?t0YzA5P9CkQybue-)-GUL~`+V5$dJ@5S7vP-NDD2s@hO>cPZpLOXBX63>UnW(S(*>>uJY-|kWg)LP>Orjd2 z@9{a&lP9N4C+n*o9F$MMor>g_r>a@!2kWWk*+T&wO@6ywHH;ih$7h3-6r^)g2smy> z{+&{}28(E$--vCw(WoSy_@0UfTZJIV`ttyMm`ec;%ADMv@8z#G)FY7iqkGypH|MU4 z(=j|H@#oKdi!i;E?)Da#=0clT@Pb=jqQ;T)qthudWty3XYA}Rfo;tB40?bcCzFGWE+H2Govb>lMxG*yYjMBSxD}Su^ECWmH67m?A0jsa z?y6bpFfBTKBy)z8V6ACSY*qZ2Xp)6ztKBbjr5WgP3fn3L44K1S<%ne){pGB3>(~kj z+#HO*NOiv2si<$nsU?03NQp~MPLw*`hCi&E~wkug3{b6~WEw)oj zPp{)_fmB9$*%lPY#VF@)LDO4dwA#$6tYXs{CBc6Ijk4Ib^W=H1FmHzWFvJIybn9j& zoez@px#a9Q)MK`*slGI2d=nChVzchzG{EstkS_Kv2-Ui`Tlx7Lb94pN#}oZTf**Yu zRB7L`c4#@~v>fx^GJGtd<%;_m|Ghw6uv$u)WW)pZ&veG+iy4%ROvG7RUNziQ*;SKE z;#D)y!B7{zW9FoH*)pBRMkdjH4vhUw{x%9O!ZkcwnpTiyt>Hm_3gIb2=A%KkZk;sm1B{tjXi}$O z+I(Ykq1kgxkPU4&jwTQVEvlN#&cLuZT#`tEcI^-u>t-D*6jNN!jF~#6(P6-qJ}k0H zYx{1RA#urL5*qQG;SxFA>LLZ9NA3M3>x1KW@~Q{>N0Y5~gy_P8v8&hY%nuCirnw~! z;R${Bi%6Qax{`Yqx*Sd zxTCSIMkJ`RaoEU?Bel4k+-!Grh*H(plhSOfMck?_i512m;-6>kagcgryNW2^fav1J z^`5@nRMH^{@9Dcp007C)p8oURcl9!J{SFBFyDC-lO(V6m{+bBO`(5_ z3)apBl9)me&Axs5;EEhV#UG^iFlR-Ir<0YoltNJUxi}M)pTJT_K8g*w4qDi7%bE7U zoH}mQ+w;;^b2ep%5>xY3W;2+#PV1vu<%Gpsgfe6FmTRx(wmR*Bh*XTswix&|E*Qrb zXpf6}J^7V*=&=0Ui57!pe5JTM+zGaEzB zOSdT;P(bmR7QS|m?G-N0~K^5C>k z{iMa&3S=n-pPD(G%^tu%YMa=)fq!V0zni|wlV)w&fVf?=sHc<)Fy6HwQd5>ZgB)O| z*r9NRm3~laK3z%e<+)1H22oq*Ns1wR*4C1rS=}0sjL&LAHG}!^PO$V4ngO#$*9JH- z+FoxF@!2gX$(@>q-VlX~$`t$NRnk=#jZfjkon5x15;b$vk>w+EUHa%2jpc2+x*zPc zn5gR<(u0Q_3b06yZMhmht3=qL-!R`GuZ7N|_VEotO~4tUVg@=c*!JUmw5I&HnmKAA znf{4_k+r&uud8lizd6M|@PU8F#MGVuj)nk~QiMKPpuHBYpg~OIc{qNHWw+$Z zoSF9*BuGJj`sj0so?H=Q#RH~=)UyX-@f5Iyq&Lg!*ea%v@VAm3a8KMEHD5*!J&~Zu z;>BHiHe{l^4)F|@m?{ zX_@^wuCw6`HTg-@ZBbR?%e^`}hwJnWUciJK;Zh0`oIHB~39)N9sM;&;OQxS<1 z7kTdCwrKF`%%>>{vrikmVa6=hlnI{{sd`ws>m)B51#9v8x`JX4Qx+_GmKAa%UWT*- z19R2{^SvS47YAqCNHQ;8wt_3nK1brmte?-&^mO5*a_lMSDeIK#hnH`1=bp%Qtg0mt zX~QPx>ZlwHc7xwx)@8>RCr1qnD0|ja@GIVJkLB$XjrJ&QoYH-2Bhq`EDqPi!Us?`Y zpmlG5eIWyyBp55Opmo6@T%;!hj3Zy#K5G7@(BLK<*Z0F+LI8IO-LEb&`ySlx-y_<^ z)!7Z?>h^nq&nQ+^5m6kgV=Hjhq*mOzx6B8vHs)SEdF+WoyO!WV&A2ufi3B-6H!?_3W#e8m zlv5H($Pbd=Z>Z9C%>9xd5k;>^QQJypM}Z{S^w_XG%hn{lK?4=#TYBx7 ze-nX52d9~<3DJxkL55PfCu<*S$cL%Qs&7e2sD%jQXhDs3O>h0TLS$7gqeP7MPSwxVR!@$ip`jrSz;71#nLd!Vl@cd+M*)DgEb71zSuj20ZPhwgNA45A27%)#3{q z!$q_G#N^)aLCqB1+4i@TNBjDLz}S*0`Y7wp_nEl?rFX?mTt|+Nc(&g@@?cbB<#Tt> z4@ZG@L;0qZGnOL>UhWE42Z)>9v-x1(VqAv?qc>eEaKz`?Z}cekQm_=Fz@?A!exxm% z(Lnkx>c-L+0(we+hD0rf6u}S5dh1^M?XwQ}YM<1T=4fVBin7Wa(teTbIat4SzkxqB z9Xv)De|hqsUFmzA{HT2Ymps4iOwof?)`*-~D07Wa58$NDj2l`(I)@^<8Cwq`n!yUU zNMEPB#O?vFt5vq7to+$*Y~eYZ{aSB9C>@H7h`(u75AqJmKv8=iY4M~SnVtMZO9sov z%1Y5{BEPkW=6Kx8wWpehHa5rPRg=lIS{Yo5&8#I-){j{!xIc01Mpd@IYTupmKW^2O zwa6x$L0fYjJfjq1Q+#z6zd>JJE6NOf51SEZ&U|sBoU; zI>HbJReviI-KAWE5mr8NWYBa5@mNNp7y9C>e4&$jTtM56m5an~j0ev*@D}@8)CxLV zpwPCKZOpqP+kpW&BmM!OwGa@4MzK&(b^-S%lkF0{?7of3)oiy<>0H)zscEu26|nDy$bpX z^)JwWkYxW1{koQT72Og22l{no@y|f7t6EnIT1(rVp zzCNvA1;)Ak1NdKO_dmnGK6YNkFNEhKJSKiKg#MY8>(kLyD`3K3to%~$wKV_C!u4U} zss&G?Uo8CV=<$0?e{a@TE#<=hxbCsPhMb?R8*box>;6rVy51A78c4r$-N66skiUPL t@BbWez0v$M_PxmF2h@`MF!uMh^KZb&@FMbk@s10453f(J;Nl_x@E=x~$)NxM literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image10.xlsx b/test/functional/xlsx_files/embed_image10.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..38ae6342c6316e33c91709e1c168264f7f13ffc2 GIT binary patch literal 9114 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 bF!pDC`!`@@c(%IS&2HNJ literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image12.xlsx b/test/functional/xlsx_files/embed_image12.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5961f64ccb30b9c585f7b507e28b2f160284d105 GIT binary patch literal 8622 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 E4_s&GVE_OC literal 0 HcmV?d00001