xlsxwriter 0.0.5 → 0.0.6

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter.h +1 -1
  3. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chart.h +55 -5
  4. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
  5. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/common.h +6 -0
  6. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/content_types.h +2 -0
  7. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/drawing.h +1 -0
  8. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/format.h +1 -1
  9. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/shared_strings.h +3 -1
  10. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/styles.h +7 -2
  11. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/utility.h +16 -0
  12. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/workbook.h +122 -24
  13. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/worksheet.h +236 -48
  14. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/xmlwriter.h +2 -0
  15. data/ext/xlsxwriter/libxlsxwriter/src/chart.c +40 -4
  16. data/ext/xlsxwriter/libxlsxwriter/src/chartsheet.c +508 -0
  17. data/ext/xlsxwriter/libxlsxwriter/src/content_types.c +10 -0
  18. data/ext/xlsxwriter/libxlsxwriter/src/drawing.c +100 -3
  19. data/ext/xlsxwriter/libxlsxwriter/src/packager.c +252 -30
  20. data/ext/xlsxwriter/libxlsxwriter/src/shared_strings.c +16 -2
  21. data/ext/xlsxwriter/libxlsxwriter/src/styles.c +54 -7
  22. data/ext/xlsxwriter/libxlsxwriter/src/utility.c +43 -1
  23. data/ext/xlsxwriter/libxlsxwriter/src/workbook.c +254 -41
  24. data/ext/xlsxwriter/libxlsxwriter/src/worksheet.c +381 -65
  25. data/ext/xlsxwriter/libxlsxwriter/src/xmlwriter.c +16 -7
  26. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
  27. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/zip.c +2 -0
  28. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
  29. data/ext/xlsxwriter/workbook.c +9 -6
  30. data/lib/xlsxwriter/version.rb +1 -1
  31. metadata +5 -4
@@ -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);
@@ -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
  *
@@ -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");
@@ -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,6 +20,7 @@ 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
+ "Error reading a tmpfile.",
23
24
  "Zlib error with a file operation while creating xlsx file.",
24
25
  "Zlib error when adding sub file to xlsx file.",
25
26
  "Zlib error when closing xlsx file.",
@@ -553,3 +554,44 @@ lxw_sprintf_dbl(char *data, double number)
553
554
  return 0;
554
555
  }
555
556
  #endif
557
+
558
+ /*
559
+ * Retrieve runtime library version
560
+ */
561
+ const char *
562
+ lxw_version(void)
563
+ {
564
+ return LXW_VERSION;
565
+ }
566
+
567
+ /*
568
+ * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz
569
+ * of OpenOffice.
570
+ */
571
+ uint16_t
572
+ lxw_hash_password(const char *password)
573
+ {
574
+ size_t count;
575
+ uint8_t i;
576
+ uint16_t hash = 0x0000;
577
+
578
+ count = strlen(password);
579
+
580
+ for (i = 0; i < count; i++) {
581
+ uint32_t low_15;
582
+ uint32_t high_15;
583
+ uint32_t letter = password[i] << (i + 1);
584
+
585
+ low_15 = letter & 0x7fff;
586
+ high_15 = letter & (0x7fff << 15);
587
+ high_15 = high_15 >> 15;
588
+ letter = low_15 | high_15;
589
+
590
+ hash ^= letter;
591
+ }
592
+
593
+ hash ^= count;
594
+ hash ^= 0xCE4B;
595
+
596
+ return hash;
597
+ }
@@ -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,10 +35,16 @@ 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)
42
+ {
43
+ return strcmp(name1->name, name2->name);
44
+ }
45
+
46
+ STATIC int
47
+ _chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2)
37
48
  {
38
49
  return strcmp(name1->name, name2->name);
39
50
  }
@@ -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);
108
117
 
118
+ if (sheet->is_chartsheet)
119
+ lxw_chartsheet_free(sheet->u.chartsheet);
120
+ else
121
+ lxw_worksheet_free(sheet->u.worksheet);
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,18 +175,35 @@ 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);
@@ -432,7 +471,7 @@ _prepare_num_formats(lxw_workbook *self)
432
471
  num_format_index = calloc(1, sizeof(uint16_t));
433
472
  *num_format_index = index;
434
473
  format->num_format_index = index;
435
- lxw_insert_hash_element(num_formats, num_format,
474
+ lxw_insert_hash_element(num_formats, format->num_format,
436
475
  num_format_index,
437
476
  LXW_FORMAT_FIELD_LEN);
438
477
  index++;
@@ -496,6 +535,7 @@ _store_defined_name(lxw_workbook *self, const char *name,
496
535
  const char *app_name, const char *formula, int16_t index,
497
536
  uint8_t hidden)
498
537
  {
538
+ lxw_sheet *sheet;
499
539
  lxw_worksheet *worksheet;
500
540
  lxw_defined_name *defined_name;
501
541
  lxw_defined_name *list_defined_name;
@@ -546,7 +586,12 @@ _store_defined_name(lxw_workbook *self, const char *name,
546
586
  worksheet_name[strlen(worksheet_name) - 1] = '\0';
547
587
 
548
588
  /* Search for worksheet name to get the equivalent worksheet index. */
549
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
589
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
590
+ if (sheet->is_chartsheet)
591
+ continue;
592
+ else
593
+ worksheet = sheet->u.worksheet;
594
+
550
595
  if (strcmp(worksheet_name, worksheet->name) == 0) {
551
596
  defined_name->index = worksheet->index;
552
597
  lxw_strcpy(defined_name->normalised_sheetname,
@@ -837,13 +882,23 @@ _add_chart_cache_data(lxw_workbook *self)
837
882
  STATIC void
838
883
  _prepare_drawings(lxw_workbook *self)
839
884
  {
885
+ lxw_sheet *sheet;
840
886
  lxw_worksheet *worksheet;
841
887
  lxw_image_options *image_options;
842
888
  uint16_t chart_ref_id = 0;
843
889
  uint16_t image_ref_id = 0;
844
890
  uint16_t drawing_id = 0;
891
+ uint8_t is_chartsheet;
845
892
 
846
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
893
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
894
+ if (sheet->is_chartsheet) {
895
+ worksheet = sheet->u.chartsheet->worksheet;
896
+ is_chartsheet = LXW_TRUE;
897
+ }
898
+ else {
899
+ worksheet = sheet->u.worksheet;
900
+ is_chartsheet = LXW_FALSE;
901
+ }
847
902
 
848
903
  if (STAILQ_EMPTY(worksheet->image_data)
849
904
  && STAILQ_EMPTY(worksheet->chart_data))
@@ -854,7 +909,7 @@ _prepare_drawings(lxw_workbook *self)
854
909
  STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
855
910
  chart_ref_id++;
856
911
  lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id,
857
- image_options);
912
+ image_options, is_chartsheet);
858
913
  if (image_options->chart)
859
914
  STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart,
860
915
  ordered_list_pointers);
@@ -889,14 +944,18 @@ STATIC void
889
944
  _prepare_defined_names(lxw_workbook *self)
890
945
  {
891
946
  lxw_worksheet *worksheet;
947
+ lxw_sheet *sheet;
892
948
  char app_name[LXW_DEFINED_NAME_LENGTH];
893
949
  char range[LXW_DEFINED_NAME_LENGTH];
894
950
  char area[LXW_MAX_CELL_RANGE_LENGTH];
895
951
  char first_col[8];
896
952
  char last_col[8];
897
953
 
898
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
899
-
954
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
955
+ if (sheet->is_chartsheet)
956
+ continue;
957
+ else
958
+ worksheet = sheet->u.worksheet;
900
959
  /*
901
960
  * Check for autofilter settings and store them.
902
961
  */
@@ -1170,13 +1229,23 @@ _write_sheet(lxw_workbook *self, const char *name, uint32_t sheet_id,
1170
1229
  STATIC void
1171
1230
  _write_sheets(lxw_workbook *self)
1172
1231
  {
1232
+ lxw_sheet *sheet;
1173
1233
  lxw_worksheet *worksheet;
1234
+ lxw_chartsheet *chartsheet;
1174
1235
 
1175
1236
  lxw_xml_start_tag(self->file, "sheets", NULL);
1176
1237
 
1177
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
1178
- _write_sheet(self, worksheet->name, worksheet->index + 1,
1179
- worksheet->hidden);
1238
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1239
+ if (sheet->is_chartsheet) {
1240
+ chartsheet = sheet->u.chartsheet;
1241
+ _write_sheet(self, chartsheet->name, chartsheet->index + 1,
1242
+ chartsheet->hidden);
1243
+ }
1244
+ else {
1245
+ worksheet = sheet->u.worksheet;
1246
+ _write_sheet(self, worksheet->name, worksheet->index + 1,
1247
+ worksheet->hidden);
1248
+ }
1180
1249
  }
1181
1250
 
1182
1251
  lxw_xml_end_tag(self->file, "sheets");
@@ -1224,9 +1293,6 @@ _write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name)
1224
1293
  LXW_FREE_ATTRIBUTES();
1225
1294
  }
1226
1295
 
1227
- /*
1228
- * Write the <definedNames> element.
1229
- */
1230
1296
  STATIC void
1231
1297
  _write_defined_names(lxw_workbook *self)
1232
1298
  {
@@ -1330,16 +1396,32 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1330
1396
  GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error);
1331
1397
  workbook->filename = lxw_strdup(filename);
1332
1398
 
1399
+ /* Add the sheets list. */
1400
+ workbook->sheets = calloc(1, sizeof(struct lxw_sheets));
1401
+ GOTO_LABEL_ON_MEM_ERROR(workbook->sheets, mem_error);
1402
+ STAILQ_INIT(workbook->sheets);
1403
+
1333
1404
  /* Add the worksheets list. */
1334
1405
  workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets));
1335
1406
  GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error);
1336
1407
  STAILQ_INIT(workbook->worksheets);
1337
1408
 
1409
+ /* Add the chartsheets list. */
1410
+ workbook->chartsheets = calloc(1, sizeof(struct lxw_chartsheets));
1411
+ GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheets, mem_error);
1412
+ STAILQ_INIT(workbook->chartsheets);
1413
+
1338
1414
  /* Add the worksheet names tree. */
1339
1415
  workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names));
1340
1416
  GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error);
1341
1417
  RB_INIT(workbook->worksheet_names);
1342
1418
 
1419
+ /* Add the chartsheet names tree. */
1420
+ workbook->chartsheet_names = calloc(1,
1421
+ sizeof(struct lxw_chartsheet_names));
1422
+ GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error);
1423
+ RB_INIT(workbook->chartsheet_names);
1424
+
1343
1425
  /* Add the charts list. */
1344
1426
  workbook->charts = calloc(1, sizeof(struct lxw_charts));
1345
1427
  GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
@@ -1404,7 +1486,8 @@ mem_error:
1404
1486
  lxw_worksheet *
1405
1487
  workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1406
1488
  {
1407
- lxw_worksheet *worksheet;
1489
+ lxw_sheet *sheet = NULL;
1490
+ lxw_worksheet *worksheet = NULL;
1408
1491
  lxw_worksheet_name *worksheet_name = NULL;
1409
1492
  lxw_error error;
1410
1493
  lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -1421,13 +1504,13 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1421
1504
  GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
1422
1505
 
1423
1506
  lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
1424
- self->num_sheets + 1);
1507
+ self->num_worksheets + 1);
1425
1508
  init_data.name = new_name;
1426
1509
  init_data.quoted_name = lxw_strdup(new_name);
1427
1510
  }
1428
1511
 
1429
1512
  /* Check that the worksheet name is valid. */
1430
- error = workbook_validate_worksheet_name(self, init_data.name);
1513
+ error = workbook_validate_sheet_name(self, init_data.name);
1431
1514
  if (error) {
1432
1515
  LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has "
1433
1516
  "error: %s", init_data.name, lxw_strerror(error));
@@ -1451,9 +1534,19 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1451
1534
  worksheet = lxw_worksheet_new(&init_data);
1452
1535
  GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
1453
1536
 
1454
- self->num_sheets++;
1537
+ /* Add it to the worksheet list. */
1538
+ self->num_worksheets++;
1455
1539
  STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers);
1456
1540
 
1541
+ /* Create a new sheet object. */
1542
+ sheet = calloc(1, sizeof(lxw_sheet));
1543
+ GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error);
1544
+ sheet->u.worksheet = worksheet;
1545
+
1546
+ /* Add it to the worksheet list. */
1547
+ self->num_sheets++;
1548
+ STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers);
1549
+
1457
1550
  /* Store the worksheet so we can look it up by name. */
1458
1551
  worksheet_name->name = init_data.name;
1459
1552
  worksheet_name->worksheet = worksheet;
@@ -1465,6 +1558,91 @@ mem_error:
1465
1558
  free(init_data.name);
1466
1559
  free(init_data.quoted_name);
1467
1560
  free(worksheet_name);
1561
+ free(worksheet);
1562
+ return NULL;
1563
+ }
1564
+
1565
+ /*
1566
+ * Add a new chartsheet to the Excel workbook.
1567
+ */
1568
+ lxw_chartsheet *
1569
+ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
1570
+ {
1571
+ lxw_sheet *sheet = NULL;
1572
+ lxw_chartsheet *chartsheet = NULL;
1573
+ lxw_chartsheet_name *chartsheet_name = NULL;
1574
+ lxw_error error;
1575
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1576
+ char *new_name = NULL;
1577
+
1578
+ if (sheetname) {
1579
+ /* Use the user supplied name. */
1580
+ init_data.name = lxw_strdup(sheetname);
1581
+ init_data.quoted_name = lxw_quote_sheetname((char *) sheetname);
1582
+ }
1583
+ else {
1584
+ /* Use the default SheetN name. */
1585
+ new_name = malloc(LXW_MAX_SHEETNAME_LENGTH);
1586
+ GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
1587
+
1588
+ lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Chart%d",
1589
+ self->num_chartsheets + 1);
1590
+ init_data.name = new_name;
1591
+ init_data.quoted_name = lxw_strdup(new_name);
1592
+ }
1593
+
1594
+ /* Check that the chartsheet name is valid. */
1595
+ error = workbook_validate_sheet_name(self, init_data.name);
1596
+ if (error) {
1597
+ LXW_WARN_FORMAT2
1598
+ ("workbook_add_chartsheet(): chartsheet name '%s' has "
1599
+ "error: %s", init_data.name, lxw_strerror(error));
1600
+ goto mem_error;
1601
+ }
1602
+
1603
+ /* Create a struct to find/store the chartsheet name/pointer. */
1604
+ chartsheet_name = calloc(1, sizeof(struct lxw_chartsheet_name));
1605
+ GOTO_LABEL_ON_MEM_ERROR(chartsheet_name, mem_error);
1606
+
1607
+ /* Initialize the metadata to pass to the chartsheet. */
1608
+ init_data.hidden = 0;
1609
+ init_data.index = self->num_sheets;
1610
+ init_data.sst = self->sst;
1611
+ init_data.optimize = self->options.constant_memory;
1612
+ init_data.active_sheet = &self->active_sheet;
1613
+ init_data.first_sheet = &self->first_sheet;
1614
+ init_data.tmpdir = self->options.tmpdir;
1615
+
1616
+ /* Create a new chartsheet object. */
1617
+ chartsheet = lxw_chartsheet_new(&init_data);
1618
+ GOTO_LABEL_ON_MEM_ERROR(chartsheet, mem_error);
1619
+
1620
+ /* Add it to the chartsheet list. */
1621
+ self->num_chartsheets++;
1622
+ STAILQ_INSERT_TAIL(self->chartsheets, chartsheet, list_pointers);
1623
+
1624
+ /* Create a new sheet object. */
1625
+ sheet = calloc(1, sizeof(lxw_sheet));
1626
+ GOTO_LABEL_ON_MEM_ERROR(sheet, mem_error);
1627
+ sheet->is_chartsheet = LXW_TRUE;
1628
+ sheet->u.chartsheet = chartsheet;
1629
+
1630
+ /* Add it to the chartsheet list. */
1631
+ self->num_sheets++;
1632
+ STAILQ_INSERT_TAIL(self->sheets, sheet, list_pointers);
1633
+
1634
+ /* Store the chartsheet so we can look it up by name. */
1635
+ chartsheet_name->name = init_data.name;
1636
+ chartsheet_name->chartsheet = chartsheet;
1637
+ RB_INSERT(lxw_chartsheet_names, self->chartsheet_names, chartsheet_name);
1638
+
1639
+ return chartsheet;
1640
+
1641
+ mem_error:
1642
+ free(init_data.name);
1643
+ free(init_data.quoted_name);
1644
+ free(chartsheet_name);
1645
+ free(chartsheet);
1468
1646
  return NULL;
1469
1647
  }
1470
1648
 
@@ -1509,6 +1687,7 @@ workbook_add_format(lxw_workbook *self)
1509
1687
  lxw_error
1510
1688
  workbook_close(lxw_workbook *self)
1511
1689
  {
1690
+ lxw_sheet *sheet = NULL;
1512
1691
  lxw_worksheet *worksheet = NULL;
1513
1692
  lxw_packager *packager = NULL;
1514
1693
  lxw_error error = LXW_NO_ERROR;
@@ -1519,13 +1698,21 @@ workbook_close(lxw_workbook *self)
1519
1698
 
1520
1699
  /* Ensure that at least one worksheet has been selected. */
1521
1700
  if (self->active_sheet == 0) {
1522
- worksheet = STAILQ_FIRST(self->worksheets);
1523
- worksheet->selected = 1;
1524
- worksheet->hidden = 0;
1701
+ sheet = STAILQ_FIRST(self->sheets);
1702
+ if (!sheet->is_chartsheet) {
1703
+ worksheet = sheet->u.worksheet;
1704
+ worksheet->selected = 1;
1705
+ worksheet->hidden = 0;
1706
+ }
1525
1707
  }
1526
1708
 
1527
1709
  /* Set the active sheet. */
1528
- STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
1710
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1711
+ if (sheet->is_chartsheet)
1712
+ continue;
1713
+ else
1714
+ worksheet = sheet->u.worksheet;
1715
+
1529
1716
  if (worksheet->index == self->active_sheet)
1530
1717
  worksheet->active = 1;
1531
1718
  }
@@ -1891,11 +2078,33 @@ workbook_get_worksheet_by_name(lxw_workbook *self, const char *name)
1891
2078
  return NULL;
1892
2079
  }
1893
2080
 
2081
+ /*
2082
+ * Get a chartsheet object from its name.
2083
+ */
2084
+ lxw_chartsheet *
2085
+ workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name)
2086
+ {
2087
+ lxw_chartsheet_name chartsheet_name;
2088
+ lxw_chartsheet_name *found;
2089
+
2090
+ if (!name)
2091
+ return NULL;
2092
+
2093
+ chartsheet_name.name = name;
2094
+ found = RB_FIND(lxw_chartsheet_names,
2095
+ self->chartsheet_names, &chartsheet_name);
2096
+
2097
+ if (found)
2098
+ return found->chartsheet;
2099
+ else
2100
+ return NULL;
2101
+ }
2102
+
1894
2103
  /*
1895
2104
  * Validate the worksheet name based on Excel's rules.
1896
2105
  */
1897
2106
  lxw_error
1898
- workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
2107
+ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
1899
2108
  {
1900
2109
  /* Check the UTF-8 length of the worksheet name. */
1901
2110
  if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX)
@@ -1909,5 +2118,9 @@ workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
1909
2118
  if (workbook_get_worksheet_by_name(self, sheetname))
1910
2119
  return LXW_ERROR_SHEETNAME_ALREADY_USED;
1911
2120
 
2121
+ /* Check if the chartsheet name is already in use. */
2122
+ if (workbook_get_chartsheet_by_name(self, sheetname))
2123
+ return LXW_ERROR_SHEETNAME_ALREADY_USED;
2124
+
1912
2125
  return LXW_NO_ERROR;
1913
2126
  }