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;