diff --git a/dev/release/fix_example_docs.pl b/dev/release/fix_example_docs.pl index a3e4a811..f9bf0128 100644 --- a/dev/release/fix_example_docs.pl +++ b/dev/release/fix_example_docs.pl @@ -35,6 +35,7 @@ my @examples = ( [ 'data_validate.c', 'Examples of worksheet data validation and drop down lists' ], [ 'conditional_format1.c', 'A simple conditional formatting example' ], [ 'conditional_format2.c', 'An advanced conditional formatting example' ], + [ 'tables.c', 'Example of table to a worksheet.' ], [ 'images.c', 'Example of adding images to a worksheet.' ], [ 'image_buffer.c', 'Example of adding an image from a memory buffer.' ], [ 'headers_footers.c', 'Example of adding worksheet headers/footers' ], diff --git a/docs/Doxyfile b/docs/Doxyfile index 64606155..e9b833c7 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -856,6 +856,7 @@ INPUT = src/mainpage.dox \ src/working_with_autofilters.dox \ src/working_with_data_validation.dox \ src/working_with_conditional_formatting.dox \ + src/working_with_tables.dox \ src/working_with_comments.dox \ src/working_with_outlines.dox \ src/working_with_memory.dox \ diff --git a/docs/images/tables1.png b/docs/images/tables1.png new file mode 100644 index 00000000..d433001e Binary files /dev/null and b/docs/images/tables1.png differ diff --git a/docs/images/tables10.png b/docs/images/tables10.png new file mode 100644 index 00000000..352a6f1d Binary files /dev/null and b/docs/images/tables10.png differ diff --git a/docs/images/tables11.png b/docs/images/tables11.png new file mode 100644 index 00000000..31131b28 Binary files /dev/null and b/docs/images/tables11.png differ diff --git a/docs/images/tables12.png b/docs/images/tables12.png new file mode 100644 index 00000000..1686f3f7 Binary files /dev/null and b/docs/images/tables12.png differ diff --git a/docs/images/tables13.png b/docs/images/tables13.png new file mode 100644 index 00000000..477dae50 Binary files /dev/null and b/docs/images/tables13.png differ diff --git a/docs/images/tables14.png b/docs/images/tables14.png new file mode 100644 index 00000000..8cc20045 Binary files /dev/null and b/docs/images/tables14.png differ diff --git a/docs/images/tables2.png b/docs/images/tables2.png new file mode 100644 index 00000000..16b7d7f6 Binary files /dev/null and b/docs/images/tables2.png differ diff --git a/docs/images/tables3.png b/docs/images/tables3.png new file mode 100644 index 00000000..88fa1b03 Binary files /dev/null and b/docs/images/tables3.png differ diff --git a/docs/images/tables4.png b/docs/images/tables4.png new file mode 100644 index 00000000..657a3981 Binary files /dev/null and b/docs/images/tables4.png differ diff --git a/docs/images/tables5.png b/docs/images/tables5.png new file mode 100644 index 00000000..57435c7b Binary files /dev/null and b/docs/images/tables5.png differ diff --git a/docs/images/tables6.png b/docs/images/tables6.png new file mode 100644 index 00000000..79a7c56d Binary files /dev/null and b/docs/images/tables6.png differ diff --git a/docs/images/tables7.png b/docs/images/tables7.png new file mode 100644 index 00000000..ed5b3694 Binary files /dev/null and b/docs/images/tables7.png differ diff --git a/docs/images/tables8.png b/docs/images/tables8.png new file mode 100644 index 00000000..e0c14e44 Binary files /dev/null and b/docs/images/tables8.png differ diff --git a/docs/images/tables9.png b/docs/images/tables9.png new file mode 100644 index 00000000..6b83b36a Binary files /dev/null and b/docs/images/tables9.png differ diff --git a/docs/src/examples.dox b/docs/src/examples.dox index 7c90663c..128c8f84 100644 --- a/docs/src/examples.dox +++ b/docs/src/examples.dox @@ -388,7 +388,7 @@ a format to a cell or a range of cells based on certain criteria. - +
@ref conditional_format1.c "<< conditional_format1.c"@ref images.c "images.c >>"@ref tables.c "tables.c >>"
@@ -402,11 +402,30 @@ criteria. -@example images.c + +@example tables.c + + +
@ref conditional_format2.c "<< conditional_format2.c"@ref images.c "images.c >>"
+ +Example of how to add tables to a worksheet. Tables in Excel are used to group +rows and columns of data into a single structure that can be referenced in a +formula or formatted collectively. + +@image html tables12.png + + + + +@example images.c + + + +
@ref tables.c "<< tables.c" @ref image_buffer.c "image_buffer.c >>"
diff --git a/docs/src/examples.txt b/docs/src/examples.txt index 473ef68e..794ed995 100644 --- a/docs/src/examples.txt +++ b/docs/src/examples.txt @@ -183,6 +183,16 @@ criteria. @image html conditional_format1.png +############################################################## + +@example tables.c + +Example of how to add tables to a worksheet. Tables in Excel are used to group +rows and columns of data into a single structure that can be referenced in a +formula or formatted collectively. + +@image html tables12.png + ############################################################## @example images.c diff --git a/docs/src/mainpage.dox b/docs/src/mainpage.dox index 35f2babd..1e5e681f 100644 --- a/docs/src/mainpage.dox +++ b/docs/src/mainpage.dox @@ -61,6 +61,7 @@ following sections for more information: - @ref working_with_autofilters - @ref working_with_data_validation - @ref working_with_conditional_formatting +- @ref working_with_tables - @ref working_with_comments - @ref working_with_outlines - @ref working_with_memory diff --git a/docs/src/working_with_conditional_formatting.dox b/docs/src/working_with_conditional_formatting.dox index 696a672c..4fead951 100644 --- a/docs/src/working_with_conditional_formatting.dox +++ b/docs/src/working_with_conditional_formatting.dox @@ -1275,6 +1275,6 @@ For example to apply one conditional format to two ranges, `B3:K6` and @endcode -Next: @ref working_with_comments +Next: @ref working_with_tables */ diff --git a/docs/src/working_with_formulas.dox b/docs/src/working_with_formulas.dox index 6b14b388..c9f80100 100644 --- a/docs/src/working_with_formulas.dox +++ b/docs/src/working_with_formulas.dox @@ -17,6 +17,28 @@ However, there are a few potential issues and differences that the user should be aware of. These are explained in the following sections. +@section ww_formulas_results Formula Results + +Libxlsxwriter doesn't calculate the result of a formula and instead stores the +value 0 as the formula result. It then sets a global flag in the XLSX file to +say that all formulas and functions should be recalculated when the file is +opened. + +This is the method recommended in the Excel documentation and in general it +works fine with spreadsheet applications. However, applications that don't +have a facility to calculate formulas will only display the 0 +results. Examples of such applications are Excel Viewer, PDF Converters, and +some mobile device applications. + +If required, it is also possible to specify the calculated result of the +formula using the `result` parameter for +`worksheet_worksheet_write_formula_num()`: + + +@code + worksheet_write_formula_num(worksheet, 0, 0, "=2+2", NULL, 4); +@endcode + @section ww_formulas_non_us Non US Excel functions and syntax Excel stores formulas in the format of the US English version, regardless @@ -504,27 +526,6 @@ The following shows how to do that using Linux `unzip` and `libxml's SUM(1, 2, 3) -@section ww_formulas_results Formula Results - -Libxlsxwriter doesn't calculate the result of a formula and instead stores the -value 0 as the formula result. It then sets a global flag in the XLSX file to -say that all formulas and functions should be recalculated when the file is -opened. - -This is the method recommended in the Excel documentation and in general it -works fine with spreadsheet applications. However, applications that don't -have a facility to calculate formulas will only display the 0 -results. Examples of such applications are Excel Viewer, PDF Converters, and -some mobile device applications. - -If required, it is also possible to specify the calculated result of the -formula using the `result` parameter for -`worksheet_worksheet_write_formula_num()`: - -@code - worksheet_write_formula_num(worksheet, 0, 0, "=2+2", NULL, 4); -@endcode - Next: @ref working_with_dates */ diff --git a/docs/src/working_with_tables.dox b/docs/src/working_with_tables.dox new file mode 100644 index 00000000..92609109 --- /dev/null +++ b/docs/src/working_with_tables.dox @@ -0,0 +1,453 @@ +/** +@page working_with_tables Working with Worksheet Tables + +@tableofcontents + +Tables in Excel are a way of grouping a range of cells into a single entity +that has common formatting or that can be referenced from formulas. Tables can +have column headers, autofilters, total rows, column formulas and default +formatting. + +@image html tables12.png + +For a general introduction to this Excel feature see [An Overview of Excel +Tables](http://office.microsoft.com/en-us/excel-help/overview-of-excel-tables-HA010048546.aspx) +in the Microsoft Office documentation. + +@section ww_tables_add_table Adding a table to a worksheet + +Tables are added to a worksheet using the `worksheet_add_table()` function: + +@code + worksheet_add_table(worksheet, 2, 1, 6, 5, NULL); +@endcode + +Or more explicitly using the RANGE() macro: + +@code + worksheet_add_table(worksheet, RANGE("B3:F7"), NULL); //Same as above. +@endcode + +@image html tables1.png + +The `worksheet_add_table()` `options` parameter should be a pointer to a +lxw_table_options struct with the parameters that describe the table options: + +@code + worksheet_add_table(worksheet, 2, 1, 6, 5, &options); +@endcode + +These options are explained in the sections below. There are no required +parameters and the `options` parameter is itself optional, in which case you +can specify NULL and get the default table parameters. + +You should take care not to overlap worksheet tables as this is not allowed by +Excel and will cause an error when the file is loaded. + + +@note Tables aren't available in libxlsxwriter when using `constant_memory` +mode in `workbook_new_opt()`. + + +@subsection ww_tables_header_row Parameter: no_header_row + +The `no_header_row` parameter can be used to turn off the header row in the +table. It is on by default: + +@code + lxw_table_options options = {.no_header_row = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B4:F7"), &options); +@endcode + +@image html tables4.png + +Without this option the header row will contain default captions such as +`Column 1`, ``Column 2``, etc. These captions can be overridden using the +`columns` parameter shown below. + + +@subsection ww_tables_autofilter Parameter: no_autofilter + +The `no_autofilter` parameter can be used to turn off the autofilter in the +header row. It is on by default: + +@code + lxw_table_options options = {.no_autofilter = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +@image html tables3.png + +The autofilter is only shown if the `no_header_row` parameter is off (the +default). Filter conditions within the table are not supported. + + +@subsection ww_tables_banded_rows Parameter: no_banded_rows + +The `no_banded_rows` parameter can be used to turn off the rows of alternating +color in the table. It is on by default: + +@code + lxw_table_options options = {.no_banded_rows = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +@image html tables6.png + +@subsection ww_tables_banded_columns Parameter: banded_columns + +The `banded_columns` parameter can be used to used to create columns of +alternating color in the table. It is off by default: + +@code + lxw_table_options options = {.banded_columns = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +The banded columns formatting is shown in the image in the previous section +above. + +@subsection ww_tables_first_column Parameter: first_column + +The `first_column` parameter can be used to highlight the first column of the +table. The type of highlighting will depend on the `style_type` of the table. +It may be bold text or a different color. It is off by default: + +@code + lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +@image html tables5.png + +@subsection ww_tables_last_column Parameter: last_column + +The `last_column` parameter can be used to highlight the last column of the +table. The type of highlighting will depend on the `style` of the table. It +may be bold text or a different color. It is off by default: + +@code + lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +The `last_column` formatting is shown in the image in the previous section +above. + + +@subsection ww_tables_style Parameter: style_type and style_type_number + +The `style_type` parameter can be used to set the style of the table, in +conjunction with the `style_type_number` parameter: + +@code + lxw_table_options options = { + .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + .style_type_number = 11, + }; + + worksheet_add_table(worksheet, RANGE("B3:G8"), &options); +@endcode + + +@image html tables11.png + +There are three types of table style in Excel: Light, Medium and Dark which +are represented using the #lxw_table_style_type enum values: + +- #LXW_TABLE_STYLE_TYPE_LIGHT + +- #LXW_TABLE_STYLE_TYPE_MEDIUM + +- #LXW_TABLE_STYLE_TYPE_DARK + +Within those ranges there are between 11 and 28 other style types which can be +set with `style_type_number` (depending on the style type). Check Excel to +find the style that you want. The dialog with the options laid out in numeric +order are shown below: + +@image html tables14.png + +The default table style in Excel is 'Table Style Medium 9' (highlighted with +a green border in the image above), which is set by default in libxlsxwriter +as: + +@code + lxw_table_options options = { + .style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, + .style_type_number = 9, + }; +@endcode + +You can also turn the table style off by setting it to Light 0: + +@code + lxw_table_options options = { + .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + .style_type_number = 0, + }; +@endcode + +@image html tables13.png + + +@subsection ww_tables_name Parameter: name + +The `name` parameter is used to set the name of the table. This parameter is +optional and by default tables are named `Table1`, `Table2`, etc. in the +worksheet order that they are added. + +@code + lxw_table_options options = {.name = "Sales"}; + + worksheet_add_table(worksheet, RANGE("B3:G8"), &options); +@endcode + +If you override the table name you must ensure that it doesn't clash with an +existing table name and that it follows Excel's requirements for table names, +see the Microsoft Office documentation on [Naming an Excel +Table](https://support.microsoft.com/en-us/office/rename-an-excel-table-fbf49a4f-82a3-43eb-8ba2-44d21233b114). + + + +@subsection ww_tables_total_row Parameter: total_row + +The `total_row` parameter can be used to turn on the total row in the last +row of a table. It is distinguished from the other rows by a different +formatting and also with dropdown `SUBTOTAL` functions: + +@code + lxw_table_options options = {.total_row = LXW_TRUE}; + + worksheet_add_table(worksheet, RANGE("B3:G8"), &options); +@endcode + +@image html tables9.png + +The default total row doesn't have any captions or functions. These must by +specified via the `columns` parameter below. + + +@subsection ww_tables_columns Parameter: columns + +The `columns` parameter can be used to set properties for columns within the +table. + +@image html tables7.png + +The sub-properties of `lxw_table_column` that can be set are: + +- `lxw_table_column.header` +- `lxw_table_column.header_format` +- `lxw_table_column.formula` +- `lxw_table_column.total_string` +- `lxw_table_column.total_function` +- `lxw_table_column.total_value` +- `lxw_table_column.format` + +The `columns` parameter should be a NULL terminated array of +`lxw_table_column` pointers. For example to override the default 'Column n' +style table headers: + +@code + lxw_table_column col1 = {.header = "Product"}; + lxw_table_column col2 = {.header = "Quarter 1"}; + lxw_table_column col3 = {.header = "Quarter 2"}; + lxw_table_column col4 = {.header = "Quarter 3"}; + lxw_table_column col5 = {.header = "Quarter 4"}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, NULL}; + + lxw_table_options options = {.columns = columns}; + + worksheet_add_table(worksheet, RANGE("B3:F7"), &options); +@endcode + +If you don't wish to specify properties for a specific column you can pass an +empty (but not NULL) struct and the defaults will be applied: + +@code + lxw_table_column col1 = {.header = "Product"}; + lxw_table_column col2 = {.header = "Quarter 1"}; + lxw_table_column col3 = {0}; // Defaults to Column3. + lxw_table_column col4 = {.header = "Quarter 3"}; + lxw_table_column col5 = {.header = "Quarter 4"}; +@endcode + +Column formulas can by applied using the column `formula` property: + +@code + lxw_table_column col1 = {.header = "Product"}; + lxw_table_column col2 = {.header = "Quarter 1"}; + lxw_table_column col3 = {.header = "Quarter 2"}; + lxw_table_column col4 = {.header = "Quarter 3"}; + lxw_table_column col5 = {.header = "Quarter 4"}; + lxw_table_column col6 = {.header = "Year", + .formula = "=SUM(Table8[@[Quarter 1]:[Quarter 4]])"}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, NULL}; + + lxw_table_options options = {.columns = columns}; + + worksheet_add_table(worksheet, RANGE("B3:G7"), &options); +@endcode + +@image html tables8.png + +The Excel 2007 style `"#This Row"` and Excel 2010 style `"@"` structural +references are supported within the formula. However, other Excel 2010 +additions to structural references aren't supported and formulas should +conform to Excel 2007 style formulas. See the Microsoft documentation on +[Using structured references with Excel tables] +(http://office.microsoft.com/en-us/excel-help/using-structured-references-with-excel-tables-HA010155686.aspx) +for details. + +As stated above the `total_row` table parameter turns on the "Total" row in +the table but it doesn't populate it with any defaults. Total captions and +functions must be specified via the `columns` property and the `total_string` +and `total_function` sub properties: + +@code + lxw_table_column col1 = {.header = "Product", + .total_string = "Totals"}; + + lxw_table_column col2 = {.header = "Quarter 1", + .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column col3 = {.header = "Quarter 2", + .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column col4 = {.header = "Quarter 3", + .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column col5 = {.header = "Quarter 4", + .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column col6 = {.header = "Year", + .formula = "=SUM(Table10[@[Quarter 1]:[Quarter 4]])", + .total_function = LXW_TABLE_FUNCTION_SUM}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + + worksheet_add_table(worksheet, RANGE("B3:G8"), &options); +@endcode + +The supported totals row `SUBTOTAL` functions are defined in +#lxw_table_total_functions: + +- #LXW_TABLE_FUNCTION_AVERAGE +- #LXW_TABLE_FUNCTION_COUNT_NUMS +- #LXW_TABLE_FUNCTION_COUNT +- #LXW_TABLE_FUNCTION_MAX +- #LXW_TABLE_FUNCTION_MIN +- #LXW_TABLE_FUNCTION_STD_DEV +- #LXW_TABLE_FUNCTION_SUM +- #LXW_TABLE_FUNCTION_VAR + +User defined functions or formulas aren't supported. + +It is also possible to set a calculated value for the `total_function` using +the `total_value` sub property. This is only necessary when creating workbooks +for applications that cannot calculate the value of formulas automatically. +This is similar to setting the `result` property in +`worksheet_write_formula_num()`. See also @ref ww_formulas_results. + +Formatting can also be applied to columns using the `format` and to the header +using `header_format` (although you will also need to add it to the data in +the column, see the next section): + +@code + lxw_table_column col1 = {.header = "Product", + .total_string = "Totals"}; + + lxw_table_column col2 = {.header = "Quarter 1", + .total_function = LXW_TABLE_FUNCTION_SUM, + .format = currency_format}; + + lxw_table_column col3 = {.header = "Quarter 2", + .total_function = LXW_TABLE_FUNCTION_SUM, + .format = currency_format}; + + lxw_table_column col4 = {.header = "Quarter 3", + .total_function = LXW_TABLE_FUNCTION_SUM, + .format = currency_format}; + + lxw_table_column col5 = {.header = "Quarter 4", + .total_function = LXW_TABLE_FUNCTION_SUM, + .format = currency_format}; + + lxw_table_column col6 = {.header = "Year", + .formula = "=SUM(Table13[@[Quarter 1]:[Quarter 4]])", + .total_function = LXW_TABLE_FUNCTION_SUM, + .format = currency_format}; + + lxw_table_column *columns[] = {&col1, &col2, &col3, &col4, &col5, &col6, NULL}; + + lxw_table_options options = {.total_row = LXW_TRUE, .columns = columns}; + + worksheet_add_table(worksheet, RANGE("B3:G8"), &options); +@endcode + +@image html tables12.png + +Standard libxlsxwriter lxw_format objects are used for this formatting. +However, they should be limited to numerical formats for the columns and +simple formatting like text wrap for the headers. Overriding other table +formatting may produce inconsistent results. You will also need to apply the +same format to any data you write to the column in the table, see the next +section. + + +@section ww_tables_data Adding data to the table + +Once you create a worksheet table you will also need to fill in the data in +the rows and columns. This is done with the standard worksheet write() +functions. For example the data in the examples above was written as follows: + +@code + worksheet_write_string(worksheet, 3, 1, "Apples", NULL); + worksheet_write_string(worksheet, 4, 1, "Pears", NULL); + worksheet_write_string(worksheet, 5, 1, "Bananas", NULL); + worksheet_write_string(worksheet, 6, 1, "Oranges", NULL); + + worksheet_write_number(worksheet, 3, 2, 10000, format); + worksheet_write_number(worksheet, 4, 2, 2000, format); + worksheet_write_number(worksheet, 5, 2, 6000, format); + worksheet_write_number(worksheet, 6, 2, 500, format); + + worksheet_write_number(worksheet, 3, 3, 5000, format); + worksheet_write_number(worksheet, 4, 3, 3000, format); + worksheet_write_number(worksheet, 5, 3, 6000, format); + worksheet_write_number(worksheet, 6, 3, 300, format); + + worksheet_write_number(worksheet, 3, 4, 8000, format); + worksheet_write_number(worksheet, 4, 4, 4000, format); + worksheet_write_number(worksheet, 5, 4, 6500, format); + worksheet_write_number(worksheet, 6, 4, 200, format); + + worksheet_write_number(worksheet, 3, 5, 6000, format); + worksheet_write_number(worksheet, 4, 5, 5000, format); + worksheet_write_number(worksheet, 5, 5, 6000, format); + worksheet_write_number(worksheet, 6, 5, 700, format); +@endcode + +@image html tables2.png + + +@section ww_tables_Example Example + +All of the images shown above are taken from @ref tables.c. + + +Next: @ref working_with_comments + +*/ diff --git a/examples/tables.c b/examples/tables.c index a4edd408..ee910d35 100644 --- a/examples/tables.c +++ b/examples/tables.c @@ -450,4 +450,5 @@ void write_worksheet_data(lxw_worksheet *worksheet, lxw_format *format) { worksheet_write_number(worksheet, CELL("F5"), 5000, format); worksheet_write_number(worksheet, CELL("F6"), 6000, format); worksheet_write_number(worksheet, CELL("F7"), 700, format); + } diff --git a/include/xlsxwriter/worksheet.h b/include/xlsxwriter/worksheet.h index 603a1396..3f40081c 100644 --- a/include/xlsxwriter/worksheet.h +++ b/include/xlsxwriter/worksheet.h @@ -554,16 +554,39 @@ enum lxw_table_style_type { LXW_TABLE_STYLE_TYPE_DARK }; +/** + * @brief Standard Excel functions for totals in tables. + * + * Definitions for the standard Excel functions that are available via the + * dropdown in the total row of an Excel table. + * + */ enum lxw_table_total_functions { LXW_TABLE_FUNCTION_NONE = 0, + + /** Use the average function as the table total. */ LXW_TABLE_FUNCTION_AVERAGE = 101, + + /** Use the count numbers function as the table total. */ LXW_TABLE_FUNCTION_COUNT_NUMS = 102, + + /** Use the count function as the table total. */ LXW_TABLE_FUNCTION_COUNT = 103, + + /** Use the max function as the table total. */ LXW_TABLE_FUNCTION_MAX = 104, + + /** Use the min function as the table total. */ LXW_TABLE_FUNCTION_MIN = 105, + + /** Use the standard deviation function as the table total. */ LXW_TABLE_FUNCTION_STD_DEV = 107, + + /** Use the sum function as the table total. */ LXW_TABLE_FUNCTION_SUM = 109, + + /** Use the var function as the table total. */ LXW_TABLE_FUNCTION_VAR = 110 }; @@ -1356,30 +1379,253 @@ typedef struct lxw_cond_format_hash_element { RB_ENTRY (lxw_cond_format_hash_element) tree_pointers; } lxw_cond_format_hash_element; +/** + * @brief Table columns options. + * + * Structure to set the options of a table column added with + * worksheet_add_table(). See @ref ww_tables_columns. + */ typedef struct lxw_table_column { + + /** Set the header name/caption for the column. If NULL the header defaults + * to Column 1, Column 2, etc. */ char *header; + + /** Set the formula for the column. */ char *formula; + + /** Set the string description for the column total. */ char *total_string; + + /** Set the function for the column total. */ uint8_t total_function; - double total_value; + + /** Set the format for the column header. */ lxw_format *header_format; + + /** Set the format for the data rows in the column. */ lxw_format *format; + + /** Set the formula value for the column total (not generally required). */ + double total_value; + } lxw_table_column; +/** + * @brief Worksheet table options. + * + * Options used to define worksheet tables. See @ref working_with_tables for + * more information. + * + */ typedef struct lxw_table_options { + + /** + * The `name` parameter is used to set the name of the table. This + * parameter is optional and by default tables are named `Table1`, + * `Table2`, etc. in the worksheet order that they are added. + * + * @code + * lxw_table_options options = {.name = "Sales"}; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * If you override the table name you must ensure that it doesn't clash + * with an existing table name and that it follows Excel's requirements + * for table names, see the Microsoft Office documentation on + * [Naming an Excel Table] + * (https://support.microsoft.com/en-us/office/rename-an-excel-table-fbf49a4f-82a3-43eb-8ba2-44d21233b114). + */ 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; + + /** + * The `no_header_row` parameter can be used to turn off the header row in + * the table. It is on by default: + * + * @code + * lxw_table_options options = {.no_header_row = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B4:F7"), &options); + * @endcode + * + * @image html tables4.png + * + * Without this option the header row will contain default captions such + * as `Column 1`, ``Column 2``, etc. These captions can be overridden + * using the `columns` parameter shown below. + * + */ uint8_t no_header_row; + + /** + * The `no_autofilter` parameter can be used to turn off the autofilter in + * the header row. It is on by default: + * + * @code + * lxw_table_options options = {.no_autofilter = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables3.png + * + * The autofilter is only shown if the `no_header_row` parameter is off + * (the default). Filter conditions within the table are not supported. + * + */ + uint8_t no_autofilter; + + /** + * The `no_banded_rows` parameter can be used to turn off the rows of alternating + * color in the table. It is on by default: + * + * @code + * lxw_table_options options = {.no_banded_rows = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables6.png + * + */ + uint8_t no_banded_rows; + + /** + * The `banded_columns` parameter can be used to used to create columns of + * alternating color in the table. It is off by default: + * + * @code + * lxw_table_options options = {.banded_columns = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * The banded columns formatting is shown in the image in the previous + * section above. + */ + uint8_t banded_columns; + + /** + * The `first_column` parameter can be used to highlight the first column + * of the table. The type of highlighting will depend on the `style_type` + * of the table. It may be bold text or a different color. It is off by + * default: + * + * @code + * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * @image html tables5.png + */ + uint8_t first_column; + + /** + * The `last_column` parameter can be used to highlight the last column of + * the table. The type of highlighting will depend on the `style` of the + * table. It may be bold text or a different color. It is off by default: + * + * @code + * lxw_table_options options = {.first_column = LXW_TRUE, .last_column = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:F7"), &options); + * @endcode + * + * The `last_column` formatting is shown in the image in the previous + * section above. + */ + uint8_t last_column; + + + /** + * The `style_type` parameter can be used to set the style of the table, + * in conjunction with the `style_type_number` parameter: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + * .style_type_number = 11, + * }; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * + * @image html tables11.png + * + * There are three types of table style in Excel: Light, Medium and Dark + * which are represented using the #lxw_table_style_type enum values: + * + * - #LXW_TABLE_STYLE_TYPE_LIGHT + * + * - #LXW_TABLE_STYLE_TYPE_MEDIUM + * + * - #LXW_TABLE_STYLE_TYPE_DARK + * + * Within those ranges there are between 11 and 28 other style types which + * can be set with `style_type_number` (depending on the style type). + * Check Excel to find the style that you want. The dialog with the + * options laid out in numeric order are shown below: + * + * @image html tables14.png + * + * The default table style in Excel is 'Table Style Medium 9' (highlighted + * with a green border in the image above), which is set by default in + * libxlsxwriter as: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_MEDIUM, + * .style_type_number = 9, + * }; + * @endcode + * + * You can also turn the table style off by setting it to Light 0: + * + * @code + * lxw_table_options options = { + * .style_type = LXW_TABLE_STYLE_TYPE_LIGHT, + * .style_type_number = 0, + * }; + * @endcode + * + * @image html tables13.png + * + */ uint8_t style_type; + + /** + * The `style_type_number` parameter is used with `style_type` to set the + * style of a worksheet table. */ uint8_t style_type_number; + + /** + * The `total_row` parameter can be used to turn on the total row in the + * last row of a table. It is distinguished from the other rows by a + * different formatting and also with dropdown `SUBTOTAL` functions: + * + * @code + * lxw_table_options options = {.total_row = LXW_TRUE}; + * + * worksheet_add_table(worksheet, RANGE("B3:G8"), &options); + * @endcode + * + * @image html tables9.png + * + * The default total row doesn't have any captions or functions. These + * must by specified via the `columns` parameter below. + */ uint8_t total_row; + /** + * The `columns` parameter can be used to set properties for columns + * within the table. See @ref ww_tables_columns for a detailed + * explanation. + */ + lxw_table_column **columns; + } lxw_table_options; typedef struct lxw_table_obj { @@ -2337,7 +2583,7 @@ lxw_error worksheet_write_dynamic_array_formula(lxw_worksheet *worksheet, * range. This is a syntactic shortcut since the array range isn't generally * known for a dynamic range and specifying the initial cell is sufficient for * Excel, as shown in the example below: - * + * * @code * worksheet_write_dynamic_formula(worksheet, 7, 1, * "=_xlfn._xlws.SORT(_xlfn.UNIQUE(B2:B17))", @@ -4013,7 +4259,36 @@ lxw_error worksheet_conditional_format_range(lxw_worksheet *worksheet, lxw_col_t last_col, lxw_conditional_format *conditional_format); - +/** + * @brief Add an Excel table to a worksheet. + * + * @param worksheet Pointer to a lxw_worksheet instance to be updated. + * @param first_row The first row of the range. (All zero indexed.) + * @param first_col The first column of the range. + * @param last_row The last row of the range. + * @param last_col The last col of the range. + * @param options A #lxw_table_options struct to define the table options. + * + * @return A #lxw_error code. + * + * The `%worksheet_add_table()` function is used to add a table to a + * worksheet. Tables in Excel are a way of grouping a range of cells into a + * single entity that has common formatting or that can be referenced from + * formulas. Tables can have column headers, autofilters, total rows, column + * formulas and default formatting. + * + * @code + * worksheet_add_table(worksheet, 2, 1, 6, 5, NULL); + * @endcode + * + * Output: + * + * @image html tables1.png + * + * See @ref working_with_tables for more detailed usage information and also + * @ref tables.c. + * + */ 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); diff --git a/src/worksheet.c b/src/worksheet.c index 2e95398d..ae5c8920 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -9128,6 +9128,13 @@ worksheet_add_table(lxw_worksheet *self, lxw_row_t first_row, lxw_table_obj *table_obj; lxw_table_column **columns; + if (self->optimize) { + LXW_WARN_FORMAT("worksheet_add_table(): " + "worksheet tables aren't supported in " + "'constant_memory' mode"); + return LXW_ERROR_FEATURE_NOT_SUPPORTED; + } + /* Swap last row/col with first row/col as necessary */ if (first_row > last_row) { tmp_row = last_row;