1 /*****************************************************************************
2  * table - A library for creating Excel XLSX table files.
3  *
4  * Used in conjunction with the libxlsxwriter library.
5  *
6  * Copyright 2014-2021, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7  *
8  */
9 
10 #include "xlsxwriter/xmlwriter.h"
11 #include "xlsxwriter/worksheet.h"
12 #include "xlsxwriter/table.h"
13 #include "xlsxwriter/utility.h"
14 
15 /*
16  * Forward declarations.
17  */
18 
19 /*****************************************************************************
20  *
21  * Private functions.
22  *
23  ****************************************************************************/
24 
25 /*
26  * Create a new table object.
27  */
28 lxw_table *
lxw_table_new(void)29 lxw_table_new(void)
30 {
31     lxw_table *table = calloc(1, sizeof(lxw_table));
32     GOTO_LABEL_ON_MEM_ERROR(table, mem_error);
33 
34     return table;
35 
36 mem_error:
37     lxw_table_free(table);
38     return NULL;
39 }
40 
41 /*
42  * Free a table object.
43  */
44 void
lxw_table_free(lxw_table * table)45 lxw_table_free(lxw_table *table)
46 {
47     if (!table)
48         return;
49 
50     free(table);
51 }
52 
53 /*****************************************************************************
54  *
55  * XML functions.
56  *
57  ****************************************************************************/
58 
59 /*
60  * Write the XML declaration.
61  */
62 STATIC void
_table_xml_declaration(lxw_table * self)63 _table_xml_declaration(lxw_table *self)
64 {
65     lxw_xml_declaration(self->file);
66 }
67 
68 /*
69  * Write the <table> element.
70  */
71 STATIC void
_table_write_table(lxw_table * self)72 _table_write_table(lxw_table *self)
73 {
74     struct xml_attribute_list attributes;
75     struct xml_attribute *attribute;
76     char xmlns[] =
77         "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
78     lxw_table_obj *table_obj = self->table_obj;
79 
80     LXW_INIT_ATTRIBUTES();
81 
82     LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns);
83     LXW_PUSH_ATTRIBUTES_INT("id", table_obj->id);
84 
85     if (table_obj->name)
86         LXW_PUSH_ATTRIBUTES_STR("name", table_obj->name);
87     else
88         LXW_PUSH_ATTRIBUTES_STR("name", "Table1");
89 
90     if (table_obj->name)
91         LXW_PUSH_ATTRIBUTES_STR("displayName", table_obj->name);
92     else
93         LXW_PUSH_ATTRIBUTES_STR("displayName", "Table1");
94 
95     LXW_PUSH_ATTRIBUTES_STR("ref", table_obj->sqref);
96 
97     if (table_obj->no_header_row)
98         LXW_PUSH_ATTRIBUTES_STR("headerRowCount", "0");
99 
100     if (table_obj->total_row)
101         LXW_PUSH_ATTRIBUTES_STR("totalsRowCount", "1");
102     else
103         LXW_PUSH_ATTRIBUTES_STR("totalsRowShown", "0");
104 
105     lxw_xml_start_tag(self->file, "table", &attributes);
106 
107     LXW_FREE_ATTRIBUTES();
108 }
109 
110 /*
111  * Write the <autoFilter> element.
112  */
113 STATIC void
_table_write_auto_filter(lxw_table * self)114 _table_write_auto_filter(lxw_table *self)
115 {
116     struct xml_attribute_list attributes;
117     struct xml_attribute *attribute;
118 
119     if (self->table_obj->no_autofilter)
120         return;
121 
122     LXW_INIT_ATTRIBUTES();
123     LXW_PUSH_ATTRIBUTES_STR("ref", self->table_obj->filter_sqref);
124 
125     lxw_xml_empty_tag(self->file, "autoFilter", &attributes);
126 
127     LXW_FREE_ATTRIBUTES();
128 }
129 
130 /*
131  * Write the <tableColumn> element.
132  */
133 STATIC void
_table_write_table_column(lxw_table * self,uint16_t id,lxw_table_column * column)134 _table_write_table_column(lxw_table *self, uint16_t id,
135                           lxw_table_column *column)
136 {
137     struct xml_attribute_list attributes;
138     struct xml_attribute *attribute;
139     int32_t dfx_id;
140 
141     LXW_INIT_ATTRIBUTES();
142     LXW_PUSH_ATTRIBUTES_INT("id", id);
143 
144     LXW_PUSH_ATTRIBUTES_STR("name", column->header);
145 
146     if (column->total_string) {
147         LXW_PUSH_ATTRIBUTES_STR("totalsRowLabel", column->total_string);
148     }
149     else if (column->total_function) {
150         if (column->total_function == LXW_TABLE_FUNCTION_AVERAGE)
151             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "average");
152         if (column->total_function == LXW_TABLE_FUNCTION_COUNT_NUMS)
153             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "countNums");
154         if (column->total_function == LXW_TABLE_FUNCTION_COUNT)
155             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "count");
156         if (column->total_function == LXW_TABLE_FUNCTION_MAX)
157             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "max");
158         if (column->total_function == LXW_TABLE_FUNCTION_MIN)
159             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "min");
160         if (column->total_function == LXW_TABLE_FUNCTION_STD_DEV)
161             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "stdDev");
162         if (column->total_function == LXW_TABLE_FUNCTION_SUM)
163             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "sum");
164         if (column->total_function == LXW_TABLE_FUNCTION_VAR)
165             LXW_PUSH_ATTRIBUTES_STR("totalsRowFunction", "var");
166     }
167 
168     if (column->format) {
169         dfx_id = lxw_format_get_dxf_index(column->format);
170         LXW_PUSH_ATTRIBUTES_INT("dataDxfId", dfx_id);
171     }
172 
173     if (column->formula) {
174         lxw_xml_start_tag(self->file, "tableColumn", &attributes);
175         lxw_xml_data_element(self->file, "calculatedColumnFormula",
176                              column->formula, NULL);
177         lxw_xml_end_tag(self->file, "tableColumn");
178     }
179     else {
180         lxw_xml_empty_tag(self->file, "tableColumn", &attributes);
181     }
182 
183     LXW_FREE_ATTRIBUTES();
184 }
185 
186 /*
187  * Write the <tableColumns> element.
188  */
189 STATIC void
_table_write_table_columns(lxw_table * self)190 _table_write_table_columns(lxw_table *self)
191 {
192     struct xml_attribute_list attributes;
193     struct xml_attribute *attribute;
194     uint16_t i;
195     uint16_t num_cols = self->table_obj->num_cols;
196     lxw_table_column **columns = self->table_obj->columns;
197 
198     LXW_INIT_ATTRIBUTES();
199     LXW_PUSH_ATTRIBUTES_INT("count", num_cols);
200 
201     lxw_xml_start_tag(self->file, "tableColumns", &attributes);
202 
203     for (i = 0; i < num_cols; i++)
204         _table_write_table_column(self, i + 1, columns[i]);
205 
206     lxw_xml_end_tag(self->file, "tableColumns");
207 
208     LXW_FREE_ATTRIBUTES();
209 }
210 
211 /*
212  * Write the <tableStyleInfo> element.
213  */
214 STATIC void
_table_write_table_style_info(lxw_table * self)215 _table_write_table_style_info(lxw_table *self)
216 {
217     struct xml_attribute_list attributes;
218     struct xml_attribute *attribute;
219     char name[LXW_ATTR_32];
220     lxw_table_obj *table_obj = self->table_obj;
221 
222     LXW_INIT_ATTRIBUTES();
223 
224     if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) {
225         if (table_obj->style_type_number != 0) {
226             lxw_snprintf(name, LXW_ATTR_32, "TableStyleLight%d",
227                          table_obj->style_type_number);
228             LXW_PUSH_ATTRIBUTES_STR("name", name);
229         }
230     }
231     else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) {
232         lxw_snprintf(name, LXW_ATTR_32, "TableStyleMedium%d",
233                      table_obj->style_type_number);
234         LXW_PUSH_ATTRIBUTES_STR("name", name);
235     }
236     else if (table_obj->style_type == LXW_TABLE_STYLE_TYPE_DARK) {
237         lxw_snprintf(name, LXW_ATTR_32, "TableStyleDark%d",
238                      table_obj->style_type_number);
239         LXW_PUSH_ATTRIBUTES_STR("name", name);
240     }
241     else {
242         LXW_PUSH_ATTRIBUTES_STR("name", "TableStyleMedium9");
243     }
244 
245     if (table_obj->first_column)
246         LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "1");
247     else
248         LXW_PUSH_ATTRIBUTES_STR("showFirstColumn", "0");
249 
250     if (table_obj->last_column)
251         LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "1");
252     else
253         LXW_PUSH_ATTRIBUTES_STR("showLastColumn", "0");
254 
255     if (table_obj->no_banded_rows)
256         LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "0");
257     else
258         LXW_PUSH_ATTRIBUTES_STR("showRowStripes", "1");
259 
260     if (table_obj->banded_columns)
261         LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "1");
262     else
263         LXW_PUSH_ATTRIBUTES_STR("showColumnStripes", "0");
264 
265     lxw_xml_empty_tag(self->file, "tableStyleInfo", &attributes);
266 
267     LXW_FREE_ATTRIBUTES();
268 }
269 
270 /*****************************************************************************
271  *
272  * XML file assembly functions.
273  *
274  ****************************************************************************/
275 
276 /*
277  * Assemble and write the XML file.
278  */
279 void
lxw_table_assemble_xml_file(lxw_table * self)280 lxw_table_assemble_xml_file(lxw_table *self)
281 {
282     /* Write the XML declaration. */
283     _table_xml_declaration(self);
284 
285     /* Write the table element. */
286     _table_write_table(self);
287 
288     /* Write the autoFilter element. */
289     _table_write_auto_filter(self);
290 
291     /* Write the tableColumns element. */
292     _table_write_table_columns(self);
293 
294     /* Write the tableStyleInfo element. */
295     _table_write_table_style_info(self);
296 
297     lxw_xml_end_tag(self->file, "table");
298 }
299 
300 /*****************************************************************************
301  *
302  * Public functions.
303  *
304  ****************************************************************************/
305