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.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +2 -0
  3. data/.gitignore +7 -0
  4. data/.travis.yml +44 -0
  5. data/CHANGELOG.md +41 -1
  6. data/Dockerfile.test +16 -0
  7. data/Gemfile +5 -2
  8. data/Gemfile.lock +30 -23
  9. data/LICENSE +21 -0
  10. data/Makefile +13 -0
  11. data/README.md +177 -40
  12. data/Rakefile +16 -0
  13. data/appveyor.yml +25 -0
  14. data/benchmarks/1k_rows.rb +17 -4
  15. data/benchmarks/20k_rows.rb +4 -0
  16. data/benchmarks/auto_width.rb +37 -0
  17. data/benchmarks/init.rb +14 -2
  18. data/benchmarks/memory.rb +8 -0
  19. data/benchmarks/profiler.rb +27 -0
  20. data/benchmarks/write_value.rb +62 -0
  21. data/examples/example.rb +3 -4
  22. data/examples/example_align.rb +23 -0
  23. data/examples/example_auto_width.rb +26 -0
  24. data/examples/example_colors.rb +37 -0
  25. data/examples/example_filters.rb +36 -0
  26. data/examples/example_formula.rb +1 -5
  27. data/examples/example_hyperlink.rb +20 -0
  28. data/examples/example_image.rb +1 -1
  29. data/examples/example_styles.rb +27 -0
  30. data/examples/logo.png +0 -0
  31. data/ext/fast_excel/extconf.rb +3 -0
  32. data/ext/fast_excel/text_width_ext.c +460 -0
  33. data/fast_excel.gemspec +2 -3
  34. data/letters.html +114 -0
  35. data/lib/fast_excel.rb +455 -78
  36. data/lib/fast_excel/binding.rb +31 -21
  37. data/lib/fast_excel/binding/chart.rb +20 -1
  38. data/lib/fast_excel/binding/format.rb +11 -4
  39. data/lib/fast_excel/binding/workbook.rb +10 -2
  40. data/lib/fast_excel/binding/worksheet.rb +44 -27
  41. data/libxlsxwriter/.gitignore +1 -0
  42. data/libxlsxwriter/.indent.pro +8 -0
  43. data/libxlsxwriter/.travis.yml +12 -0
  44. data/libxlsxwriter/CMakeLists.txt +338 -0
  45. data/libxlsxwriter/CONTRIBUTING.md +1 -1
  46. data/libxlsxwriter/Changes.txt +162 -0
  47. data/libxlsxwriter/LICENSE.txt +65 -4
  48. data/libxlsxwriter/Makefile +33 -11
  49. data/libxlsxwriter/Readme.md +3 -1
  50. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +2 -1
  51. data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
  52. data/libxlsxwriter/include/xlsxwriter.h +2 -2
  53. data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
  54. data/libxlsxwriter/include/xlsxwriter/chart.h +164 -13
  55. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
  56. data/libxlsxwriter/include/xlsxwriter/common.h +35 -6
  57. data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
  58. data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
  59. data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
  60. data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
  61. data/libxlsxwriter/include/xlsxwriter/format.h +8 -8
  62. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  63. data/libxlsxwriter/include/xlsxwriter/packager.h +18 -8
  64. data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
  65. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
  66. data/libxlsxwriter/include/xlsxwriter/styles.h +10 -5
  67. data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
  68. data/libxlsxwriter/include/xlsxwriter/utility.h +35 -5
  69. data/libxlsxwriter/include/xlsxwriter/workbook.h +234 -57
  70. data/libxlsxwriter/include/xlsxwriter/worksheet.h +780 -91
  71. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
  72. data/libxlsxwriter/libxlsxwriter.podspec +4 -2
  73. data/libxlsxwriter/src/Makefile +31 -6
  74. data/libxlsxwriter/src/app.c +2 -2
  75. data/libxlsxwriter/src/chart.c +116 -23
  76. data/libxlsxwriter/src/chartsheet.c +508 -0
  77. data/libxlsxwriter/src/content_types.c +12 -4
  78. data/libxlsxwriter/src/core.c +11 -11
  79. data/libxlsxwriter/src/custom.c +3 -3
  80. data/libxlsxwriter/src/drawing.c +114 -17
  81. data/libxlsxwriter/src/format.c +5 -5
  82. data/libxlsxwriter/src/hash_table.c +1 -1
  83. data/libxlsxwriter/src/packager.c +378 -61
  84. data/libxlsxwriter/src/relationships.c +2 -2
  85. data/libxlsxwriter/src/shared_strings.c +18 -4
  86. data/libxlsxwriter/src/styles.c +59 -12
  87. data/libxlsxwriter/src/theme.c +2 -2
  88. data/libxlsxwriter/src/utility.c +93 -6
  89. data/libxlsxwriter/src/workbook.c +379 -61
  90. data/libxlsxwriter/src/worksheet.c +1240 -174
  91. data/libxlsxwriter/src/xmlwriter.c +18 -9
  92. data/libxlsxwriter/third_party/minizip/Makefile +6 -1
  93. data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
  94. data/libxlsxwriter/third_party/minizip/zip.c +2 -0
  95. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
  96. data/libxlsxwriter/version.txt +1 -1
  97. data/test/auto_width_test.rb +19 -0
  98. data/test/date_test.rb +34 -0
  99. data/test/format_test.rb +179 -0
  100. data/test/reopen_test.rb +22 -0
  101. data/test/test_helper.rb +23 -4
  102. data/test/text_width_test.rb +80 -0
  103. data/test/tmpfile_test.rb +1 -0
  104. data/test/validations_test.rb +47 -0
  105. data/test/worksheet_test.rb +129 -0
  106. metadata +34 -5
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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
- _write_si(self, sst_element->string);
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);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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, uint16_t font_size)
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
- LXW_PUSH_ATTRIBUTES_INT("val", font_size);
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
- lxw_xml_empty_tag(self->file, "name", &attributes);
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
- lxw_xml_start_tag(self->file, "font", NULL);
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
- lxw_xml_end_tag(self->file, "font");
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");
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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/utility.h"
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
- "Zlib error with a file operation while creating xlsx file.",
24
- "Zlib error when adding sub file to xlsx file.",
25
- "Zlib error when closing xlsx file.",
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 contains invalid Excel character: '[]:*?/\\'",
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-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
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 _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2);
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
- LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers,
19
- _name_cmp);
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
- * Comparator for the worksheet names structure red/black tree.
38
+ * Comparators for the sheet names structure red/black tree.
34
39
  */
35
40
  STATIC int
36
- _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2)
41
+ _worksheet_name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2)
37
42
  {
38
- return strcmp(name1->name, name2->name);
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
- lxw_worksheet *worksheet;
95
+ lxw_sheet *sheet;
85
96
  struct lxw_worksheet_name *worksheet_name;
86
- struct lxw_worksheet_name *next_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 worksheets in the workbook. */
101
- if (workbook->worksheets) {
102
- while (!STAILQ_EMPTY(workbook->worksheets)) {
103
- worksheet = STAILQ_FIRST(workbook->worksheets);
104
- STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers);
105
- lxw_worksheet_free(worksheet);
106
- }
107
- free(workbook->worksheets);
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 = next_name) {
178
+ worksheet_name; worksheet_name = next_worksheet_name) {
157
179
 
158
- next_name = RB_NEXT(lxw_worksheet_names,
159
- workbook->worksheet_name, worksheet_name);
160
- RB_REMOVE(lxw_worksheet_names,
161
- workbook->worksheet_names, worksheet_name);
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
- num_format = format->num_format;
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
- strlen(num_format));
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, strlen(num_format));
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(worksheet, self->worksheets, list_pointers) {
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
- uint16_t chart_ref_id = 0;
841
- uint16_t image_ref_id = 0;
842
- uint16_t drawing_id = 0;
843
-
844
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
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(worksheet, self->worksheets, list_pointers) {
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(worksheet, self->worksheets, list_pointers) {
1176
- _write_sheet(self, worksheet->name, worksheet->index + 1,
1177
- worksheet->hidden);
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
- lxw_worksheet *worksheet;
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->num_sheets + 1);
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 = workbook_validate_worksheet_name(self, init_data.name);
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
- self->num_sheets++;
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
- worksheet = STAILQ_FIRST(self->worksheets);
1540
- worksheet->selected = 1;
1541
- worksheet->hidden = 0;
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(worksheet, self->worksheets, list_pointers) {
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, self->options.tmpdir);
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
- "Error = %s\n", self->filename, strerror(errno));
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
- "Error = %s\n", self->filename, strerror(errno));
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 zlib. */
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
- "Zlib error while creating xlsx file '%s'. "
1589
- "Error = %s\n", self->filename, strerror(errno));
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
- "Zlib error adding file to xlsx file '%s'.\n",
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
- "Zlib error closing xlsx file '%s'.\n", self->filename);
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
- workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
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
  }