From 6d6d99eeaf9d870cbe6a7da5a42e9451704de9db Mon Sep 17 00:00:00 2001 From: John McNamara Date: Mon, 16 Aug 2021 21:29:27 +0100 Subject: [PATCH] Added support for worksheet tables. Feature request #33 --- .indent.pro | 4 + include/xlsxwriter/content_types.h | 2 + include/xlsxwriter/packager.h | 1 + include/xlsxwriter/table.h | 51 ++ include/xlsxwriter/worksheet.h | 97 ++- src/content_types.c | 10 + src/packager.c | 95 +++ src/table.c | 304 ++++++++ src/workbook.c | 32 +- src/worksheet.c | 694 ++++++++++++++++++- src/xmlwriter.c | 7 +- test/functional/src/test_table01.c | 22 + test/functional/src/test_table02.c | 29 + test/functional/src/test_table03.c | 26 + test/functional/src/test_table04.c | 30 + test/functional/src/test_table05.c | 32 + test/functional/src/test_table06.c | 35 + test/functional/src/test_table07.c | 25 + test/functional/src/test_table08.c | 36 + test/functional/src/test_table09.c | 69 ++ test/functional/src/test_table10.c | 74 ++ test/functional/src/test_table11.c | 42 ++ test/functional/src/test_table12.c | 34 + test/functional/src/test_table14.c | 63 ++ test/functional/src/test_table15.c | 42 ++ test/functional/src/test_table16.c | 31 + test/functional/src/test_table17.c | 69 ++ test/functional/src/test_table18.c | 37 + test/functional/src/test_table19.c | 31 + test/functional/src/test_table21.c | 29 + test/functional/src/test_table22.c | 32 + test/functional/src/test_table23.c | 38 + test/functional/src/test_table24.c | 25 + test/functional/src/test_table25.c | 25 + test/functional/src/test_table26.c | 56 ++ test/functional/test_table.py | 106 +++ test/functional/xlsx_files/table01.xlsx | Bin 0 -> 8180 bytes test/functional/xlsx_files/table02.xlsx | Bin 0 -> 10913 bytes test/functional/xlsx_files/table03.xlsx | Bin 0 -> 8275 bytes test/functional/xlsx_files/table04.xlsx | Bin 0 -> 9513 bytes test/functional/xlsx_files/table05.xlsx | Bin 0 -> 10802 bytes test/functional/xlsx_files/table06.xlsx | Bin 0 -> 11718 bytes test/functional/xlsx_files/table07.xlsx | Bin 0 -> 8137 bytes test/functional/xlsx_files/table08.xlsx | Bin 0 -> 8579 bytes test/functional/xlsx_files/table09.xlsx | Bin 0 -> 8870 bytes test/functional/xlsx_files/table10.xlsx | Bin 0 -> 9032 bytes test/functional/xlsx_files/table11.xlsx | Bin 0 -> 8324 bytes test/functional/xlsx_files/table12.xlsx | Bin 0 -> 8267 bytes test/functional/xlsx_files/table14.xlsx | Bin 0 -> 8492 bytes test/functional/xlsx_files/table15.xlsx | Bin 0 -> 8329 bytes test/functional/xlsx_files/table17.xlsx | Bin 0 -> 8895 bytes test/functional/xlsx_files/table18.xlsx | Bin 0 -> 8268 bytes test/functional/xlsx_files/table19.xlsx | Bin 0 -> 8209 bytes test/functional/xlsx_files/table21.xlsx | Bin 0 -> 8154 bytes test/functional/xlsx_files/table22.xlsx | Bin 0 -> 8167 bytes test/functional/xlsx_files/table23.xlsx | Bin 0 -> 8643 bytes test/functional/xlsx_files/table24.xlsx | Bin 0 -> 8189 bytes test/functional/xlsx_files/table25.xlsx | Bin 0 -> 8178 bytes test/functional/xlsx_files/table26.xlsx | Bin 0 -> 8570 bytes test/unit/Makefile | 3 + test/unit/table/Makefile | 8 + test/unit/table/main.c | 15 + test/unit/table/test_table01.c | 54 ++ test/unit/table/test_table02.c | 58 ++ test/unit/table/test_table03.c | 54 ++ test/unit/table/test_table04.c | 55 ++ test/unit/table/test_table05.c | 55 ++ test/unit/table/test_table06.c | 62 ++ test/unit/table/test_table07.c | 56 ++ test/unit/table/test_table08.c | 62 ++ test/unit/table/test_table09.c | 75 ++ test/unit/table/test_table10.c | 56 ++ test/unit/table/test_table11.c | 56 ++ test/unit/table/test_table12.c | 56 ++ test/unit/table/test_table13.c | 56 ++ test/unit/table/test_table14.c | 72 ++ test/unit/table/test_table15.c | 72 ++ test/unit/table/test_table_xml_declaration.c | 28 + 78 files changed, 3279 insertions(+), 9 deletions(-) create mode 100644 include/xlsxwriter/table.h create mode 100644 src/table.c create mode 100644 test/functional/src/test_table01.c create mode 100644 test/functional/src/test_table02.c create mode 100644 test/functional/src/test_table03.c create mode 100644 test/functional/src/test_table04.c create mode 100644 test/functional/src/test_table05.c create mode 100644 test/functional/src/test_table06.c create mode 100644 test/functional/src/test_table07.c create mode 100644 test/functional/src/test_table08.c create mode 100644 test/functional/src/test_table09.c create mode 100644 test/functional/src/test_table10.c create mode 100644 test/functional/src/test_table11.c create mode 100644 test/functional/src/test_table12.c create mode 100644 test/functional/src/test_table14.c create mode 100644 test/functional/src/test_table15.c create mode 100644 test/functional/src/test_table16.c create mode 100644 test/functional/src/test_table17.c create mode 100644 test/functional/src/test_table18.c create mode 100644 test/functional/src/test_table19.c create mode 100644 test/functional/src/test_table21.c create mode 100644 test/functional/src/test_table22.c create mode 100644 test/functional/src/test_table23.c create mode 100644 test/functional/src/test_table24.c create mode 100644 test/functional/src/test_table25.c create mode 100644 test/functional/src/test_table26.c create mode 100644 test/functional/test_table.py create mode 100644 test/functional/xlsx_files/table01.xlsx create mode 100644 test/functional/xlsx_files/table02.xlsx create mode 100644 test/functional/xlsx_files/table03.xlsx create mode 100644 test/functional/xlsx_files/table04.xlsx create mode 100644 test/functional/xlsx_files/table05.xlsx create mode 100644 test/functional/xlsx_files/table06.xlsx create mode 100644 test/functional/xlsx_files/table07.xlsx create mode 100644 test/functional/xlsx_files/table08.xlsx create mode 100644 test/functional/xlsx_files/table09.xlsx create mode 100644 test/functional/xlsx_files/table10.xlsx create mode 100644 test/functional/xlsx_files/table11.xlsx create mode 100644 test/functional/xlsx_files/table12.xlsx create mode 100644 test/functional/xlsx_files/table14.xlsx create mode 100644 test/functional/xlsx_files/table15.xlsx create mode 100644 test/functional/xlsx_files/table17.xlsx create mode 100644 test/functional/xlsx_files/table18.xlsx create mode 100644 test/functional/xlsx_files/table19.xlsx create mode 100644 test/functional/xlsx_files/table21.xlsx create mode 100644 test/functional/xlsx_files/table22.xlsx create mode 100644 test/functional/xlsx_files/table23.xlsx create mode 100644 test/functional/xlsx_files/table24.xlsx create mode 100644 test/functional/xlsx_files/table25.xlsx create mode 100644 test/functional/xlsx_files/table26.xlsx create mode 100644 test/unit/table/Makefile create mode 100644 test/unit/table/main.c create mode 100644 test/unit/table/test_table01.c create mode 100644 test/unit/table/test_table02.c create mode 100644 test/unit/table/test_table03.c create mode 100644 test/unit/table/test_table04.c create mode 100644 test/unit/table/test_table05.c create mode 100644 test/unit/table/test_table06.c create mode 100644 test/unit/table/test_table07.c create mode 100644 test/unit/table/test_table08.c create mode 100644 test/unit/table/test_table09.c create mode 100644 test/unit/table/test_table10.c create mode 100644 test/unit/table/test_table11.c create mode 100644 test/unit/table/test_table12.c create mode 100644 test/unit/table/test_table13.c create mode 100644 test/unit/table/test_table14.c create mode 100644 test/unit/table/test_table15.c create mode 100644 test/unit/table/test_table_xml_declaration.c diff --git a/.indent.pro b/.indent.pro index 2800b51c..b8c50265 100644 --- a/.indent.pro +++ b/.indent.pro @@ -141,6 +141,10 @@ -T lxw_sheet -T lxw_sst -T lxw_styles +-T lxw_table +-T lxw_table_column +-T lxw_table_obj +-T lxw_table_options -T lxw_theme -T lxw_tuple -T lxw_vml diff --git a/include/xlsxwriter/content_types.h b/include/xlsxwriter/content_types.h index bce2985d..5c7839f0 100644 --- a/include/xlsxwriter/content_types.h +++ b/include/xlsxwriter/content_types.h @@ -53,6 +53,8 @@ void lxw_ct_add_chart_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_drawing_name(lxw_content_types *content_types, const char *name); +void lxw_ct_add_table_name(lxw_content_types *content_types, + const char *name); void lxw_ct_add_comment_name(lxw_content_types *content_types, const char *name); void lxw_ct_add_vml_name(lxw_content_types *content_types); diff --git a/include/xlsxwriter/packager.h b/include/xlsxwriter/packager.h index 67548cf6..fd74eb5c 100644 --- a/include/xlsxwriter/packager.h +++ b/include/xlsxwriter/packager.h @@ -24,6 +24,7 @@ #include "app.h" #include "core.h" #include "custom.h" +#include "table.h" #include "theme.h" #include "styles.h" #include "format.h" diff --git a/include/xlsxwriter/table.h b/include/xlsxwriter/table.h new file mode 100644 index 00000000..a3556bc4 --- /dev/null +++ b/include/xlsxwriter/table.h @@ -0,0 +1,51 @@ +/* + * libxlsxwriter + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + * table - A libxlsxwriter library for creating Excel XLSX table files. + * + */ +#ifndef __LXW_TABLE_H__ +#define __LXW_TABLE_H__ + +#include + +#include "common.h" + +/* + * Struct to represent a table object. + */ +typedef struct lxw_table { + + FILE *file; + + struct lxw_table_obj *table_obj; + +} lxw_table; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + +lxw_table *lxw_table_new(void); +void lxw_table_free(lxw_table *table); +void lxw_table_assemble_xml_file(lxw_table *self); + +/* Declarations required for unit testing. */ +#ifdef TESTING + +STATIC void _table_xml_declaration(lxw_table *self); + +#endif /* TESTING */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif /* __LXW_TABLE_H__ */ diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 9e3a19f2..603a1396 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -536,6 +536,37 @@ enum lxw_conditional_icon_types { LXW_CONDITIONAL_ICONS_5_QUARTERS }; +/** @brief The type of table style. + * + * The type of table style (Light, Medium or Dark). + */ +enum lxw_table_style_type { + + LXW_TABLE_STYLE_TYPE_DEFAULT, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_LIGHT, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_MEDIUM, + + /** Light table style. */ + LXW_TABLE_STYLE_TYPE_DARK +}; + +enum lxw_table_total_functions { + + LXW_TABLE_FUNCTION_NONE = 0, + LXW_TABLE_FUNCTION_AVERAGE = 101, + LXW_TABLE_FUNCTION_COUNT_NUMS = 102, + LXW_TABLE_FUNCTION_COUNT = 103, + LXW_TABLE_FUNCTION_MAX = 104, + LXW_TABLE_FUNCTION_MIN = 105, + LXW_TABLE_FUNCTION_STD_DEV = 107, + LXW_TABLE_FUNCTION_SUM = 109, + LXW_TABLE_FUNCTION_VAR = 110 +}; + /** @brief The criteria used in autofilter rules. * * Criteria used to define an autofilter rule condition. @@ -765,6 +796,7 @@ STAILQ_HEAD(lxw_cond_format_list, lxw_cond_format_obj); STAILQ_HEAD(lxw_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); /** * @brief Options for rows and columns. @@ -1324,6 +1356,59 @@ typedef struct lxw_cond_format_hash_element { RB_ENTRY (lxw_cond_format_hash_element) tree_pointers; } lxw_cond_format_hash_element; +typedef struct lxw_table_column { + char *header; + char *formula; + char *total_string; + uint8_t total_function; + double total_value; + lxw_format *header_format; + lxw_format *format; +} lxw_table_column; + +typedef struct lxw_table_options { + char *name; + char *total_string; + lxw_table_column **columns; + uint8_t banded_columns; + uint8_t first_column; + uint8_t last_column; + uint8_t no_autofilter; + uint8_t no_banded_rows; + uint8_t no_header_row; + uint8_t style_type; + uint8_t style_type_number; + uint8_t total_row; + +} lxw_table_options; + +typedef struct lxw_table_obj { + char *name; + char *total_string; + lxw_table_column **columns; + uint8_t banded_columns; + uint8_t first_column; + uint8_t last_column; + uint8_t no_autofilter; + uint8_t no_banded_rows; + uint8_t no_header_row; + uint8_t style_type; + uint8_t style_type_number; + uint8_t total_row; + + lxw_row_t first_row; + lxw_col_t first_col; + lxw_row_t last_row; + lxw_col_t last_col; + lxw_col_t num_cols; + uint32_t id; + + char sqref[LXW_MAX_ATTRIBUTE_LENGTH]; + char filter_sqref[LXW_MAX_ATTRIBUTE_LENGTH]; + STAILQ_ENTRY (lxw_table_obj) list_pointers; + +} lxw_table_obj; + /** * @brief Options for autofilter rules. * @@ -1747,6 +1832,8 @@ typedef struct lxw_worksheet { struct lxw_vml_drawing_rel_ids *vml_drawing_rel_ids; struct lxw_comment_objs *comment_objs; struct lxw_comment_objs *header_image_objs; + struct lxw_table_objs *table_objs; + uint16_t table_count; lxw_row_t dim_rowmin; lxw_row_t dim_rowmax; @@ -1854,6 +1941,7 @@ typedef struct lxw_worksheet { struct lxw_rel_tuples *external_drawing_links; struct lxw_rel_tuples *drawing_links; struct lxw_rel_tuples *vml_drawing_links; + struct lxw_rel_tuples *external_table_links; struct lxw_panes panes; @@ -3926,6 +4014,10 @@ lxw_error worksheet_conditional_format_range(lxw_worksheet *worksheet, lxw_conditional_format *conditional_format); +lxw_error worksheet_add_table(lxw_worksheet *worksheet, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, lxw_table_options *options); + /** * @brief Make a worksheet the active, i.e., visible worksheet. * @@ -5308,10 +5400,13 @@ uint32_t lxw_worksheet_prepare_vml_objects(lxw_worksheet *worksheet, uint32_t vml_drawing_id, uint32_t comment_id); -void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *self, +void lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *worksheet, uint32_t vml_header_id, uint32_t vml_drawing_id); +void lxw_worksheet_prepare_tables(lxw_worksheet *worksheet, + uint32_t table_id); + lxw_row *lxw_worksheet_find_row(lxw_worksheet *worksheet, lxw_row_t row_num); lxw_cell *lxw_worksheet_find_cell_in_row(lxw_row *row, lxw_col_t col_num); /* diff --git a/src/content_types.c b/src/content_types.c index c83a1e0b..220ba453 100644 --- a/src/content_types.c +++ b/src/content_types.c @@ -322,6 +322,16 @@ lxw_ct_add_drawing_name(lxw_content_types *self, const char *name) lxw_ct_add_override(self, name, LXW_APP_DOCUMENT "drawing+xml"); } +/* + * Add the name of a table to the ContentTypes overrides. + */ +void +lxw_ct_add_table_name(lxw_content_types *self, const char *name) +{ + lxw_ct_add_override(self, name, + LXW_APP_DOCUMENT "spreadsheetml.table+xml"); +} + /* * Add the name of a VML drawing to the ContentTypes overrides. */ diff --git a/src/packager.c b/src/packager.c index 301d2b27..f8f635c5 100644 --- a/src/packager.c +++ b/src/packager.c @@ -446,6 +446,85 @@ _get_drawing_count(lxw_packager *self) return drawing_count; } +/* + * Write the worksheet table files. + */ +STATIC lxw_error +_write_table_files(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + lxw_table *table; + lxw_table_obj *table_obj; + lxw_error err; + + char filename[LXW_FILENAME_LENGTH] = { 0 }; + uint32_t index = 1; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + if (STAILQ_EMPTY(worksheet->table_objs)) + continue; + + STAILQ_FOREACH(table_obj, worksheet->table_objs, list_pointers) { + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "xl/tables/table%d.xml", index++); + + table = lxw_table_new(); + if (!table) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + RETURN_ON_ERROR(err); + } + + table->file = lxw_tmpfile(self->tmpdir); + if (!table->file) { + lxw_table_free(table); + return LXW_ERROR_CREATING_TMPFILE; + } + + table->table_obj = table_obj; + + lxw_table_assemble_xml_file(table); + + err = _add_file_to_zip(self, table->file, filename); + fclose(table->file); + lxw_table_free(table); + RETURN_ON_ERROR(err); + } + } + + return LXW_NO_ERROR; +} + +/* + * Count the table files. + */ +uint32_t +_get_table_count(lxw_packager *self) +{ + lxw_workbook *workbook = self->workbook; + lxw_sheet *sheet; + lxw_worksheet *worksheet; + uint32_t table_count = 0; + + STAILQ_FOREACH(sheet, workbook->sheets, list_pointers) { + if (sheet->is_chartsheet) + worksheet = sheet->u.chartsheet->worksheet; + else + worksheet = sheet->u.worksheet; + + table_count += worksheet->table_count; + } + + return table_count; +} + /* * Write the comment/header VML files. */ @@ -947,6 +1026,7 @@ _write_content_types_file(lxw_packager *self) uint32_t chartsheet_index = 1; uint32_t drawing_count = _get_drawing_count(self); uint32_t chart_count = _get_chart_count(self); + uint32_t table_count = _get_table_count(self); lxw_error err = LXW_NO_ERROR; if (!content_types) { @@ -1008,6 +1088,12 @@ _write_content_types_file(lxw_packager *self) lxw_ct_add_drawing_name(content_types, filename); } + for (index = 1; index <= table_count; index++) { + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "/xl/tables/table%d.xml", index); + lxw_ct_add_table_name(content_types, filename); + } + if (workbook->has_vml) lxw_ct_add_vml_name(content_types); @@ -1130,6 +1216,7 @@ _write_worksheet_rels_file(lxw_packager *self) if (STAILQ_EMPTY(worksheet->external_hyperlinks) && STAILQ_EMPTY(worksheet->external_drawing_links) && + STAILQ_EMPTY(worksheet->external_table_links) && !worksheet->external_vml_header_link && !worksheet->external_vml_comment_link && !worksheet->external_background_link && @@ -1164,6 +1251,11 @@ _write_worksheet_rels_file(lxw_packager *self) lxw_add_worksheet_relationship(rels, rel->type, rel->target, rel->target_mode); + STAILQ_FOREACH(rel, worksheet->external_table_links, list_pointers) { + lxw_add_worksheet_relationship(rels, rel->type, rel->target, + rel->target_mode); + } + rel = worksheet->external_background_link; if (rel) lxw_add_worksheet_relationship(rels, rel->type, rel->target, @@ -1528,6 +1620,9 @@ lxw_create_package(lxw_packager *self) error = _write_comment_files(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_table_files(self); + RETURN_AND_ZIPCLOSE_ON_ERROR(error); + error = _write_shared_strings_file(self); RETURN_AND_ZIPCLOSE_ON_ERROR(error); diff --git a/src/table.c b/src/table.c new file mode 100644 index 00000000..12228eb2 --- /dev/null +++ b/src/table.c @@ -0,0 +1,304 @@ +/***************************************************************************** + * table - A library for creating Excel XLSX table files. + * + * Used in conjunction with the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt. + * + */ + +#include "xlsxwriter/xmlwriter.h" +#include "xlsxwriter/worksheet.h" +#include "xlsxwriter/table.h" +#include "xlsxwriter/utility.h" + +/* + * Forward declarations. + */ + +/***************************************************************************** + * + * Private functions. + * + ****************************************************************************/ + +/* + * Create a new table object. + */ +lxw_table * +lxw_table_new(void) +{ + lxw_table *table = calloc(1, sizeof(lxw_table)); + GOTO_LABEL_ON_MEM_ERROR(table, mem_error); + + return table; + +mem_error: + lxw_table_free(table); + return NULL; +} + +/* + * Free a table object. + */ +void +lxw_table_free(lxw_table *table) +{ + if (!table) + return; + + free(table); +} + +/***************************************************************************** + * + * XML functions. + * + ****************************************************************************/ + +/* + * Write the XML declaration. + */ +STATIC void +_table_xml_declaration(lxw_table *self) +{ + lxw_xml_declaration(self->file); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char xmlns[] = + "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + lxw_table_obj *table_obj = self->table_obj; + + LXW_INIT_ATTRIBUTES(); + + LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); + LXW_PUSH_ATTRIBUTES_INT("id", table_obj->id); + + if (table_obj->name) + LXW_PUSH_ATTRIBUTES_STR("name", table_obj->name); + else + LXW_PUSH_ATTRIBUTES_STR("name", "Table1"); + + if (table_obj->name) + LXW_PUSH_ATTRIBUTES_STR("displayName", table_obj->name); + else + LXW_PUSH_ATTRIBUTES_STR("displayName", "Table1"); + + LXW_PUSH_ATTRIBUTES_STR("ref", table_obj->sqref); + + if (table_obj->no_header_row) + LXW_PUSH_ATTRIBUTES_STR("headerRowCount", "0"); + + if (table_obj->total_row) + LXW_PUSH_ATTRIBUTES_STR("totalsRowCount", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("totalsRowShown", "0"); + + lxw_xml_start_tag(self->file, "table", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_auto_filter(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + if (self->table_obj->no_autofilter) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("ref", self->table_obj->filter_sqref); + + lxw_xml_empty_tag(self->file, "autoFilter", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_column(lxw_table *self, uint16_t id, + lxw_table_column *column) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + int32_t dfx_id; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("id", id); + + LXW_PUSH_ATTRIBUTES_STR("name", column->header); + + if (column->total_string) { + LXW_PUSH_ATTRIBUTES_STR("totalsRowLabel", column->total_string); + } + else if (column->total_function) { + if (column->total_function == LXW_TABLE_FUNCTION_AVERAGE) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "average"); + if (column->total_function == LXW_TABLE_FUNCTION_COUNT_NUMS) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "countNums"); + if (column->total_function == LXW_TABLE_FUNCTION_COUNT) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "count"); + if (column->total_function == LXW_TABLE_FUNCTION_MAX) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "max"); + if (column->total_function == LXW_TABLE_FUNCTION_MIN) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "min"); + if (column->total_function == LXW_TABLE_FUNCTION_STD_DEV) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "stdDev"); + if (column->total_function == LXW_TABLE_FUNCTION_SUM) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "sum"); + if (column->total_function == LXW_TABLE_FUNCTION_VAR) + LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "var"); + } + + if (column->format) { + dfx_id = lxw_format_get_dxf_index(column->format); + LXW_PUSH_ATTRIBUTES_INT("dataDxfId", dfx_id); + } + + if (column->formula) { + lxw_xml_start_tag(self->file, "tableColumn", &attributes); + lxw_xml_data_element(self->file, "calculatedColumnFormula", + column->formula, NULL); + lxw_xml_end_tag(self->file, "tableColumn"); + } + else { + lxw_xml_empty_tag(self->file, "tableColumn", &attributes); + } + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_columns(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + uint16_t i; + uint16_t num_cols = self->table_obj->num_cols; + lxw_table_column **columns = self->table_obj->columns; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", num_cols); + + lxw_xml_start_tag(self->file, "tableColumns", &attributes); + + for (i = 0; i < num_cols; i++) + _table_write_table_column(self, i + 1, columns[i]); + + lxw_xml_end_tag(self->file, "tableColumns"); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_table_write_table_style_info(lxw_table *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + char name[LXW_ATTR_32]; + lxw_table_obj *table_obj = self->table_obj; + + LXW_INIT_ATTRIBUTES(); + + if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { + if (table_obj->style_type_number != 0) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleLight%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + } + else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleMedium%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_DARK) { + lxw_snprintf(name, LXW_ATTR_32, "TableStyleDark%d", + table_obj->style_type_number); + LXW_PUSH_ATTRIBUTES_STR("name", name); + } + else { + LXW_PUSH_ATTRIBUTES_STR("name", "TableStyleMedium9"); + } + + if (table_obj->first_column) + LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "0"); + + if (table_obj->last_column) + LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "0"); + + if (table_obj->no_banded_rows) + LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "0"); + else + LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "1"); + + if (table_obj->banded_columns) + LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "1"); + else + LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "0"); + + lxw_xml_empty_tag(self->file, "tableStyleInfo", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/***************************************************************************** + * + * XML file assembly functions. + * + ****************************************************************************/ + +/* + * Assemble and write the XML file. + */ +void +lxw_table_assemble_xml_file(lxw_table *self) +{ + /* Write the XML declaration. */ + _table_xml_declaration(self); + + /* Write the table element. */ + _table_write_table(self); + + /* Write the autoFilter element. */ + _table_write_auto_filter(self); + + /* Write the tableColumns element. */ + _table_write_table_columns(self); + + /* Write the tableStyleInfo element. */ + _table_write_table_style_info(self); + + lxw_xml_end_tag(self->file, "table"); +} + +/***************************************************************************** + * + * Public functions. + * + ****************************************************************************/ diff --git a/src/workbook.c b/src/workbook.c index 10260cb6..8dbf037b 100644 --- a/src/workbook.c +++ b/src/workbook.c @@ -1209,7 +1209,6 @@ _prepare_drawings(lxw_workbook *self) /* * Iterate through the worksheets and set up the VML objects. */ - STATIC void _prepare_vml(lxw_workbook *self) { @@ -1410,6 +1409,34 @@ _prepare_defined_names(lxw_workbook *self) } } +/* + * Iterate through the worksheets and set up the table objects. + */ +STATIC void +_prepare_tables(lxw_workbook *self) +{ + lxw_worksheet *worksheet; + lxw_sheet *sheet; + uint32_t table_id = 0; + uint32_t table_count = 0; + + STAILQ_FOREACH(sheet, self->sheets, list_pointers) { + if (sheet->is_chartsheet) + continue; + else + worksheet = sheet->u.worksheet; + + table_count = worksheet->table_count; + + if (table_count == 0) + continue; + + lxw_worksheet_prepare_tables(worksheet, table_id + 1); + + table_id += table_count; + } +} + /***************************************************************************** * * XML functions. @@ -2142,6 +2169,9 @@ workbook_close(lxw_workbook *self) /* Add cached data to charts. */ _add_chart_cache_data(self); + /* Set the table ids for the worksheet tables. */ + _prepare_tables(self); + /* Create a packager object to assemble sub-elements into a zip file. */ packager = lxw_packager_new(self->filename, self->options.tmpdir, diff --git a/src/worksheet.c b/src/worksheet.c index 06af3f59..2e95398d 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -29,7 +29,7 @@ #define LXW_PRINT_ACROSS 1 #define LXW_VALIDATION_MAX_TITLE_LENGTH 32 #define LXW_VALIDATION_MAX_STRING_LENGTH 255 - +#define LXW_THIS_ROW "[#This Row]," /* * Forward declarations. */ @@ -162,6 +162,10 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error); STAILQ_INIT(worksheet->data_validations); + worksheet->table_objs = calloc(1, sizeof(struct lxw_table_objs)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->table_objs, mem_error); + STAILQ_INIT(worksheet->table_objs); + worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples)); GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error); STAILQ_INIT(worksheet->external_hyperlinks); @@ -179,6 +183,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_links, mem_error); STAILQ_INIT(worksheet->vml_drawing_links); + worksheet->external_table_links = + calloc(1, sizeof(struct lxw_rel_tuples)); + GOTO_LABEL_ON_MEM_ERROR(worksheet->external_table_links, mem_error); + STAILQ_INIT(worksheet->external_table_links); + if (init_data && init_data->optimize) { FILE *tmpfile; @@ -200,7 +209,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data) RB_INIT(worksheet->drawing_rel_ids); worksheet->vml_drawing_rel_ids = - calloc(1, sizeof(struct lxw_drawing_rel_ids)); + calloc(1, sizeof(struct lxw_vml_drawing_rel_ids)); GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_rel_ids, mem_error); RB_INIT(worksheet->vml_drawing_rel_ids); @@ -458,6 +467,43 @@ _free_relationship(lxw_rel_tuple *relationship) free(relationship); } +/* + * Free a worksheet table column object. + */ +STATIC void +_free_worksheet_table_column(lxw_table_column *column) +{ + if (!column) + return; + + free(column->header); + free(column->formula); + free(column->total_string); + + free(column); +} + +/* + * Free a worksheet table object. + */ +STATIC void +_free_worksheet_table(lxw_table_obj *table) +{ + uint16_t i; + + if (!table) + return; + + for (i = 0; i < table->num_cols; i++) + _free_worksheet_table_column(table->columns[i]); + + free(table->name); + free(table->total_string); + free(table->columns); + + free(table); +} + /* * Free a worksheet object. */ @@ -474,6 +520,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet) lxw_data_val_obj *data_validation; lxw_rel_tuple *relationship; lxw_cond_format_obj *cond_format; + lxw_table_obj *table_obj; struct lxw_drawing_rel_id *drawing_rel_id; struct lxw_drawing_rel_id *next_drawing_rel_id; struct lxw_cond_format_hash_element *cond_format_elem; @@ -582,6 +629,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet) free(worksheet->selections); } + if (worksheet->table_objs) { + while (!STAILQ_EMPTY(worksheet->table_objs)) { + table_obj = STAILQ_FIRST(worksheet->table_objs); + STAILQ_REMOVE_HEAD(worksheet->table_objs, list_pointers); + _free_worksheet_table(table_obj); + } + + free(worksheet->table_objs); + } + if (worksheet->data_validations) { while (!STAILQ_EMPTY(worksheet->data_validations)) { data_validation = STAILQ_FIRST(worksheet->data_validations); @@ -620,6 +677,13 @@ lxw_worksheet_free(lxw_worksheet *worksheet) } free(worksheet->vml_drawing_links); + while (!STAILQ_EMPTY(worksheet->external_table_links)) { + relationship = STAILQ_FIRST(worksheet->external_table_links); + STAILQ_REMOVE_HEAD(worksheet->external_table_links, list_pointers); + _free_relationship(relationship); + } + free(worksheet->external_table_links); + if (worksheet->drawing_rel_ids) { for (drawing_rel_id = RB_MIN(lxw_drawing_rel_ids, worksheet->drawing_rel_ids); @@ -1448,7 +1512,7 @@ _pixels_to_height(double pixels) /* Check and set if an autofilter is a standard or custom filter. */ void -set_custom_filter(lxw_filter_rule_obj *rule_obj) +_set_custom_filter(lxw_filter_rule_obj *rule_obj) { rule_obj->is_custom = LXW_TRUE; @@ -1476,6 +1540,404 @@ set_custom_filter(lxw_filter_rule_obj *rule_obj) rule_obj->is_custom = LXW_TRUE; } +/* Check and copy user input for table styles in worksheet_add_table(). */ +void +_check_and_copy_table_style(lxw_table_obj *table_obj, + lxw_table_options *user_options) +{ + if (!user_options) + return; + + /* Set the defaults. */ + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + + if (user_options->style_type > LXW_TABLE_STYLE_TYPE_DARK) { + LXW_WARN_FORMAT1 + ("worksheet_add_table(): invalid style_type = %d. " + "Using default TableStyleMedium9", user_options->style_type); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type = user_options->style_type; + } + + /* Each type (light, medium and dark) has a different number of styles. */ + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { + if (user_options->style_type_number > 21) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_LIGHT. " + "Using default TableStyleMedium9", + user_options->style_type); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } + + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { + if (user_options->style_type_number < 1 + || user_options->style_type_number > 28) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_MEDIUM. " + "Using default TableStyleMedium9", + user_options->style_type_number); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } + + if (user_options->style_type == LXW_TABLE_STYLE_TYPE_DARK) { + if (user_options->style_type_number < 1 + || user_options->style_type_number > 11) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid style_type_number = %d for style type " + "LXW_TABLE_STYLE_TYPE_DARK. " + "Using default TableStyleMedium9", + user_options->style_type_number); + + table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; + table_obj->style_type_number = 9; + } + else { + table_obj->style_type_number = user_options->style_type_number; + } + } +} + +/* Set the defaults for table columns in worksheet_add_table(). */ +lxw_error +_set_default_table_columns(lxw_table_obj *table_obj) +{ + + char col_name[LXW_ATTR_32]; + char *header; + uint16_t i; + lxw_table_column *column; + uint16_t num_cols = table_obj->num_cols; + lxw_table_column **columns = table_obj->columns; + + for (i = 0; i < num_cols; i++) { + lxw_snprintf(col_name, LXW_ATTR_32, "Column%d", i + 1); + + column = calloc(num_cols, sizeof(lxw_table_column)); + RETURN_ON_MEM_ERROR(column, LXW_ERROR_MEMORY_MALLOC_FAILED); + + header = lxw_strdup(col_name); + if (!header) { + free(column); + RETURN_ON_MEM_ERROR(header, LXW_ERROR_MEMORY_MALLOC_FAILED); + } + columns[i] = column; + columns[i]->header = header; + } + + return LXW_NO_ERROR; +} + +/* Convert Excel 2010 style "@" structural references to the Excel 2007 style + * "[#This Row]" in table formulas. This is the format that Excel uses to + * store the references. */ +char * +_expand_table_formula(char *formula) +{ + char *expanded; + char *ptr; + size_t i; + size_t ref_count = 0; + size_t expanded_len; + + ptr = formula; + + while (*ptr++) { + if (*ptr == '@') + ref_count++; + } + + if (ref_count == 0) { + /* String doesn't need to be expanded. Just copy it. */ + expanded = lxw_strdup_formula(formula); + } + else { + /* Convert "@" in the formula string to "[#This Row],". */ + expanded_len = strlen(formula) + (sizeof(LXW_THIS_ROW) * ref_count); + expanded = calloc(1, expanded_len); + + if (!expanded) + return NULL; + + i = 0; + ptr = formula; + /* Ignore the = in the formula. */ + if (*ptr == '=') + ptr++; + + /* Do the "@" expansion. */ + while (*ptr) { + if (*ptr == '@') { + strcat(&expanded[i], LXW_THIS_ROW); + i += sizeof(LXW_THIS_ROW) - 1; + } + else { + expanded[i] = *ptr; + i++; + } + + ptr++; + } + } + + return expanded; +} + +/* Set user values for table columns in worksheet_add_table(). */ +lxw_error +_set_custom_table_columns(lxw_table_obj *table_obj, + lxw_table_options *user_options) +{ + char *str; + uint16_t i; + lxw_table_column *table_column; + lxw_table_column *user_column; + uint16_t num_cols = table_obj->num_cols; + lxw_table_column **user_columns = user_options->columns; + + for (i = 0; i < num_cols; i++) { + + user_column = user_columns[i]; + table_column = table_obj->columns[i]; + + /* NULL indicates end of user input array. */ + if (user_column == NULL) + return LXW_NO_ERROR; + + if (user_column->header) { + if (lxw_utf8_strlen(user_column->header) > 255) { + LXW_WARN_FORMAT("worksheet_add_table(): column parameter " + "'header' exceeds Excel length limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + str = lxw_strdup(user_column->header); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + /* Free the default column header. */ + free(table_column->header); + table_column->header = str; + } + + if (user_column->total_string) { + str = lxw_strdup(user_column->total_string); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + table_column->total_string = str; + } + + if (user_column->formula) { + str = _expand_table_formula(user_column->formula); + RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); + + table_column->formula = str; + } + + table_column->format = user_column->format; + table_column->total_value = user_column->total_value; + table_column->header_format = user_column->header_format; + table_column->total_function = user_column->total_function; + } + + return LXW_NO_ERROR; +} + +/* Write a worksheet table column formula like SUBTOTAL(109,[Column1]). */ +void +_write_column_function(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, + lxw_table_column *column) +{ + size_t offset; + char formula[LXW_MAX_ATTRIBUTE_LENGTH]; + lxw_format *format = column->format; + uint8_t total_function = column->total_function; + double value = column->total_value; + const char *header = column->header; + + /* Write the subtotal formula number. */ + lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "SUBTOTAL(%d,[", + total_function); + + /* Copy the header string but escape any special characters. Note, this is + * guaranteed to fit in the 2k buffer since the header is max 255 + * characters, checked in _set_custom_table_columns(). */ + offset = strlen(formula); + while (*header) { + switch (*header) { + case '\'': + case '#': + case '[': + case ']': + formula[offset++] = '\''; + formula[offset] = *header; + break; + default: + formula[offset] = *header; + break; + } + offset++; + header++; + } + + /* Write the end of the string. */ + memcpy(&formula[offset], "])\0", sizeof("])\0")); + + worksheet_write_formula_num(self, row, col, formula, format, value); +} + +/* Write a worksheet table column formula. */ +void +_write_column_formula(lxw_worksheet *self, lxw_row_t first_row, + lxw_row_t last_row, lxw_col_t col, + lxw_table_column *column) +{ + lxw_row_t row; + const char *formula = column->formula; + lxw_format *format = column->format; + + for (row = first_row; row <= last_row; row++) + worksheet_write_formula(self, row, col, formula, format); +} + +/* Set the defaults for table columns in worksheet_add_table(). */ +void +_write_table_column_data(lxw_worksheet *self, lxw_table_obj *table_obj) +{ + uint16_t i; + lxw_table_column *column; + lxw_table_column **columns = table_obj->columns; + + lxw_col_t col; + lxw_row_t first_row = table_obj->first_row; + lxw_col_t first_col = table_obj->first_col; + lxw_row_t last_row = table_obj->last_row; + lxw_row_t first_data_row = first_row; + lxw_row_t last_data_row = last_row; + + if (!table_obj->no_header_row) + first_data_row++; + + if (table_obj->total_row) + last_data_row--; + + for (i = 0; i < table_obj->num_cols; i++) { + col = first_col + i; + column = columns[i]; + + if (table_obj->no_header_row == LXW_FALSE) + worksheet_write_string(self, first_row, col, column->header, + column->header_format); + + if (column->total_string) + worksheet_write_string(self, last_row, col, column->total_string, + NULL); + + if (column->total_function) + _write_column_function(self, last_row, col, column); + + if (column->formula) + _write_column_formula(self, first_data_row, last_data_row, col, + column); + } +} + +/* + * Check that there are sufficient data rows in a worksheet table. + */ +lxw_error +_check_table_rows(lxw_row_t first_row, lxw_row_t last_row, + lxw_table_options *user_options) +{ + lxw_row_t num_non_header_rows = last_row - first_row; + + if (user_options && user_options->no_header_row == LXW_TRUE) + num_non_header_rows++; + + if (num_non_header_rows == 0) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "table must have at least 1 non-header row."); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + return LXW_NO_ERROR; +} + +/* + * Check that the the table name is valid. + */ +lxw_error +_check_table_name(lxw_table_options *user_options) +{ + const char *name; + char *ptr; + char first[2] = { 0, 0 }; + + if (!user_options) + return LXW_NO_ERROR; + + if (!user_options->name) + return LXW_NO_ERROR; + + name = user_options->name; + + /* Check table name length. */ + if (lxw_utf8_strlen(name) > 255) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "Table name exceeds Excel's limit of 255."); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Check some short invalid names. */ + if (strlen(name) == 1 + && (name[0] == 'C' || name[0] == 'c' || name[0] == 'R' + || name[0] == 'r')) { + LXW_WARN_FORMAT1("worksheet_add_table(): " + "invalid table name \"%s\".", name); + return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; + } + + /* Check for invalid characters in Table name, while trying to allow + * for utf8 strings. */ + ptr = strpbrk(name, " !\"#$%&'()*+,-/:;<=>?@[\\]^`{|}~"); + if (ptr) { + LXW_WARN_FORMAT2("worksheet_add_table(): " + "invalid character '%c' in table name \"%s\".", + *ptr, name); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + /* Check for invalid initial character in Table name, while trying to allow + * for utf8 strings. */ + first[0] = name[0]; + ptr = strpbrk(first, " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^`{|}~"); + if (ptr) { + LXW_WARN_FORMAT2("worksheet_add_table(): " + "invalid first character '%c' in table name \"%s\".", + *ptr, name); + return LXW_ERROR_PARAMETER_VALIDATION; + } + + return LXW_NO_ERROR; +} + /***************************************************************************** * * XML functions. @@ -3238,6 +3700,56 @@ mem_error: return; } +/* + * Set up external linkage for VML header/footer images. + */ +void +lxw_worksheet_prepare_tables(lxw_worksheet *self, uint32_t table_id) +{ + lxw_table_obj *table_obj; + lxw_rel_tuple *relationship; + char name[LXW_ATTR_32]; + char filename[LXW_FILENAME_LENGTH]; + + STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { + + relationship = calloc(1, sizeof(lxw_rel_tuple)); + GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); + + relationship->type = lxw_strdup("/table"); + GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); + + lxw_snprintf(filename, LXW_FILENAME_LENGTH, + "../tables/table%d.xml", table_id); + + relationship->target = lxw_strdup(filename); + GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); + + STAILQ_INSERT_TAIL(self->external_table_links, relationship, + list_pointers); + + if (!table_obj->name) { + lxw_snprintf(name, LXW_ATTR_32, "Table%d", table_id); + table_obj->name = lxw_strdup(name); + GOTO_LABEL_ON_MEM_ERROR(table_obj->name, mem_error); + } + table_obj->id = table_id; + table_id++; + } + + return; + +mem_error: + if (relationship) { + free(relationship->type); + free(relationship->target); + free(relationship->target_mode); + free(relationship); + } + + return; +} + /* * Extract width and height information from a PNG file. */ @@ -6911,6 +7423,56 @@ _worksheet_write_ignored_errors(lxw_worksheet *self) lxw_xml_end_tag(self->file, "ignoredErrors"); } +/* + * Write the element. + */ +STATIC void +_worksheet_write_table_part(lxw_worksheet *self, uint16_t id) +{ + 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", id); + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); + + lxw_xml_empty_tag(self->file, "tablePart", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_worksheet_write_table_parts(lxw_worksheet *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + lxw_table_obj *table_obj; + + if (!self->table_count) + return; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_INT("count", self->table_count); + + lxw_xml_start_tag(self->file, "tableParts", &attributes); + + STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { + self->rel_count++; + + /* Write the tablePart element. */ + _worksheet_write_table_part(self, self->rel_count); + } + + lxw_xml_end_tag(self->file, "tableParts"); + + LXW_FREE_ATTRIBUTES(); +} + /* * External functions to call intern XML methods shared with chartsheet. */ @@ -7041,6 +7603,9 @@ lxw_worksheet_assemble_xml_file(lxw_worksheet *self) /* Write the picture element. */ _worksheet_write_picture(self); + /* Write the tableParts element. */ + _worksheet_write_table_parts(self); + /* Write the extLst element. */ _worksheet_write_ext_list(self); @@ -8359,7 +8924,7 @@ worksheet_filter_column(lxw_worksheet *self, lxw_col_t col, if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->has_blanks = LXW_TRUE; - set_custom_filter(rule_obj); + _set_custom_filter(rule_obj); self->filter_rules[rule_index] = rule_obj; self->filter_on = LXW_TRUE; @@ -8443,7 +9008,7 @@ worksheet_filter_column2(lxw_worksheet *self, lxw_col_t col, if (rule_obj->criteria2 == LXW_FILTER_CRITERIA_BLANKS) rule_obj->has_blanks = LXW_TRUE; - set_custom_filter(rule_obj); + _set_custom_filter(rule_obj); self->filter_rules[rule_index] = rule_obj; self->filter_on = LXW_TRUE; @@ -8464,7 +9029,7 @@ worksheet_filter_list(lxw_worksheet *self, lxw_col_t col, char **list) uint16_t num_filters = 0; uint16_t input_list_index; uint16_t rule_obj_list_index; - char *str; + const char *str; char **tmp_list; if (!list) { @@ -8548,6 +9113,123 @@ mem_error: } +/* + * Add an Excel table to the worksheet. + */ +lxw_error +worksheet_add_table(lxw_worksheet *self, lxw_row_t first_row, + lxw_col_t first_col, lxw_row_t last_row, + lxw_col_t last_col, lxw_table_options *user_options) +{ + lxw_row_t tmp_row; + lxw_col_t tmp_col; + lxw_col_t num_cols; + lxw_error err; + lxw_table_obj *table_obj; + lxw_table_column **columns; + + /* Swap last row/col with first row/col as necessary */ + if (first_row > last_row) { + tmp_row = last_row; + last_row = first_row; + first_row = tmp_row; + } + if (first_col > last_col) { + tmp_col = last_col; + last_col = first_col; + first_col = tmp_col; + } + + /* Check that column number is valid and store the max value */ + err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); + if (err) + return err; + + num_cols = last_col - first_col + 1; + + /* Check that there are sufficient data rows. */ + err = _check_table_rows(first_row, last_row, user_options); + if (err) + return err; + + /* Check that the the table name is valid. */ + err = _check_table_name(user_options); + if (err) + return err; + + /* Create a table object to copy from the user options. */ + table_obj = calloc(1, sizeof(lxw_table_obj)); + RETURN_ON_MEM_ERROR(table_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); + + columns = calloc(num_cols, sizeof(lxw_table_column *)); + GOTO_LABEL_ON_MEM_ERROR(columns, error); + + table_obj->columns = columns; + table_obj->num_cols = num_cols; + table_obj->first_row = first_row; + table_obj->first_col = first_col; + table_obj->last_row = last_row; + table_obj->last_col = last_col; + + err = _set_default_table_columns(table_obj); + if (err) + goto error; + + /* Create the table range. */ + lxw_rowcol_to_range(table_obj->sqref, + first_row, first_col, last_row, last_col); + lxw_rowcol_to_range(table_obj->filter_sqref, + first_row, first_col, last_row, last_col); + + /* Validate and copy user options to an internal object. */ + if (user_options) { + + _check_and_copy_table_style(table_obj, user_options); + + table_obj->total_row = user_options->total_row; + table_obj->last_column = user_options->last_column; + table_obj->first_column = user_options->first_column; + table_obj->no_autofilter = user_options->no_autofilter; + table_obj->no_header_row = user_options->no_header_row; + table_obj->no_banded_rows = user_options->no_banded_rows; + table_obj->banded_columns = user_options->banded_columns; + + if (user_options->no_header_row) + table_obj->no_autofilter = LXW_TRUE; + + if (user_options->columns) { + err = _set_custom_table_columns(table_obj, user_options); + if (err) + goto error; + } + + if (user_options->total_row) { + lxw_rowcol_to_range(table_obj->filter_sqref, + first_row, first_col, last_row - 1, last_col); + } + + if (user_options->name) { + table_obj->name = lxw_strdup(user_options->name); + if (!table_obj->name) { + err = LXW_ERROR_MEMORY_MALLOC_FAILED; + goto error; + } + } + } + + _write_table_column_data(self, table_obj); + + STAILQ_INSERT_TAIL(self->table_objs, table_obj, list_pointers); + self->table_count++; + + return LXW_NO_ERROR; + +error: + _free_worksheet_table(table_obj); + return err; + +} + /* * Set this worksheet as a selected worksheet, i.e. the worksheet has its tab * highlighted. diff --git a/src/xmlwriter.c b/src/xmlwriter.c index f5f640cd..ef0055de 100644 --- a/src/xmlwriter.c +++ b/src/xmlwriter.c @@ -17,6 +17,7 @@ #define LXW_LT "<" #define LXW_GT ">" #define LXW_QUOT """ +#define LXW_NL " " /* Defines. */ #define LXW_MAX_ENCODED_ATTRIBUTE_LENGTH (LXW_MAX_ATTRIBUTE_LENGTH*6) @@ -178,6 +179,10 @@ _escape_attributes(struct xml_attribute *attribute) memcpy(p_encoded, LXW_QUOT, sizeof(LXW_QUOT) - 1); p_encoded += sizeof(LXW_QUOT) - 1; break; + case '\n': + memcpy(p_encoded, LXW_NL, sizeof(LXW_NL) - 1); + p_encoded += sizeof(LXW_NL) - 1; + break; default: *p_encoded = *p_attr; p_encoded++; @@ -373,7 +378,7 @@ _fprint_escaped_attributes(FILE * xmlfile, STAILQ_FOREACH(attribute, attributes, list_entries) { fprintf(xmlfile, " %s=", attribute->key); - if (!strpbrk(attribute->value, "&<>\"")) { + if (!strpbrk(attribute->value, "&<>\"\n")) { fprintf(xmlfile, "\"%s\"", attribute->value); } else { diff --git a/test/functional/src/test_table01.c b/test/functional/src/test_table01.c new file mode 100644 index 00000000..8fe976d2 --- /dev/null +++ b/test/functional/src/test_table01.c @@ -0,0 +1,22 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table02.c b/test/functional/src/test_table02.c new file mode 100644 index 00000000..6a78d5e6 --- /dev/null +++ b/test/functional/src/test_table02.c @@ -0,0 +1,29 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table02.xlsx"); + lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet1, COLS("B:J"), 10.288, NULL); + worksheet_set_column(worksheet2, COLS("C:L"), 10.288, NULL); + + worksheet_add_table(worksheet1, RANGE("B3:E11"), NULL); + worksheet_add_table(worksheet1, RANGE("G10:J16"), NULL); + worksheet_add_table(worksheet1, RANGE("C18:F25"), NULL); + + worksheet_add_table(worksheet2, RANGE("I4:L11"), NULL); + worksheet_add_table(worksheet2, RANGE("C16:H23"), NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table03.c b/test/functional/src/test_table03.c new file mode 100644 index 00000000..104a3224 --- /dev/null +++ b/test/functional/src/test_table03.c @@ -0,0 +1,26 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table03.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + + /* Add other objects to check rId handling. */ + workbook_unset_default_url_format(workbook); + worksheet_write_url(worksheet, CELL("A1"), "http://perl.com/", NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table04.c b/test/functional/src/test_table04.c new file mode 100644 index 00000000..fcde4aae --- /dev/null +++ b/test/functional/src/test_table04.c @@ -0,0 +1,30 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table04.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + + /* Add other objects to check rId handling. */ + workbook_unset_default_url_format(workbook); + worksheet_write_url(worksheet, CELL("A1"), "http://perl.com/", NULL); + + worksheet_set_comments_author(worksheet, "John"); + worksheet_write_comment(worksheet, CELL("H1"), "Test1"); + worksheet_write_comment(worksheet, CELL("J1"), "Test2"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table05.c b/test/functional/src/test_table05.c new file mode 100644 index 00000000..47f0e904 --- /dev/null +++ b/test/functional/src/test_table05.c @@ -0,0 +1,32 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table05.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + + /* Add other objects to check rId handling. */ + workbook_unset_default_url_format(workbook); + worksheet_write_url(worksheet, CELL("A1"), "http://perl.com/", NULL); + + worksheet_set_comments_author(worksheet, "John"); + worksheet_write_comment(worksheet, CELL("H1"), "Test1"); + worksheet_write_comment(worksheet, CELL("J1"), "Test2"); + + worksheet_insert_image(worksheet, CELL("A4"), "images/blue.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table06.c b/test/functional/src/test_table06.c new file mode 100644 index 00000000..5decf44a --- /dev/null +++ b/test/functional/src/test_table06.c @@ -0,0 +1,35 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table06.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:H"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + worksheet_add_table(worksheet, RANGE("F15:H20"), NULL); + worksheet_add_table(worksheet, RANGE("C23:D30"), NULL); + + /* Add other objects to check rId handling. */ + workbook_unset_default_url_format(workbook); + worksheet_write_url(worksheet, CELL("A1"), "http://perl.com/", NULL); + worksheet_write_url(worksheet, CELL("C1"), "http://perl.com/", NULL); + + worksheet_set_comments_author(worksheet, "John"); + worksheet_write_comment(worksheet, CELL("H1"), "Test1"); + worksheet_write_comment(worksheet, CELL("J1"), "Test2"); + + worksheet_insert_image(worksheet, CELL("A4"), "images/blue.png"); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table07.c b/test/functional/src/test_table07.c new file mode 100644 index 00000000..5d84daeb --- /dev/null +++ b/test/functional/src/test_table07.c @@ -0,0 +1,25 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table07.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Foo", NULL); + + lxw_table_options options = {.no_header_row = LXW_TRUE}; + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table08.c b/test/functional/src/test_table08.c new file mode 100644 index 00000000..0e11c7c8 --- /dev/null +++ b/test/functional/src/test_table08.c @@ -0,0 +1,36 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table08.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column1", NULL); + worksheet_write_string(worksheet, CELL("B1"), "Column2", NULL); + worksheet_write_string(worksheet, CELL("C1"), "Column3", NULL); + worksheet_write_string(worksheet, CELL("D1"), "Column4", NULL); + worksheet_write_string(worksheet, CELL("E1"), "Total", NULL); + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {0}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + worksheet_add_table(worksheet, RANGE("C3:F14"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table09.c b/test/functional/src/test_table09.c new file mode 100644 index 00000000..ce62eb20 --- /dev/null +++ b/test/functional/src/test_table09.c @@ -0,0 +1,69 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table09.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("B:K"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column1", NULL); + worksheet_write_string(worksheet, CELL("B1"), "Column2", NULL); + worksheet_write_string(worksheet, CELL("C1"), "Column3", NULL); + worksheet_write_string(worksheet, CELL("D1"), "Column4", NULL); + worksheet_write_string(worksheet, CELL("E1"), "Column5", NULL); + worksheet_write_string(worksheet, CELL("F1"), "Column6", NULL); + worksheet_write_string(worksheet, CELL("G1"), "Column7", NULL); + worksheet_write_string(worksheet, CELL("H1"), "Column8", NULL); + worksheet_write_string(worksheet, CELL("I1"), "Column9", NULL); + worksheet_write_string(worksheet, CELL("J1"), "Column10", NULL); + worksheet_write_string(worksheet, CELL("K1"), "Total", NULL); + + worksheet_write_number(worksheet, 3, 1, 0, NULL); + worksheet_write_number(worksheet, 3, 2, 0, NULL); + worksheet_write_number(worksheet, 3, 3, 0, NULL); + worksheet_write_number(worksheet, 3, 6, 0, NULL); + worksheet_write_number(worksheet, 3, 7, 0, NULL); + worksheet_write_number(worksheet, 3, 8, 0, NULL); + worksheet_write_number(worksheet, 3, 9, 0, NULL); + worksheet_write_number(worksheet, 3, 10, 0, NULL); + + worksheet_write_number(worksheet, 4, 1, 0, NULL); + worksheet_write_number(worksheet, 4, 2, 0, NULL); + worksheet_write_number(worksheet, 4, 3, 0, NULL); + worksheet_write_number(worksheet, 4, 6, 0, NULL); + worksheet_write_number(worksheet, 4, 7, 0, NULL); + worksheet_write_number(worksheet, 4, 8, 0, NULL); + worksheet_write_number(worksheet, 4, 9, 0, NULL); + worksheet_write_number(worksheet, 4, 10, 0, NULL); + + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {.total_function = LXW_TABLE_FUNCTION_AVERAGE}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column col5 = {.total_function = LXW_TABLE_FUNCTION_COUNT_NUMS}; + lxw_table_column col6 = {.total_function = LXW_TABLE_FUNCTION_MAX}; + lxw_table_column col7 = {.total_function = LXW_TABLE_FUNCTION_MIN}; + lxw_table_column col8 = {.total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col9 = {.total_function = LXW_TABLE_FUNCTION_STD_DEV}; + lxw_table_column col10 = {.total_function = LXW_TABLE_FUNCTION_VAR}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8, &col9, &col10, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + + worksheet_add_table(worksheet, RANGE("B3:K6"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table10.c b/test/functional/src/test_table10.c new file mode 100644 index 00000000..69378c19 --- /dev/null +++ b/test/functional/src/test_table10.c @@ -0,0 +1,74 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table10.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_format *format = workbook_add_format(workbook); + format_set_num_format_index(format, 2); + + worksheet_set_column(worksheet, COLS("B:K"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column1", NULL); + worksheet_write_string(worksheet, CELL("B1"), "Column2", NULL); + worksheet_write_string(worksheet, CELL("C1"), "Column3", NULL); + worksheet_write_string(worksheet, CELL("D1"), "Column4", NULL); + worksheet_write_string(worksheet, CELL("E1"), "Column5", NULL); + worksheet_write_string(worksheet, CELL("F1"), "Column6", NULL); + worksheet_write_string(worksheet, CELL("G1"), "Column7", NULL); + worksheet_write_string(worksheet, CELL("H1"), "Column8", NULL); + worksheet_write_string(worksheet, CELL("I1"), "Column9", NULL); + worksheet_write_string(worksheet, CELL("J1"), "Column10", NULL); + worksheet_write_string(worksheet, CELL("K1"), "Total", NULL); + + worksheet_write_number(worksheet, 3, 1, 0, NULL); + worksheet_write_number(worksheet, 3, 2, 0, NULL); + worksheet_write_number(worksheet, 3, 3, 0, NULL); + worksheet_write_number(worksheet, 3, 6, 0, NULL); + worksheet_write_number(worksheet, 3, 7, 0, NULL); + worksheet_write_number(worksheet, 3, 8, 0, NULL); + worksheet_write_number(worksheet, 3, 9, 0, NULL); + worksheet_write_number(worksheet, 3, 10, 0, NULL); + + worksheet_write_number(worksheet, 4, 1, 0, NULL); + worksheet_write_number(worksheet, 4, 2, 0, NULL); + worksheet_write_number(worksheet, 4, 3, 0, NULL); + worksheet_write_number(worksheet, 4, 6, 0, NULL); + worksheet_write_number(worksheet, 4, 7, 0, NULL); + worksheet_write_number(worksheet, 4, 8, 0, NULL); + worksheet_write_number(worksheet, 4, 9, 0, NULL); + worksheet_write_number(worksheet, 4, 10, 0, NULL); + + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {.total_function = LXW_TABLE_FUNCTION_AVERAGE}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column col5 = {.total_function = LXW_TABLE_FUNCTION_COUNT_NUMS}; + lxw_table_column col6 = {.total_function = LXW_TABLE_FUNCTION_MAX}; + lxw_table_column col7 = {.total_function = LXW_TABLE_FUNCTION_MIN}; + lxw_table_column col8 = {.total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col9 = {.total_function = LXW_TABLE_FUNCTION_STD_DEV}; + lxw_table_column col10 = {.total_function = LXW_TABLE_FUNCTION_VAR, + .formula = "SUM(Table1[[#This Row],[Column1]:[Column3]])", + .format = format}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8, &col9, &col10, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + + worksheet_add_table(worksheet, RANGE("B3:K6"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table11.c b/test/functional/src/test_table11.c new file mode 100644 index 00000000..31f9e01f --- /dev/null +++ b/test/functional/src/test_table11.c @@ -0,0 +1,42 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table11.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C2:F6"), NULL); + + worksheet_write_string(worksheet, 2, 2, "Foo", NULL); + worksheet_write_string(worksheet, 3, 2, "Bar", NULL); + worksheet_write_string(worksheet, 4, 2, "Baz", NULL); + worksheet_write_string(worksheet, 5, 2, "Bop", NULL); + + worksheet_write_number(worksheet, 2, 3, 1234, NULL); + worksheet_write_number(worksheet, 3, 3, 1256, NULL); + worksheet_write_number(worksheet, 4, 3, 2234, NULL); + worksheet_write_number(worksheet, 5, 3, 1324, NULL); + + worksheet_write_number(worksheet, 2, 4, 2000, NULL); + worksheet_write_number(worksheet, 3, 4, 4000, NULL); + worksheet_write_number(worksheet, 4, 4, 3000, NULL); + worksheet_write_number(worksheet, 5, 4, 1000, NULL); + + worksheet_write_number(worksheet, 2, 5, 4321, NULL); + worksheet_write_number(worksheet, 3, 5, 4320, NULL); + worksheet_write_number(worksheet, 4, 5, 4332, NULL); + worksheet_write_number(worksheet, 5, 5, 4333, NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table12.c b/test/functional/src/test_table12.c new file mode 100644 index 00000000..8baf7f20 --- /dev/null +++ b/test/functional/src/test_table12.c @@ -0,0 +1,34 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table12.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C2:F6"), NULL); + + worksheet_write_string(worksheet, 2, 2, "Foo", NULL); + worksheet_write_string(worksheet, 3, 2, "Bar", NULL); + worksheet_write_string(worksheet, 4, 2, "Baz", NULL); + + worksheet_write_number(worksheet, 2, 3, 1234, NULL); + worksheet_write_number(worksheet, 3, 3, 1256, NULL); + worksheet_write_number(worksheet, 4, 3, 2234, NULL); + + worksheet_write_number(worksheet, 2, 4, 2000, NULL); + worksheet_write_number(worksheet, 3, 4, 4000, NULL); + worksheet_write_number(worksheet, 4, 4, 3000, NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table14.c b/test/functional/src/test_table14.c new file mode 100644 index 00000000..80be56c2 --- /dev/null +++ b/test/functional/src/test_table14.c @@ -0,0 +1,63 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table14.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + lxw_format *format1 = workbook_add_format(workbook); + lxw_format *format2 = workbook_add_format(workbook); + lxw_format *format3 = workbook_add_format(workbook); + + format_set_num_format(format1, "0.00;[Red]0.00"); + format_set_num_format(format2, "0.00_ ;\\-0.00\\ "); + format_set_num_format(format3, "0.00_ ;[Red]\\-0.00\\ "); + + /* We manually set the indices to get the same order as the target file. */ + lxw_format_get_dxf_index(format3); + lxw_format_get_dxf_index(format2); + lxw_format_get_dxf_index(format1); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + lxw_table_column col1 = {0}; + lxw_table_column col2 = {.format = format1}; + lxw_table_column col3 = {.format = format2}; + lxw_table_column col4 = {.format = format3}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + lxw_table_options options = {.columns = columns}; + + worksheet_add_table(worksheet, RANGE("C2:F6"), &options); + + worksheet_write_string(worksheet, 2, 2, "Foo", NULL); + worksheet_write_string(worksheet, 3, 2, "Bar", NULL); + worksheet_write_string(worksheet, 4, 2, "Baz", NULL); + worksheet_write_string(worksheet, 5, 2, "Bop", NULL); + + worksheet_write_number(worksheet, 2, 3, 1234, format1); + worksheet_write_number(worksheet, 3, 3, 1256, format1); + worksheet_write_number(worksheet, 4, 3, 2234, format1); + worksheet_write_number(worksheet, 5, 3, 1324, format1); + + worksheet_write_number(worksheet, 2, 4, 2000, format2); + worksheet_write_number(worksheet, 3, 4, 4000, format2); + worksheet_write_number(worksheet, 4, 4, 3000, format2); + worksheet_write_number(worksheet, 5, 4, 1000, format2); + + worksheet_write_number(worksheet, 2, 5, 4321, format3); + worksheet_write_number(worksheet, 3, 5, 4320, format3); + worksheet_write_number(worksheet, 4, 5, 4332, format3); + worksheet_write_number(worksheet, 5, 5, 4333, format3); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table15.c b/test/functional/src/test_table15.c new file mode 100644 index 00000000..380b8d42 --- /dev/null +++ b/test/functional/src/test_table15.c @@ -0,0 +1,42 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table15.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + worksheet_add_table(worksheet, RANGE("C2:F6"), NULL); + + worksheet_write_string(worksheet, 2, 2, "Foo", NULL); + worksheet_write_string(worksheet, 3, 2, "Bar", NULL); + worksheet_write_string(worksheet, 4, 2, "Baz", NULL); + worksheet_write_string(worksheet, 5, 2, "Bop", NULL); + + worksheet_write_number(worksheet, 2, 3, 1234, NULL); + worksheet_write_number(worksheet, 3, 3, 1256, NULL); + worksheet_write_number(worksheet, 4, 3, 2234, NULL); + worksheet_write_number(worksheet, 5, 3, 1324, NULL); + + worksheet_write_number(worksheet, 2, 4, 2000, NULL); + worksheet_write_number(worksheet, 3, 4, 0, NULL); + worksheet_write_number(worksheet, 4, 4, 3000, NULL); + worksheet_write_number(worksheet, 5, 4, 1000, NULL); + + worksheet_write_number(worksheet, 2, 5, 4321, NULL); + worksheet_write_number(worksheet, 3, 5, 4320, NULL); + worksheet_write_number(worksheet, 4, 5, 4332, NULL); + worksheet_write_number(worksheet, 5, 5, 4333, NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table16.c b/test/functional/src/test_table16.c new file mode 100644 index 00000000..6887ef75 --- /dev/null +++ b/test/functional/src/test_table16.c @@ -0,0 +1,31 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table16.xlsx"); + lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, NULL); + lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet1, COLS("B:J"), 10.288, NULL); + worksheet_set_column(worksheet2, COLS("C:L"), 10.288, NULL); + + /* Add the tables in reverse worksheet order to test_table02.c */ + worksheet_add_table(worksheet2, RANGE("I4:L11"), NULL); + worksheet_add_table(worksheet2, RANGE("C16:H23"), NULL); + + worksheet_add_table(worksheet1, RANGE("B3:E11"), NULL); + worksheet_add_table(worksheet1, RANGE("G10:J16"), NULL); + worksheet_add_table(worksheet1, RANGE("C18:F25"), NULL); + + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table17.c b/test/functional/src/test_table17.c new file mode 100644 index 00000000..65d71607 --- /dev/null +++ b/test/functional/src/test_table17.c @@ -0,0 +1,69 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table17.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("B:K"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column1", NULL); + worksheet_write_string(worksheet, CELL("B1"), "Column2", NULL); + worksheet_write_string(worksheet, CELL("C1"), "Column3", NULL); + worksheet_write_string(worksheet, CELL("D1"), "Column4", NULL); + worksheet_write_string(worksheet, CELL("E1"), "Column5", NULL); + worksheet_write_string(worksheet, CELL("F1"), "Column6", NULL); + worksheet_write_string(worksheet, CELL("G1"), "Column7", NULL); + worksheet_write_string(worksheet, CELL("H1"), "Column8", NULL); + worksheet_write_string(worksheet, CELL("I1"), "Column9", NULL); + worksheet_write_string(worksheet, CELL("J1"), "Column10", NULL); + worksheet_write_string(worksheet, CELL("K1"), "Total", NULL); + + worksheet_write_number(worksheet, 3, 1, 0, NULL); + worksheet_write_number(worksheet, 3, 2, 0, NULL); + worksheet_write_number(worksheet, 3, 3, 0, NULL); + worksheet_write_number(worksheet, 3, 6, 4, NULL); + worksheet_write_number(worksheet, 3, 7, 0, NULL); + worksheet_write_number(worksheet, 3, 8, 1, NULL); + worksheet_write_number(worksheet, 3, 9, 0, NULL); + worksheet_write_number(worksheet, 3, 10, 0, NULL); + + worksheet_write_number(worksheet, 4, 1, 0, NULL); + worksheet_write_number(worksheet, 4, 2, 0, NULL); + worksheet_write_number(worksheet, 4, 3, 0, NULL); + worksheet_write_number(worksheet, 4, 6, 5, NULL); + worksheet_write_number(worksheet, 4, 7, 0, NULL); + worksheet_write_number(worksheet, 4, 8, 2, NULL); + worksheet_write_number(worksheet, 4, 9, 0, NULL); + worksheet_write_number(worksheet, 4, 10, 0, NULL); + + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {.total_function = LXW_TABLE_FUNCTION_AVERAGE}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column col5 = {.total_function = LXW_TABLE_FUNCTION_COUNT_NUMS}; + lxw_table_column col6 = {.total_function = LXW_TABLE_FUNCTION_MAX, .total_value = 5}; + lxw_table_column col7 = {.total_function = LXW_TABLE_FUNCTION_MIN}; + lxw_table_column col8 = {.total_function = LXW_TABLE_FUNCTION_SUM, .total_value = 3}; + lxw_table_column col9 = {.total_function = LXW_TABLE_FUNCTION_STD_DEV}; + lxw_table_column col10 = {.total_function = LXW_TABLE_FUNCTION_VAR}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8, &col9, &col10, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + + worksheet_add_table(worksheet, RANGE("B3:K6"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table18.c b/test/functional/src/test_table18.c new file mode 100644 index 00000000..d9f00b27 --- /dev/null +++ b/test/functional/src/test_table18.c @@ -0,0 +1,37 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table18.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_format *wrap = workbook_add_format(workbook); + + format_set_text_wrap(wrap); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + worksheet_set_row(worksheet, 2, 39, NULL); + + lxw_table_column col1 = {0}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {0}; + lxw_table_column col4 = {.header = "Column\n4", .header_format = wrap}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + lxw_table_options options = {.columns = columns}; + + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + worksheet_write_string(worksheet, CELL("A16"), "hello", NULL); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table19.c b/test/functional/src/test_table19.c new file mode 100644 index 00000000..fa2b0433 --- /dev/null +++ b/test/functional/src/test_table19.c @@ -0,0 +1,31 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table19.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + lxw_table_column col1 = {0}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {0}; + lxw_table_column col4 = {.header = " Column4 "}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + lxw_table_options options = {.columns = columns}; + + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table21.c b/test/functional/src/test_table21.c new file mode 100644 index 00000000..17372ec9 --- /dev/null +++ b/test/functional/src/test_table21.c @@ -0,0 +1,29 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table21.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:D"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column", NULL); + + lxw_table_column col1 = {.header = "Column"}; + lxw_table_column *columns[] = {&col1, NULL}; + + lxw_table_options options = {.columns = columns}; + + worksheet_add_table(worksheet, RANGE("C3:D13"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table22.c b/test/functional/src/test_table22.c new file mode 100644 index 00000000..dddd5ea1 --- /dev/null +++ b/test/functional/src/test_table22.c @@ -0,0 +1,32 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table22.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("B:C"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("B2"), "apple", NULL); + worksheet_write_string(worksheet, CELL("C2"), "pie", NULL); + worksheet_write_string(worksheet, CELL("B3"), "pine", NULL); + worksheet_write_string(worksheet, CELL("C3"), "tree", NULL); + + + lxw_table_options options = {.no_header_row = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B2:C3"), &options); + + + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table23.c b/test/functional/src/test_table23.c new file mode 100644 index 00000000..9ba4198e --- /dev/null +++ b/test/functional/src/test_table23.c @@ -0,0 +1,38 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table23.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("B:F"), 10.288, NULL); + + worksheet_write_string(worksheet, CELL("A1"), "Column1", NULL); + worksheet_write_string(worksheet, CELL("F1"), "Total", NULL); + worksheet_write_string(worksheet, CELL("B1"), "Column'", NULL); + worksheet_write_string(worksheet, CELL("C1"), "Column#", NULL); + worksheet_write_string(worksheet, CELL("D1"), "Column[", NULL); + worksheet_write_string(worksheet, CELL("E1"), "Column]", NULL); + + lxw_table_column col1 = {.header = "Column1", .total_string = "Total"}; + lxw_table_column col2 = {.header = "Column'", .total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col3 = {.header = "Column#", .total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col4 = {.header = "Column[", .total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col5 = {.header = "Column]", .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, NULL}; + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + worksheet_add_table(worksheet, RANGE("B3:F9"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table24.c b/test/functional/src/test_table24.c new file mode 100644 index 00000000..bc600329 --- /dev/null +++ b/test/functional/src/test_table24.c @@ -0,0 +1,25 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table24.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, + .style_type_number = 10}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table25.c b/test/functional/src/test_table25.c new file mode 100644 index 00000000..ba475558 --- /dev/null +++ b/test/functional/src/test_table25.c @@ -0,0 +1,25 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table25.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:F"), 10.288, NULL); + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + .style_type_number = 0}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_table26.c b/test/functional/src/test_table26.c new file mode 100644 index 00000000..aa3f1542 --- /dev/null +++ b/test/functional/src/test_table26.c @@ -0,0 +1,56 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = workbook_new("test_table26.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + + worksheet_set_column(worksheet, COLS("C:D"), 10.288, NULL); + worksheet_set_column(worksheet, COLS("F:G"), 10.288, NULL); + + /* Add some valid tables. */ + worksheet_add_table(worksheet, RANGE("C2:D3"), NULL); + lxw_table_options options1 = {.name = "Table2", .no_header_row = LXW_TRUE}; + worksheet_add_table(worksheet, RANGE("F3:G3"), &options1); + + /* + * Test incorrect tables. These should be ignored with a warning. + * */ + + /* Add a table with an incorrect range. */ + worksheet_add_table(worksheet, RANGE("I2:J2"), NULL); + + /* Check incorrect table names. */ + lxw_table_options options2 = {.name = "Has space"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options2); + + lxw_table_options options3 = {.name = "Table["}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options3); + + lxw_table_options options4 = {.name = "This_is_a_long_table_name_that_exceeds_a_limit_of_255_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options4); + + lxw_table_options options5 = {.name = "c"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options5); + + lxw_table_options options6 = {.name = "R"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options6); + + lxw_table_options options7 = {.name = ".Table"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options7); + + lxw_table_options options8 = {.name = "1Table"}; + worksheet_add_table(worksheet, RANGE("F3:G33"), &options8); + + + return workbook_close(workbook); +} diff --git a/test/functional/test_table.py b/test/functional/test_table.py new file mode 100644 index 00000000..1c2979c4 --- /dev/null +++ b/test/functional/test_table.py @@ -0,0 +1,106 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# Copyright 2014-2021, John McNamara, jmcnamara@cpan.org +# + +import base_test_class + +class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): + """ + Test file created with libxlsxwriter against a file created by Excel. + + """ + + def test_table01(self): + self.run_exe_test('test_table01') + + def test_table02(self): + self.run_exe_test('test_table02') + + def test_table03(self): + self.run_exe_test('test_table03') + + def test_table04(self): + self.run_exe_test('test_table04') + + def test_table05(self): + self.run_exe_test('test_table05') + + def test_table06(self): + self.run_exe_test('test_table06') + + def test_table07(self): + self.run_exe_test('test_table07') + + def test_table08(self): + self.ignore_files = ['xl/calcChain.xml', + '[Content_Types].xml', + 'xl/_rels/workbook.xml.rels'] + self.run_exe_test('test_table08') + + def test_table09(self): + self.ignore_files = ['xl/calcChain.xml', + '[Content_Types].xml', + 'xl/_rels/workbook.xml.rels'] + self.run_exe_test('test_table09') + + def test_table10(self): + self.ignore_files = ['xl/calcChain.xml', + '[Content_Types].xml', + 'xl/_rels/workbook.xml.rels'] + self.run_exe_test('test_table10') + + def test_table11(self): + self.run_exe_test('test_table11') + + def test_table12(self): + self.run_exe_test('test_table12') + + # Skip test_table13 since we can't set the dxf index for + # format_set_num_format_index() formats with C hash struct. + + def test_table14(self): + self.run_exe_test('test_table14') + + def test_table15(self): + self.run_exe_test('test_table15') + + def test_table16(self): + self.run_exe_test('test_table16', 'table02.xlsx') + + def test_table17(self): + self.ignore_files = ['xl/calcChain.xml', + '[Content_Types].xml', + 'xl/_rels/workbook.xml.rels'] + self.run_exe_test('test_table17') + + def test_table18(self): + self.run_exe_test('test_table18') + + def test_table19(self): + self.run_exe_test('test_table19') + + # TODO. Duplicate names. + + def test_table21(self): + self.run_exe_test('test_table21') + + def test_table22(self): + self.run_exe_test('test_table22') + + def test_table23(self): + self.ignore_files = ['xl/calcChain.xml', + '[Content_Types].xml', + 'xl/_rels/workbook.xml.rels'] + self.run_exe_test('test_table23') + + def test_table24(self): + self.run_exe_test('test_table24') + + def test_table25(self): + self.run_exe_test('test_table25') + + def test_table26(self): + self.run_exe_test('test_table26') diff --git a/test/functional/xlsx_files/table01.xlsx b/test/functional/xlsx_files/table01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..43a326b362176bf2a5a91b307bd99b0ea75189a8 GIT binary patch literal 8180 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 B5Lo~K literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table02.xlsx b/test/functional/xlsx_files/table02.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1687c1bf3be541f60e4cc5ad61aedbc1d99ffefe GIT binary patch literal 10913 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 Du=rOh literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table03.xlsx b/test/functional/xlsx_files/table03.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c43791531ce9df4e3d3fb3d122c536746b92f2c1 GIT binary patch literal 8275 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 literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table09.xlsx b/test/functional/xlsx_files/table09.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0b501da3929ba8af035ff46893e043420b0a5244 GIT binary patch literal 8870 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 Xz^f~vAn5wNQ^y8qAQ&$}^L_O{*18?F literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table10.xlsx b/test/functional/xlsx_files/table10.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9491fcde1d5ae5ca303b7c8c23c62755282b6400 GIT binary patch literal 9032 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 Yk>C|&U?6n;p(UUIlpu^>CHXP>A9%!Sj{pDw literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table11.xlsx b/test/functional/xlsx_files/table11.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f0582cfaf3000d910344a18e4a11060a44339373 GIT binary patch literal 8324 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 NA0Yy>hE(65{sYOhI`;qo literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table12.xlsx b/test/functional/xlsx_files/table12.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..373763b5df2aaeaa5142648ab65ebd3edcb09e7c GIT binary patch literal 8267 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 JhWz{6{{Ww47N-CJ literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table14.xlsx b/test/functional/xlsx_files/table14.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9f526ef4080c95c295953d562db27e101f3fa1ef GIT binary patch literal 8492 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 T1~>o!4fex@Vb+lJ``iBjH`g@O literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table17.xlsx b/test/functional/xlsx_files/table17.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a481d97b9f42ef32b3175ca91647f389d5f51469 GIT binary patch literal 8895 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 EKg7a1`~Uy| literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table19.xlsx b/test/functional/xlsx_files/table19.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..118803e660244ffa4b06f1c8e4883d1da2611690 GIT binary patch literal 8209 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 D{#6yQ literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table21.xlsx b/test/functional/xlsx_files/table21.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b83e61b1b39c4d05a1afe8853d683343cf5a79cb GIT binary patch literal 8154 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 literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table22.xlsx b/test/functional/xlsx_files/table22.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..37f3db5ef46a57dc5976ac84ed77568eedcf8079 GIT binary patch literal 8167 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 E0Vx2;NdN!< literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/table24.xlsx b/test/functional/xlsx_files/table24.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bbaf91c6717585510e9da6f030ecc7c281ad90d8 GIT binary patch literal 8189 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

0$D@QYh4R-$5njC09-C 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\n" + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + worksheet_add_table(worksheet, RANGE("C3:F13"), NULL); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table02.c b/test/unit/table/test_table02.c new file mode 100644 index 00000000..3b039729 --- /dev/null +++ b/test/unit/table/test_table02.c @@ -0,0 +1,58 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table02) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_LIGHT, .style_type_number = 17}; + + worksheet_add_table(worksheet, RANGE("D4:I15"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table03.c b/test/unit/table/test_table03.c new file mode 100644 index 00000000..95fbdd55 --- /dev/null +++ b/test/unit/table/test_table03.c @@ -0,0 +1,54 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table03) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE, .no_banded_rows = LXW_TRUE, .banded_columns = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("C5:D16"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table04.c b/test/unit/table/test_table04.c new file mode 100644 index 00000000..0f8bd30d --- /dev/null +++ b/test/unit/table/test_table04.c @@ -0,0 +1,55 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table04) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.no_autofilter = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table05.c b/test/unit/table/test_table05.c new file mode 100644 index 00000000..cc3dcbad --- /dev/null +++ b/test/unit/table/test_table05.c @@ -0,0 +1,55 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table05) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.no_header_row = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("C4:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table06.c b/test/unit/table/test_table06.c new file mode 100644 index 00000000..78e370bb --- /dev/null +++ b/test/unit/table/test_table06.c @@ -0,0 +1,62 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table06) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_column col1 = {.header = "Foo"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {0}; + lxw_table_column col4 = {.header = "Baz"}; + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + + lxw_table_options options = {.columns = columns}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table07.c b/test/unit/table/test_table07.c new file mode 100644 index 00000000..4c945723 --- /dev/null +++ b/test/unit/table/test_table07.c @@ -0,0 +1,56 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table07) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.total_row = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("C3:F14"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table08.c b/test/unit/table/test_table08.c new file mode 100644 index 00000000..6268d4a5 --- /dev/null +++ b/test/unit/table/test_table08.c @@ -0,0 +1,62 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table08) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {0}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + worksheet_add_table(worksheet, RANGE("C3:F14"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table09.c b/test/unit/table/test_table09.c new file mode 100644 index 00000000..1c9f31f3 --- /dev/null +++ b/test/unit/table/test_table09.c @@ -0,0 +1,75 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table09) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_column col1 = {.total_string = "Total"}; + lxw_table_column col2 = {0}; + lxw_table_column col3 = {.total_function = LXW_TABLE_FUNCTION_AVERAGE}; + lxw_table_column col4 = {.total_function = LXW_TABLE_FUNCTION_COUNT}; + lxw_table_column col5 = {.total_function = LXW_TABLE_FUNCTION_COUNT_NUMS}; + lxw_table_column col6 = {.total_function = LXW_TABLE_FUNCTION_MAX}; + lxw_table_column col7 = {.total_function = LXW_TABLE_FUNCTION_MIN}; + lxw_table_column col8 = {.total_function = LXW_TABLE_FUNCTION_SUM}; + lxw_table_column col9 = {.total_function = LXW_TABLE_FUNCTION_STD_DEV}; + lxw_table_column col10 = {.total_function = LXW_TABLE_FUNCTION_VAR}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8, &col9, &col10, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + worksheet_add_table(worksheet, RANGE("B2:K8"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table10.c b/test/unit/table/test_table10.c new file mode 100644 index 00000000..211c2ba9 --- /dev/null +++ b/test/unit/table/test_table10.c @@ -0,0 +1,56 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table10) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.name = "MyTable"}; + + worksheet_add_table(worksheet, RANGE("C2:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table11.c b/test/unit/table/test_table11.c new file mode 100644 index 00000000..4feb6b22 --- /dev/null +++ b/test/unit/table/test_table11.c @@ -0,0 +1,56 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table11) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_LIGHT, .style_type_number = 0}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table12.c b/test/unit/table/test_table12.c new file mode 100644 index 00000000..6f452319 --- /dev/null +++ b/test/unit/table/test_table12.c @@ -0,0 +1,56 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table12) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, .style_type_number = 1}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table13.c b/test/unit/table/test_table13.c new file mode 100644 index 00000000..4ebd559f --- /dev/null +++ b/test/unit/table/test_table13.c @@ -0,0 +1,56 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table13) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + lxw_table_options options = {.style_type = LXW_TABLE_STYLE_TYPE_DARK, .style_type_number = 11}; + + worksheet_add_table(worksheet, RANGE("C3:F13"), &options); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table14.c b/test/unit/table/test_table14.c new file mode 100644 index 00000000..41890cb1 --- /dev/null +++ b/test/unit/table/test_table14.c @@ -0,0 +1,72 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table14) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "SUM(Table1[[#This Row],[Quarter 1]:[Quarter 4]])" + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + /* Set the table options. */ + lxw_table_column col8_1 = {.header = "Product"}; + lxw_table_column col8_2 = {.header = "Quarter 1"}; + lxw_table_column col8_3 = {.header = "Quarter 2"}; + lxw_table_column col8_4 = {.header = "Quarter 3"}; + lxw_table_column col8_5 = {.header = "Quarter 4"}; + lxw_table_column col8_6 = {.header = "Year", + .formula = "=SUM(Table1[@[Quarter 1]:[Quarter 4]])"}; + + lxw_table_column *columns8[] = {&col8_1, &col8_2, &col8_3, &col8_4, &col8_5, &col8_6, NULL}; + + lxw_table_options options8 = {.columns = columns8}; + + /* Add a table to the worksheet. */ + worksheet_add_table(worksheet, RANGE("B3:G7"), &options8); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table15.c b/test/unit/table/test_table15.c new file mode 100644 index 00000000..3a3551bc --- /dev/null +++ b/test/unit/table/test_table15.c @@ -0,0 +1,72 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "../../../include/xlsxwriter/worksheet.h" +#include "../../../include/xlsxwriter/table.h" + +// Test assembling a complete Table file. +CTEST(worksheet, worksheet_table15) { + + char* got; + char exp[] = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "[#This Row],[#This Row], [#This Row], [#This Row],[#This Row]," + "" + "" + "" + "
"; + + FILE* testfile = lxw_tmpfile(NULL); + FILE* testfile2 = lxw_tmpfile(NULL); + + lxw_worksheet *worksheet = lxw_worksheet_new(NULL); + worksheet->file = testfile2; + worksheet->sst = lxw_sst_new(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + /* Set the table options. */ + lxw_table_column col8_1 = {.header = "Product"}; + lxw_table_column col8_2 = {.header = "Quarter 1"}; + lxw_table_column col8_3 = {.header = "Quarter 2"}; + lxw_table_column col8_4 = {.header = "Quarter 3"}; + lxw_table_column col8_5 = {.header = "Quarter 4"}; + lxw_table_column col8_6 = {.header = "Year", + .formula = "@@ @ @@"}; + + lxw_table_column *columns8[] = {&col8_1, &col8_2, &col8_3, &col8_4, &col8_5, &col8_6, NULL}; + + lxw_table_options options8 = {.columns = columns8}; + + /* Add a table to the worksheet. */ + worksheet_add_table(worksheet, RANGE("B3:G7"), &options8); + + table->table_obj = STAILQ_FIRST(worksheet->table_objs); + table->table_obj->id = 1; + + lxw_table_assemble_xml_file(table); + + RUN_XLSX_STREQ_SHORT(exp, got); + + lxw_worksheet_free(worksheet); + lxw_table_free(table); +} + + diff --git a/test/unit/table/test_table_xml_declaration.c b/test/unit/table/test_table_xml_declaration.c new file mode 100644 index 00000000..3e9e456a --- /dev/null +++ b/test/unit/table/test_table_xml_declaration.c @@ -0,0 +1,28 @@ +/* + * Tests for the libxlsxwriter library. + * + * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org + * + */ + +#include "../ctest.h" +#include "../helper.h" + +#include "xlsxwriter/table.h" + +// Test _xml_declaration(). +CTEST(table, xml_declaration) { + + char* got; + char exp[] = "\n"; + FILE* testfile = tmpfile(); + + lxw_table *table = lxw_table_new(); + table->file = testfile; + + _table_xml_declaration(table); + + RUN_XLSX_STREQ(exp, got); + + lxw_table_free(table); +}