fast_excel 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.dockerignore +2 -0
- data/.gitignore +7 -0
- data/.travis.yml +44 -0
- data/CHANGELOG.md +41 -1
- data/Dockerfile.test +16 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +30 -23
- data/LICENSE +21 -0
- data/Makefile +13 -0
- data/README.md +177 -40
- data/Rakefile +16 -0
- data/appveyor.yml +25 -0
- 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 -4
- data/examples/example_align.rb +23 -0
- data/examples/example_auto_width.rb +26 -0
- data/examples/example_colors.rb +37 -0
- data/examples/example_filters.rb +36 -0
- data/examples/example_formula.rb +1 -5
- data/examples/example_hyperlink.rb +20 -0
- data/examples/example_image.rb +1 -1
- data/examples/example_styles.rb +27 -0
- data/examples/logo.png +0 -0
- data/ext/fast_excel/extconf.rb +3 -0
- data/ext/fast_excel/text_width_ext.c +460 -0
- data/fast_excel.gemspec +2 -3
- data/letters.html +114 -0
- data/lib/fast_excel.rb +455 -78
- data/lib/fast_excel/binding.rb +31 -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 +179 -0
- data/test/reopen_test.rb +22 -0
- data/test/test_helper.rb +23 -4
- 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 +129 -0
- metadata +34 -5
@@ -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
|
}
|