From 4cfe3cc0415c99bdd6b7a2695dc5c9b9831c4199 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Tue, 30 Jul 2024 00:33:14 +0100 Subject: [PATCH] worksheet: add initial embedded image support Feature request #417 --- .indent.pro | 4 + examples/embed_images.c | 37 +++ include/xlsxwriter/content_types.h | 1 + include/xlsxwriter/metadata.h | 3 + include/xlsxwriter/packager.h | 4 + include/xlsxwriter/relationships.h | 1 + include/xlsxwriter/rich_value.h | 51 +++ include/xlsxwriter/rich_value_rel.h | 50 +++ include/xlsxwriter/rich_value_structure.h | 53 ++++ include/xlsxwriter/rich_value_types.h | 49 +++ include/xlsxwriter/workbook.h | 5 + include/xlsxwriter/worksheet.h | 37 ++- src/content_types.c | 20 ++ src/metadata.c | 193 +++++++++-- src/packager.c | 300 +++++++++++++++++- src/relationships.c | 23 ++ src/rich_value.c | 190 +++++++++++ src/rich_value_rel.c | 142 +++++++++ src/rich_value_structure.c | 176 ++++++++++ src/rich_value_types.c | 199 ++++++++++++ src/workbook.c | 77 ++++- src/worksheet.c | 173 +++++++++- test/functional/src/test_embed_image01.c | 21 ++ test/functional/src/test_embed_image02.c | 21 ++ test/functional/src/test_embed_image03.c | 21 ++ test/functional/src/test_embed_image04.c | 22 ++ test/functional/src/test_embed_image05.c | 22 ++ test/functional/src/test_embed_image06.c | 21 ++ test/functional/src/test_embed_image07.c | 22 ++ test/functional/src/test_embed_image11.c | 23 ++ test/functional/src/test_embed_image13.c | 34 ++ test/functional/test_embed_image.py | 44 +++ test/functional/xlsx_files/embed_image01.xlsx | Bin 0 -> 8567 bytes test/functional/xlsx_files/embed_image02.xlsx | Bin 0 -> 8579 bytes test/functional/xlsx_files/embed_image03.xlsx | Bin 0 -> 8954 bytes test/functional/xlsx_files/embed_image04.xlsx | Bin 0 -> 9462 bytes test/functional/xlsx_files/embed_image05.xlsx | Bin 0 -> 8763 bytes test/functional/xlsx_files/embed_image06.xlsx | Bin 0 -> 10329 bytes test/functional/xlsx_files/embed_image07.xlsx | Bin 0 -> 10716 bytes test/functional/xlsx_files/embed_image11.xlsx | Bin 0 -> 8610 bytes test/functional/xlsx_files/embed_image13.xlsx | Bin 0 -> 9475 bytes test/unit/Makefile | 12 + test/unit/rich_value/Makefile | 8 + test/unit/rich_value/main.c | 15 + .../test_rich_value_xml_declaration.c | 28 ++ test/unit/rich_value_rel/Makefile | 8 + test/unit/rich_value_rel/main.c | 15 + .../test_rich_value_rel_xml_declaration.c | 28 ++ test/unit/rich_value_structure/Makefile | 8 + test/unit/rich_value_structure/main.c | 15 + ...est_rich_value_structure_xml_declaration.c | 28 ++ test/unit/rich_value_types/Makefile | 8 + test/unit/rich_value_types/main.c | 15 + .../test_rich_value_types_xml_declaration.c | 28 ++ 54 files changed, 2229 insertions(+), 26 deletions(-) create mode 100644 examples/embed_images.c create mode 100644 include/xlsxwriter/rich_value.h create mode 100644 include/xlsxwriter/rich_value_rel.h create mode 100644 include/xlsxwriter/rich_value_structure.h create mode 100644 include/xlsxwriter/rich_value_types.h create mode 100644 src/rich_value.c create mode 100644 src/rich_value_rel.c create mode 100644 src/rich_value_structure.c create mode 100644 src/rich_value_types.c create mode 100644 test/functional/src/test_embed_image01.c create mode 100644 test/functional/src/test_embed_image02.c create mode 100644 test/functional/src/test_embed_image03.c create mode 100644 test/functional/src/test_embed_image04.c create mode 100644 test/functional/src/test_embed_image05.c create mode 100644 test/functional/src/test_embed_image06.c create mode 100644 test/functional/src/test_embed_image07.c create mode 100644 test/functional/src/test_embed_image11.c create mode 100644 test/functional/src/test_embed_image13.c create mode 100644 test/functional/test_embed_image.py create mode 100644 test/functional/xlsx_files/embed_image01.xlsx create mode 100644 test/functional/xlsx_files/embed_image02.xlsx create mode 100644 test/functional/xlsx_files/embed_image03.xlsx create mode 100644 test/functional/xlsx_files/embed_image04.xlsx create mode 100644 test/functional/xlsx_files/embed_image05.xlsx create mode 100644 test/functional/xlsx_files/embed_image06.xlsx create mode 100644 test/functional/xlsx_files/embed_image07.xlsx create mode 100644 test/functional/xlsx_files/embed_image11.xlsx create mode 100644 test/functional/xlsx_files/embed_image13.xlsx create mode 100644 test/unit/rich_value/Makefile create mode 100644 test/unit/rich_value/main.c create mode 100644 test/unit/rich_value/test_rich_value_xml_declaration.c create mode 100644 test/unit/rich_value_rel/Makefile create mode 100644 test/unit/rich_value_rel/main.c create mode 100644 test/unit/rich_value_rel/test_rich_value_rel_xml_declaration.c create mode 100644 test/unit/rich_value_structure/Makefile create mode 100644 test/unit/rich_value_structure/main.c create mode 100644 test/unit/rich_value_structure/test_rich_value_structure_xml_declaration.c create mode 100644 test/unit/rich_value_types/Makefile create mode 100644 test/unit/rich_value_types/main.c create mode 100644 test/unit/rich_value_types/test_rich_value_types_xml_declaration.c diff --git a/.indent.pro b/.indent.pro index f473baeb..676d57b3 100644 --- a/.indent.pro +++ b/.indent.pro @@ -132,6 +132,10 @@ -T lxw_repeat_cols -T lxw_repeat_rows -T lxw_rich_string_tuple +-T lxw_rich_value +-T lxw_rich_value_rel +-T lxw_rich_value_structure +-T lxw_rich_value_types -T lxw_row -T lxw_row_col_options -T lxw_row_t diff --git a/examples/embed_images.c b/examples/embed_images.c new file mode 100644 index 00000000..eb240d7f --- /dev/null +++ b/examples/embed_images.c @@ -0,0 +1,37 @@ +/* + * An example of embedding images into a worksheet using the libxlsxwriter + * library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + /* Create a new workbook and add a worksheet. */ + lxw_workbook *workbook = workbook_new("embed_images.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + /* Change some of the column widths for clarity. */ + worksheet_set_column(worksheet, COLS("A:B"), 30, NULL); + + /* Embed an image. */ + worksheet_write_string(worksheet, CELL("A2"), "Embed an image in a cell:", NULL); + worksheet_embed_image(worksheet, CELL("B2"), "logo.png"); + + /* Make a row bigger and embed the image. */ + worksheet_set_row(worksheet, 3, 72, NULL); + worksheet_write_string(worksheet, CELL("A4"), "Embed an image in a cell:", NULL); + worksheet_embed_image(worksheet, CELL("B4"), "logo.png"); + + /* Make a row bigger and embed the image. */ + worksheet_set_row(worksheet, 5, 150, NULL); + worksheet_write_string(worksheet, CELL("A6"), "Embed an image in a cell:", NULL); + worksheet_embed_image(worksheet, CELL("B6"), "logo.png"); + + workbook_close(workbook); + + return 0; +} diff --git a/include/xlsxwriter/content_types.h b/include/xlsxwriter/content_types.h index 8abeb5c3..e337ca1f 100644 --- a/include/xlsxwriter/content_types.h +++ b/include/xlsxwriter/content_types.h @@ -64,6 +64,7 @@ void lxw_ct_add_shared_strings(lxw_content_types *content_types); void lxw_ct_add_calc_chain(lxw_content_types *content_types); void lxw_ct_add_custom_properties(lxw_content_types *content_types); void lxw_ct_add_metadata(lxw_content_types *content_types); +void lxw_ct_add_rich_value(lxw_content_types *content_types); /* Declarations required for unit testing. */ #ifdef TESTING diff --git a/include/xlsxwriter/metadata.h b/include/xlsxwriter/metadata.h index 7a515ab4..85fbb7ab 100644 --- a/include/xlsxwriter/metadata.h +++ b/include/xlsxwriter/metadata.h @@ -20,6 +20,9 @@ typedef struct lxw_metadata { FILE *file; + uint8_t has_dynamic_functions; + uint8_t has_embedded_images; + uint32_t num_embedded_images; } lxw_metadata; diff --git a/include/xlsxwriter/packager.h b/include/xlsxwriter/packager.h index f9876d94..7016573e 100644 --- a/include/xlsxwriter/packager.h +++ b/include/xlsxwriter/packager.h @@ -37,6 +37,10 @@ #include "vml.h" #include "comment.h" #include "metadata.h" +#include "rich_value.h" +#include "rich_value_rel.h" +#include "rich_value_types.h" +#include "rich_value_structure.h" #define LXW_ZIP_BUFFER_SIZE (16384) diff --git a/include/xlsxwriter/relationships.h b/include/xlsxwriter/relationships.h index d33d2625..882d9ff0 100644 --- a/include/xlsxwriter/relationships.h +++ b/include/xlsxwriter/relationships.h @@ -61,6 +61,7 @@ void lxw_add_ms_package_relationship(lxw_relationships *self, void lxw_add_worksheet_relationship(lxw_relationships *self, const char *type, const char *target, const char *target_mode); +void lxw_add_rich_value_relationship(lxw_relationships *self); /* Declarations required for unit testing. */ #ifdef TESTING diff --git a/include/xlsxwriter/rich_value.h b/include/xlsxwriter/rich_value.h new file mode 100644 index 00000000..1ea43888 --- /dev/null +++ b/include/xlsxwriter/rich_value.h @@ -0,0 +1,51 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * rich_value - A libxlsxwriter library for creating Excel XLSX rich_value files. + * + */ +#ifndef __LXW_RICH_VALUE_H__ +#define __LXW_RICH_VALUE_H__ + +#include + +#include "common.h" +#include "workbook.h" + +/* + * Struct to represent a rich_value object. + */ +typedef struct lxw_rich_value { + + FILE *file; + lxw_workbook *workbook; + +} lxw_rich_value; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_rich_value *lxw_rich_value_new(void); +void lxw_rich_value_free(lxw_rich_value *rich_value); +void lxw_rich_value_assemble_xml_file(lxw_rich_value *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _rich_value_xml_declaration(lxw_rich_value *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_RICH_VALUE_H__ */ diff --git a/include/xlsxwriter/rich_value_rel.h b/include/xlsxwriter/rich_value_rel.h new file mode 100644 index 00000000..2d35ef0d --- /dev/null +++ b/include/xlsxwriter/rich_value_rel.h @@ -0,0 +1,50 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * rich_value_rel - A libxlsxwriter library for creating Excel XLSX rich_value_rel files. + * + */ +#ifndef __LXW_RICH_VALUE_REL_H__ +#define __LXW_RICH_VALUE_REL_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a rich_value_rel object. + */ +typedef struct lxw_rich_value_rel { + + FILE *file; + uint32_t num_embedded_images; + +} lxw_rich_value_rel; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_rich_value_rel *lxw_rich_value_rel_new(void); +void lxw_rich_value_rel_free(lxw_rich_value_rel *rich_value_rel); +void lxw_rich_value_rel_assemble_xml_file(lxw_rich_value_rel *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _rich_value_rel_xml_declaration(lxw_rich_value_rel *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_RICH_VALUE_REL_H__ */ diff --git a/include/xlsxwriter/rich_value_structure.h b/include/xlsxwriter/rich_value_structure.h new file mode 100644 index 00000000..750adc14 --- /dev/null +++ b/include/xlsxwriter/rich_value_structure.h @@ -0,0 +1,53 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * rich_value_structure - A libxlsxwriter library for creating Excel XLSX rich_value_structure files. + * + */ +#ifndef __LXW_RICH_VALUE_STRUCTURE_H__ +#define __LXW_RICH_VALUE_STRUCTURE_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a rich_value_structure object. + */ +typedef struct lxw_rich_value_structure { + + FILE *file; + uint8_t has_embedded_image_descriptions; + +} lxw_rich_value_structure; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_rich_value_structure *lxw_rich_value_structure_new(void); +void lxw_rich_value_structure_free(lxw_rich_value_structure + *rich_value_structure); +void lxw_rich_value_structure_assemble_xml_file(lxw_rich_value_structure + *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _rich_value_structure_xml_declaration(lxw_rich_value_structure + *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_RICH_VALUE_STRUCTURE_H__ */ diff --git a/include/xlsxwriter/rich_value_types.h b/include/xlsxwriter/rich_value_types.h new file mode 100644 index 00000000..24bb9aa0 --- /dev/null +++ b/include/xlsxwriter/rich_value_types.h @@ -0,0 +1,49 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * rich_value_types - A libxlsxwriter library for creating Excel XLSX rich_value_types files. + * + */ +#ifndef __LXW_RICH_VALUE_TYPES_H__ +#define __LXW_RICH_VALUE_TYPES_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a rich_value_types object. + */ +typedef struct lxw_rich_value_types { + + FILE *file; + +} lxw_rich_value_types; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_rich_value_types *lxw_rich_value_types_new(void); +void lxw_rich_value_types_free(lxw_rich_value_types *rich_value_types); +void lxw_rich_value_types_assemble_xml_file(lxw_rich_value_types *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _rich_value_types_xml_declaration(lxw_rich_value_types *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_RICH_VALUE_TYPES_H__ */ diff --git a/include/xlsxwriter/workbook.h b/include/xlsxwriter/workbook.h index 4d90f342..f32ad0cf 100644 --- a/include/xlsxwriter/workbook.h +++ b/include/xlsxwriter/workbook.h @@ -299,6 +299,7 @@ typedef struct lxw_workbook { struct lxw_worksheet_names *worksheet_names; struct lxw_chartsheet_names *chartsheet_names; struct lxw_image_md5s *image_md5s; + struct lxw_image_md5s *embedded_image_md5s; struct lxw_image_md5s *header_image_md5s; struct lxw_image_md5s *background_md5s; struct lxw_charts *charts; @@ -337,6 +338,10 @@ typedef struct lxw_workbook { uint8_t has_vml; uint8_t has_comments; uint8_t has_metadata; + uint8_t has_embedded_images; + uint8_t has_dynamic_functions; + + uint32_t num_embedded_images; 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 01aa3f45..9a28b397 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -721,6 +721,7 @@ enum cell_types { DYNAMIC_ARRAY_FORMULA_CELL, BLANK_CELL, BOOLEAN_CELL, + ERROR_CELL, COMMENT, HYPERLINK_URL, HYPERLINK_INTERNAL, @@ -818,6 +819,7 @@ STAILQ_HEAD(lxw_selections, lxw_selection); STAILQ_HEAD(lxw_data_validations, lxw_data_val_obj); STAILQ_HEAD(lxw_cond_format_list, lxw_cond_format_obj); STAILQ_HEAD(lxw_image_props, lxw_object_properties); +STAILQ_HEAD(lxw_embedded_image_props, lxw_object_properties); STAILQ_HEAD(lxw_chart_props, lxw_object_properties); STAILQ_HEAD(lxw_comment_objs, lxw_vml_obj); STAILQ_HEAD(lxw_table_objs, lxw_table_obj); @@ -2120,6 +2122,7 @@ typedef struct lxw_worksheet { struct lxw_data_validations *data_validations; struct lxw_cond_format_hash *conditional_formats; struct lxw_image_props *image_props; + struct lxw_image_props *embedded_image_props; struct lxw_chart_props *chart_data; struct lxw_drawing_rel_ids *drawing_rel_ids; struct lxw_vml_drawing_rel_ids *vml_drawing_rel_ids; @@ -2193,7 +2196,7 @@ typedef struct lxw_worksheet { uint8_t zoom_scale_normal; uint8_t black_white; uint8_t num_validations; - uint8_t has_dynamic_arrays; + uint8_t has_dynamic_functions; char *vba_codename; uint16_t num_buttons; @@ -3783,6 +3786,34 @@ lxw_error worksheet_insert_image_buffer_opt(lxw_worksheet *worksheet, size_t image_size, lxw_image_options *options); +/** + * @brief TODO + * + * @param worksheet + * @param row + * @param col + * @param filename + * @return lxw_error + */ +lxw_error worksheet_embed_image(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + const char *filename); + +/** + * @brief TODO + * + * @param worksheet + * @param row + * @param col + * @param filename + * @param options + * @return lxw_error + */ +lxw_error worksheet_embed_image_opt(lxw_worksheet *worksheet, + lxw_row_t row, lxw_col_t col, + const char *filename, + lxw_image_options *options); + /** * @brief Set the background image for a worksheet. * @@ -5812,6 +5843,10 @@ void lxw_worksheet_write_sheet_pr(lxw_worksheet *worksheet); void lxw_worksheet_write_page_setup(lxw_worksheet *worksheet); void lxw_worksheet_write_header_footer(lxw_worksheet *worksheet); +void worksheet_set_error_cell(lxw_worksheet *worksheet, + lxw_object_properties *object_props, + uint32_t ref_id); + /* Declarations required for unit testing. */ #ifdef TESTING diff --git a/src/content_types.c b/src/content_types.c index bda0b826..ecb669a1 100644 --- a/src/content_types.c +++ b/src/content_types.c @@ -391,3 +391,23 @@ lxw_ct_add_metadata(lxw_content_types *self) lxw_ct_add_override(self, "/xl/metadata.xml", LXW_APP_DOCUMENT "spreadsheetml.sheetMetadata+xml"); } + +/* + * Add the richValue files to the ContentTypes overrides. + */ +void +lxw_ct_add_rich_value(lxw_content_types *self) +{ + lxw_ct_add_override(self, "/xl/richData/rdRichValueTypes.xml", + LXW_APP_MSEXCEL "rdrichvaluetypes+xml"); + + lxw_ct_add_override(self, "/xl/richData/rdrichvalue.xml", + LXW_APP_MSEXCEL "rdrichvalue+xml"); + + lxw_ct_add_override(self, "/xl/richData/rdrichvaluestructure.xml", + LXW_APP_MSEXCEL "rdrichvaluestructure+xml"); + + lxw_ct_add_override(self, "/xl/richData/richValueRel.xml", + LXW_APP_MSEXCEL "richvaluerel+xml"); + +} diff --git a/src/metadata.c b/src/metadata.c index 197cec42..36b19f1f 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -77,10 +77,17 @@ _metadata_write_metadata(lxw_metadata *self) "spreadsheetml/2006/main"; char xmlns_xda[] = "http://schemas.microsoft.com/office/" "spreadsheetml/2017/dynamicarray"; + char xmlns_xlrd[] = "http://schemas.microsoft.com/office/" + "spreadsheetml/2017/richdata"; LXW_INIT_ATTRIBUTES(); LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); - LXW_PUSH_ATTRIBUTES_STR("xmlns:xda", xmlns_xda); + + if (self->has_embedded_images) + LXW_PUSH_ATTRIBUTES_STR("xmlns:xlrd", xmlns_xlrd); + + if (self->has_dynamic_functions) + LXW_PUSH_ATTRIBUTES_STR("xmlns:xda", xmlns_xda); lxw_xml_start_tag(self->file, "metadata", &attributes); @@ -88,10 +95,10 @@ _metadata_write_metadata(lxw_metadata *self) } /* - * Write the element. + * Write the element for dynamic functions. */ STATIC void -_metadata_write_metadata_type(lxw_metadata *self) +_metadata_write_cell_metadata_type(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -116,6 +123,34 @@ _metadata_write_metadata_type(lxw_metadata *self) LXW_FREE_ATTRIBUTES(); } +/* + * Write the element for embedded images. + */ +STATIC void +_metadata_write_value_metadata_type(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", "XLRICHVALUE"); + LXW_PUSH_ATTRIBUTES_INT("minSupportedVersion", 120000); + LXW_PUSH_ATTRIBUTES_INT("copy", 1); + LXW_PUSH_ATTRIBUTES_INT("pasteAll", 1); + LXW_PUSH_ATTRIBUTES_INT("pasteValues", 1); + LXW_PUSH_ATTRIBUTES_INT("merge", 1); + LXW_PUSH_ATTRIBUTES_INT("splitFirst", 1); + LXW_PUSH_ATTRIBUTES_INT("rowColShift", 1); + LXW_PUSH_ATTRIBUTES_INT("clearFormats", 1); + LXW_PUSH_ATTRIBUTES_INT("clearComments", 1); + LXW_PUSH_ATTRIBUTES_INT("assign", 1); + LXW_PUSH_ATTRIBUTES_INT("coerce", 1); + + lxw_xml_empty_tag(self->file, "metadataType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + /* * Write the element. */ @@ -124,14 +159,24 @@ _metadata_write_metadata_types(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; + uint8_t count = 0; + + if (self->has_dynamic_functions) + count++; + + if (self->has_embedded_images) + count++; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_INT("count", 1); + LXW_PUSH_ATTRIBUTES_INT("count", count); lxw_xml_start_tag(self->file, "metadataTypes", &attributes); /* Write the metadataType element. */ - _metadata_write_metadata_type(self); + if (self->has_dynamic_functions) + _metadata_write_cell_metadata_type(self); + if (self->has_embedded_images) + _metadata_write_value_metadata_type(self); lxw_xml_end_tag(self->file, "metadataTypes"); @@ -157,10 +202,10 @@ _metadata_write_xda_dynamic_array_properties(lxw_metadata *self) } /* - * Write the element. + * Write the element for dynamic functions. */ STATIC void -_metadata_write_ext(lxw_metadata *self) +_metadata_write_cell_ext(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -179,10 +224,49 @@ _metadata_write_ext(lxw_metadata *self) } /* - * Write the element. + * Write the element. */ STATIC void -_metadata_write_future_metadata(lxw_metadata *self) +_metadata_write_xlrd_rvb(lxw_metadata *self, uint32_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("i", index); + + lxw_xml_empty_tag(self->file, "xlrd:rvb", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for embedded images. + */ +STATIC void +_metadata_write_value_ext(lxw_metadata *self, uint32_t index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("uri", "{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}"); + + lxw_xml_start_tag(self->file, "ext", &attributes); + + /* Write the xlrd:rvb element. */ + _metadata_write_xlrd_rvb(self, index); + + lxw_xml_end_tag(self->file, "ext"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element for dynamic functions. + */ +STATIC void +_metadata_write_cell_future_metadata(lxw_metadata *self) { struct xml_attribute_list attributes; struct xml_attribute *attribute; @@ -194,14 +278,12 @@ _metadata_write_future_metadata(lxw_metadata *self) lxw_xml_start_tag(self->file, "futureMetadata", &attributes); lxw_xml_start_tag(self->file, "bk", NULL); - lxw_xml_start_tag(self->file, "extLst", NULL); /* Write the ext element. */ - _metadata_write_ext(self); + _metadata_write_cell_ext(self); lxw_xml_end_tag(self->file, "extLst"); - lxw_xml_end_tag(self->file, "bk"); lxw_xml_end_tag(self->file, "futureMetadata"); @@ -209,18 +291,52 @@ _metadata_write_future_metadata(lxw_metadata *self) LXW_FREE_ATTRIBUTES(); } +/* + * Write the element for embedded images. + */ +STATIC void +_metadata_write_value_future_metadata(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint32_t i; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", "XLRICHVALUE"); + LXW_PUSH_ATTRIBUTES_INT("count", self->num_embedded_images); + + lxw_xml_start_tag(self->file, "futureMetadata", &attributes); + + for (i = 0; i < self->num_embedded_images; i++) { + lxw_xml_start_tag(self->file, "bk", NULL); + + lxw_xml_start_tag(self->file, "extLst", NULL); + + /* Write the ext element. */ + _metadata_write_value_ext(self, i); + + lxw_xml_end_tag(self->file, "extLst"); + lxw_xml_end_tag(self->file, "bk"); + + } + + lxw_xml_end_tag(self->file, "futureMetadata"); + + LXW_FREE_ATTRIBUTES(); +} + /* * Write the element. */ STATIC void -_metadata_write_rc(lxw_metadata *self) +_metadata_write_rc(lxw_metadata *self, uint8_t type, uint32_t index) { struct xml_attribute_list attributes; struct xml_attribute *attribute; LXW_INIT_ATTRIBUTES(); - LXW_PUSH_ATTRIBUTES_STR("t", "1"); - LXW_PUSH_ATTRIBUTES_STR("v", "0"); + LXW_PUSH_ATTRIBUTES_INT("t", type); + LXW_PUSH_ATTRIBUTES_INT("v", index); lxw_xml_empty_tag(self->file, "rc", &attributes); @@ -228,7 +344,7 @@ _metadata_write_rc(lxw_metadata *self) } /* - * Write the element. + * Write the element for dynamic functions. */ STATIC void _metadata_write_cell_metadata(lxw_metadata *self) @@ -244,7 +360,7 @@ _metadata_write_cell_metadata(lxw_metadata *self) lxw_xml_start_tag(self->file, "bk", NULL); /* Write the rc element. */ - _metadata_write_rc(self); + _metadata_write_rc(self, 1, 0); lxw_xml_end_tag(self->file, "bk"); @@ -253,6 +369,39 @@ _metadata_write_cell_metadata(lxw_metadata *self) LXW_FREE_ATTRIBUTES(); } +/* + * Write the element for embedded images. + */ +STATIC void +_metadata_write_value_metadata(lxw_metadata *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint8_t type = 1; + uint32_t i; + + if (self->has_dynamic_functions) + type = 2; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->num_embedded_images); + + lxw_xml_start_tag(self->file, "valueMetadata", &attributes); + + for (i = 0; i < self->num_embedded_images; i++) { + lxw_xml_start_tag(self->file, "bk", NULL); + + /* Write the rc element. */ + _metadata_write_rc(self, type, i); + + lxw_xml_end_tag(self->file, "bk"); + } + + lxw_xml_end_tag(self->file, "valueMetadata"); + + LXW_FREE_ATTRIBUTES(); +} + /***************************************************************************** * * XML file assembly functions. @@ -275,10 +424,16 @@ lxw_metadata_assemble_xml_file(lxw_metadata *self) _metadata_write_metadata_types(self); /* Write the futureMetadata element. */ - _metadata_write_future_metadata(self); + if (self->has_dynamic_functions) + _metadata_write_cell_future_metadata(self); + if (self->has_embedded_images) + _metadata_write_value_future_metadata(self); /* Write the cellMetadata element. */ - _metadata_write_cell_metadata(self); + if (self->has_dynamic_functions) + _metadata_write_cell_metadata(self); + if (self->has_embedded_images) + _metadata_write_value_metadata(self); lxw_xml_end_tag(self->file, "metadata"); } diff --git a/src/packager.c b/src/packager.c index bf156d38..c455f653 100644 --- a/src/packager.c +++ b/src/packager.c @@ -383,9 +383,43 @@ _write_image_files(lxw_packager *self) else worksheet = sheet->u.worksheet; - if (STAILQ_EMPTY(worksheet->image_props)) + if (STAILQ_EMPTY(worksheet->image_props) + && STAILQ_EMPTY(worksheet->embedded_image_props)) continue; + STAILQ_FOREACH(object_props, worksheet->embedded_image_props, + list_pointers) { + + if (object_props->is_duplicate) + continue; + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/media/image%d.%s", index++, + object_props->extension); + + if (!object_props->is_image_buffer) { + /* Check that the image file exists and can be opened. */ + image_stream = lxw_fopen(object_props->filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("Error adding image to xlsx file: file " + "doesn't exist or can't be opened: %s.", + object_props->filename); + return LXW_ERROR_CREATING_TMPFILE; + } + + err = _add_file_to_zip(self, image_stream, filename); + fclose(image_stream); + } + else { + err = _add_buffer_to_zip(self, + object_props->image_buffer, + object_props->image_buffer_size, + filename); + } + + RETURN_ON_ERROR(err); + } + STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) { if (object_props->is_duplicate) @@ -1053,6 +1087,10 @@ _write_metadata_file(lxw_packager *self) goto mem_error; } + metadata->has_embedded_images = self->workbook->has_embedded_images; + metadata->num_embedded_images = self->workbook->num_embedded_images; + metadata->has_dynamic_functions = self->workbook->has_dynamic_functions; + lxw_metadata_assemble_xml_file(metadata); err = _add_to_zip(self, metadata->file, &buffer, &buffer_size, @@ -1067,6 +1105,176 @@ _write_metadata_file(lxw_packager *self) return err; } +/* + * Write the rdrichvalue.xml file. + */ +STATIC lxw_error +_write_rich_value_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_rich_value *rich_value; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_embedded_images) + 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->file = + lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rich_value->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_rich_value_assemble_xml_file(rich_value); + + err = _add_to_zip(self, rich_value->file, &buffer, &buffer_size, + "xl/richData/rdrichvalue.xml"); + + fclose(rich_value->file); + free(buffer); + +mem_error: + lxw_rich_value_free(rich_value); + + return err; +} + +/* + * Write the richValueRel.xml file. + */ +STATIC lxw_error +_write_rich_value_rel_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_rich_value_rel *rich_value_rel; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_embedded_images) + 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->file = + lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rich_value_rel->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_rich_value_rel_assemble_xml_file(rich_value_rel); + + err = _add_to_zip(self, rich_value_rel->file, &buffer, &buffer_size, + "xl/richData/richValueRel.xml"); + + fclose(rich_value_rel->file); + free(buffer); + +mem_error: + lxw_rich_value_rel_free(rich_value_rel); + + return err; +} + +/* + * Write the rdRichValueTypes.xml file. + */ +STATIC lxw_error +_write_rich_value_types_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_rich_value_types *rich_value_types; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_embedded_images) + 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; + } + + rich_value_types->file = + lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rich_value_types->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_rich_value_types_assemble_xml_file(rich_value_types); + + err = _add_to_zip(self, rich_value_types->file, &buffer, &buffer_size, + "xl/richData/rdRichValueTypes.xml"); + + fclose(rich_value_types->file); + free(buffer); + +mem_error: + lxw_rich_value_types_free(rich_value_types); + + return err; +} + +/* + * Write the rdrichvaluestructure.xml file. + */ +STATIC lxw_error +_write_rich_value_structure_file(lxw_packager *self) +{ + lxw_error err = LXW_NO_ERROR; + lxw_rich_value_structure *rich_value_structure; + char *buffer = NULL; + size_t buffer_size = 0; + + if (!self->workbook->has_embedded_images) + 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->file = + lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rich_value_structure->file) { + err = LXW_ERROR_CREATING_TMPFILE; + goto mem_error; + } + + lxw_rich_value_structure_assemble_xml_file(rich_value_structure); + + err = _add_to_zip(self, rich_value_structure->file, &buffer, &buffer_size, + "xl/richData/rdrichvaluestructure.xml"); + + fclose(rich_value_structure->file); + free(buffer); + +mem_error: + lxw_rich_value_structure_free(rich_value_structure); + + return err; +} + /* * Write the custom.xml file. */ @@ -1327,6 +1535,9 @@ _write_content_types_file(lxw_packager *self) if (workbook->has_metadata) lxw_ct_add_metadata(content_types); + if (workbook->has_embedded_images) + lxw_ct_add_rich_value(content_types); + lxw_content_types_assemble_xml_file(content_types); err = _add_to_zip(self, content_types->file, &buffer, &buffer_size, @@ -1397,6 +1608,9 @@ _write_workbook_rels_file(lxw_packager *self) if (workbook->has_metadata) lxw_add_document_relationship(rels, "/sheetMetadata", "metadata.xml"); + if (workbook->has_embedded_images) + lxw_add_rich_value_relationship(rels); + lxw_relationships_assemble_xml_file(rels); err = _add_to_zip(self, rels->file, &buffer, &buffer_size, @@ -1715,6 +1929,75 @@ _write_vba_project_rels_file(lxw_packager *self) return err; } +/* + * Write the richValueRel.xml.rels files for embedded images. + */ +STATIC lxw_error +_write_rich_value_rels_file(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_object_properties *object_props; + + lxw_relationships *rels; + char *buffer = NULL; + size_t buffer_size = 0; + char sheetname[LXW_FILENAME_LENGTH] = { 0 }; + char target[LXW_FILENAME_LENGTH] = { 0 }; + lxw_error err = LXW_NO_ERROR; + uint32_t index = 1; + + if (!workbook->has_embedded_images) + return LXW_NO_ERROR; + + rels = lxw_relationships_new(); + + rels->file = lxw_get_filehandle(&buffer, &buffer_size, self->tmpdir); + if (!rels->file) { + lxw_free_relationships(rels); + return LXW_ERROR_CREATING_TMPFILE; + } + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (STAILQ_EMPTY(worksheet->embedded_image_props)) + continue; + + STAILQ_FOREACH(object_props, worksheet->embedded_image_props, + list_pointers) { + + if (object_props->is_duplicate) + continue; + + lxw_snprintf(target, LXW_FILENAME_LENGTH, + "../media/image%d.%s", index++, + object_props->extension); + + lxw_add_document_relationship(rels, "/image", target); + + } + + } + + lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, + "xl/richData/_rels/richValueRel.xml.rels"); + + lxw_relationships_assemble_xml_file(rels); + + err = _add_to_zip(self, rels->file, &buffer, &buffer_size, sheetname); + + fclose(rels->file); + free(buffer); + lxw_free_relationships(rels); + + return err; +} + /* * Write the _rels/.rels xml file. */ @@ -1954,6 +2237,21 @@ lxw_create_package(lxw_packager *self) error = _write_metadata_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_rich_value_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_rich_value_rel_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_rich_value_types_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_rich_value_structure_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + + error = _write_rich_value_rels_file(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_app_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); diff --git a/src/relationships.c b/src/relationships.c index a3940bd2..116dc3c8 100644 --- a/src/relationships.c +++ b/src/relationships.c @@ -244,3 +244,26 @@ lxw_add_worksheet_relationship(lxw_relationships *self, const char *type, { _add_relationship(self, LXW_SCHEMA_DOCUMENT, type, target, target_mode); } + +/* + * Add a richValue relationship to sheet .rels xml files. + */ +void +lxw_add_rich_value_relationship(lxw_relationships *self) +{ + _add_relationship(self, + "http://schemas.microsoft.com/office/2022/10/relationships/", + "richValueRel", "richData/richValueRel.xml", NULL); + _add_relationship(self, + "http://schemas.microsoft.com/office/2017/06/relationships/", + "rdRichValue", "richData/rdrichvalue.xml", NULL); + _add_relationship(self, + "http://schemas.microsoft.com/office/2017/06/relationships/", + "rdRichValueStructure", + "richData/rdrichvaluestructure.xml", NULL); + _add_relationship(self, + "http://schemas.microsoft.com/office/2017/06/relationships/", + "rdRichValueTypes", "richData/rdRichValueTypes.xml", + NULL); + +} diff --git a/src/rich_value.c b/src/rich_value.c new file mode 100644 index 00000000..f5476bad --- /dev/null +++ b/src/rich_value.c @@ -0,0 +1,190 @@ +/***************************************************************************** + * rich_value - A library for creating Excel XLSX rich_value files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/rich_value.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new rich_value object. + */ +lxw_rich_value * +lxw_rich_value_new(void) +{ + lxw_rich_value *rich_value = calloc(1, sizeof(lxw_rich_value)); + GOTO_LABEL_ON_MEM_ERROR(rich_value, mem_error); + + return rich_value; + +mem_error: + lxw_rich_value_free(rich_value); + return NULL; +} + +/* + * Free a rich_value object. + */ +void +lxw_rich_value_free(lxw_rich_value *rich_value) +{ + if (!rich_value) + return; + + free(rich_value); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_rich_value_xml_declaration(lxw_rich_value *self) +{ + lxw_xml_declaration(self->file); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write the element. + */ +STATIC void +_rich_value_write_v(lxw_rich_value *self, char *value) +{ + lxw_xml_data_element(self->file, "v", value, NULL); + +} + +/* + * Write the element. + */ +STATIC void +_rich_value_write_rv(lxw_rich_value *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("s", "0"); + + lxw_xml_start_tag(self->file, "rv", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the metadata for each embedded image. + */ +void +lxw_rich_value_write_images(lxw_rich_value *self) +{ + + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_object_properties *object_props; + char value[LXW_UINT32_T_LENGTH]; + uint32_t index = 0; + uint8_t type = 5; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + STAILQ_FOREACH(object_props, worksheet->embedded_image_props, + list_pointers) { + + if (object_props->is_duplicate) + continue; + + if (object_props->decorative) + type = 6; + + /* Write the rv element. */ + _rich_value_write_rv(self); + + /* Write the v element. */ + lxw_snprintf(value, LXW_UINT32_T_LENGTH, "%u", index); + _rich_value_write_v(self, value); + + lxw_snprintf(value, LXW_UINT32_T_LENGTH, "%u", type); + _rich_value_write_v(self, value); + + lxw_xml_end_tag(self->file, "rv"); + + index++; + + } + } + +} + +/* + * Write the element. + */ +STATIC void +_rich_value_write_rv_data(lxw_rich_value *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_INT("count", self->workbook->num_embedded_images); + + lxw_xml_start_tag(self->file, "rvData", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Assemble and write the XML file. + */ +void +lxw_rich_value_assemble_xml_file(lxw_rich_value *self) +{ + /* Write the XML declaration. */ + _rich_value_xml_declaration(self); + + /* Write the rvData element. */ + _rich_value_write_rv_data(self); + + lxw_rich_value_write_images(self); + + lxw_xml_end_tag(self->file, "rvData"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/rich_value_rel.c b/src/rich_value_rel.c new file mode 100644 index 00000000..2bda9096 --- /dev/null +++ b/src/rich_value_rel.c @@ -0,0 +1,142 @@ +/***************************************************************************** + * rich_value_rel - A library for creating Excel XLSX rich_value_rel files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/rich_value_rel.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new rich_value_rel object. + */ +lxw_rich_value_rel * +lxw_rich_value_rel_new(void) +{ + lxw_rich_value_rel *rich_value_rel = + calloc(1, sizeof(lxw_rich_value_rel)); + GOTO_LABEL_ON_MEM_ERROR(rich_value_rel, mem_error); + + return rich_value_rel; + +mem_error: + lxw_rich_value_rel_free(rich_value_rel); + return NULL; +} + +/* + * Free a rich_value_rel object. + */ +void +lxw_rich_value_rel_free(lxw_rich_value_rel *rich_value_rel) +{ + if (!rich_value_rel) + return; + + free(rich_value_rel); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_rich_value_rel_xml_declaration(lxw_rich_value_rel *self) +{ + lxw_xml_declaration(self->file); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write the element. + */ +STATIC void +_rich_value_rel_write_rel(lxw_rich_value_rel *self, uint32_t rel_index) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; + + lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", rel_index); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "rel", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_rel_write_rich_value_rels(lxw_rich_value_rel *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.microsoft.com/office/spreadsheetml/2022/richvaluerel"; + char xmlns_r[] = + "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); + + lxw_xml_start_tag(self->file, "richValueRels", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Assemble and write the XML file. + */ +void +lxw_rich_value_rel_assemble_xml_file(lxw_rich_value_rel *self) +{ + uint32_t i; + + /* Write the XML declaration. */ + _rich_value_rel_xml_declaration(self); + + /* Write the richValueRels element. */ + _rich_value_rel_write_rich_value_rels(self); + + for (i = 1; i <= self->num_embedded_images; i++) { + /* Write the rel element. */ + _rich_value_rel_write_rel(self, i); + } + + lxw_xml_end_tag(self->file, "richValueRels"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/rich_value_structure.c b/src/rich_value_structure.c new file mode 100644 index 00000000..43bd7b58 --- /dev/null +++ b/src/rich_value_structure.c @@ -0,0 +1,176 @@ +/***************************************************************************** + * rich_value_structure - A library for creating Excel XLSX rich_value_structure files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/rich_value_structure.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new rich_value_structure object. + */ +lxw_rich_value_structure * +lxw_rich_value_structure_new(void) +{ + lxw_rich_value_structure *rich_value_structure = + calloc(1, sizeof(lxw_rich_value_structure)); + GOTO_LABEL_ON_MEM_ERROR(rich_value_structure, mem_error); + + return rich_value_structure; + +mem_error: + lxw_rich_value_structure_free(rich_value_structure); + return NULL; +} + +/* + * Free a rich_value_structure object. + */ +void +lxw_rich_value_structure_free(lxw_rich_value_structure *rich_value_structure) +{ + if (!rich_value_structure) + return; + + free(rich_value_structure); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_rich_value_structure_xml_declaration(lxw_rich_value_structure *self) +{ + lxw_xml_declaration(self->file); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write the element. + */ +STATIC void +_rich_value_structure_write_k2(lxw_rich_value_structure *self) +{ + 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_xml_empty_tag(self->file, "k", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_structure_write_s(lxw_rich_value_structure *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("t", "_localImage"); + + lxw_xml_start_tag(self->file, "s", &attributes); + + /* Write the k element. */ + _rich_value_structure_write_k1(self); + _rich_value_structure_write_k2(self); + + lxw_xml_end_tag(self->file, "s"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_structure_write_rv_structures(lxw_rich_value_structure *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("count", "1"); + + lxw_xml_start_tag(self->file, "rvStructures", &attributes); + + /* Write the s element. */ + _rich_value_structure_write_s(self); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Assemble and write the XML file. + */ +void +lxw_rich_value_structure_assemble_xml_file(lxw_rich_value_structure *self) +{ + /* Write the XML declaration. */ + _rich_value_structure_xml_declaration(self); + + /* Write the rvStructures element. */ + _rich_value_structure_write_rv_structures(self); + + lxw_xml_end_tag(self->file, "rvStructures"); + +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/rich_value_types.c b/src/rich_value_types.c new file mode 100644 index 00000000..cb3e8134 --- /dev/null +++ b/src/rich_value_types.c @@ -0,0 +1,199 @@ +/***************************************************************************** + * rich_value_types - A library for creating Excel XLSX rich_value_types files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/rich_value_types.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new rich_value_types object. + */ +lxw_rich_value_types * +lxw_rich_value_types_new(void) +{ + lxw_rich_value_types *rich_value_types = + calloc(1, sizeof(lxw_rich_value_types)); + GOTO_LABEL_ON_MEM_ERROR(rich_value_types, mem_error); + + return rich_value_types; + +mem_error: + lxw_rich_value_types_free(rich_value_types); + return NULL; +} + +/* + * Free a rich_value_types object. + */ +void +lxw_rich_value_types_free(lxw_rich_value_types *rich_value_types) +{ + if (!rich_value_types) + return; + + free(rich_value_types); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_rich_value_types_xml_declaration(lxw_rich_value_types *self) +{ + lxw_xml_declaration(self->file); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Write the element. + */ +STATIC void +_rich_value_types_write_rv_types_info(lxw_rich_value_types *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata2"; + char xmlns_mc[] = + "http://schemas.openxmlformats.org/markup-compatibility/2006"; + char xmlns_x[] = + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + char mc_ignorable[] = "x"; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_STR("xmlns:mc", xmlns_mc); + LXW_PUSH_ATTRIBUTES_STR("mc:Ignorable", mc_ignorable); + LXW_PUSH_ATTRIBUTES_STR("xmlns:x", xmlns_x); + + lxw_xml_start_tag(self->file, "rvTypesInfo", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_types_write_flag(lxw_rich_value_types *self, char *name) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", name); + LXW_PUSH_ATTRIBUTES_STR("value", "1"); + + lxw_xml_empty_tag(self->file, "flag", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_types_write_key(lxw_rich_value_types *self, char *name) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("name", name); + + lxw_xml_start_tag(self->file, "key", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_rich_value_types_write_key_flags(lxw_rich_value_types *self) +{ + int i; + char *key_flags[10][3] = { + {"_Self", "ExcludeFromFile", "ExcludeFromCalcComparison"}, + {"_DisplayString", "ExcludeFromCalcComparison", ""}, + {"_Flags", "ExcludeFromCalcComparison", ""}, + {"_Format", "ExcludeFromCalcComparison", ""}, + {"_SubLabel", "ExcludeFromCalcComparison", ""}, + {"_Attribution", "ExcludeFromCalcComparison", ""}, + {"_Icon", "ExcludeFromCalcComparison", ""}, + {"_Display", "ExcludeFromCalcComparison", ""}, + {"_CanonicalPropertyNames", "ExcludeFromCalcComparison", ""}, + {"_ClassificationId", "ExcludeFromCalcComparison", ""}, + }; + + lxw_xml_start_tag(self->file, "global", NULL); + lxw_xml_start_tag(self->file, "keyFlags", NULL); + + for (i = 0; i < 10; i++) { + char **flags = key_flags[i]; + + _rich_value_types_write_key(self, flags[0]); + _rich_value_types_write_flag(self, flags[1]); + + if (*flags[2]) { + _rich_value_types_write_flag(self, flags[2]); + + } + + lxw_xml_end_tag(self->file, "key"); + } + + lxw_xml_end_tag(self->file, "keyFlags"); + lxw_xml_end_tag(self->file, "global"); +} + +/* + * Assemble and write the XML file. + */ +void +lxw_rich_value_types_assemble_xml_file(lxw_rich_value_types *self) +{ + /* Write the XML declaration. */ + _rich_value_types_xml_declaration(self); + + /* Write the rvTypesInfo element. */ + _rich_value_types_write_rv_types_info(self); + + /* Write the keyFlags element. */ + _rich_value_types_write_key_flags(self); + + lxw_xml_end_tag(self->file, "rvTypesInfo"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/workbook.c b/src/workbook.c index 0c8b22b3..dbd2f28c 100644 --- a/src/workbook.c +++ b/src/workbook.c @@ -217,6 +217,7 @@ 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) { @@ -231,12 +232,30 @@ lxw_workbook_free(lxw_workbook *workbook) free(workbook->image_md5s); } + if (workbook->embedded_image_md5s) { + for (image_md5 = + RB_MIN(lxw_image_md5s, workbook->embedded_image_md5s); image_md5; + image_md5 = next_image_md5) { + + next_image_md5 = + RB_NEXT(lxw_image_md5s, workbook->embedded_image_md5s, + image_md5); + RB_REMOVE(lxw_image_md5s, workbook->embedded_image_md5s, + image_md5); + free(image_md5->md5); + free(image_md5); + } + + free(workbook->embedded_image_md5s); + } + if (workbook->header_image_md5s) { for (image_md5 = RB_MIN(lxw_image_md5s, workbook->header_image_md5s); image_md5; image_md5 = next_image_md5) { next_image_md5 = - RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); + RB_NEXT(lxw_image_md5s, workbook->header_image_md5s, + image_md5); RB_REMOVE(lxw_image_md5s, workbook->header_image_md5s, image_md5); free(image_md5->md5); free(image_md5); @@ -250,7 +269,7 @@ lxw_workbook_free(lxw_workbook *workbook) image_md5; image_md5 = next_image_md5) { next_image_md5 = - RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5); + RB_NEXT(lxw_image_md5s, workbook->background_md5s, image_md5); RB_REMOVE(lxw_image_md5s, workbook->background_md5s, image_md5); free(image_md5->md5); free(image_md5); @@ -1082,6 +1101,7 @@ _prepare_drawings(lxw_workbook *self) } if (STAILQ_EMPTY(worksheet->image_props) + && STAILQ_EMPTY(worksheet->embedded_image_props) && STAILQ_EMPTY(worksheet->chart_data) && !worksheet->has_header_vml && !worksheet->has_background_image) { continue; @@ -1089,6 +1109,44 @@ _prepare_drawings(lxw_workbook *self) drawing_id++; + /* Prepare embedded worksheet images. */ + STAILQ_FOREACH(object_props, worksheet->embedded_image_props, + list_pointers) { + + _store_image_type(self, object_props->image_type); + + /* Check for duplicate images and only store the first instance. */ + if (object_props->md5) { + tmp_image_md5.md5 = object_props->md5; + found_duplicate_image = RB_FIND(lxw_image_md5s, + self->embedded_image_md5s, + &tmp_image_md5); + } + + if (found_duplicate_image) { + ref_id = found_duplicate_image->id; + object_props->is_duplicate = LXW_TRUE; + } + else { + image_ref_id++; + ref_id = image_ref_id; + self->num_embedded_images++; + +#ifndef USE_NO_MD5 + new_image_md5 = calloc(1, sizeof(lxw_image_md5)); +#endif + if (new_image_md5 && object_props->md5) { + new_image_md5->id = ref_id; + new_image_md5->md5 = lxw_strdup(object_props->md5); + + RB_INSERT(lxw_image_md5s, self->embedded_image_md5s, + new_image_md5); + } + } + + worksheet_set_error_cell(worksheet, object_props, ref_id); + } + /* Prepare background images. */ if (worksheet->has_background_image) { @@ -1830,6 +1888,11 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options) GOTO_LABEL_ON_MEM_ERROR(workbook->image_md5s, mem_error); RB_INIT(workbook->image_md5s); + /* Add the embedded image MD5 tree. */ + workbook->embedded_image_md5s = calloc(1, sizeof(struct lxw_image_md5s)); + GOTO_LABEL_ON_MEM_ERROR(workbook->embedded_image_md5s, mem_error); + RB_INIT(workbook->embedded_image_md5s); + /* Add the header image MD5 tree. */ workbook->header_image_md5s = calloc(1, sizeof(struct lxw_image_md5s)); GOTO_LABEL_ON_MEM_ERROR(workbook->header_image_md5s, mem_error); @@ -2153,8 +2216,16 @@ workbook_close(lxw_workbook *self) if (worksheet->index == self->active_sheet) worksheet->active = LXW_TRUE; - if (worksheet->has_dynamic_arrays) + if (worksheet->has_dynamic_functions) { self->has_metadata = LXW_TRUE; + self->has_dynamic_functions = LXW_TRUE; + + } + + if (!STAILQ_EMPTY(worksheet->embedded_image_props)) { + self->has_metadata = LXW_TRUE; + self->has_embedded_images = LXW_TRUE; + } } /* Set workbook and worksheet VBA codenames if a macro has been added. */ diff --git a/src/worksheet.c b/src/worksheet.c index df13a073..44c2b28f 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -142,6 +142,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->image_props, mem_error); STAILQ_INIT(worksheet->image_props); + worksheet->embedded_image_props = + calloc(1, sizeof(struct lxw_embedded_image_props)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->embedded_image_props, mem_error); + STAILQ_INIT(worksheet->embedded_image_props); + worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_props)); GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error); STAILQ_INIT(worksheet->chart_data); @@ -368,7 +373,8 @@ _free_cell(lxw_cell *cell) return; if (cell->type != NUMBER_CELL && cell->type != STRING_CELL - && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) { + && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL + && cell->type != ERROR_CELL) { free((void *) cell->u.string); } @@ -607,6 +613,17 @@ lxw_worksheet_free(lxw_worksheet *worksheet) free(worksheet->image_props); } + if (worksheet->embedded_image_props) { + while (!STAILQ_EMPTY(worksheet->embedded_image_props)) { + object_props = STAILQ_FIRST(worksheet->embedded_image_props); + STAILQ_REMOVE_HEAD(worksheet->embedded_image_props, + list_pointers); + _free_object_properties(object_props); + } + + free(worksheet->embedded_image_props); + } + if (worksheet->chart_data) { while (!STAILQ_EMPTY(worksheet->chart_data)) { object_props = STAILQ_FIRST(worksheet->chart_data); @@ -991,6 +1008,25 @@ _new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value, return cell; } +/* + * Create a new worksheet error cell object. + */ +STATIC lxw_cell * +_new_error_cell(lxw_row_t row_num, lxw_col_t col_num, uint32_t value, + 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 = ERROR_CELL; + cell->format = format; + cell->u.number = value; + + return cell; +} + /* * Create a new comment cell object. */ @@ -4522,6 +4558,15 @@ _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell) lxw_xml_data_element(self->file, "v", data, NULL); } +/* + * Write out a error worksheet cell. + */ +STATIC void +_write_error_cell(lxw_worksheet *self) +{ + lxw_xml_data_element(self->file, "v", "#VALUE!", NULL); +} + /* * Calculate the "spans" attribute of the tag. This is an XLSX * optimization and isn't strictly required. However, it makes comparing @@ -4652,6 +4697,13 @@ _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format) _write_array_formula_num_cell(self, cell); lxw_xml_end_tag(self->file, "c"); } + else if (cell->type == ERROR_CELL) { + LXW_PUSH_ATTRIBUTES_STR("t", "e"); + LXW_PUSH_ATTRIBUTES_DBL("vm", cell->u.number); + lxw_xml_start_tag(self->file, "c", &attributes); + _write_error_cell(self); + lxw_xml_end_tag(self->file, "c"); + } LXW_FREE_ATTRIBUTES(); } @@ -8093,7 +8145,7 @@ _store_array_formula(lxw_worksheet *self, _insert_cell(self, first_row, first_col, cell); if (is_dynamic) - self->has_dynamic_arrays = LXW_TRUE; + self->has_dynamic_functions = LXW_TRUE; /* Pad out the rest of the area with formatted zeroes. */ if (!self->optimize) { @@ -10591,6 +10643,108 @@ worksheet_insert_image_buffer(lxw_worksheet *self, image_buffer, image_size, NULL); } +/* + * Embed an image with options into the worksheet. + */ +lxw_error +worksheet_embed_image_opt(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + const char *filename, + lxw_image_options *user_options) +{ + FILE *image_stream; + const char *description; + lxw_object_properties *object_props; + lxw_error err; + + if (!filename) { + LXW_WARN("worksheet_embed_image()/_opt(): " + "filename must be specified."); + return LXW_ERROR_NULL_PARAMETER_IGNORED; + } + + /* Check that the image file exists and can be opened. */ + image_stream = lxw_fopen(filename, "rb"); + if (!image_stream) { + LXW_WARN_FORMAT1("worksheet_embed_image()/_opt(): " + "file doesn't exist or can't be opened: %s.", + filename); + 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; + } + + 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; + } + + 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->description) + description = 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; + + 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; + } + +} + +/* + * Embed an image into the worksheet. + */ +lxw_error +worksheet_embed_image(lxw_worksheet *self, + lxw_row_t row_num, lxw_col_t col_num, + const char *filename) +{ + return worksheet_embed_image_opt(self, row_num, col_num, filename, NULL); +} + /* * Set an image as a worksheet background. */ @@ -11421,3 +11575,18 @@ worksheet_ignore_errors(lxw_worksheet *self, uint8_t type, const char *range) return LXW_NO_ERROR; } + +/* + * Write an error cell for versions of Excel that don't support embedded images. + */ +void +worksheet_set_error_cell(lxw_worksheet *self, + lxw_object_properties *object_props, uint32_t ref_id) +{ + 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); + _insert_cell(self, row_num, col_num, cell); + +} diff --git a/test/functional/src/test_embed_image01.c b/test/functional/src/test_embed_image01.c new file mode 100644 index 00000000..e46b7113 --- /dev/null +++ b/test/functional/src/test_embed_image01.c @@ -0,0 +1,21 @@ +/***************************************************************************** + * 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_image01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet, CELL("A1"), "images/red.png"); + + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image02.c b/test/functional/src/test_embed_image02.c new file mode 100644 index 00000000..f891975e --- /dev/null +++ b/test/functional/src/test_embed_image02.c @@ -0,0 +1,21 @@ +/***************************************************************************** + * 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_image02.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet, 0, 0, "images/red.png"); + worksheet_embed_image(worksheet, 8, 4, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image03.c b/test/functional/src/test_embed_image03.c new file mode 100644 index 00000000..820a361d --- /dev/null +++ b/test/functional/src/test_embed_image03.c @@ -0,0 +1,21 @@ +/***************************************************************************** + * 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_image03.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet, 0, 0, "images/red.png"); + worksheet_embed_image(worksheet, 8, 4, "images/blue.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image04.c b/test/functional/src/test_embed_image04.c new file mode 100644 index 00000000..8550a7a0 --- /dev/null +++ b/test/functional/src/test_embed_image04.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_image04.xlsx"); + lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet1, 0, 0, "images/red.png"); + worksheet_embed_image(worksheet2, 8, 4, "images/blue.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image05.c b/test/functional/src/test_embed_image05.c new file mode 100644 index 00000000..0ad6d987 --- /dev/null +++ b/test/functional/src/test_embed_image05.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_image05.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_write_dynamic_array_formula(worksheet, 0, 0, 2, 0, "=LEN(B1:B3)", NULL); + + worksheet_embed_image(worksheet, 8, 4, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image06.c b/test/functional/src/test_embed_image06.c new file mode 100644 index 00000000..ec5ec8b2 --- /dev/null +++ b/test/functional/src/test_embed_image06.c @@ -0,0 +1,21 @@ +/***************************************************************************** + * 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_image06.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet, 0, 0, "images/red.png"); + worksheet_insert_image(worksheet, 8, 4, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image07.c b/test/functional/src/test_embed_image07.c new file mode 100644 index 00000000..ef5300d8 --- /dev/null +++ b/test/functional/src/test_embed_image07.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_image07.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet, 0, 0, "images/red.png"); + worksheet_embed_image(worksheet, 2, 0, "images/blue.png"); + worksheet_insert_image(worksheet, 8, 4, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image11.c b/test/functional/src/test_embed_image11.c new file mode 100644 index 00000000..4744f6c8 --- /dev/null +++ b/test/functional/src/test_embed_image11.c @@ -0,0 +1,23 @@ +/***************************************************************************** + * 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_image11.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, 0, 0, 11, NULL); + worksheet_set_row(worksheet, 0, 72, NULL); + + worksheet_embed_image(worksheet, 0, 0, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_embed_image13.c b/test/functional/src/test_embed_image13.c new file mode 100644 index 00000000..11f527f3 --- /dev/null +++ b/test/functional/src/test_embed_image13.c @@ -0,0 +1,34 @@ +/***************************************************************************** + * 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_image13.xlsx"); + lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet1, 0, 0, "images/red.png"); + worksheet_embed_image(worksheet1, 2, 0, "images/blue.png"); + worksheet_embed_image(worksheet1, 4, 0, "images/yellow.png"); + + lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet2, 0, 0, "images/yellow.png"); + worksheet_embed_image(worksheet2, 2, 0, "images/red.png"); + worksheet_embed_image(worksheet2, 4, 0, "images/blue.png"); + + lxw_worksheet *worksheet3 = workbook_add_worksheet(workbook, NULL); + + worksheet_embed_image(worksheet3, 0, 0, "images/blue.png"); + worksheet_embed_image(worksheet3, 2, 0, "images/yellow.png"); + worksheet_embed_image(worksheet3, 4, 0, "images/red.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/test_embed_image.py b/test/functional/test_embed_image.py new file mode 100644 index 00000000..742aeedd --- /dev/null +++ b/test/functional/test_embed_image.py @@ -0,0 +1,44 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright 2014-2024, John McNamara, jmcnamara@cpan.org. +# + +import os +import pytest +import base_test_class + +class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): + """ + Test file created with libxlsxwriter against a file created by Excel. + + """ + + def test_embed_image01(self): + self.run_exe_test('test_embed_image01') + + def test_embed_image02(self): + self.run_exe_test('test_embed_image02') + + def test_embed_image03(self): + self.run_exe_test('test_embed_image03') + + def test_embed_image04(self): + self.run_exe_test('test_embed_image04') + + def test_embed_image05(self): + self.run_exe_test('test_embed_image05') + + def test_embed_image06(self): + self.run_exe_test('test_embed_image06') + + def test_embed_image07(self): + self.run_exe_test('test_embed_image07') + + def test_embed_image11(self): + self.run_exe_test('test_embed_image11') + + def test_embed_image13(self): + self.run_exe_test('test_embed_image13') diff --git a/test/functional/xlsx_files/embed_image01.xlsx b/test/functional/xlsx_files/embed_image01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cd3818c6452e73123e5e668a47f6c88603dc46e2 GIT binary patch literal 8567 zcma)Bby(D0w;kz}kdkhsyF{eB1*8=i1{}Iu>5^_ix&$O-fKjAD8eyaxB&8%oI_{wN zzM_8b<@@7IJw+-{mf{iUZT1XLxdX)LP+l_dc^->X1zBdOa+;$)kwQv*cC`S{Rj&0#xO_^z3Dk+FgQl*XnkvyFpLuR`OmUW6!al{#?Wx(-J=RNwWjR(q~z_NZ9Po_gPgud=}Kn*`(3LdZv2s~$ay zzNyuQuoDz!lMROb09Owx$5&LnGiB?Tw<%hu@K@(4j2R45Bn(`KxLFENJLMaBbjAAB zmWf{?Hn~x)_|#`!d`JhEz-$4H1Wb=o2QAKgV$v@JP&r2(c5&4IAsPUn0X%yDYS0Z% z8!jSz!HG7r1FAdP**kHX*xUcZGN7Oj|8o7oa(Y~|>N*#$%z?MeqNvLQnmsLH*+O64 zZRva>T{4RST_Nbw?VP0Y4u#<2D3 zd{gdcGyZh<`@bpO6hY9DaG`7$ca=AE-W!E=uV>(73ZBI5=DoYwOj>&9Ebr9LdGCu= zx2+p#$=qPJyo42O>l5tJvcH;A#YlGx4|0#b9#V57={%)YV`U-_UjCOSpZpl9c=@Qn zR!&LvEP<4-%+jCB3Y(%%W|LH5%WE_o9tL9rvgd$KF`8RiFERD7g6UNwy^$1-2^@f( z2=_-=30H~dO%?BPy+L1izry-#n?mh4seaE!or66ldae1Lpf?7YPcB;2`aPTHF1fT? zt0?U*(bx`|?{YTJdSq+ZwdDHyC47$hLr^)wkJ!{^?N>T7<{sPWJwy1Nz%J{EF$Oq+ z>IeV;-c#1eR1ndWSDRTZBdhUt`@bZ)9gy2YQRi zujD=Xw1~x5ES43oVfx0oI==Uk^gS8~sbnKlHqu>%9)78}}D!Vtito865QXIaDP8`1X(+Aa$LUt$cRx|KA_@R9ktp z?EG5lylFW$#OqouxsH;9&k5Il7#WVIW%LOW`@`5#rpj)j6EXmwI zv>NK#6t!1ew&i@><+~hM1nm=_c+XIH#y!AqZEIk+8oOzdLv5jd+I`1NnIg>3V`wWa zSZ6t#MIE+c^l-HHRPlVzytX;$4{j)aO}qSK#^6Y?pyATV9b*2Q6O*dN;^sS8IrI3tA9?%;w-RPymboJ7 ztmy?{qPtR~4^6k81tWku(uZ31g!44tZ-C9Zuyn=d(g_+EX~I{CH_?|G_Jw;Z*={a> z;yn2L3<&Z>JD{|eydYRh-Dk2Uj`KANH?t?{P%KkaywweRp=!}Zt~<=^A~*5+nSeJJ zniMc@p6nMPq$ROBMm%Q6GH^nO;e?R1`;j4qsvZ zOO23r9Ttc_BpDiIXrYk=jJM5wDbx^NrG>)S(sCXmp`DLmsOd2D#NA)ln!)R&Od~zu z9(!VW(9^&+mH=%8jf^Ou8wTnDD-qiP)tG6s?(vdDVClYeh`RTFkx-SakEdX6t{|&o z$zsKl8)=66Ft%6c={*~Ez2%~Oi7~vdcW+a&=+V!Z;l<6QZi}MjjDA+k*gqP!{SJ9r zr<^;Z#&Npi8y^xj)z@teq0nypDF4k(+?e z)$(v)1M|2}kHJZi%U;s~giWxAByZPErBf5!#7Zd?1_(g5k+jEbSLe$Tjw(UCB~tx8 zHAMGVRBFXfXAFQ;s+&~6TTsL2OwAV6t@9Y(PT74LF`+LphnD60a%S!De|x}}PO6^~ zWTSdB25mAGH}a_^ZmWy)H?K4I#=6OwvP>V1oO}<<^ve6<)uK%JE(P2KCeuZ&xEql7C zoJ>Cm)W?C9cJXsbuNP$3(zwk>hY8=*DkKO z-&He)1f;ykVI(XMZAJWx-edhgwLd_Qc*A1jP}CPs9@ zbU9#USM1jbyG(hcb);R$lCbHwcA6h%o1 zodOjGYLE? zd?QCuY84wO_T6UlLi5yF*YQ|{B@@)XC-V$E`TQM8+#j8U)q{4ekyyrBbxy19K$O}` zpDK-ZCb%s0DIg!H=m;IN5x@!3L;)E=DVd+QgFRvi5aDAc-JQ9U)CLbL<$F*A8hByls$;RCsP<9CT(7!6+BZk~;BvLz91SU+NF zpv`(;mQMwUrpO-Oxc$}uv_M*4=Pid}^Gg<5ZVB;>PL(MrZT^7DWYT?Wphws%dOt5Z z4$~52W=Rp;L$tB>-m+0k6_Iwn3UzE8;;#08n7h7`8COP+*8V+B zHLz7Ud(cggW2^UQ6!X02-J|l+O<|{J!hnIs z)ornJzDr@3>u<-l{T07K+v@$~cZ?aMxq8r<$mFzbrT+oZ;WB=Mk4Nt6!yVx}rhKE=y*dSglcUA-vVY*8JZA4R^!?*CQI$Iw!{0xPXUX~ZO<9yRWx~8?$5m!qQrR4 zd&526cvH1vsVBcwnJBIytH{!T>JyLPIU44FL@OtEA6>IIh>GCrJA{^1wsWCXyS{)K!UV{ z{0Ah`WFho47@&j07r2PyqKd4~rt=%-;-U%O%W0r$<|?5$7xqezg0UP@QV9apb-XLR zh+TwuD`sgzwCM?u%o&pcwWr*%)d->@Nf%zMcD~e=VPwE5Xek#mWC?YaCy}f7l?Uh4 zuon=z*cpG3Zhx}{0UOF-$aF9+s`S=GO?_Ul4O|#1Txu-LY7=SxwXH^Y1sBy-E7l9xKG zVq!_6Vj9*T>?Cl+lJGuDw%ypsB(lejsh8Q;O3~?FCEu177`m)dh!R*C&^TB^oI?ZT zqLWxY@NZ^gTcM=A{nDNgFC4% z@dJ3mU(I0Fnl9L_=NIm~4m!&BUqz#0N_^Eb$J4Pop2#&`*whkQ$c5TM-+e7TD^&Ts z?FZp}GNpBB%{_bb@V+$FMvuXFN}JaG zyfN5TUsEX>P+mW1WW$+USW02GJ3K(8=Iu^pw$&s7Zb@L9VHEYvHFw)jzOh|FoM%9M ze&bh7uXB~r-EJsqlY-auT_ga2^k+@~dGEV=nfY}G4A^f11;W=KeRFBTj~mTTy~F`O zt5ki^mL-p4ETLrv!i2+LLD!!LYwTE!hvABMcV~>&%K^IvkL2{+4c@#Uqo{=8yg=0_ zO)YsVNlE?A!Q9XXGGi!2rES*(=g~?QC?#l)`c;63j3`!Y4Wk z5$wn{u!1^#N4f`d8n_Yf&WfAO*;VX{OwE&7%w{}wnjcrG#4X|z$&N5QS$i|L)o#m$ zNX^7zjX_Z9gmH9^_ViV^yPz^3Jywn0JKv2~DM<2XTX~c`qtBFkSPr2l8(XN=b^_;E z^a<1s*>|E-?ni}C33VIl2Qbn+PTdWiE6EKFX%=*Q*|VWSz%O(b?h$V!^zC6ywAr3m zoPBTkzVYyy*$8q@>N>P&-ys5Z$(_bPsd$r9+}(1W?j@Guh`b4tcI}e%2ZP7M8@GPZ za@lH09S6IB;I!nzX`%i}iz68LL>k^Tb2^$mfPZ{wV(kL@p;=zneU&E6TD5TDcFdxl zP$|yvuLY2qvgR6ObG3^f+$#g?2PEgw7gb%HslM7EZfQSGGUUkIT6$(yv&JXuwOR*Z zG#}gvlo>!XVA1T@0L4ez>MbI^z=x6Eu6*PPRV*t{vTa-?TXoVL4<%{uuqKnNoSOI z7AvDgOyzqtiqE=R^kvS>^9vF*zc+0-MY1bL)L3bsc_I1qfp{$C%tFH3<#lXT)5io` ziFUZhF7{fl!v}mMDKq(T*Io>m=&nQE110CVs%ph=vrdR(i(zpQOU0@(P@p9FSID9_ zS;*OZmAsJN_bYx$>t6mO>vK$d-6?9KPsA-THIj?H8hX3m>FYnAxmORDQjp*jlK)BH z|B?3qWPKfVpnh&8ByL>fxkuY#fveNwlaglR8~h>0td>-9<4V+BY`is67xltbcs(5f z(FaKj7G2AVIbpAZTDknQ*M##tq1)&Cr`t%fFJCu<%FI586GW|_P1AOD;3RYIDe9@{ z6zhkUZt~_F%eSqn#}PlAnV74gwlmlbe2-a^6^nfgCW3d|~~#@k*k(jA#fVk@5$) zOAz2Lq5sn*W|zV3dKuA9&W&GN@hn(} zFdM`=e1lFN0={_qR(*mJB{(JaaH3W2xh;bRM@@iD-I$(U`{lO!L0)i@qhzC^>BgP615p;AO^%y}C7PLyA zwge`nzn|bgKA+ZLxQ&wK~76F=al(#$%_Q9l!8$xp%qaCqg z;pa%K+;Q7rLr$pfr?oKt-uw~^=JAgt>~7rel$AAC$z>D<3CI;6@#^>Unudi>y*|I= zWM64Vf*X}lRE20vs>hdOSwza;dHlV#A9UX1Hhxt=LwOmk!^!ro^DTU<^zeXS`eVC4 zOVDLd{77^Eds2Rtp{ZBiAgQCj%1~r~g(V_47Rp>b%#CZpYT5-YKaEof-Hg2p5zSzQ zSG1?yRdRQq-x-`GC8uyY8(nb5ZoAf)ZtSOy! zV`b&lYP_JOsMcuA>$T@vh*nld6crPRblT}WN{wtq(w0xzD0#;@cO%MM-?Z*d`W`iF z$ysEPPou3l_n%UUuq(Yejon~?REe>0eVCb+V99uSqm&zhxmQW$LWCovcDOxxiv4@; zaGg|jD2Mx{6TU77RPg(l4af{^!U?u9u>f&%*xOqCkYg|JV?y+Q`CuJu+i7JfZr;?` zc<%e2Jvzbw26b;U68(jIoe@?ZNqE3iI>|_Sya)Q?n>>-@yF6Uh8!PAWofr>NHt-gE zn$+{#n_#e(rftkSL)-p-*+ag5pS2MX1BS6sPhCj(tEk0rk>nrLUlWV#VgHumufmeTZ|c7Z^KigRVg8!k|E^YF z1(b)+LI2XOuYbzF71yhv%X(`duMUgf;;ga7XG!*`0p(JUZJmAN`-&< z-DCfBG(YS0^#=Z~iB}DT-TvLc|63v9#x5)5HT?fxXnq>IOtN`?*`z;={atqcZ5PP! UByu@r#|3SPgBGJDzt!&G)HKU^Q!L8&8MMPZ~UFhH_=J*@av!=iFNs^}EJ^_T^V!7dG2Z z`0!g2i>#)2)}}T$&yKYkrf7&T#dsm0#EhQeM}h&SSCxpPOzjLK$f`xup{N{Mi>M;x z>Bsl44%uYx&@P$u1>TEMNHg(`n19~2(ayYR{N&NBIpjs*XGp@lb>%C=F|ZE2uK##? z@PM8E$(}U54380Zp=*oFnryXiQW+h%OgZb?Y7Djn-O}U1D-<&~s=)8D%WbajXd09+ z^ukv@$u8*3`ENp5igVQJ(RM#^DU-hqA}YO|z$EwnIsBB#_!zUh^M!r#2x=6tlPgyP z?AYt^(h@G0BcSje(88yFw$+lg%?qq5bssa6_tr z>D88~6LdC<4W|7NPahh$5}Ll*>UEr(RGrg=s|!@-OeX2l#-8ta*^4pWDz)+%NDitm zlRial^P*XK*qn3mI?J{a;RI+UVlm4YwmEy4kaZ!1!9C`>i?4BsXtP%(!(K?*2vCs+ zNwk$SNW;z9#hu&2#pQP_XC=0)t@9AbANb2Ji9@vL_H;xQN&^kH<%&oQC~SrdL|`9p z=BGaERvt+};efv4cZWRSgT0)r7h0#y->J5G!)bF{%Gc@QI)FF|OnaZr1~W(uegR$+ zL(!LpPxD(Qly${n8P zSc|V#I0Ck*)Q?k}_Z&5DaV5mBwZ9bh$EJ8#fEl-b$MF%QfL>=6y$ceL=bG~}fAb8k z(7>gmI5;T%@Mtgs<5u($r}~`BN_Y1BJ!iuJl*1&Z>-=u`^l2yVyu9S7GCP=l!t2=sT_5*eEwRd8Sdd#jd}*!gd&1Cs}nUUa3J4y9j~!Ty=&YUV zP->WBt2LmK#8d$N&0cp3_C?j0#ujL1P+MI}h# z#@b@Uxo)i3z}Nfl#ID6^?Nj1%R*(~2g%3mww8779UGdp*&?6Wy_*@EcpDtUU-DOZI z>uHWRBvyKh?)&&K7XlMkeZmn()T<7+m0=rvzZ&V;7PnVkz2$M-6Sy2&1{;u?e8p6H z#yceF;AHH)`gqeKpVr3cw3pTDE>%>J&-<;+aQ)>xb`8Xesoq%Qsd!1Y5+W}bmU;iW zcA44&+^7{oIc18qG8hAtd2!J|nGqJu;AMyfI^`Q;5k~j}E_*A4%#KM38J)~XZ{Z`o zFjL(yV#7?gd;sh(Da3Pbk>&Ij=@N<>@`qFCQ#0Hka<=>k4g+mHFPe% zuEXZCSjWFO^KQVm;kMAD2YHa8Pn3zuxVRA8(Dx^l5<>A~!flx=MlN81WfvKYiUqp~ z3co{v<+l4~jg)ptC|k{8AlRW(T(dFW#QrgkKEttsye*}TW@k}&ZD+@M zcc)GY=gw@hX~<-bDkuAATR(mHsB}$bVaKK@!J^|y2Zhl|X&~F@(~tye`D@zw#ody@ zo0C&&sF~&QMu8Jc%o+0LuoL=22q@>2oUn;!IT9hE>QeU$Z(^Yfzr(ya!n*5=14-w{wBoz zJ{aub&i(y~_b+CsRr|rh$n#5ek4>BdBAMx)JL9nQ_gqab<8bUYr@+R}ciJ-abL`A8 zQHJVW_;FKFpgtIvoex-&@R|cAPP}k3z!+r{V55nrA81=LVAU@2KdXQBV4=^)7JDK$ zDET6Av37%f_1$L^y!o3~Vt_uI4=8Xx?2^}cFgUbp6w6f$)s~>?hO?0x@_Ed%ews<| zZ<-E}L2ZZ#-g74jb-eCHcO;X?n*2zzzQUr^3y#hDv1Ny5Tr#BGPCcQgY)ZyAor%QH z8snusczi@88L-JQ_OO|jV7X5e4(i{A4cRVq|d?O zodnbBszFylktX>dZcmZLVSP^&dhc(FzV%)V zWP+x_NQ!FyzB7(s4+|>`4~xH;`H#NK@v*w|0uRBPt(1K+gBFa4K-TC)9b%*RDtkj( zb^42UBXuv5x-FzD&OWa*a&b9w!@3KOj+$3amrIF70~KvmrMnA~33)p7hBk1Hn+%!U zRd`&qUBP%nduU3|y)^m_;cXn$B2j>ltBx`*I9(b7xuS8Es5ivwzh*=j9E;1Y1nG|h z(5ehli}{Ni1UzUt<9hY)AzLfwccT`JmDaH8qCoE49lJihpydABxA%~t0;)XXpPvCFEJkg6AFA%qn35?RcYbrN**wpD3uy-BSgdHziB zP<)TYLnS8!SHt?cnv4Vy5X|w#>y?IeLbCRm+V_E8e%&&7mRbPYQY9 z%ohtD2l6-%#~+I%?;FvB4E*TVw>Pvj(rI4#4;)KG?F8dSQO+iaFIp~#tn5l&rjopg zyeA$>B?#GF;r+O6?jAl4VE5k==5ZN(5sIEcR1|h+8Jwf z2gZ5fjf{7x`dtTd@s&Bkqa#E&$8Q}{gJ*D(!Ms<} zY;n&hC}c{^_g%`8X322t(`kf*_1#&#WYg;l zB4j}Hi(wxAui#C{ZsP4-cyn0?E?yNmCl!eell}?Ieg2o-OlvPh{W1#lP82iV?^~>y zpHYAPg;mM%ExMYk0Dv~dkKqVe)I9Ek9l<{y|4yI(aY;7~RpW;h$XdQ|D71tuzP>g= zY*^RBjOggF6V`Ce9KYfBb{y1y*R!W87*wR9YdZogn)NN6qz`(Jb0#>YRj;$az4uiy zamN~JWl=#QeJ#r7v}ukepu55Ct8^cN?t0>Gp`&P;9SMEZAZkXake9#kvG)G=f3%zJ3Dv64D_tMfHN9S?M3_tM6zG z=N$gh?Ah3+sC$4Y;6%b;jniT#0KVGiZI`>?mhSapd1RX1Y;n$@!`b6Dm2*L`Gw1ea z!WLcoS5sfHXHDmu!DnJqGfuU^2PB8fge?z!3Rd-YL|H8bZgS_GIq-=K3`9}^#^nEk%v;!-}7`kZBa2kj^^Bfwka-2gT?>ojP&8d6Sm{^ zRWELuhu|(C)y0W=&e{HYnsx3_5O+(!Zg(vUcgxY~APqIe{1gg4 zQ$uy9z46qE;$q1cpRB%pY9PA-0{z^ndn|VpKuOV*w!=h8@;`>s5ReGwE$CR>cG_lO85*+Yc+j@Fk=8EL9sw%e- zJbWxlo;LKo)uyYhysB#UT`@Ak=ZF}aZAWi`bd?1w9J4S#42rF5*^GW@ZfCM{X9%yk z?v}>#)Ui!iG`jt|yXyeAnJ8mCD+t4`_pNIB3u`PDj0fX=q@wTqnbjFya=y`Z$!j|j zBs6&-qw7KNf#|hJL#Re-rEHYetq+XmRZCekENo;sTRye?w7E5tAEj$%5QE|FLPzY$ zuW}W-%uOv~;m$05Y=QPF?ssYhwsdS^%c`a5p|v5c!E(mqc23R}YWkZW z0+V^&V!}mY1!qQmo5uufDPmtF!PF8wTt8#G&Wg&F!HAgr8Ym65O!F8Jy%l~WyRMTk z1s>&ga3+dmx*%;|N1@|UxxmNzH~vHB?nbxkQ_%J03T4WvI17T=TLt|fiJW`S{G65} zzVNmoVn>Jb-?^R3a&08#F%?|j8F`fT2HMok@+dNdRYXQs==urSo@ew1c4?@M!UGtA zMxIl;_ed{y#rzUS2J|)$kilfXFqPy4CHvkW&?>_d-93r_(G-IK;wb&{h2V~Fi z7GH-49Ww?>>1b?H8YxSv!mxI5757>LCjv*>(2AWdwb^;VgDE1qMR%Ju!-TBt0Xe;N z-f)=`0klL#>|SSI+4|tv%>w9P-$;t>ju>M^C?0gp(R$yQH3O0~bY1kb6~aLqg4cO| z;l1mse^;U;9s@`Ev!V5Mef#6d0`tX99g)QXm=o;f=c=<()eqZ2Q11KFI)@ItbJq?f zG!q)PTn4_%e~eY*LfowyR_R%r}7u@~MNPwtq7vfvBWFbozV zTDw=15Cl@az1d^UilCn1BSk}R`TPcV$gQ`QBnpRJcnVSPym)ywB0w}hcu zeVOR@xEe+)9Aapew<0Id#mP`kEvlF<&4v&rzOSzw!*#U*QQSo6#-KH?Nf7h$ti0Wt zOVzo|(mIXZYSvf3{a&4F;u0Z=!YI@IwdeC&T~0ixv@Gln*hIDN*hlA>4@!Eyh3^V5 z;x-t*4BY6Hg{FOQQbI2@{Xo5k>l%5ov4v6ZEOd^`m`v-M#~Pn55f?!t(raQA!c2EB zV>fcXvLG^|UD)d>d_$E;P~#(wY*BFeC z-gL&m@=b0jZ@YDdr?@JkN){}-jUVM+8{Zq*xN(^l+2e2s7)eV3k`~$@w7A)V?#m&& zW^OktE#yl*3kL}JyMB48`*Q!6VLf@yzT?|~gKp0cqR?gPP0IP;I0R_L+CMdJ@@J^`w5T-G^cJ~H2Db~qw3H1pf zg8XK!nJ-MG`dO+|>ng>nyY@sRSy#6Mg-q@Ibad7G`EDaDo93zx1A~4?T{gM~=giO{ z=OSG4BL}|b4{A}4SXZn!sOk_47;vE>gatS&T*_GA9nWb@h`~~rKqpTlEITksG`h}U zslMhKc40yz|BDm6eLv)9Y;2v0;8+;oZmQUX4Q9P$H9cyEfY}%!$8OoDc`M&fXt1Kb z%#n1No_uk0;6B@8+NqY*W9r$(PP+5OxP30+O6 z7%2~9Zb+(=UFt{3cJ>B?e+1LZ1|_KN#e)|blho+p>5mU5 zI~5-}F=^gv2yv{vqIeVD=Cd|`wz>NGN~Y0U*+?dBjGRK3PtMLphWAuS>g1J7A0*a? zOl-V5Fmo*?s#A{9s*4#lS8j=6VDM?i4wJT_2fo`9n)2h0`9i=M*a@|&AVa)ZD;Mn! zrlZz=QmR0ze|-?zqA{Hl5WOfh;cZ2DC}FWJ3GXJ@G4x(I)%UJCj-P$_i--6e*}^lB z9%@7$ivRY|?_!22PWoC0Es@*-^K8&*b65shESm@DP7rlq+cPG%)9`pXXYMs?YoRaJ z;XA~nn!N70g?U>)|reTF1xG_)tuZ=Am6TxT=4Dj z*UmxkIoxaFS22y7^UK>8$iK>n3<#EA{_c+w^erg9r@8;VDZed4v4b^ssJyu7^UVk^ zo=N)|2xd_xHxSE;s|OX+c!gga-sLH?yD#Wrn=7lRd^#6je8%Op)>{5^V7#0Owc+>cAnR>Gt%tYPp?$* zLUHzLX&@x{BI<|RQ>S>BbBE`Yn(H&9U*00mw`CP_9&-d+*;;VhI$GF(d2hKm+59EV zmi@C7uj6jJuY62gurxQH|GMXbg))Rq+t-f9c%jr}id#q)9WtFoHky^>gSGU$Q0(|N zACJSv%6ZaTY_0T->r3!9jiRnL1frvD8;AA%c5qPM`@o}3JHeujD2{=I zPT9BO=%{CV9u9~x@;lfXjMg|Bx@(_6-FJ;CtnVajf}M=+(|?4Hzk3rHCJXEsBc_Np zoDH)`Sv767(I;iUqH4A6@zvdMB^iN+6j-mIP^13Ws*Vil0{{RSAb`T4Z?FD!ad-8{ z;_jE0g9dK`{k>oGbzqc2E276hS{~4AXIjLU~>HiGy zvU2@1pb~N?`VSrZ_n`k&Uw;Oj!1xRFU$j^x`Zq21H-udl4u3}X$NG+bSw{T*PyVJx zUY4+a235rQ4*Ij8^=FWm^^>0=sqnr-{!>l)GvLdu`Om-;MV&f;eap`^c9_|HD$&n#W8&_7!$M!vc1 zvA;T+ANBgr3|y{>KO0E7dD+1KTOt3<*yTd=!`QbZTNqSD{@vI=%g%oRUqvR7ZyR?4 Pz-wfFDnlxX0KoqMKF#-c literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image03.xlsx b/test/functional/xlsx_files/embed_image03.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cfb4cf6b8091ec142aab1f8dcc1848077a78eb79 GIT binary patch literal 8954 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 literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image04.xlsx b/test/functional/xlsx_files/embed_image04.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b626b7711b0b49bfe293ddb35d25fb9694b21bd3 GIT binary patch literal 9462 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 P5H9fY1fHOzm!JL*r6fNK literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image05.xlsx b/test/functional/xlsx_files/embed_image05.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5430cdc4f91c8d0d46d117a57694b6e57c8942c3 GIT binary patch literal 8763 zcma)iby!sG^ZpXjNJw|*A}t}(y_AHsSzJEMt&)MtRd#*G0JTvp$vnn72L_7chfC3<_Q`V9&^hnNt0{{Y%0DxNnGJvjx zy`2l#&c#68(*f+Pf7`>>MnfGHfIyzn4`zZbR}XXm9O6Fg7vDhMB!HBcIPiiGX}?g? z*xz4lfzP_HM$p=82cUjdUv5uY^0>Q!d8=`C%11U3O|fXAEzD=&INE@C4p>@5fE|DE z3QX7+sxhCdIn}3gaJE;_V5T9ykiY>I>v<46(YL(Wd1Q%CrIG7a1SU5=>h_Ey*Lq4q z+4P}nM20n1cqtN@des=|%!%ji;?P~hC%k4@cZla&)c24|=0NKY8By&ziVZg2Mp~s* zX=j`sKa@g@hssH!NQ3-&`U#cldo=91cyocgMZ?rD)S8ilgmyU%bKVuSN9u^}Q_($`g5qooDJppUkWwS5$AD$Vzu|R7<|~K-NOlG#gAJca02RgX0=1!x$Tg zh(B)s2-q3zJdQa0x?RH^g+GNkpS;)xaz1%^i3RyO_i=tjPzef9`N4JCJ7q0K{)jej z7;`uX001_OYg2m@6(@TKXLch8haW`qrT2psVe2=dU&M)7FL7W8ZwH@aNgqnOX(i#B zCdO?JwI(6ORhes0bY?^7c+}$e#x>nSH!8h#GN8Re>B2E|b~j7=1Vb(AShYC^w2_of z-`DtbQ@Hz@jRPfAU#X7WWtzl^o$aF3G`{8ICyT@Z!np|=!w#ljEhM$T?;oDXliM;W zVM=ry@X;s5v@V;|acCp-yohM^Z_-fdYa!}V3hRl!;H%tyyV_bX%b6?0Y$x$rnpNJH zc{?ha!bfp(I}nNW$c0IDkYG=j$0eb5epuD!4dk_aGvon**v>#m4ZpSR9*0GJ&utCLx~Ap3swM7J9Yk}ZO2!hmptF@h-5(FED@$lcM81wbST++=Ct z06L$r8g5fK%Jr!5J*-7zc_EJ1s7qC=tPNjYD4)dD@7k)|W{ZtkFLNwAB653NiqgMP zXA^FlL!-Wi)ZrR~>6l!Ty>(t9U3*X6eBe;b_gJ}r@ODJIkn)_v>T3FYhQ0RV-$*=& zpLoj%Bk?{wjL7RGn%Fym|IT8MRVxR!)M4;N;0I_D_%Qf2xdrF}CSK~$(2H1xu)=*& z)s3U=l8LZ*(Fgj`pI>mhBWYBWTE!bUJS5Sljay-Y>x+u@($6_u5%LiY!%{#WBf;cd zZ_d$n_R(+z)jIC1N8TW$JEDuLESMRyxD}8`553&Q+J^Yj%(_V` zea`5HKPmwc6i)3f&IbzSqo7KWSLA>I6ALl_n-uYp^ErHeY8S?>nQ8oq81=ysV}bm& z)en_AUTr{Lk-g*;Y0ij9t~MG~U!N`I&&g` z{rG7)mLx91Am?YXW2TJsWakk)&-?kgvfby9F3ZQD?!IxmW$jCx!UKs5MIk`P%xXsF zF~YCgM&*%G^ExbHD!@FB4)eHy6WGR?{r1)MTWb859a9?DCy&61?YAAUjr|eYR}~P- zGY!J=x>bb5!B8Qx$#5&~ezm< z!$&Pb4{o5HRQ9YpB{!~)q$84}S@nbwa3-$PNlb23N1L2#wH$LVr49D^-hN`1HQ)6@ zvxTTJa_<8s59LpH-2|v;srG)_2EhW95#}x|n7ce|*xl`&tc~sMt$&1PT3$c61h#%l zGEq+zm0LM5Lbheko^-j7VcFt8A5&TcDTw>EP4s6uj$2r&(le7^QWU5m&+t!{nTYIe z05v*>dZb&_Xa_0V5Mc91m;HW$~_X&rP>2uBKVz*{6s zZ27zkrO_OlaNy%)v+^p6w5?&ZJbY@+=CoiXIakOOwFI=P8F8QiZu%1C7n_E;=WmjX zTp*Sg))egPv*{wYL*me?8D%1?x47*swsYX~YrY^mlA5pm2}55utp1c3Esoq>6@7R$R%xx$AV=P;>4bVtZo!(x z^vju^w`6UZ&&{vE2n3-MDay}$()q~F_SDLsa>Vt9FA}DMbK71?abb6q$Cbp*1aa`> z4{LKFKX8?wRo#*jAr=O-41UTzLi)*xAdC~KvacUPV4N(&I3fDUiL(V5?BdM+?Su0N zIomK|T(bToM^#aIh6B4ER_SNf^2oHz-QTcDMt`6ozz=&+_-LlSo4;Ikv+K^}?l!S` z4_YGrqM@bm9<+{+!ttQmdeTpr$Q=Ed5|^O`NRwEX(81T;^AS8y63jX(7;!_$#E;w4 zgX&JdNC*-qm+|W?KZXwBRiOk-Hx>oGm?XieV-`*2ln}hObJ*=(-^@ zz9Fraw(@ljD{4>f*vSc1vBX^rJ`?uCOU!!6)h4tKpEUf)6-_VPaW}A8(c@M{;aK&A zF>!hJ4FITPtp8sANBl$x;>z~xVqA1^)kBq5r!z3XSitkkJ%Pq`?96uG> zW7)DwWtTNi2?rG?>08NX5oaYHt`oZrB|FECm(0!e7aSj-$vUvLQ*k#V<7y1iexCK4 zgu-zj0l4PSQ+yExW|6Zn8NZ;|9GRQN8c-&UAC|CRmhbRItZ76wCX_{CzLZi#oLols zocrt;nX}Pp0cW#i>@sUTHprHxOzb(>-#$*~GyYj{P2J$PVlaFA)aC%j>nP%ny=Dt` zF)}rBG5SI0zoe&8HGTUj4m_WX%1guJd;D~fNRq15f|YXW#V7s;>l1v2eUwkPI0h>( zAI1aN)MqQ}x9GdyIL8dsu6j7tYv9^82^zUXpVkx64)!`^X$m~{N|Pa$?pNz8;vsRW zLwHga5ktv5&YK3tEW9+D~) z3G+0dj&}88W3$KUt;flkuLT-cSBgP?F|%_1ubvwcF5a;PPFPF?WxfTcsv+Pg#Z8pYkarjKHuuL<+H-_ZkXNXaU=4wqJ| zmr$t6?HQAs6Tn&K^*D!54Yk+l&mQ6-W( zI1~h_7YL%a@+BATQ7CdohThE^6R#`GlHurvjiLJ1{OXhu!a!xds=XI@w5Ru|1Eg(;_q9}aA0)K9WqDGP0x_Od`T-uiH00(i{TYq ze_~8;{(8lbwG*82*eQ*{o#T^42Jn*poKDy6*v0Wd8nxt7dEE4A6rNwyIMO@$2J!>< zx!llP71j+L76I`D<^5!?A&Z55M9~0LB34hD^+rM|8>M;my2E=^-a3?NG|-S&p+0~- z6M3hq3EMkZ!D>ig{VEFP@D425nKs}E_>8sxJp_^{rGE!{LKXTYHb6^kg-S+<)jGPF zu3V#kg0trxodS8EV}B-=9MCD)GAzyXr9R0-H|(cnp;Ytfe8yE+CIc%^*=bGkYp}B4 zWp-?R-q2{NbePS-N@c%1`J`P8LT7Un=a^+KT|T7|2&*%ZARYBoze7F8Z5CLLsdbvl zuvbuTWT4|8IDOaaL*nxNg5k#JGlMo}3Q)QWUbYI6DE#Duq{hqwalSn%d`oEJSz>yE zKF%UP5eVsWn1g#CyydzZC%D^FU(|wuSxN#VCoreiJY}@tuIZ#-uNLr5&3SMtlR9!> zv~GA#u`lu~t0EKI)a5q-05#I@M><#mcCi54g1=vX=J|g~3|rcYF~ibC4PRNM8v>Ww zZcgHBS9UWDwzOFBsW_%h-16?4FzHip>n;s2$&=Tx90lb;y$Yvj{72Bwd8bw1s4ueb z?aRb{F@u;Il@N&C3^PBgo1=W(S?jbf);maz6StddE0AnOKod5AkQ!vW(VyvC4yG=0 zz%}f+HB^-^siRqaTd(;!3k|23XnL38OchPukm7Wbu#JglSOJ~CH|=fXmxj#Zcd?I9 zCOUdwjaw<+?cga_2{4)W^5)4F77L8bd&6r2HQ7mIO0hjsAUmp+d_mKRWWsp*RBaLr zPi{O`C92dEgS6}q!ERM;j;hk=5lO0dZBM_&`6l2&&c5aIg-1Uv_mfEe zcXmP50B9TtjA&l`drg5sNXL=l)rz=0HX^Y>|mPay#2eUXqE4ro6DBs*8?SVSIFaPU!1< z%6zi1=FU#(3+@1sU!E#woF8l?n`I99vo}26?W|yAZ#X_1prjz3pN7YBJ@V_2%+_5- z+4@Rk!;L~E;dm!pEXXn#UdE3H;LTDDuvg^dUJ%S(uX_hi;)m*C>(rRNAx6g#PU6Rx zT?0R}lIB{&k?KqvpZA98*&9f>bmgRd^G+@^hEZQ9se3 z!$UM)J2v^rruB1GP{;7AK%s9@RMZ6 z>urdoo)nr?C&RL0Z*BCt;^#{+0e(Bd4O0bw{K)-^F00`HrmzPm27aDf+`;Z)DQ#o9T4kh)(Ac zbLWHZbDa(Eij&5-sv?lAH=G^&*&hnf#W0y5TfOg6OsO_Ql}GlO=p_{R@Q6W~wg%X) z;gHpI%!{k%Bd+0sy@1!oUmK*7^im>B_4WdtVd?SfZmP`ksTHyE*};Jj z=R3zN32!r{I}G)WqI&EZdzt;L<(-8pc(&Cot3E0gA_Y|hHV(fe%%(QspcVUg7|;v^ zu2RqtFZd;JIz@)?NAb>%dDV~eSdvCn$5$!Ex;TDDbA$>=7FFTVd)Ja_tLkUb4!$q( zCfIeHs*>iAYX+ymi=^;U^|s{N9vAbhylHifBz|zR;?HUK% zEAzJd_~a{R(_qxtaP|kM3kkN(_$>MyoG+u{FWMn>os8k3flU135_fP;iT2zgHqi=$ z%%y#*uvN0`QancAvzN>-v!+4>IY6{}2f0ZEr|{_p1Fl_~sa^+YvvY$99RjI~^^N(a zDd;BFv|U*097rgU5Hed`y~0olLV4Qt!y6bkYk`C4V%nxG)JgUB{Vw!RA{sPym{RqK zihM|D#IlBqWU(Oy@^=kddy6&(#))$v1HGe(mS67DJqf~utlOF$=rX0c#t-2Ld^R1l zQFFy?y}0z)b<|W4E{H)!7yGPjhNEeHGL>Vvw586!lv8C_Rr9&@yijpr#~;FOF{6HD z!#Q{JNLZ4xk5L{m%%fZ*Y3`9TieNR*64!&v&qQxf#H^+22Aq#Z(q^?5Hiz5mYb)*r zmemjI+p;GYmXe$9jt)^OKk}e7-EI=IY)Jq@8SeSzn7JP$!|jw4=IRn&!2Qhr=rBP~ z+l*yF3RcsO37l#$pP7nUp}>PtVaROL99{#BNs- zmj%s)VtC*o(%Okwzt#o~>oRaw^w60?y)>H*Diu=+a0#Tx(0E+6cKA&Ao*~5E!oPzM zJ}D6V$Tz4#4O~acK!O`G1Jn~eSfHNlakxu|F!aiW-7AE@&F8>9aEg!A7ali^$}vl& zQ-QMc9E+?tb}Un_J6W0>C(RY_3&+u-9ePS*I0UJ{m(w1CZVqKRd>VNngByKZeQ(0l zm1_fDE_I`-=)f@&dBuZ47gW5(F6v>mLHis-eoWSgQRCf;WSg$R=;m)Sg2YLPD;P#h z4vZM8@5DG+npjA}dUbXuQ&rgQ10x$(@HgG{Z~a$k!kl#r2X^Ni@+qY}ly^Ol)R;9# zH;bb~^ib%PrA}aSE?rUO<+)P9CSgm*Ns`{}jO~@jrnT!l(%x%z5C*g1FF{g6D7q|a zotxnJC_C+CglD*eB*YaDy{hD2l_l9Vu92=et4)Ryb#&U0idW3fM3j!qcj};;*O#_v zY4zD^FjLpsrv(k!=V6c>+i=w{D23Ug!kKN7R}L;B_uLs8Gy=Z}5!Kan#-O%*?HE;HWBqLekwybCfqCuV@fbc^;1A zvhEgrnm6_Ogjki=n>Lyv-kp8V5Ol!2lzgTt`ji5?lu-R~15?S^0B<|d9{a@ALA@km z$XA>qgBN@K*^rUeMwLg9_##K;J5gfRDN#%j3=Tqxr%LqXNJ#%Oa(%OQAqYXegB_4Rj|$9 zr~?mhf)F{eZ_Gd35eZtGotzdoo!sPoV#sPm88-={>IQPxN?g_pRO0k>2F4sFEtz+J zl+O+;32x;G$XXZ3^{U#rI5^uulzv{)41Q(05P=uHaXw4a-HDaVz9+A(s9CHNTDrxZ zeInbwrV>Z^06H~aOJ%RS8}t^vHuGs=V&pKtqDRFmJ`huD3~!G}lzVagv{tzFz4yn- zLgkHk#ib@ov~I0$E~HJS@W=AZX`Rssmg&g=<2NpC3>vScnk$z;5C|07U@n1&xrFX_ zmzZ7!x7$@jJG(f!nz*?BU7b{n23fa?VgzjmUp$dI^orNh3D_b-3l_~B>=Xb(Y$7&k zWg(WAKGiBy6i6W{PmiWrWy0;~@87Nsw5@=XA?|7NT%SMRTKf!_rqd-6O0R~Tk?Ziu z-d;!BK#`zQN><+|?oHt2=G()Ej`?_%a*?W)kz?uI}*7?F{yW@`Vdg7&()6 zA$sgpS|8TKczg3+nln%K5wW>*)+i{bt&vH|4dao?KjhXK;5H76m?^nna(1Y&C&G?S zFRDZ^B+=%{wkjgw?K;_S9ROeSxKBR%O^#p(e(kD;y(%3nAQ*pp-S0){Dk#3Cx&OCQ zeiovu)5z8OT@oB9w?g2=K$@=~bmy3|o^?gZOJfJ2nzD5xpy;l0-|Ojc6W=}Hb+OEp zkdZr^i^)G{vs-_k7ea?5eb3Lhy!*x%r2c}|9@4@oSu$I>iKZ8(m_teK@ zOV-2H5v;9`$;+n_X*FJOff|8Dl2$%I3hqhv-N>@m*R8wLe#gz~GUl0NvncB>180<7c}4z0!J~BkiG;6yo0y(!?jDl9ZgLE?e^`zOUG?vsWnz^tL?0I3US`mB{dU@ z-2&U`SkSCMCO)+L1xtWh#_>rbw4uR9iEH|e=9+{oaEhioF8j{fs|kaMu$X{@r$G3( z5hD@I2LJ#pKmfV^S9kw7aJ=z-;P_iCTnGMho^>5~6NdlibPI<14-xh|>aPQq>!{^0 zndArR&vDCtgZ*`ia2=Kwc2@sorto*bKXvx&fU2-r?jNc<4D?EM{~s>>Qz*U;`VsjD z=s(EDF!U?g_8Thp(UN@jf x@~eUWSt0+Mv0n?#cVkycHrF2v69fOT?ED+>4OkMn`kMeYpbeIvjL5E@{tpIJP^AC> literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image06.xlsx b/test/functional/xlsx_files/embed_image06.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3b9f0e35868ff3e6e9c8cf4357503c9d3f094f95 GIT binary patch literal 10329 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

_X-xB?(Y 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 literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image11.xlsx b/test/functional/xlsx_files/embed_image11.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..989ea03b6bf9f9abec7c5ac5429f0120b657eea7 GIT binary patch literal 8610 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 literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/embed_image13.xlsx b/test/functional/xlsx_files/embed_image13.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..038f97473df0000e9fb6ce26a082a84b0607562a GIT binary patch literal 9475 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\n"; + FILE* testfile = tmpfile(); + + lxw_rich_value *rich_value = lxw_rich_value_new(); + rich_value->file = testfile; + + _rich_value_xml_declaration(rich_value); + + RUN_XLSX_STREQ(exp, got); + + lxw_rich_value_free(rich_value); +} diff --git a/test/unit/rich_value_rel/Makefile b/test/unit/rich_value_rel/Makefile new file mode 100644 index 00000000..2c05b8e2 --- /dev/null +++ b/test/unit/rich_value_rel/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for libxlsxwriter library. +# +# Copyright 2014-2015, John McNamara, jmcnamara@cpan.org +# + +include ../Makefile.unit diff --git a/test/unit/rich_value_rel/main.c b/test/unit/rich_value_rel/main.c new file mode 100644 index 00000000..91ddf170 --- /dev/null +++ b/test/unit/rich_value_rel/main.c @@ -0,0 +1,15 @@ +/* + * Test runner for xmlwriter using ctest. + * + * Copyright 2014-2024 John McNamara, jmcnamara@cpan.org + * + */ +#define CTEST_MAIN + +#include "../ctest.h" + +int main(int argc, const char *argv[]) +{ + return ctest_main(argc, argv); +} + diff --git a/test/unit/rich_value_rel/test_rich_value_rel_xml_declaration.c b/test/unit/rich_value_rel/test_rich_value_rel_xml_declaration.c new file mode 100644 index 00000000..c7067928 --- /dev/null +++ b/test/unit/rich_value_rel/test_rich_value_rel_xml_declaration.c @@ -0,0 +1,28 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "xlsxwriter/rich_value_rel.h" + +// Test _xml_declaration(). +CTEST(rich_value_rel, xml_declaration) { + + char* got; + char exp[] = "\n"; + FILE* testfile = tmpfile(); + + lxw_rich_value_rel *rich_value_rel = lxw_rich_value_rel_new(); + rich_value_rel->file = testfile; + + _rich_value_rel_xml_declaration(rich_value_rel); + + RUN_XLSX_STREQ(exp, got); + + lxw_rich_value_rel_free(rich_value_rel); +} diff --git a/test/unit/rich_value_structure/Makefile b/test/unit/rich_value_structure/Makefile new file mode 100644 index 00000000..2c05b8e2 --- /dev/null +++ b/test/unit/rich_value_structure/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for libxlsxwriter library. +# +# Copyright 2014-2015, John McNamara, jmcnamara@cpan.org +# + +include ../Makefile.unit diff --git a/test/unit/rich_value_structure/main.c b/test/unit/rich_value_structure/main.c new file mode 100644 index 00000000..91ddf170 --- /dev/null +++ b/test/unit/rich_value_structure/main.c @@ -0,0 +1,15 @@ +/* + * Test runner for xmlwriter using ctest. + * + * Copyright 2014-2024 John McNamara, jmcnamara@cpan.org + * + */ +#define CTEST_MAIN + +#include "../ctest.h" + +int main(int argc, const char *argv[]) +{ + return ctest_main(argc, argv); +} + diff --git a/test/unit/rich_value_structure/test_rich_value_structure_xml_declaration.c b/test/unit/rich_value_structure/test_rich_value_structure_xml_declaration.c new file mode 100644 index 00000000..58250e34 --- /dev/null +++ b/test/unit/rich_value_structure/test_rich_value_structure_xml_declaration.c @@ -0,0 +1,28 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "xlsxwriter/rich_value_structure.h" + +// Test _xml_declaration(). +CTEST(rich_value_structure, xml_declaration) { + + char* got; + char exp[] = "\n"; + FILE* testfile = tmpfile(); + + lxw_rich_value_structure *rich_value_structure = lxw_rich_value_structure_new(); + rich_value_structure->file = testfile; + + _rich_value_structure_xml_declaration(rich_value_structure); + + RUN_XLSX_STREQ(exp, got); + + lxw_rich_value_structure_free(rich_value_structure); +} diff --git a/test/unit/rich_value_types/Makefile b/test/unit/rich_value_types/Makefile new file mode 100644 index 00000000..2c05b8e2 --- /dev/null +++ b/test/unit/rich_value_types/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for libxlsxwriter library. +# +# Copyright 2014-2015, John McNamara, jmcnamara@cpan.org +# + +include ../Makefile.unit diff --git a/test/unit/rich_value_types/main.c b/test/unit/rich_value_types/main.c new file mode 100644 index 00000000..91ddf170 --- /dev/null +++ b/test/unit/rich_value_types/main.c @@ -0,0 +1,15 @@ +/* + * Test runner for xmlwriter using ctest. + * + * Copyright 2014-2024 John McNamara, jmcnamara@cpan.org + * + */ +#define CTEST_MAIN + +#include "../ctest.h" + +int main(int argc, const char *argv[]) +{ + return ctest_main(argc, argv); +} + diff --git a/test/unit/rich_value_types/test_rich_value_types_xml_declaration.c b/test/unit/rich_value_types/test_rich_value_types_xml_declaration.c new file mode 100644 index 00000000..2a035242 --- /dev/null +++ b/test/unit/rich_value_types/test_rich_value_types_xml_declaration.c @@ -0,0 +1,28 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2024, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "xlsxwriter/rich_value_types.h" + +// Test _xml_declaration(). +CTEST(rich_value_types, xml_declaration) { + + char* got; + char exp[] = "\n"; + FILE* testfile = tmpfile(); + + lxw_rich_value_types *rich_value_types = lxw_rich_value_types_new(); + rich_value_types->file = testfile; + + _rich_value_types_xml_declaration(rich_value_types); + + RUN_XLSX_STREQ(exp, got); + + lxw_rich_value_types_free(rich_value_types); +}