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 00000000..43a326b3 Binary files /dev/null and b/test/functional/xlsx_files/table01.xlsx differ diff --git a/test/functional/xlsx_files/table02.xlsx b/test/functional/xlsx_files/table02.xlsx new file mode 100644 index 00000000..1687c1bf Binary files /dev/null and b/test/functional/xlsx_files/table02.xlsx differ diff --git a/test/functional/xlsx_files/table03.xlsx b/test/functional/xlsx_files/table03.xlsx new file mode 100644 index 00000000..c4379153 Binary files /dev/null and b/test/functional/xlsx_files/table03.xlsx differ diff --git a/test/functional/xlsx_files/table04.xlsx b/test/functional/xlsx_files/table04.xlsx new file mode 100644 index 00000000..b93fa225 Binary files /dev/null and b/test/functional/xlsx_files/table04.xlsx differ diff --git a/test/functional/xlsx_files/table05.xlsx b/test/functional/xlsx_files/table05.xlsx new file mode 100644 index 00000000..e9f7b8a1 Binary files /dev/null and b/test/functional/xlsx_files/table05.xlsx differ diff --git a/test/functional/xlsx_files/table06.xlsx b/test/functional/xlsx_files/table06.xlsx new file mode 100644 index 00000000..a655781e Binary files /dev/null and b/test/functional/xlsx_files/table06.xlsx differ diff --git a/test/functional/xlsx_files/table07.xlsx b/test/functional/xlsx_files/table07.xlsx new file mode 100644 index 00000000..ffa7d711 Binary files /dev/null and b/test/functional/xlsx_files/table07.xlsx differ diff --git a/test/functional/xlsx_files/table08.xlsx b/test/functional/xlsx_files/table08.xlsx new file mode 100644 index 00000000..c20181b6 Binary files /dev/null and b/test/functional/xlsx_files/table08.xlsx differ diff --git a/test/functional/xlsx_files/table09.xlsx b/test/functional/xlsx_files/table09.xlsx new file mode 100644 index 00000000..0b501da3 Binary files /dev/null and b/test/functional/xlsx_files/table09.xlsx differ diff --git a/test/functional/xlsx_files/table10.xlsx b/test/functional/xlsx_files/table10.xlsx new file mode 100644 index 00000000..9491fcde Binary files /dev/null and b/test/functional/xlsx_files/table10.xlsx differ diff --git a/test/functional/xlsx_files/table11.xlsx b/test/functional/xlsx_files/table11.xlsx new file mode 100644 index 00000000..f0582cfa Binary files /dev/null and b/test/functional/xlsx_files/table11.xlsx differ diff --git a/test/functional/xlsx_files/table12.xlsx b/test/functional/xlsx_files/table12.xlsx new file mode 100644 index 00000000..373763b5 Binary files /dev/null and b/test/functional/xlsx_files/table12.xlsx differ diff --git a/test/functional/xlsx_files/table14.xlsx b/test/functional/xlsx_files/table14.xlsx new file mode 100644 index 00000000..9f526ef4 Binary files /dev/null and b/test/functional/xlsx_files/table14.xlsx differ diff --git a/test/functional/xlsx_files/table15.xlsx b/test/functional/xlsx_files/table15.xlsx new file mode 100644 index 00000000..038489b6 Binary files /dev/null and b/test/functional/xlsx_files/table15.xlsx differ diff --git a/test/functional/xlsx_files/table17.xlsx b/test/functional/xlsx_files/table17.xlsx new file mode 100644 index 00000000..a481d97b Binary files /dev/null and b/test/functional/xlsx_files/table17.xlsx differ diff --git a/test/functional/xlsx_files/table18.xlsx b/test/functional/xlsx_files/table18.xlsx new file mode 100644 index 00000000..16cdbe18 Binary files /dev/null and b/test/functional/xlsx_files/table18.xlsx differ diff --git a/test/functional/xlsx_files/table19.xlsx b/test/functional/xlsx_files/table19.xlsx new file mode 100644 index 00000000..118803e6 Binary files /dev/null and b/test/functional/xlsx_files/table19.xlsx differ diff --git a/test/functional/xlsx_files/table21.xlsx b/test/functional/xlsx_files/table21.xlsx new file mode 100644 index 00000000..b83e61b1 Binary files /dev/null and b/test/functional/xlsx_files/table21.xlsx differ diff --git a/test/functional/xlsx_files/table22.xlsx b/test/functional/xlsx_files/table22.xlsx new file mode 100644 index 00000000..37f3db5e Binary files /dev/null and b/test/functional/xlsx_files/table22.xlsx differ diff --git a/test/functional/xlsx_files/table23.xlsx b/test/functional/xlsx_files/table23.xlsx new file mode 100644 index 00000000..546d7130 Binary files /dev/null and b/test/functional/xlsx_files/table23.xlsx differ diff --git a/test/functional/xlsx_files/table24.xlsx b/test/functional/xlsx_files/table24.xlsx new file mode 100644 index 00000000..bbaf91c6 Binary files /dev/null and b/test/functional/xlsx_files/table24.xlsx differ diff --git a/test/functional/xlsx_files/table25.xlsx b/test/functional/xlsx_files/table25.xlsx new file mode 100644 index 00000000..b60aa66f Binary files /dev/null and b/test/functional/xlsx_files/table25.xlsx differ diff --git a/test/functional/xlsx_files/table26.xlsx b/test/functional/xlsx_files/table26.xlsx new file mode 100644 index 00000000..e5af2013 Binary files /dev/null and b/test/functional/xlsx_files/table26.xlsx differ diff --git a/test/unit/Makefile b/test/unit/Makefile index 8819c7d3..c57d105c 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -41,6 +41,7 @@ SRCS += $(wildcard chartsheet/test*.c) SRCS += $(wildcard vml/test*.c) SRCS += $(wildcard comment/test*.c) SRCS += $(wildcard metadata/test*.c) +SRCS += $(wildcard table/test*.c) # End of SRCS OBJS = $(patsubst %.c,%.o,$(SRCS)) @@ -77,6 +78,7 @@ all : $(Q)$(MAKE) -C vml $(Q)$(MAKE) -C comment $(Q)$(MAKE) -C metadata + $(Q)$(MAKE) -C table # END make all clean : @@ -98,6 +100,7 @@ clean : $(Q)$(MAKE) clean -C vml $(Q)$(MAKE) clean -C comment $(Q)$(MAKE) clean -C metadata + $(Q)$(MAKE) clean -C table # END make clean diff --git a/test/unit/table/Makefile b/test/unit/table/Makefile new file mode 100644 index 00000000..2c05b8e2 --- /dev/null +++ b/test/unit/table/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for libxlsxwriter library. +# +# Copyright 2014-2015, John McNamara, jmcnamara@cpan.org +# + +include ../Makefile.unit diff --git a/test/unit/table/main.c b/test/unit/table/main.c new file mode 100644 index 00000000..586c44ef --- /dev/null +++ b/test/unit/table/main.c @@ -0,0 +1,15 @@ +/* + * Test runner for xmlwriter using ctest. + * + * Copyright 2014-2021 John McNamara, jmcnamara@cpan.org + * + */ +#define CTEST_MAIN + +#include "../ctest.h" + +int main(int argc, const char *argv[]) +{ + return ctest_main(argc, argv); +} + diff --git a/test/unit/table/test_table01.c b/test/unit/table/test_table01.c new file mode 100644 index 00000000..0b67782e --- /dev/null +++ b/test/unit/table/test_table01.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_table01) { + + 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; + + 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); +}