fast_excel 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +2 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +18 -6
  5. data/CHANGELOG.md +14 -1
  6. data/Dockerfile.test +16 -0
  7. data/Gemfile +1 -1
  8. data/Gemfile.lock +21 -21
  9. data/Makefile +13 -2
  10. data/README.md +148 -38
  11. data/Rakefile +2 -0
  12. data/examples/example.rb +3 -3
  13. data/examples/example_filters.rb +36 -0
  14. data/examples/example_formula.rb +1 -3
  15. data/examples/example_hyperlink.rb +20 -0
  16. data/fast_excel.gemspec +1 -1
  17. data/lib/fast_excel.rb +36 -12
  18. data/lib/fast_excel/binding.rb +31 -21
  19. data/lib/fast_excel/binding/chart.rb +20 -1
  20. data/lib/fast_excel/binding/workbook.rb +10 -2
  21. data/lib/fast_excel/binding/worksheet.rb +44 -27
  22. data/libxlsxwriter/.gitignore +1 -0
  23. data/libxlsxwriter/.indent.pro +5 -0
  24. data/libxlsxwriter/CMakeLists.txt +1 -11
  25. data/libxlsxwriter/CONTRIBUTING.md +1 -1
  26. data/libxlsxwriter/Changes.txt +84 -0
  27. data/libxlsxwriter/LICENSE.txt +1 -1
  28. data/libxlsxwriter/Makefile +7 -5
  29. data/libxlsxwriter/Readme.md +1 -1
  30. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +1 -0
  31. data/libxlsxwriter/include/xlsxwriter.h +2 -2
  32. data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
  33. data/libxlsxwriter/include/xlsxwriter/chart.h +56 -6
  34. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
  35. data/libxlsxwriter/include/xlsxwriter/common.h +27 -6
  36. data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
  37. data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
  38. data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
  39. data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
  40. data/libxlsxwriter/include/xlsxwriter/format.h +3 -3
  41. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  42. data/libxlsxwriter/include/xlsxwriter/packager.h +13 -8
  43. data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
  44. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
  45. data/libxlsxwriter/include/xlsxwriter/styles.h +9 -4
  46. data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
  47. data/libxlsxwriter/include/xlsxwriter/utility.h +26 -2
  48. data/libxlsxwriter/include/xlsxwriter/workbook.h +232 -55
  49. data/libxlsxwriter/include/xlsxwriter/worksheet.h +264 -53
  50. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +3 -1
  51. data/libxlsxwriter/libxlsxwriter.podspec +1 -1
  52. data/libxlsxwriter/src/Makefile +3 -3
  53. data/libxlsxwriter/src/app.c +2 -2
  54. data/libxlsxwriter/src/chart.c +41 -5
  55. data/libxlsxwriter/src/chartsheet.c +508 -0
  56. data/libxlsxwriter/src/content_types.c +12 -4
  57. data/libxlsxwriter/src/core.c +2 -2
  58. data/libxlsxwriter/src/custom.c +2 -2
  59. data/libxlsxwriter/src/drawing.c +114 -17
  60. data/libxlsxwriter/src/format.c +3 -3
  61. data/libxlsxwriter/src/hash_table.c +1 -1
  62. data/libxlsxwriter/src/packager.c +369 -65
  63. data/libxlsxwriter/src/relationships.c +2 -2
  64. data/libxlsxwriter/src/shared_strings.c +18 -4
  65. data/libxlsxwriter/src/styles.c +56 -9
  66. data/libxlsxwriter/src/theme.c +2 -2
  67. data/libxlsxwriter/src/utility.c +53 -6
  68. data/libxlsxwriter/src/workbook.c +372 -56
  69. data/libxlsxwriter/src/worksheet.c +425 -76
  70. data/libxlsxwriter/src/xmlwriter.c +17 -8
  71. data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
  72. data/libxlsxwriter/third_party/minizip/zip.c +2 -0
  73. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
  74. data/libxlsxwriter/version.txt +1 -1
  75. data/test/tmpfile_test.rb +1 -0
  76. data/test/validations_test.rb +26 -6
  77. data/test/worksheet_test.rb +43 -0
  78. metadata +9 -6
  79. data/libxlsxwriter/.drone.yml +0 -27
  80. data/libxlsxwriter/appveyor.yml +0 -65
  81. data/libxlsxwriter/cmake/FindZLIB.cmake +0 -123
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2018, 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-2018, 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-2018, 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");
@@ -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);
@@ -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-2018, 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-2018, 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,20 @@ 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.",
31
37
  "Parameter exceeds Excel's limit of 32 characters.",
32
38
  "Parameter exceeds Excel's limit of 128 characters.",
33
39
  "Parameter exceeds Excel's limit of 255 characters.",
@@ -553,3 +559,44 @@ lxw_sprintf_dbl(char *data, double number)
553
559
  return 0;
554
560
  }
555
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-2018, 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
 
@@ -432,7 +473,7 @@ _prepare_num_formats(lxw_workbook *self)
432
473
  num_format_index = calloc(1, sizeof(uint16_t));
433
474
  *num_format_index = index;
434
475
  format->num_format_index = index;
435
- lxw_insert_hash_element(num_formats, num_format,
476
+ lxw_insert_hash_element(num_formats, format->num_format,
436
477
  num_format_index,
437
478
  LXW_FORMAT_FIELD_LEN);
438
479
  index++;
@@ -496,6 +537,7 @@ _store_defined_name(lxw_workbook *self, const char *name,
496
537
  const char *app_name, const char *formula, int16_t index,
497
538
  uint8_t hidden)
498
539
  {
540
+ lxw_sheet *sheet;
499
541
  lxw_worksheet *worksheet;
500
542
  lxw_defined_name *defined_name;
501
543
  lxw_defined_name *list_defined_name;
@@ -546,7 +588,12 @@ _store_defined_name(lxw_workbook *self, const char *name,
546
588
  worksheet_name[strlen(worksheet_name) - 1] = '\0';
547
589
 
548
590
  /* Search for worksheet name to get the equivalent worksheet index. */
549
- 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
+
550
597
  if (strcmp(worksheet_name, worksheet->name) == 0) {
551
598
  defined_name->index = worksheet->index;
552
599
  lxw_strcpy(defined_name->normalised_sheetname,
@@ -837,13 +884,23 @@ _add_chart_cache_data(lxw_workbook *self)
837
884
  STATIC void
838
885
  _prepare_drawings(lxw_workbook *self)
839
886
  {
887
+ lxw_sheet *sheet;
840
888
  lxw_worksheet *worksheet;
841
889
  lxw_image_options *image_options;
842
- uint16_t chart_ref_id = 0;
843
- uint16_t image_ref_id = 0;
844
- uint16_t drawing_id = 0;
845
-
846
- 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
+ }
847
904
 
848
905
  if (STAILQ_EMPTY(worksheet->image_data)
849
906
  && STAILQ_EMPTY(worksheet->chart_data))
@@ -854,7 +911,7 @@ _prepare_drawings(lxw_workbook *self)
854
911
  STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
855
912
  chart_ref_id++;
856
913
  lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id,
857
- image_options);
914
+ image_options, is_chartsheet);
858
915
  if (image_options->chart)
859
916
  STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart,
860
917
  ordered_list_pointers);
@@ -889,14 +946,18 @@ STATIC void
889
946
  _prepare_defined_names(lxw_workbook *self)
890
947
  {
891
948
  lxw_worksheet *worksheet;
949
+ lxw_sheet *sheet;
892
950
  char app_name[LXW_DEFINED_NAME_LENGTH];
893
951
  char range[LXW_DEFINED_NAME_LENGTH];
894
952
  char area[LXW_MAX_CELL_RANGE_LENGTH];
895
953
  char first_col[8];
896
954
  char last_col[8];
897
955
 
898
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
899
-
956
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
957
+ if (sheet->is_chartsheet)
958
+ continue;
959
+ else
960
+ worksheet = sheet->u.worksheet;
900
961
  /*
901
962
  * Check for autofilter settings and store them.
902
963
  */
@@ -1076,6 +1137,10 @@ _write_file_version(lxw_workbook *self)
1076
1137
  LXW_PUSH_ATTRIBUTES_STR("lowestEdited", "4");
1077
1138
  LXW_PUSH_ATTRIBUTES_STR("rupBuild", "4505");
1078
1139
 
1140
+ if (self->vba_project)
1141
+ LXW_PUSH_ATTRIBUTES_STR("codeName",
1142
+ "{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}");
1143
+
1079
1144
  lxw_xml_empty_tag(self->file, "fileVersion", &attributes);
1080
1145
 
1081
1146
  LXW_FREE_ATTRIBUTES();
@@ -1091,6 +1156,10 @@ _write_workbook_pr(lxw_workbook *self)
1091
1156
  struct xml_attribute *attribute;
1092
1157
 
1093
1158
  LXW_INIT_ATTRIBUTES();
1159
+
1160
+ if (self->vba_codename)
1161
+ LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename);
1162
+
1094
1163
  LXW_PUSH_ATTRIBUTES_STR("defaultThemeVersion", "124226");
1095
1164
 
1096
1165
  lxw_xml_empty_tag(self->file, "workbookPr", &attributes);
@@ -1170,13 +1239,23 @@ _write_sheet(lxw_workbook *self, const char *name, uint32_t sheet_id,
1170
1239
  STATIC void
1171
1240
  _write_sheets(lxw_workbook *self)
1172
1241
  {
1242
+ lxw_sheet *sheet;
1173
1243
  lxw_worksheet *worksheet;
1244
+ lxw_chartsheet *chartsheet;
1174
1245
 
1175
1246
  lxw_xml_start_tag(self->file, "sheets", NULL);
1176
1247
 
1177
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
1178
- _write_sheet(self, worksheet->name, worksheet->index + 1,
1179
- 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
+ }
1180
1259
  }
1181
1260
 
1182
1261
  lxw_xml_end_tag(self->file, "sheets");
@@ -1224,9 +1303,6 @@ _write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name)
1224
1303
  LXW_FREE_ATTRIBUTES();
1225
1304
  }
1226
1305
 
1227
- /*
1228
- * Write the <definedNames> element.
1229
- */
1230
1306
  STATIC void
1231
1307
  _write_defined_names(lxw_workbook *self)
1232
1308
  {
@@ -1330,16 +1406,32 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1330
1406
  GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error);
1331
1407
  workbook->filename = lxw_strdup(filename);
1332
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
+
1333
1414
  /* Add the worksheets list. */
1334
1415
  workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets));
1335
1416
  GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error);
1336
1417
  STAILQ_INIT(workbook->worksheets);
1337
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
+
1338
1424
  /* Add the worksheet names tree. */
1339
1425
  workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names));
1340
1426
  GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error);
1341
1427
  RB_INIT(workbook->worksheet_names);
1342
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
+
1343
1435
  /* Add the charts list. */
1344
1436
  workbook->charts = calloc(1, sizeof(struct lxw_charts));
1345
1437
  GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
@@ -1388,6 +1480,7 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1388
1480
  if (options) {
1389
1481
  workbook->options.constant_memory = options->constant_memory;
1390
1482
  workbook->options.tmpdir = lxw_strdup(options->tmpdir);
1483
+ workbook->options.use_zip64 = options->use_zip64;
1391
1484
  }
1392
1485
 
1393
1486
  return workbook;
@@ -1404,7 +1497,8 @@ mem_error:
1404
1497
  lxw_worksheet *
1405
1498
  workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1406
1499
  {
1407
- lxw_worksheet *worksheet;
1500
+ lxw_sheet *sheet = NULL;
1501
+ lxw_worksheet *worksheet = NULL;
1408
1502
  lxw_worksheet_name *worksheet_name = NULL;
1409
1503
  lxw_error error;
1410
1504
  lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -1421,13 +1515,13 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1421
1515
  GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
1422
1516
 
1423
1517
  lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
1424
- self->num_sheets + 1);
1518
+ self->num_worksheets + 1);
1425
1519
  init_data.name = new_name;
1426
1520
  init_data.quoted_name = lxw_strdup(new_name);
1427
1521
  }
1428
1522
 
1429
1523
  /* Check that the worksheet name is valid. */
1430
- error = workbook_validate_worksheet_name(self, init_data.name);
1524
+ error = workbook_validate_sheet_name(self, init_data.name);
1431
1525
  if (error) {
1432
1526
  LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has "
1433
1527
  "error: %s", init_data.name, lxw_strerror(error));
@@ -1451,9 +1545,19 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1451
1545
  worksheet = lxw_worksheet_new(&init_data);
1452
1546
  GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
1453
1547
 
1454
- self->num_sheets++;
1548
+ /* Add it to the worksheet list. */
1549
+ self->num_worksheets++;
1455
1550
  STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers);
1456
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
+
1457
1561
  /* Store the worksheet so we can look it up by name. */
1458
1562
  worksheet_name->name = init_data.name;
1459
1563
  worksheet_name->worksheet = worksheet;
@@ -1465,6 +1569,91 @@ mem_error:
1465
1569
  free(init_data.name);
1466
1570
  free(init_data.quoted_name);
1467
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);
1468
1657
  return NULL;
1469
1658
  }
1470
1659
 
@@ -1528,6 +1717,7 @@ workbook_default_format(lxw_workbook *self)
1528
1717
  lxw_error
1529
1718
  workbook_close(lxw_workbook *self)
1530
1719
  {
1720
+ lxw_sheet *sheet = NULL;
1531
1721
  lxw_worksheet *worksheet = NULL;
1532
1722
  lxw_packager *packager = NULL;
1533
1723
  lxw_error error = LXW_NO_ERROR;
@@ -1538,17 +1728,41 @@ workbook_close(lxw_workbook *self)
1538
1728
 
1539
1729
  /* Ensure that at least one worksheet has been selected. */
1540
1730
  if (self->active_sheet == 0) {
1541
- worksheet = STAILQ_FIRST(self->worksheets);
1542
- worksheet->selected = 1;
1543
- 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
+ }
1544
1737
  }
1545
1738
 
1546
1739
  /* Set the active sheet. */
1547
- 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
+
1548
1746
  if (worksheet->index == self->active_sheet)
1549
1747
  worksheet->active = 1;
1550
1748
  }
1551
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
+
1552
1766
  /* Set the defined names for the worksheets such as Print Titles. */
1553
1767
  _prepare_defined_names(self);
1554
1768
 
@@ -1559,13 +1773,15 @@ workbook_close(lxw_workbook *self)
1559
1773
  _add_chart_cache_data(self);
1560
1774
 
1561
1775
  /* Create a packager object to assemble sub-elements into a zip file. */
1562
- 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);
1563
1779
 
1564
1780
  /* If the packager fails it is generally due to a zip permission error. */
1565
1781
  if (packager == NULL) {
1566
1782
  fprintf(stderr, "[ERROR] workbook_close(): "
1567
1783
  "Error creating '%s'. "
1568
- "Error = %s\n", self->filename, strerror(errno));
1784
+ "System error = %s\n", self->filename, strerror(errno));
1569
1785
 
1570
1786
  error = LXW_ERROR_CREATING_XLSX_FILE;
1571
1787
  goto mem_error;
@@ -1581,26 +1797,47 @@ workbook_close(lxw_workbook *self)
1581
1797
  if (error == LXW_ERROR_CREATING_TMPFILE) {
1582
1798
  fprintf(stderr, "[ERROR] workbook_close(): "
1583
1799
  "Error creating tmpfile(s) to assemble '%s'. "
1584
- "Error = %s\n", self->filename, strerror(errno));
1800
+ "System error = %s\n", self->filename, strerror(errno));
1585
1801
  }
1586
1802
 
1587
- /* 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. */
1588
1804
  if (error == LXW_ERROR_ZIP_FILE_OPERATION) {
1589
1805
  fprintf(stderr, "[ERROR] workbook_close(): "
1590
- "Zlib error while creating xlsx file '%s'. "
1591
- "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));
1592
1830
  }
1593
1831
 
1594
1832
  /* The next 2 error conditions don't set errno. */
1595
1833
  if (error == LXW_ERROR_ZIP_FILE_ADD) {
1596
1834
  fprintf(stderr, "[ERROR] workbook_close(): "
1597
- "Zlib error adding file to xlsx file '%s'.\n",
1598
- self->filename);
1835
+ "Zip error adding file to xlsx file '%s'.\n", self->filename);
1599
1836
  }
1600
1837
 
1601
1838
  if (error == LXW_ERROR_ZIP_CLOSE) {
1602
1839
  fprintf(stderr, "[ERROR] workbook_close(): "
1603
- "Zlib error closing xlsx file '%s'.\n", self->filename);
1840
+ "Zip error closing xlsx file '%s'.\n", self->filename);
1604
1841
  }
1605
1842
 
1606
1843
  mem_error:
@@ -1910,11 +2147,33 @@ workbook_get_worksheet_by_name(lxw_workbook *self, const char *name)
1910
2147
  return NULL;
1911
2148
  }
1912
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
+
1913
2172
  /*
1914
2173
  * Validate the worksheet name based on Excel's rules.
1915
2174
  */
1916
2175
  lxw_error
1917
- workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
2176
+ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
1918
2177
  {
1919
2178
  /* Check the UTF-8 length of the worksheet name. */
1920
2179
  if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX)
@@ -1924,9 +2183,66 @@ workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
1924
2183
  if (strpbrk(sheetname, "[]:*?/\\"))
1925
2184
  return LXW_ERROR_INVALID_SHEETNAME_CHARACTER;
1926
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
+
1927
2194
  /* Check if the worksheet name is already in use. */
1928
2195
  if (workbook_get_worksheet_by_name(self, sheetname))
1929
2196
  return LXW_ERROR_SHEETNAME_ALREADY_USED;
1930
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
+
1931
2247
  return LXW_NO_ERROR;
1932
2248
  }