fast_excel 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.dockerignore +2 -0
- data/.gitignore +7 -0
- data/.travis.yml +32 -9
- data/CHANGELOG.md +36 -1
- data/Dockerfile.test +17 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +33 -24
- data/LICENSE +21 -0
- data/Makefile +13 -0
- data/README.md +177 -40
- data/Rakefile +11 -1
- data/benchmarks/1k_rows.rb +17 -4
- data/benchmarks/20k_rows.rb +4 -0
- data/benchmarks/auto_width.rb +37 -0
- data/benchmarks/init.rb +14 -2
- data/benchmarks/memory.rb +8 -0
- data/benchmarks/profiler.rb +27 -0
- data/benchmarks/write_value.rb +62 -0
- data/examples/example.rb +3 -3
- data/examples/example_auto_width.rb +26 -0
- data/examples/example_filters.rb +36 -0
- data/examples/example_formula.rb +1 -3
- data/examples/example_hyperlink.rb +20 -0
- data/ext/fast_excel/extconf.rb +3 -0
- data/ext/fast_excel/text_width_ext.c +460 -0
- data/fast_excel.gemspec +3 -4
- data/letters.html +114 -0
- data/lib/fast_excel.rb +131 -25
- data/lib/fast_excel/binding.rb +33 -21
- data/lib/fast_excel/binding/chart.rb +20 -1
- data/lib/fast_excel/binding/format.rb +11 -4
- data/lib/fast_excel/binding/workbook.rb +10 -2
- data/lib/fast_excel/binding/worksheet.rb +44 -27
- data/libxlsxwriter/.gitignore +1 -0
- data/libxlsxwriter/.indent.pro +8 -0
- data/libxlsxwriter/.travis.yml +12 -0
- data/libxlsxwriter/CMakeLists.txt +338 -0
- data/libxlsxwriter/CONTRIBUTING.md +1 -1
- data/libxlsxwriter/Changes.txt +162 -0
- data/libxlsxwriter/LICENSE.txt +65 -4
- data/libxlsxwriter/Makefile +33 -11
- data/libxlsxwriter/Readme.md +3 -1
- data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +2 -1
- data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
- data/libxlsxwriter/include/xlsxwriter.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/chart.h +164 -13
- data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
- data/libxlsxwriter/include/xlsxwriter/common.h +35 -6
- data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
- data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
- data/libxlsxwriter/include/xlsxwriter/format.h +8 -8
- data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
- data/libxlsxwriter/include/xlsxwriter/packager.h +18 -8
- data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
- data/libxlsxwriter/include/xlsxwriter/styles.h +10 -5
- data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
- data/libxlsxwriter/include/xlsxwriter/utility.h +35 -5
- data/libxlsxwriter/include/xlsxwriter/workbook.h +234 -57
- data/libxlsxwriter/include/xlsxwriter/worksheet.h +780 -91
- data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
- data/libxlsxwriter/libxlsxwriter.podspec +4 -2
- data/libxlsxwriter/src/Makefile +31 -6
- data/libxlsxwriter/src/app.c +2 -2
- data/libxlsxwriter/src/chart.c +116 -23
- data/libxlsxwriter/src/chartsheet.c +508 -0
- data/libxlsxwriter/src/content_types.c +12 -4
- data/libxlsxwriter/src/core.c +11 -11
- data/libxlsxwriter/src/custom.c +3 -3
- data/libxlsxwriter/src/drawing.c +114 -17
- data/libxlsxwriter/src/format.c +5 -5
- data/libxlsxwriter/src/hash_table.c +1 -1
- data/libxlsxwriter/src/packager.c +378 -61
- data/libxlsxwriter/src/relationships.c +2 -2
- data/libxlsxwriter/src/shared_strings.c +18 -4
- data/libxlsxwriter/src/styles.c +59 -12
- data/libxlsxwriter/src/theme.c +2 -2
- data/libxlsxwriter/src/utility.c +93 -6
- data/libxlsxwriter/src/workbook.c +379 -61
- data/libxlsxwriter/src/worksheet.c +1240 -174
- data/libxlsxwriter/src/xmlwriter.c +18 -9
- data/libxlsxwriter/third_party/minizip/Makefile +6 -1
- data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
- data/libxlsxwriter/third_party/minizip/zip.c +2 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
- data/libxlsxwriter/version.txt +1 -1
- data/test/auto_width_test.rb +19 -0
- data/test/date_test.rb +34 -0
- data/test/format_test.rb +8 -0
- data/test/reopen_test.rb +22 -0
- data/test/test_helper.rb +8 -5
- data/test/text_width_test.rb +80 -0
- data/test/tmpfile_test.rb +1 -0
- data/test/validations_test.rb +47 -0
- data/test/worksheet_test.rb +44 -1
- metadata +33 -9
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* Create a new relationships object.
|
|
27
27
|
*/
|
|
28
28
|
lxw_relationships *
|
|
29
|
-
lxw_relationships_new()
|
|
29
|
+
lxw_relationships_new(void)
|
|
30
30
|
{
|
|
31
31
|
lxw_relationships *rels = calloc(1, sizeof(lxw_relationships));
|
|
32
32
|
GOTO_LABEL_ON_MEM_ERROR(rels, mem_error);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -34,7 +34,7 @@ LXW_RB_GENERATE_ELEMENT(sst_rb_tree, sst_element, sst_tree_pointers,
|
|
|
34
34
|
* Create a new SST SharedString object.
|
|
35
35
|
*/
|
|
36
36
|
lxw_sst *
|
|
37
|
-
lxw_sst_new()
|
|
37
|
+
lxw_sst_new(void)
|
|
38
38
|
{
|
|
39
39
|
/* Create the new shared string table. */
|
|
40
40
|
lxw_sst *sst = calloc(1, sizeof(lxw_sst));
|
|
@@ -161,6 +161,15 @@ _write_si(lxw_sst *self, char *string)
|
|
|
161
161
|
free(string);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/*
|
|
165
|
+
* Write the <si> element for rich strings.
|
|
166
|
+
*/
|
|
167
|
+
STATIC void
|
|
168
|
+
_write_rich_si(lxw_sst *self, char *string)
|
|
169
|
+
{
|
|
170
|
+
lxw_xml_rich_si_element(self->file, string);
|
|
171
|
+
}
|
|
172
|
+
|
|
164
173
|
/*
|
|
165
174
|
* Write the <sst> element.
|
|
166
175
|
*/
|
|
@@ -198,7 +207,11 @@ _write_sst_strings(lxw_sst *self)
|
|
|
198
207
|
|
|
199
208
|
STAILQ_FOREACH(sst_element, self->order_list, sst_order_pointers) {
|
|
200
209
|
/* Write the si element. */
|
|
201
|
-
|
|
210
|
+
if (sst_element->is_rich_string)
|
|
211
|
+
_write_rich_si(self, sst_element->string);
|
|
212
|
+
else
|
|
213
|
+
_write_si(self, sst_element->string);
|
|
214
|
+
|
|
202
215
|
}
|
|
203
216
|
}
|
|
204
217
|
|
|
@@ -230,7 +243,7 @@ lxw_sst_assemble_xml_file(lxw_sst *self)
|
|
|
230
243
|
* Add to or find a string in the SST SharedString table and return it's index.
|
|
231
244
|
*/
|
|
232
245
|
struct sst_element *
|
|
233
|
-
lxw_get_sst_index(lxw_sst *sst, const char *string)
|
|
246
|
+
lxw_get_sst_index(lxw_sst *sst, const char *string, uint8_t is_rich_string)
|
|
234
247
|
{
|
|
235
248
|
struct sst_element *element;
|
|
236
249
|
struct sst_element *existing_element;
|
|
@@ -243,6 +256,7 @@ lxw_get_sst_index(lxw_sst *sst, const char *string)
|
|
|
243
256
|
/* Create potential new element with the string and its index. */
|
|
244
257
|
element->index = sst->unique_count;
|
|
245
258
|
element->string = lxw_strdup(string);
|
|
259
|
+
element->is_rich_string = is_rich_string;
|
|
246
260
|
|
|
247
261
|
/* Try to insert it and see whether we already have that string. */
|
|
248
262
|
existing_element = RB_INSERT(sst_rb_tree, sst->rb_tree, element);
|
data/libxlsxwriter/src/styles.c
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
/*
|
|
15
15
|
* Forward declarations.
|
|
16
16
|
*/
|
|
17
|
+
STATIC void _write_font(lxw_styles *self, lxw_format *format,
|
|
18
|
+
uint8_t is_rich_string);
|
|
17
19
|
|
|
18
20
|
/*****************************************************************************
|
|
19
21
|
*
|
|
@@ -25,7 +27,7 @@
|
|
|
25
27
|
* Create a new styles object.
|
|
26
28
|
*/
|
|
27
29
|
lxw_styles *
|
|
28
|
-
lxw_styles_new()
|
|
30
|
+
lxw_styles_new(void)
|
|
29
31
|
{
|
|
30
32
|
lxw_styles *styles = calloc(1, sizeof(lxw_styles));
|
|
31
33
|
GOTO_LABEL_ON_MEM_ERROR(styles, mem_error);
|
|
@@ -66,6 +68,34 @@ lxw_styles_free(lxw_styles *styles)
|
|
|
66
68
|
free(styles);
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
/*
|
|
72
|
+
* Write the <t> element for rich strings.
|
|
73
|
+
*/
|
|
74
|
+
void
|
|
75
|
+
lxw_styles_write_string_fragment(lxw_styles *self, char *string)
|
|
76
|
+
{
|
|
77
|
+
struct xml_attribute_list attributes;
|
|
78
|
+
struct xml_attribute *attribute;
|
|
79
|
+
|
|
80
|
+
LXW_INIT_ATTRIBUTES();
|
|
81
|
+
|
|
82
|
+
/* Add attribute to preserve leading or trailing whitespace. */
|
|
83
|
+
if (isspace((unsigned char) string[0])
|
|
84
|
+
|| isspace((unsigned char) string[strlen(string) - 1]))
|
|
85
|
+
LXW_PUSH_ATTRIBUTES_STR("xml:space", "preserve");
|
|
86
|
+
|
|
87
|
+
lxw_xml_data_element(self->file, "t", string, &attributes);
|
|
88
|
+
|
|
89
|
+
LXW_FREE_ATTRIBUTES();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void
|
|
93
|
+
lxw_styles_write_rich_font(lxw_styles *self, lxw_format *format)
|
|
94
|
+
{
|
|
95
|
+
|
|
96
|
+
_write_font(self, format, LXW_TRUE);
|
|
97
|
+
}
|
|
98
|
+
|
|
69
99
|
/*****************************************************************************
|
|
70
100
|
*
|
|
71
101
|
* XML functions.
|
|
@@ -125,6 +155,7 @@ _write_num_fmts(lxw_styles *self)
|
|
|
125
155
|
struct xml_attribute_list attributes;
|
|
126
156
|
struct xml_attribute *attribute;
|
|
127
157
|
lxw_format *format;
|
|
158
|
+
uint16_t last_format_index = 0;
|
|
128
159
|
|
|
129
160
|
if (!self->num_format_count)
|
|
130
161
|
return;
|
|
@@ -141,7 +172,13 @@ _write_num_fmts(lxw_styles *self)
|
|
|
141
172
|
if (format->num_format_index < 164)
|
|
142
173
|
continue;
|
|
143
174
|
|
|
175
|
+
/* Ignore duplicates which have an already used index. */
|
|
176
|
+
if (format->num_format_index <= last_format_index)
|
|
177
|
+
continue;
|
|
178
|
+
|
|
144
179
|
_write_num_fmt(self, format->num_format_index, format->num_format);
|
|
180
|
+
|
|
181
|
+
last_format_index = format->num_format_index;
|
|
145
182
|
}
|
|
146
183
|
|
|
147
184
|
lxw_xml_end_tag(self->file, "numFmts");
|
|
@@ -153,13 +190,13 @@ _write_num_fmts(lxw_styles *self)
|
|
|
153
190
|
* Write the <sz> element.
|
|
154
191
|
*/
|
|
155
192
|
STATIC void
|
|
156
|
-
_write_font_size(lxw_styles *self,
|
|
193
|
+
_write_font_size(lxw_styles *self, double font_size)
|
|
157
194
|
{
|
|
158
195
|
struct xml_attribute_list attributes;
|
|
159
196
|
struct xml_attribute *attribute;
|
|
160
197
|
|
|
161
198
|
LXW_INIT_ATTRIBUTES();
|
|
162
|
-
|
|
199
|
+
LXW_PUSH_ATTRIBUTES_DBL("val", font_size);
|
|
163
200
|
|
|
164
201
|
lxw_xml_empty_tag(self->file, "sz", &attributes);
|
|
165
202
|
|
|
@@ -207,7 +244,8 @@ _write_font_color_rgb(lxw_styles *self, int32_t rgb)
|
|
|
207
244
|
* Write the <name> element.
|
|
208
245
|
*/
|
|
209
246
|
STATIC void
|
|
210
|
-
_write_font_name(lxw_styles *self, const char *font_name
|
|
247
|
+
_write_font_name(lxw_styles *self, const char *font_name,
|
|
248
|
+
uint8_t is_rich_string)
|
|
211
249
|
{
|
|
212
250
|
struct xml_attribute_list attributes;
|
|
213
251
|
struct xml_attribute *attribute;
|
|
@@ -219,7 +257,10 @@ _write_font_name(lxw_styles *self, const char *font_name)
|
|
|
219
257
|
else
|
|
220
258
|
LXW_PUSH_ATTRIBUTES_STR("val", LXW_DEFAULT_FONT_NAME);
|
|
221
259
|
|
|
222
|
-
|
|
260
|
+
if (is_rich_string)
|
|
261
|
+
lxw_xml_empty_tag(self->file, "rFont", &attributes);
|
|
262
|
+
else
|
|
263
|
+
lxw_xml_empty_tag(self->file, "name", &attributes);
|
|
223
264
|
|
|
224
265
|
LXW_FREE_ATTRIBUTES();
|
|
225
266
|
}
|
|
@@ -309,9 +350,12 @@ _write_vert_align(lxw_styles *self, const char *align)
|
|
|
309
350
|
* Write the <font> element.
|
|
310
351
|
*/
|
|
311
352
|
STATIC void
|
|
312
|
-
_write_font(lxw_styles *self, lxw_format *format)
|
|
353
|
+
_write_font(lxw_styles *self, lxw_format *format, uint8_t is_rich_string)
|
|
313
354
|
{
|
|
314
|
-
|
|
355
|
+
if (is_rich_string)
|
|
356
|
+
lxw_xml_start_tag(self->file, "rPr", NULL);
|
|
357
|
+
else
|
|
358
|
+
lxw_xml_start_tag(self->file, "font", NULL);
|
|
315
359
|
|
|
316
360
|
if (format->bold)
|
|
317
361
|
lxw_xml_empty_tag(self->file, "b", NULL);
|
|
@@ -337,7 +381,7 @@ _write_font(lxw_styles *self, lxw_format *format)
|
|
|
337
381
|
if (format->font_script == LXW_FONT_SUBSCRIPT)
|
|
338
382
|
_write_vert_align(self, "subscript");
|
|
339
383
|
|
|
340
|
-
if (format->font_size)
|
|
384
|
+
if (format->font_size > 0.0)
|
|
341
385
|
_write_font_size(self, format->font_size);
|
|
342
386
|
|
|
343
387
|
if (format->theme)
|
|
@@ -347,7 +391,7 @@ _write_font(lxw_styles *self, lxw_format *format)
|
|
|
347
391
|
else
|
|
348
392
|
_write_font_color_theme(self, LXW_DEFAULT_FONT_THEME);
|
|
349
393
|
|
|
350
|
-
_write_font_name(self, format->font_name);
|
|
394
|
+
_write_font_name(self, format->font_name, is_rich_string);
|
|
351
395
|
_write_font_family(self, format->font_family);
|
|
352
396
|
|
|
353
397
|
/* Only write the scheme element for the default font type if it
|
|
@@ -358,7 +402,10 @@ _write_font(lxw_styles *self, lxw_format *format)
|
|
|
358
402
|
_write_font_scheme(self, format->font_scheme);
|
|
359
403
|
}
|
|
360
404
|
|
|
361
|
-
|
|
405
|
+
if (is_rich_string)
|
|
406
|
+
lxw_xml_end_tag(self->file, "rPr");
|
|
407
|
+
else
|
|
408
|
+
lxw_xml_end_tag(self->file, "font");
|
|
362
409
|
}
|
|
363
410
|
|
|
364
411
|
/*
|
|
@@ -378,7 +425,7 @@ _write_fonts(lxw_styles *self)
|
|
|
378
425
|
|
|
379
426
|
STAILQ_FOREACH(format, self->xf_formats, list_pointers) {
|
|
380
427
|
if (format->has_font)
|
|
381
|
-
_write_font(self, format);
|
|
428
|
+
_write_font(self, format, LXW_FALSE);
|
|
382
429
|
}
|
|
383
430
|
|
|
384
431
|
lxw_xml_end_tag(self->file, "fonts");
|
data/libxlsxwriter/src/theme.c
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -289,7 +289,7 @@ const char *theme_strs[] = {
|
|
|
289
289
|
* Create a new theme object.
|
|
290
290
|
*/
|
|
291
291
|
lxw_theme *
|
|
292
|
-
lxw_theme_new()
|
|
292
|
+
lxw_theme_new(void)
|
|
293
293
|
{
|
|
294
294
|
lxw_theme *theme = calloc(1, sizeof(lxw_theme));
|
|
295
295
|
GOTO_LABEL_ON_MEM_ERROR(theme, mem_error);
|
data/libxlsxwriter/src/utility.c
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
#include <string.h>
|
|
13
13
|
#include <stdint.h>
|
|
14
14
|
#include <stdlib.h>
|
|
15
|
-
#include "xlsxwriter
|
|
15
|
+
#include "xlsxwriter.h"
|
|
16
16
|
#include "xlsxwriter/third_party/tmpfileplus.h"
|
|
17
17
|
|
|
18
18
|
char *error_strings[LXW_MAX_ERRNO + 1] = {
|
|
@@ -20,14 +20,21 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
|
|
|
20
20
|
"Memory error, failed to malloc() required memory.",
|
|
21
21
|
"Error creating output xlsx file. Usually a permissions error.",
|
|
22
22
|
"Error encountered when creating a tmpfile during file assembly.",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
23
|
+
"Error reading a tmpfile.",
|
|
24
|
+
"Zip generic error ZIP_ERRNO while creating the xlsx file.",
|
|
25
|
+
"Zip error ZIP_PARAMERROR while creating the xlsx file.",
|
|
26
|
+
"Zip error ZIP_BADZIPFILE (use_zip64 option may be required).",
|
|
27
|
+
"Zip error ZIP_INTERNALERROR while creating the xlsx file.",
|
|
28
|
+
"File error or unknown zip error when adding sub file to xlsx file.",
|
|
29
|
+
"Unknown zip error when closing xlsx file.",
|
|
26
30
|
"NULL function parameter ignored.",
|
|
27
31
|
"Function parameter validation error.",
|
|
28
32
|
"Worksheet name exceeds Excel's limit of 31 characters.",
|
|
29
|
-
"Worksheet name
|
|
33
|
+
"Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'",
|
|
34
|
+
"Worksheet name cannot start or end with an apostrophe.",
|
|
30
35
|
"Worksheet name is already in use.",
|
|
36
|
+
"Worksheet name 'History' is reserved by Excel.",
|
|
37
|
+
"Parameter exceeds Excel's limit of 32 characters.",
|
|
31
38
|
"Parameter exceeds Excel's limit of 128 characters.",
|
|
32
39
|
"Parameter exceeds Excel's limit of 255 characters.",
|
|
33
40
|
"String exceeds Excel's limit of 32,767 characters.",
|
|
@@ -419,6 +426,19 @@ lxw_strdup(const char *str)
|
|
|
419
426
|
return copy;
|
|
420
427
|
}
|
|
421
428
|
|
|
429
|
+
/* Simple function to strdup() a formula string without the leading "=". */
|
|
430
|
+
char *
|
|
431
|
+
lxw_strdup_formula(const char *formula)
|
|
432
|
+
{
|
|
433
|
+
if (!formula)
|
|
434
|
+
return NULL;
|
|
435
|
+
|
|
436
|
+
if (formula[0] == '=')
|
|
437
|
+
return lxw_strdup(formula + 1);
|
|
438
|
+
else
|
|
439
|
+
return lxw_strdup(formula);
|
|
440
|
+
}
|
|
441
|
+
|
|
422
442
|
/* Simple strlen that counts UTF-8 characters. Assumes well formed UTF-8. */
|
|
423
443
|
size_t
|
|
424
444
|
lxw_utf8_strlen(const char *str)
|
|
@@ -513,3 +533,70 @@ lxw_tmpfile(char *tmpdir)
|
|
|
513
533
|
return tmpfile();
|
|
514
534
|
#endif
|
|
515
535
|
}
|
|
536
|
+
|
|
537
|
+
/*
|
|
538
|
+
* Sample function to handle sprintf of doubles for locale portable code. This
|
|
539
|
+
* is usually handled by a lxw_sprintf_dbl() macro but it can be replaced with
|
|
540
|
+
* a function of the same name.
|
|
541
|
+
*
|
|
542
|
+
* The code below is a simplified example that changes numbers like 123,45 to
|
|
543
|
+
* 123.45. End-users can replace this with something more rigorous if
|
|
544
|
+
* required.
|
|
545
|
+
*/
|
|
546
|
+
#ifdef USE_DOUBLE_FUNCTION
|
|
547
|
+
int
|
|
548
|
+
lxw_sprintf_dbl(char *data, double number)
|
|
549
|
+
{
|
|
550
|
+
char *tmp;
|
|
551
|
+
|
|
552
|
+
lxw_snprintf(data, LXW_ATTR_32, "%.16g", number);
|
|
553
|
+
|
|
554
|
+
/* Replace comma with decimal point. */
|
|
555
|
+
tmp = strchr(data, ',');
|
|
556
|
+
if (tmp)
|
|
557
|
+
*tmp = '.';
|
|
558
|
+
|
|
559
|
+
return 0;
|
|
560
|
+
}
|
|
561
|
+
#endif
|
|
562
|
+
|
|
563
|
+
/*
|
|
564
|
+
* Retrieve runtime library version
|
|
565
|
+
*/
|
|
566
|
+
const char *
|
|
567
|
+
lxw_version(void)
|
|
568
|
+
{
|
|
569
|
+
return LXW_VERSION;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/*
|
|
573
|
+
* Hash a worksheet password. Based on the algorithm provided by Daniel Rentz
|
|
574
|
+
* of OpenOffice.
|
|
575
|
+
*/
|
|
576
|
+
uint16_t
|
|
577
|
+
lxw_hash_password(const char *password)
|
|
578
|
+
{
|
|
579
|
+
size_t count;
|
|
580
|
+
uint8_t i;
|
|
581
|
+
uint16_t hash = 0x0000;
|
|
582
|
+
|
|
583
|
+
count = strlen(password);
|
|
584
|
+
|
|
585
|
+
for (i = 0; i < count; i++) {
|
|
586
|
+
uint32_t low_15;
|
|
587
|
+
uint32_t high_15;
|
|
588
|
+
uint32_t letter = password[i] << (i + 1);
|
|
589
|
+
|
|
590
|
+
low_15 = letter & 0x7fff;
|
|
591
|
+
high_15 = letter & (0x7fff << 15);
|
|
592
|
+
high_15 = high_15 >> 15;
|
|
593
|
+
letter = low_15 | high_15;
|
|
594
|
+
|
|
595
|
+
hash ^= letter;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
hash ^= count;
|
|
599
|
+
hash ^= 0xCE4B;
|
|
600
|
+
|
|
601
|
+
return hash;
|
|
602
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Used in conjunction with the libxlsxwriter library.
|
|
5
5
|
*
|
|
6
|
-
* Copyright 2014-
|
|
6
|
+
* Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
#include "xlsxwriter/packager.h"
|
|
14
14
|
#include "xlsxwriter/hash_table.h"
|
|
15
15
|
|
|
16
|
-
STATIC int
|
|
16
|
+
STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1,
|
|
17
|
+
lxw_worksheet_name *name2);
|
|
18
|
+
STATIC int _chartsheet_name_cmp(lxw_chartsheet_name *name1,
|
|
19
|
+
lxw_chartsheet_name *name2);
|
|
17
20
|
#ifndef __clang_analyzer__
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
LXW_RB_GENERATE_WORKSHEET_NAMES(lxw_worksheet_names, lxw_worksheet_name,
|
|
22
|
+
tree_pointers, _worksheet_name_cmp);
|
|
23
|
+
LXW_RB_GENERATE_CHARTSHEET_NAMES(lxw_chartsheet_names, lxw_chartsheet_name,
|
|
24
|
+
tree_pointers, _chartsheet_name_cmp);
|
|
20
25
|
#endif
|
|
21
26
|
|
|
22
27
|
/*
|
|
@@ -30,12 +35,18 @@ LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers,
|
|
|
30
35
|
****************************************************************************/
|
|
31
36
|
|
|
32
37
|
/*
|
|
33
|
-
*
|
|
38
|
+
* Comparators for the sheet names structure red/black tree.
|
|
34
39
|
*/
|
|
35
40
|
STATIC int
|
|
36
|
-
|
|
41
|
+
_worksheet_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2)
|
|
37
42
|
{
|
|
38
|
-
return
|
|
43
|
+
return lxw_strcasecmp(name1->name, name2->name);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
STATIC int
|
|
47
|
+
_chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2)
|
|
48
|
+
{
|
|
49
|
+
return lxw_strcasecmp(name1->name, name2->name);
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
/*
|
|
@@ -81,9 +92,11 @@ _free_custom_doc_property(lxw_custom_property *custom_property)
|
|
|
81
92
|
void
|
|
82
93
|
lxw_workbook_free(lxw_workbook *workbook)
|
|
83
94
|
{
|
|
84
|
-
|
|
95
|
+
lxw_sheet *sheet;
|
|
85
96
|
struct lxw_worksheet_name *worksheet_name;
|
|
86
|
-
struct lxw_worksheet_name *
|
|
97
|
+
struct lxw_worksheet_name *next_worksheet_name;
|
|
98
|
+
struct lxw_chartsheet_name *chartsheet_name;
|
|
99
|
+
struct lxw_chartsheet_name *next_chartsheet_name;
|
|
87
100
|
lxw_chart *chart;
|
|
88
101
|
lxw_format *format;
|
|
89
102
|
lxw_defined_name *defined_name;
|
|
@@ -97,17 +110,26 @@ lxw_workbook_free(lxw_workbook *workbook)
|
|
|
97
110
|
|
|
98
111
|
free(workbook->filename);
|
|
99
112
|
|
|
100
|
-
/* Free the
|
|
101
|
-
if (workbook->
|
|
102
|
-
while (!STAILQ_EMPTY(workbook->
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
/* Free the sheets in the workbook. */
|
|
114
|
+
if (workbook->sheets) {
|
|
115
|
+
while (!STAILQ_EMPTY(workbook->sheets)) {
|
|
116
|
+
sheet = STAILQ_FIRST(workbook->sheets);
|
|
117
|
+
|
|
118
|
+
if (sheet->is_chartsheet)
|
|
119
|
+
lxw_chartsheet_free(sheet->u.chartsheet);
|
|
120
|
+
else
|
|
121
|
+
lxw_worksheet_free(sheet->u.worksheet);
|
|
108
122
|
|
|
123
|
+
STAILQ_REMOVE_HEAD(workbook->sheets, list_pointers);
|
|
124
|
+
free(sheet);
|
|
125
|
+
}
|
|
126
|
+
free(workbook->sheets);
|
|
109
127
|
}
|
|
110
128
|
|
|
129
|
+
/* Free the sheet lists. The worksheet objects are freed above. */
|
|
130
|
+
free(workbook->worksheets);
|
|
131
|
+
free(workbook->chartsheets);
|
|
132
|
+
|
|
111
133
|
/* Free the charts in the workbook. */
|
|
112
134
|
if (workbook->charts) {
|
|
113
135
|
while (!STAILQ_EMPTY(workbook->charts)) {
|
|
@@ -153,22 +175,41 @@ lxw_workbook_free(lxw_workbook *workbook)
|
|
|
153
175
|
if (workbook->worksheet_names) {
|
|
154
176
|
for (worksheet_name =
|
|
155
177
|
RB_MIN(lxw_worksheet_names, workbook->worksheet_names);
|
|
156
|
-
worksheet_name; worksheet_name =
|
|
178
|
+
worksheet_name; worksheet_name = next_worksheet_name) {
|
|
157
179
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
180
|
+
next_worksheet_name = RB_NEXT(lxw_worksheet_names,
|
|
181
|
+
workbook->worksheet_name,
|
|
182
|
+
worksheet_name);
|
|
183
|
+
RB_REMOVE(lxw_worksheet_names, workbook->worksheet_names,
|
|
184
|
+
worksheet_name);
|
|
162
185
|
free(worksheet_name);
|
|
163
186
|
}
|
|
164
187
|
|
|
165
188
|
free(workbook->worksheet_names);
|
|
166
189
|
}
|
|
167
190
|
|
|
191
|
+
if (workbook->chartsheet_names) {
|
|
192
|
+
for (chartsheet_name =
|
|
193
|
+
RB_MIN(lxw_chartsheet_names, workbook->chartsheet_names);
|
|
194
|
+
chartsheet_name; chartsheet_name = next_chartsheet_name) {
|
|
195
|
+
|
|
196
|
+
next_chartsheet_name = RB_NEXT(lxw_chartsheet_names,
|
|
197
|
+
workbook->chartsheet_name,
|
|
198
|
+
chartsheet_name);
|
|
199
|
+
RB_REMOVE(lxw_chartsheet_names, workbook->chartsheet_names,
|
|
200
|
+
chartsheet_name);
|
|
201
|
+
free(chartsheet_name);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
free(workbook->chartsheet_names);
|
|
205
|
+
}
|
|
206
|
+
|
|
168
207
|
lxw_hash_free(workbook->used_xf_formats);
|
|
169
208
|
lxw_sst_free(workbook->sst);
|
|
170
209
|
free(workbook->options.tmpdir);
|
|
171
210
|
free(workbook->ordered_charts);
|
|
211
|
+
free(workbook->vba_project);
|
|
212
|
+
free(workbook->vba_codename);
|
|
172
213
|
free(workbook);
|
|
173
214
|
}
|
|
174
215
|
|
|
@@ -404,7 +445,6 @@ _prepare_num_formats(lxw_workbook *self)
|
|
|
404
445
|
lxw_hash_element *used_format_element;
|
|
405
446
|
uint16_t index = 0xA4;
|
|
406
447
|
uint16_t num_format_count = 0;
|
|
407
|
-
char *num_format;
|
|
408
448
|
uint16_t *num_format_index;
|
|
409
449
|
|
|
410
450
|
LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
|
|
@@ -415,12 +455,14 @@ _prepare_num_formats(lxw_workbook *self)
|
|
|
415
455
|
continue;
|
|
416
456
|
|
|
417
457
|
/* Check if there is a user defined number format string. */
|
|
418
|
-
|
|
458
|
+
if (*format->num_format) {
|
|
459
|
+
char num_format[LXW_FORMAT_FIELD_LEN] = { 0 };
|
|
460
|
+
lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s",
|
|
461
|
+
format->num_format);
|
|
419
462
|
|
|
420
|
-
if (*num_format) {
|
|
421
463
|
/* Look up the num_format in the hash table. */
|
|
422
464
|
hash_element = lxw_hash_key_exists(num_formats, num_format,
|
|
423
|
-
|
|
465
|
+
LXW_FORMAT_FIELD_LEN);
|
|
424
466
|
|
|
425
467
|
if (hash_element) {
|
|
426
468
|
/* Num_Format has already been used. */
|
|
@@ -431,8 +473,9 @@ _prepare_num_formats(lxw_workbook *self)
|
|
|
431
473
|
num_format_index = calloc(1, sizeof(uint16_t));
|
|
432
474
|
*num_format_index = index;
|
|
433
475
|
format->num_format_index = index;
|
|
434
|
-
lxw_insert_hash_element(num_formats, num_format,
|
|
435
|
-
num_format_index,
|
|
476
|
+
lxw_insert_hash_element(num_formats, format->num_format,
|
|
477
|
+
num_format_index,
|
|
478
|
+
LXW_FORMAT_FIELD_LEN);
|
|
436
479
|
index++;
|
|
437
480
|
num_format_count++;
|
|
438
481
|
}
|
|
@@ -494,6 +537,7 @@ _store_defined_name(lxw_workbook *self, const char *name,
|
|
|
494
537
|
const char *app_name, const char *formula, int16_t index,
|
|
495
538
|
uint8_t hidden)
|
|
496
539
|
{
|
|
540
|
+
lxw_sheet *sheet;
|
|
497
541
|
lxw_worksheet *worksheet;
|
|
498
542
|
lxw_defined_name *defined_name;
|
|
499
543
|
lxw_defined_name *list_defined_name;
|
|
@@ -544,7 +588,12 @@ _store_defined_name(lxw_workbook *self, const char *name,
|
|
|
544
588
|
worksheet_name[strlen(worksheet_name) - 1] = '\0';
|
|
545
589
|
|
|
546
590
|
/* Search for worksheet name to get the equivalent worksheet index. */
|
|
547
|
-
STAILQ_FOREACH(
|
|
591
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
592
|
+
if (sheet->is_chartsheet)
|
|
593
|
+
continue;
|
|
594
|
+
else
|
|
595
|
+
worksheet = sheet->u.worksheet;
|
|
596
|
+
|
|
548
597
|
if (strcmp(worksheet_name, worksheet->name) == 0) {
|
|
549
598
|
defined_name->index = worksheet->index;
|
|
550
599
|
lxw_strcpy(defined_name->normalised_sheetname,
|
|
@@ -835,13 +884,23 @@ _add_chart_cache_data(lxw_workbook *self)
|
|
|
835
884
|
STATIC void
|
|
836
885
|
_prepare_drawings(lxw_workbook *self)
|
|
837
886
|
{
|
|
887
|
+
lxw_sheet *sheet;
|
|
838
888
|
lxw_worksheet *worksheet;
|
|
839
889
|
lxw_image_options *image_options;
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
890
|
+
uint32_t chart_ref_id = 0;
|
|
891
|
+
uint32_t image_ref_id = 0;
|
|
892
|
+
uint32_t drawing_id = 0;
|
|
893
|
+
uint8_t is_chartsheet;
|
|
894
|
+
|
|
895
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
896
|
+
if (sheet->is_chartsheet) {
|
|
897
|
+
worksheet = sheet->u.chartsheet->worksheet;
|
|
898
|
+
is_chartsheet = LXW_TRUE;
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
worksheet = sheet->u.worksheet;
|
|
902
|
+
is_chartsheet = LXW_FALSE;
|
|
903
|
+
}
|
|
845
904
|
|
|
846
905
|
if (STAILQ_EMPTY(worksheet->image_data)
|
|
847
906
|
&& STAILQ_EMPTY(worksheet->chart_data))
|
|
@@ -852,7 +911,7 @@ _prepare_drawings(lxw_workbook *self)
|
|
|
852
911
|
STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
|
|
853
912
|
chart_ref_id++;
|
|
854
913
|
lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id,
|
|
855
|
-
image_options);
|
|
914
|
+
image_options, is_chartsheet);
|
|
856
915
|
if (image_options->chart)
|
|
857
916
|
STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart,
|
|
858
917
|
ordered_list_pointers);
|
|
@@ -887,14 +946,18 @@ STATIC void
|
|
|
887
946
|
_prepare_defined_names(lxw_workbook *self)
|
|
888
947
|
{
|
|
889
948
|
lxw_worksheet *worksheet;
|
|
949
|
+
lxw_sheet *sheet;
|
|
890
950
|
char app_name[LXW_DEFINED_NAME_LENGTH];
|
|
891
951
|
char range[LXW_DEFINED_NAME_LENGTH];
|
|
892
952
|
char area[LXW_MAX_CELL_RANGE_LENGTH];
|
|
893
953
|
char first_col[8];
|
|
894
954
|
char last_col[8];
|
|
895
955
|
|
|
896
|
-
STAILQ_FOREACH(
|
|
897
|
-
|
|
956
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
957
|
+
if (sheet->is_chartsheet)
|
|
958
|
+
continue;
|
|
959
|
+
else
|
|
960
|
+
worksheet = sheet->u.worksheet;
|
|
898
961
|
/*
|
|
899
962
|
* Check for autofilter settings and store them.
|
|
900
963
|
*/
|
|
@@ -1074,6 +1137,10 @@ _write_file_version(lxw_workbook *self)
|
|
|
1074
1137
|
LXW_PUSH_ATTRIBUTES_STR("lowestEdited", "4");
|
|
1075
1138
|
LXW_PUSH_ATTRIBUTES_STR("rupBuild", "4505");
|
|
1076
1139
|
|
|
1140
|
+
if (self->vba_project)
|
|
1141
|
+
LXW_PUSH_ATTRIBUTES_STR("codeName",
|
|
1142
|
+
"{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}");
|
|
1143
|
+
|
|
1077
1144
|
lxw_xml_empty_tag(self->file, "fileVersion", &attributes);
|
|
1078
1145
|
|
|
1079
1146
|
LXW_FREE_ATTRIBUTES();
|
|
@@ -1089,6 +1156,10 @@ _write_workbook_pr(lxw_workbook *self)
|
|
|
1089
1156
|
struct xml_attribute *attribute;
|
|
1090
1157
|
|
|
1091
1158
|
LXW_INIT_ATTRIBUTES();
|
|
1159
|
+
|
|
1160
|
+
if (self->vba_codename)
|
|
1161
|
+
LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename);
|
|
1162
|
+
|
|
1092
1163
|
LXW_PUSH_ATTRIBUTES_STR("defaultThemeVersion", "124226");
|
|
1093
1164
|
|
|
1094
1165
|
lxw_xml_empty_tag(self->file, "workbookPr", &attributes);
|
|
@@ -1168,13 +1239,23 @@ _write_sheet(lxw_workbook *self, const char *name, uint32_t sheet_id,
|
|
|
1168
1239
|
STATIC void
|
|
1169
1240
|
_write_sheets(lxw_workbook *self)
|
|
1170
1241
|
{
|
|
1242
|
+
lxw_sheet *sheet;
|
|
1171
1243
|
lxw_worksheet *worksheet;
|
|
1244
|
+
lxw_chartsheet *chartsheet;
|
|
1172
1245
|
|
|
1173
1246
|
lxw_xml_start_tag(self->file, "sheets", NULL);
|
|
1174
1247
|
|
|
1175
|
-
STAILQ_FOREACH(
|
|
1176
|
-
|
|
1177
|
-
|
|
1248
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
1249
|
+
if (sheet->is_chartsheet) {
|
|
1250
|
+
chartsheet = sheet->u.chartsheet;
|
|
1251
|
+
_write_sheet(self, chartsheet->name, chartsheet->index + 1,
|
|
1252
|
+
chartsheet->hidden);
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
worksheet = sheet->u.worksheet;
|
|
1256
|
+
_write_sheet(self, worksheet->name, worksheet->index + 1,
|
|
1257
|
+
worksheet->hidden);
|
|
1258
|
+
}
|
|
1178
1259
|
}
|
|
1179
1260
|
|
|
1180
1261
|
lxw_xml_end_tag(self->file, "sheets");
|
|
@@ -1222,9 +1303,6 @@ _write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name)
|
|
|
1222
1303
|
LXW_FREE_ATTRIBUTES();
|
|
1223
1304
|
}
|
|
1224
1305
|
|
|
1225
|
-
/*
|
|
1226
|
-
* Write the <definedNames> element.
|
|
1227
|
-
*/
|
|
1228
1306
|
STATIC void
|
|
1229
1307
|
_write_defined_names(lxw_workbook *self)
|
|
1230
1308
|
{
|
|
@@ -1328,16 +1406,32 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
|
|
|
1328
1406
|
GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error);
|
|
1329
1407
|
workbook->filename = lxw_strdup(filename);
|
|
1330
1408
|
|
|
1409
|
+
/* Add the sheets list. */
|
|
1410
|
+
workbook->sheets = calloc(1, sizeof(struct lxw_sheets));
|
|
1411
|
+
GOTO_LABEL_ON_MEM_ERROR(workbook->sheets, mem_error);
|
|
1412
|
+
STAILQ_INIT(workbook->sheets);
|
|
1413
|
+
|
|
1331
1414
|
/* Add the worksheets list. */
|
|
1332
1415
|
workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets));
|
|
1333
1416
|
GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error);
|
|
1334
1417
|
STAILQ_INIT(workbook->worksheets);
|
|
1335
1418
|
|
|
1419
|
+
/* Add the chartsheets list. */
|
|
1420
|
+
workbook->chartsheets = calloc(1, sizeof(struct lxw_chartsheets));
|
|
1421
|
+
GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheets, mem_error);
|
|
1422
|
+
STAILQ_INIT(workbook->chartsheets);
|
|
1423
|
+
|
|
1336
1424
|
/* Add the worksheet names tree. */
|
|
1337
1425
|
workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names));
|
|
1338
1426
|
GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error);
|
|
1339
1427
|
RB_INIT(workbook->worksheet_names);
|
|
1340
1428
|
|
|
1429
|
+
/* Add the chartsheet names tree. */
|
|
1430
|
+
workbook->chartsheet_names = calloc(1,
|
|
1431
|
+
sizeof(struct lxw_chartsheet_names));
|
|
1432
|
+
GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error);
|
|
1433
|
+
RB_INIT(workbook->chartsheet_names);
|
|
1434
|
+
|
|
1341
1435
|
/* Add the charts list. */
|
|
1342
1436
|
workbook->charts = calloc(1, sizeof(struct lxw_charts));
|
|
1343
1437
|
GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
|
|
@@ -1386,6 +1480,7 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
|
|
|
1386
1480
|
if (options) {
|
|
1387
1481
|
workbook->options.constant_memory = options->constant_memory;
|
|
1388
1482
|
workbook->options.tmpdir = lxw_strdup(options->tmpdir);
|
|
1483
|
+
workbook->options.use_zip64 = options->use_zip64;
|
|
1389
1484
|
}
|
|
1390
1485
|
|
|
1391
1486
|
return workbook;
|
|
@@ -1402,7 +1497,8 @@ mem_error:
|
|
|
1402
1497
|
lxw_worksheet *
|
|
1403
1498
|
workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
|
|
1404
1499
|
{
|
|
1405
|
-
|
|
1500
|
+
lxw_sheet *sheet = NULL;
|
|
1501
|
+
lxw_worksheet *worksheet = NULL;
|
|
1406
1502
|
lxw_worksheet_name *worksheet_name = NULL;
|
|
1407
1503
|
lxw_error error;
|
|
1408
1504
|
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
@@ -1419,13 +1515,13 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
|
|
|
1419
1515
|
GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
|
|
1420
1516
|
|
|
1421
1517
|
lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
|
|
1422
|
-
self->
|
|
1518
|
+
self->num_worksheets + 1);
|
|
1423
1519
|
init_data.name = new_name;
|
|
1424
1520
|
init_data.quoted_name = lxw_strdup(new_name);
|
|
1425
1521
|
}
|
|
1426
1522
|
|
|
1427
1523
|
/* Check that the worksheet name is valid. */
|
|
1428
|
-
error =
|
|
1524
|
+
error = workbook_validate_sheet_name(self, init_data.name);
|
|
1429
1525
|
if (error) {
|
|
1430
1526
|
LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has "
|
|
1431
1527
|
"error: %s", init_data.name, lxw_strerror(error));
|
|
@@ -1449,9 +1545,19 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
|
|
|
1449
1545
|
worksheet = lxw_worksheet_new(&init_data);
|
|
1450
1546
|
GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
|
|
1451
1547
|
|
|
1452
|
-
|
|
1548
|
+
/* Add it to the worksheet list. */
|
|
1549
|
+
self->num_worksheets++;
|
|
1453
1550
|
STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers);
|
|
1454
1551
|
|
|
1552
|
+
/* Create a new sheet object. */
|
|
1553
|
+
sheet = calloc(1, sizeof(lxw_sheet));
|
|
1554
|
+
GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error);
|
|
1555
|
+
sheet->u.worksheet = worksheet;
|
|
1556
|
+
|
|
1557
|
+
/* Add it to the worksheet list. */
|
|
1558
|
+
self->num_sheets++;
|
|
1559
|
+
STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers);
|
|
1560
|
+
|
|
1455
1561
|
/* Store the worksheet so we can look it up by name. */
|
|
1456
1562
|
worksheet_name->name = init_data.name;
|
|
1457
1563
|
worksheet_name->worksheet = worksheet;
|
|
@@ -1463,6 +1569,91 @@ mem_error:
|
|
|
1463
1569
|
free(init_data.name);
|
|
1464
1570
|
free(init_data.quoted_name);
|
|
1465
1571
|
free(worksheet_name);
|
|
1572
|
+
free(worksheet);
|
|
1573
|
+
return NULL;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/*
|
|
1577
|
+
* Add a new chartsheet to the Excel workbook.
|
|
1578
|
+
*/
|
|
1579
|
+
lxw_chartsheet *
|
|
1580
|
+
workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
|
|
1581
|
+
{
|
|
1582
|
+
lxw_sheet *sheet = NULL;
|
|
1583
|
+
lxw_chartsheet *chartsheet = NULL;
|
|
1584
|
+
lxw_chartsheet_name *chartsheet_name = NULL;
|
|
1585
|
+
lxw_error error;
|
|
1586
|
+
lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
1587
|
+
char *new_name = NULL;
|
|
1588
|
+
|
|
1589
|
+
if (sheetname) {
|
|
1590
|
+
/* Use the user supplied name. */
|
|
1591
|
+
init_data.name = lxw_strdup(sheetname);
|
|
1592
|
+
init_data.quoted_name = lxw_quote_sheetname((char *) sheetname);
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
/* Use the default SheetN name. */
|
|
1596
|
+
new_name = malloc(LXW_MAX_SHEETNAME_LENGTH);
|
|
1597
|
+
GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
|
|
1598
|
+
|
|
1599
|
+
lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Chart%d",
|
|
1600
|
+
self->num_chartsheets + 1);
|
|
1601
|
+
init_data.name = new_name;
|
|
1602
|
+
init_data.quoted_name = lxw_strdup(new_name);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
/* Check that the chartsheet name is valid. */
|
|
1606
|
+
error = workbook_validate_sheet_name(self, init_data.name);
|
|
1607
|
+
if (error) {
|
|
1608
|
+
LXW_WARN_FORMAT2
|
|
1609
|
+
("workbook_add_chartsheet(): chartsheet name '%s' has "
|
|
1610
|
+
"error: %s", init_data.name, lxw_strerror(error));
|
|
1611
|
+
goto mem_error;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/* Create a struct to find/store the chartsheet name/pointer. */
|
|
1615
|
+
chartsheet_name = calloc(1, sizeof(struct lxw_chartsheet_name));
|
|
1616
|
+
GOTO_LABEL_ON_MEM_ERROR(chartsheet_name, mem_error);
|
|
1617
|
+
|
|
1618
|
+
/* Initialize the metadata to pass to the chartsheet. */
|
|
1619
|
+
init_data.hidden = 0;
|
|
1620
|
+
init_data.index = self->num_sheets;
|
|
1621
|
+
init_data.sst = self->sst;
|
|
1622
|
+
init_data.optimize = self->options.constant_memory;
|
|
1623
|
+
init_data.active_sheet = &self->active_sheet;
|
|
1624
|
+
init_data.first_sheet = &self->first_sheet;
|
|
1625
|
+
init_data.tmpdir = self->options.tmpdir;
|
|
1626
|
+
|
|
1627
|
+
/* Create a new chartsheet object. */
|
|
1628
|
+
chartsheet = lxw_chartsheet_new(&init_data);
|
|
1629
|
+
GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error);
|
|
1630
|
+
|
|
1631
|
+
/* Add it to the chartsheet list. */
|
|
1632
|
+
self->num_chartsheets++;
|
|
1633
|
+
STAILQ_INSERT_TAIL(self->chartsheets, chartsheet, list_pointers);
|
|
1634
|
+
|
|
1635
|
+
/* Create a new sheet object. */
|
|
1636
|
+
sheet = calloc(1, sizeof(lxw_sheet));
|
|
1637
|
+
GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error);
|
|
1638
|
+
sheet->is_chartsheet = LXW_TRUE;
|
|
1639
|
+
sheet->u.chartsheet = chartsheet;
|
|
1640
|
+
|
|
1641
|
+
/* Add it to the chartsheet list. */
|
|
1642
|
+
self->num_sheets++;
|
|
1643
|
+
STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers);
|
|
1644
|
+
|
|
1645
|
+
/* Store the chartsheet so we can look it up by name. */
|
|
1646
|
+
chartsheet_name->name = init_data.name;
|
|
1647
|
+
chartsheet_name->chartsheet = chartsheet;
|
|
1648
|
+
RB_INSERT(lxw_chartsheet_names, self->chartsheet_names, chartsheet_name);
|
|
1649
|
+
|
|
1650
|
+
return chartsheet;
|
|
1651
|
+
|
|
1652
|
+
mem_error:
|
|
1653
|
+
free(init_data.name);
|
|
1654
|
+
free(init_data.quoted_name);
|
|
1655
|
+
free(chartsheet_name);
|
|
1656
|
+
free(chartsheet);
|
|
1466
1657
|
return NULL;
|
|
1467
1658
|
}
|
|
1468
1659
|
|
|
@@ -1526,6 +1717,7 @@ workbook_default_format(lxw_workbook *self)
|
|
|
1526
1717
|
lxw_error
|
|
1527
1718
|
workbook_close(lxw_workbook *self)
|
|
1528
1719
|
{
|
|
1720
|
+
lxw_sheet *sheet = NULL;
|
|
1529
1721
|
lxw_worksheet *worksheet = NULL;
|
|
1530
1722
|
lxw_packager *packager = NULL;
|
|
1531
1723
|
lxw_error error = LXW_NO_ERROR;
|
|
@@ -1536,17 +1728,41 @@ workbook_close(lxw_workbook *self)
|
|
|
1536
1728
|
|
|
1537
1729
|
/* Ensure that at least one worksheet has been selected. */
|
|
1538
1730
|
if (self->active_sheet == 0) {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1731
|
+
sheet = STAILQ_FIRST(self->sheets);
|
|
1732
|
+
if (!sheet->is_chartsheet) {
|
|
1733
|
+
worksheet = sheet->u.worksheet;
|
|
1734
|
+
worksheet->selected = 1;
|
|
1735
|
+
worksheet->hidden = 0;
|
|
1736
|
+
}
|
|
1542
1737
|
}
|
|
1543
1738
|
|
|
1544
1739
|
/* Set the active sheet. */
|
|
1545
|
-
STAILQ_FOREACH(
|
|
1740
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
1741
|
+
if (sheet->is_chartsheet)
|
|
1742
|
+
continue;
|
|
1743
|
+
else
|
|
1744
|
+
worksheet = sheet->u.worksheet;
|
|
1745
|
+
|
|
1546
1746
|
if (worksheet->index == self->active_sheet)
|
|
1547
1747
|
worksheet->active = 1;
|
|
1548
1748
|
}
|
|
1549
1749
|
|
|
1750
|
+
/* Set workbook and worksheet VBA codenames if a macro has been added. */
|
|
1751
|
+
if (self->vba_project) {
|
|
1752
|
+
if (!self->vba_codename)
|
|
1753
|
+
workbook_set_vba_name(self, "ThisWorkbook");
|
|
1754
|
+
|
|
1755
|
+
STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
|
|
1756
|
+
if (sheet->is_chartsheet)
|
|
1757
|
+
continue;
|
|
1758
|
+
else
|
|
1759
|
+
worksheet = sheet->u.worksheet;
|
|
1760
|
+
|
|
1761
|
+
if (!worksheet->vba_codename)
|
|
1762
|
+
worksheet_set_vba_name(worksheet, worksheet->name);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1550
1766
|
/* Set the defined names for the worksheets such as Print Titles. */
|
|
1551
1767
|
_prepare_defined_names(self);
|
|
1552
1768
|
|
|
@@ -1557,13 +1773,15 @@ workbook_close(lxw_workbook *self)
|
|
|
1557
1773
|
_add_chart_cache_data(self);
|
|
1558
1774
|
|
|
1559
1775
|
/* Create a packager object to assemble sub-elements into a zip file. */
|
|
1560
|
-
packager = lxw_packager_new(self->filename,
|
|
1776
|
+
packager = lxw_packager_new(self->filename,
|
|
1777
|
+
self->options.tmpdir,
|
|
1778
|
+
self->options.use_zip64);
|
|
1561
1779
|
|
|
1562
1780
|
/* If the packager fails it is generally due to a zip permission error. */
|
|
1563
1781
|
if (packager == NULL) {
|
|
1564
1782
|
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1565
1783
|
"Error creating '%s'. "
|
|
1566
|
-
"
|
|
1784
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1567
1785
|
|
|
1568
1786
|
error = LXW_ERROR_CREATING_XLSX_FILE;
|
|
1569
1787
|
goto mem_error;
|
|
@@ -1579,26 +1797,47 @@ workbook_close(lxw_workbook *self)
|
|
|
1579
1797
|
if (error == LXW_ERROR_CREATING_TMPFILE) {
|
|
1580
1798
|
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1581
1799
|
"Error creating tmpfile(s) to assemble '%s'. "
|
|
1582
|
-
"
|
|
1800
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1583
1801
|
}
|
|
1584
1802
|
|
|
1585
|
-
/* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by
|
|
1803
|
+
/* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zip. */
|
|
1586
1804
|
if (error == LXW_ERROR_ZIP_FILE_OPERATION) {
|
|
1587
1805
|
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1588
|
-
"
|
|
1589
|
-
"
|
|
1806
|
+
"Zip ZIP_ERRNO error while creating xlsx file '%s'. "
|
|
1807
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/* If LXW_ERROR_ZIP_PARAMETER_ERROR then errno is set by zip. */
|
|
1811
|
+
if (error == LXW_ERROR_ZIP_PARAMETER_ERROR) {
|
|
1812
|
+
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1813
|
+
"Zip ZIP_PARAMERROR error while creating xlsx file '%s'. "
|
|
1814
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/* If LXW_ERROR_ZIP_BAD_ZIP_FILE then errno is set by zip. */
|
|
1818
|
+
if (error == LXW_ERROR_ZIP_BAD_ZIP_FILE) {
|
|
1819
|
+
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1820
|
+
"Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. "
|
|
1821
|
+
"This may require the use_zip64 option for large files. "
|
|
1822
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/* If LXW_ERROR_ZIP_INTERNAL_ERROR then errno is set by zip. */
|
|
1826
|
+
if (error == LXW_ERROR_ZIP_INTERNAL_ERROR) {
|
|
1827
|
+
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1828
|
+
"Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. "
|
|
1829
|
+
"System error = %s\n", self->filename, strerror(errno));
|
|
1590
1830
|
}
|
|
1591
1831
|
|
|
1592
1832
|
/* The next 2 error conditions don't set errno. */
|
|
1593
1833
|
if (error == LXW_ERROR_ZIP_FILE_ADD) {
|
|
1594
1834
|
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1595
|
-
"
|
|
1596
|
-
self->filename);
|
|
1835
|
+
"Zip error adding file to xlsx file '%s'.\n", self->filename);
|
|
1597
1836
|
}
|
|
1598
1837
|
|
|
1599
1838
|
if (error == LXW_ERROR_ZIP_CLOSE) {
|
|
1600
1839
|
fprintf(stderr, "[ERROR] workbook_close(): "
|
|
1601
|
-
"
|
|
1840
|
+
"Zip error closing xlsx file '%s'.\n", self->filename);
|
|
1602
1841
|
}
|
|
1603
1842
|
|
|
1604
1843
|
mem_error:
|
|
@@ -1908,11 +2147,33 @@ workbook_get_worksheet_by_name(lxw_workbook *self, const char *name)
|
|
|
1908
2147
|
return NULL;
|
|
1909
2148
|
}
|
|
1910
2149
|
|
|
2150
|
+
/*
|
|
2151
|
+
* Get a chartsheet object from its name.
|
|
2152
|
+
*/
|
|
2153
|
+
lxw_chartsheet *
|
|
2154
|
+
workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name)
|
|
2155
|
+
{
|
|
2156
|
+
lxw_chartsheet_name chartsheet_name;
|
|
2157
|
+
lxw_chartsheet_name *found;
|
|
2158
|
+
|
|
2159
|
+
if (!name)
|
|
2160
|
+
return NULL;
|
|
2161
|
+
|
|
2162
|
+
chartsheet_name.name = name;
|
|
2163
|
+
found = RB_FIND(lxw_chartsheet_names,
|
|
2164
|
+
self->chartsheet_names, &chartsheet_name);
|
|
2165
|
+
|
|
2166
|
+
if (found)
|
|
2167
|
+
return found->chartsheet;
|
|
2168
|
+
else
|
|
2169
|
+
return NULL;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
1911
2172
|
/*
|
|
1912
2173
|
* Validate the worksheet name based on Excel's rules.
|
|
1913
2174
|
*/
|
|
1914
2175
|
lxw_error
|
|
1915
|
-
|
|
2176
|
+
workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
|
|
1916
2177
|
{
|
|
1917
2178
|
/* Check the UTF-8 length of the worksheet name. */
|
|
1918
2179
|
if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX)
|
|
@@ -1922,9 +2183,66 @@ workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
|
|
|
1922
2183
|
if (strpbrk(sheetname, "[]:*?/\\"))
|
|
1923
2184
|
return LXW_ERROR_INVALID_SHEETNAME_CHARACTER;
|
|
1924
2185
|
|
|
2186
|
+
/* Check that the worksheet doesn't start or end with an apostrophe. */
|
|
2187
|
+
if (sheetname[0] == '\'' || sheetname[strlen(sheetname) - 1] == '\'')
|
|
2188
|
+
return LXW_ERROR_SHEETNAME_START_END_APOSTROPHE;
|
|
2189
|
+
|
|
2190
|
+
/* Check that the worksheet name isn't the reserved work "History". */
|
|
2191
|
+
if (lxw_strcasecmp(sheetname, "history") == 0)
|
|
2192
|
+
return LXW_ERROR_SHEETNAME_RESERVED;
|
|
2193
|
+
|
|
1925
2194
|
/* Check if the worksheet name is already in use. */
|
|
1926
2195
|
if (workbook_get_worksheet_by_name(self, sheetname))
|
|
1927
2196
|
return LXW_ERROR_SHEETNAME_ALREADY_USED;
|
|
1928
2197
|
|
|
2198
|
+
/* Check if the chartsheet name is already in use. */
|
|
2199
|
+
if (workbook_get_chartsheet_by_name(self, sheetname))
|
|
2200
|
+
return LXW_ERROR_SHEETNAME_ALREADY_USED;
|
|
2201
|
+
|
|
2202
|
+
return LXW_NO_ERROR;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
/*
|
|
2206
|
+
* Add a vbaProject binary to the Excel workbook.
|
|
2207
|
+
*/
|
|
2208
|
+
lxw_error
|
|
2209
|
+
workbook_add_vba_project(lxw_workbook *self, const char *filename)
|
|
2210
|
+
{
|
|
2211
|
+
FILE *filehandle;
|
|
2212
|
+
|
|
2213
|
+
if (!filename) {
|
|
2214
|
+
LXW_WARN("workbook_add_vba_project(): "
|
|
2215
|
+
"filename must be specified.");
|
|
2216
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
/* Check that the vbaProject file exists and can be opened. */
|
|
2220
|
+
filehandle = fopen(filename, "rb");
|
|
2221
|
+
if (!filehandle) {
|
|
2222
|
+
LXW_WARN_FORMAT1("workbook_add_vba_project(): "
|
|
2223
|
+
"file doesn't exist or can't be opened: %s.",
|
|
2224
|
+
filename);
|
|
2225
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
2226
|
+
}
|
|
2227
|
+
fclose(filehandle);
|
|
2228
|
+
|
|
2229
|
+
self->vba_project = lxw_strdup(filename);
|
|
2230
|
+
|
|
2231
|
+
return LXW_NO_ERROR;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
/*
|
|
2235
|
+
* Set the VBA name for the workbook.
|
|
2236
|
+
*/
|
|
2237
|
+
lxw_error
|
|
2238
|
+
workbook_set_vba_name(lxw_workbook *self, const char *name)
|
|
2239
|
+
{
|
|
2240
|
+
if (!name) {
|
|
2241
|
+
LXW_WARN("workbook_set_vba_name(): " "name must be specified.");
|
|
2242
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
self->vba_codename = lxw_strdup(name);
|
|
2246
|
+
|
|
1929
2247
|
return LXW_NO_ERROR;
|
|
1930
2248
|
}
|