xlsxwriter 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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
  }