xlsxwriter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +40 -0
  3. data/ext/xlsxwriter/chart.c +105 -0
  4. data/ext/xlsxwriter/chart.h +27 -0
  5. data/ext/xlsxwriter/extconf.rb +14 -0
  6. data/ext/xlsxwriter/format.c +67 -0
  7. data/ext/xlsxwriter/format.h +9 -0
  8. data/ext/xlsxwriter/libxlsxwriter/LICENSE.txt +89 -0
  9. data/ext/xlsxwriter/libxlsxwriter/Makefile +141 -0
  10. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter.h +23 -0
  11. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/app.h +79 -0
  12. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chart.h +1093 -0
  13. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/common.h +336 -0
  14. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/content_types.h +74 -0
  15. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/core.h +51 -0
  16. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/custom.h +52 -0
  17. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/drawing.h +111 -0
  18. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/format.h +1214 -0
  19. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/hash_table.h +76 -0
  20. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/packager.h +80 -0
  21. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/relationships.h +77 -0
  22. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/shared_strings.h +83 -0
  23. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/styles.h +77 -0
  24. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/theme.h +47 -0
  25. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +215 -0
  26. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/queue.h +694 -0
  27. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/tmpfileplus.h +53 -0
  28. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/tree.h +801 -0
  29. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/zip.h +375 -0
  30. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/utility.h +166 -0
  31. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/workbook.h +751 -0
  32. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/worksheet.h +2641 -0
  33. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/xmlwriter.h +178 -0
  34. data/ext/xlsxwriter/libxlsxwriter/lib/.gitignore +0 -0
  35. data/ext/xlsxwriter/libxlsxwriter/src/Makefile +125 -0
  36. data/ext/xlsxwriter/libxlsxwriter/src/app.c +439 -0
  37. data/ext/xlsxwriter/libxlsxwriter/src/chart.c +3420 -0
  38. data/ext/xlsxwriter/libxlsxwriter/src/content_types.c +341 -0
  39. data/ext/xlsxwriter/libxlsxwriter/src/core.c +293 -0
  40. data/ext/xlsxwriter/libxlsxwriter/src/custom.c +224 -0
  41. data/ext/xlsxwriter/libxlsxwriter/src/drawing.c +746 -0
  42. data/ext/xlsxwriter/libxlsxwriter/src/format.c +728 -0
  43. data/ext/xlsxwriter/libxlsxwriter/src/hash_table.c +223 -0
  44. data/ext/xlsxwriter/libxlsxwriter/src/packager.c +877 -0
  45. data/ext/xlsxwriter/libxlsxwriter/src/relationships.c +242 -0
  46. data/ext/xlsxwriter/libxlsxwriter/src/shared_strings.c +264 -0
  47. data/ext/xlsxwriter/libxlsxwriter/src/styles.c +1086 -0
  48. data/ext/xlsxwriter/libxlsxwriter/src/theme.c +348 -0
  49. data/ext/xlsxwriter/libxlsxwriter/src/utility.c +512 -0
  50. data/ext/xlsxwriter/libxlsxwriter/src/workbook.c +1895 -0
  51. data/ext/xlsxwriter/libxlsxwriter/src/worksheet.c +4992 -0
  52. data/ext/xlsxwriter/libxlsxwriter/src/xmlwriter.c +355 -0
  53. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/Makefile +44 -0
  54. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/crypt.h +131 -0
  55. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/ioapi.c +247 -0
  56. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/ioapi.h +209 -0
  57. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/iowin32.c +456 -0
  58. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/iowin32.h +28 -0
  59. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/miniunz.c +660 -0
  60. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/minizip.c +520 -0
  61. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/mztools.c +291 -0
  62. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/mztools.h +37 -0
  63. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/unzip.c +2125 -0
  64. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/unzip.h +437 -0
  65. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/zip.c +2007 -0
  66. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/zip.h +367 -0
  67. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/Makefile +42 -0
  68. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +342 -0
  69. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.h +53 -0
  70. data/ext/xlsxwriter/workbook.c +257 -0
  71. data/ext/xlsxwriter/workbook.h +42 -0
  72. data/ext/xlsxwriter/workbook_properties.c +103 -0
  73. data/ext/xlsxwriter/workbook_properties.h +10 -0
  74. data/ext/xlsxwriter/worksheet.c +1064 -0
  75. data/ext/xlsxwriter/worksheet.h +74 -0
  76. data/ext/xlsxwriter/xlsxwriter.c +239 -0
  77. data/lib/xlsxwriter.rb +6 -0
  78. data/lib/xlsxwriter/version.rb +3 -0
  79. data/lib/xlsxwriter/worksheet.rb +72 -0
  80. data/test/run-test.rb +11 -0
  81. data/test/support/xlsx_comparable.rb +109 -0
  82. data/test/test-array-formula.rb +33 -0
  83. data/test/test-autofilter.rb +70 -0
  84. data/test/test-chart-area.rb +25 -0
  85. data/test/test-data.rb +65 -0
  86. data/test/test-default-row.rb +25 -0
  87. data/test/test-defined-name.rb +46 -0
  88. data/test/test-escapes.rb +33 -0
  89. data/test/test-fit-to-pages.rb +21 -0
  90. data/test/test-formatting.rb +137 -0
  91. data/test/test-gridlines.rb +15 -0
  92. data/test/test-hyperlink.rb +67 -0
  93. data/test/test-image.rb +84 -0
  94. data/test/test-merge-range.rb +18 -0
  95. data/test/test-misc.rb +29 -0
  96. data/test/test-optimize.rb +32 -0
  97. data/test/test-page-breaks.rb +13 -0
  98. data/test/test-page-setup.rb +28 -0
  99. data/test/test-panes.rb +45 -0
  100. data/test/test-print-area.rb +19 -0
  101. data/test/test-print-options.rb +61 -0
  102. data/test/test-print-scale.rb +12 -0
  103. data/test/test-properties.rb +51 -0
  104. data/test/test-protect.rb +27 -0
  105. data/test/test-repeat.rb +23 -0
  106. data/test/test-row-col-format.rb +35 -0
  107. data/test/test-set-selection.rb +13 -0
  108. data/test/test-set-start-page.rb +13 -0
  109. data/test/test-simple.rb +62 -0
  110. data/test/test-types.rb +17 -0
  111. data/test/xlsx-func-testcase.rb +36 -0
  112. metadata +228 -0
@@ -0,0 +1,1895 @@
1
+ /*****************************************************************************
2
+ * workbook - A library for creating Excel XLSX workbook files.
3
+ *
4
+ * Used in conjunction with the libxlsxwriter library.
5
+ *
6
+ * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
+ *
8
+ */
9
+
10
+ #include "xlsxwriter/xmlwriter.h"
11
+ #include "xlsxwriter/workbook.h"
12
+ #include "xlsxwriter/utility.h"
13
+ #include "xlsxwriter/packager.h"
14
+ #include "xlsxwriter/hash_table.h"
15
+
16
+ STATIC int _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2);
17
+ LXW_RB_GENERATE_NAMES(lxw_worksheet_names, lxw_worksheet_name, tree_pointers,
18
+ _name_cmp);
19
+
20
+ /*
21
+ * Forward declarations.
22
+ */
23
+
24
+ /*****************************************************************************
25
+ *
26
+ * Private functions.
27
+ *
28
+ ****************************************************************************/
29
+
30
+ /*
31
+ * Comparator for the worksheet names structure red/black tree.
32
+ */
33
+ STATIC int
34
+ _name_cmp(lxw_worksheet_name *name1, lxw_worksheet_name *name2)
35
+ {
36
+ return strcmp(name1->name, name2->name);
37
+ }
38
+
39
+ /*
40
+ * Free workbook properties.
41
+ */
42
+ STATIC void
43
+ _free_doc_properties(lxw_doc_properties *properties)
44
+ {
45
+ if (properties) {
46
+ free(properties->title);
47
+ free(properties->subject);
48
+ free(properties->author);
49
+ free(properties->manager);
50
+ free(properties->company);
51
+ free(properties->category);
52
+ free(properties->keywords);
53
+ free(properties->comments);
54
+ free(properties->status);
55
+ free(properties->hyperlink_base);
56
+ }
57
+
58
+ free(properties);
59
+ }
60
+
61
+ /*
62
+ * Free workbook custom property.
63
+ */
64
+ STATIC void
65
+ _free_custom_doc_property(lxw_custom_property *custom_property)
66
+ {
67
+ if (custom_property) {
68
+ free(custom_property->name);
69
+ if (custom_property->type == LXW_CUSTOM_STRING)
70
+ free(custom_property->u.string);
71
+ }
72
+
73
+ free(custom_property);
74
+ }
75
+
76
+ /*
77
+ * Free a workbook object.
78
+ */
79
+ void
80
+ lxw_workbook_free(lxw_workbook *workbook)
81
+ {
82
+ lxw_worksheet *worksheet;
83
+ struct lxw_worksheet_name *worksheet_name;
84
+ struct lxw_worksheet_name *next_name;
85
+ lxw_chart *chart;
86
+ lxw_format *format;
87
+ lxw_defined_name *defined_name;
88
+ lxw_custom_property *custom_property;
89
+
90
+ if (!workbook)
91
+ return;
92
+
93
+ _free_doc_properties(workbook->properties);
94
+
95
+ free(workbook->filename);
96
+
97
+ /* Free the worksheets in the workbook. */
98
+ while (!STAILQ_EMPTY(workbook->worksheets)) {
99
+ worksheet = STAILQ_FIRST(workbook->worksheets);
100
+ STAILQ_REMOVE_HEAD(workbook->worksheets, list_pointers);
101
+ lxw_worksheet_free(worksheet);
102
+ }
103
+
104
+ /* Free the charts in the workbook. */
105
+ while (!STAILQ_EMPTY(workbook->charts)) {
106
+ chart = STAILQ_FIRST(workbook->charts);
107
+ STAILQ_REMOVE_HEAD(workbook->charts, list_pointers);
108
+ lxw_chart_free(chart);
109
+ }
110
+
111
+ /* Free the formats in the workbook. */
112
+ while (!STAILQ_EMPTY(workbook->formats)) {
113
+ format = STAILQ_FIRST(workbook->formats);
114
+ STAILQ_REMOVE_HEAD(workbook->formats, list_pointers);
115
+ lxw_format_free(format);
116
+ }
117
+
118
+ /* Free the defined_names in the workbook. */
119
+ while (!TAILQ_EMPTY(workbook->defined_names)) {
120
+ defined_name = TAILQ_FIRST(workbook->defined_names);
121
+ TAILQ_REMOVE(workbook->defined_names, defined_name, list_pointers);
122
+ free(defined_name);
123
+ }
124
+
125
+ /* Free the custom_properties in the workbook. */
126
+ while (!STAILQ_EMPTY(workbook->custom_properties)) {
127
+ custom_property = STAILQ_FIRST(workbook->custom_properties);
128
+ STAILQ_REMOVE_HEAD(workbook->custom_properties, list_pointers);
129
+ _free_custom_doc_property(custom_property);
130
+ }
131
+
132
+ if (workbook->worksheet_names) {
133
+ for (worksheet_name =
134
+ RB_MIN(lxw_worksheet_names, workbook->worksheet_names);
135
+ worksheet_name; worksheet_name = next_name) {
136
+
137
+ next_name = RB_NEXT(lxw_worksheet_names,
138
+ workbook->worksheet_name, worksheet_name);
139
+ RB_REMOVE(lxw_worksheet_names,
140
+ workbook->worksheet_names, worksheet_name);
141
+ free(worksheet_name);
142
+ }
143
+
144
+ free(workbook->worksheet_names);
145
+ }
146
+
147
+ lxw_hash_free(workbook->used_xf_formats);
148
+ lxw_sst_free(workbook->sst);
149
+ free(workbook->options.tmpdir);
150
+ free(workbook->worksheets);
151
+ free(workbook->charts);
152
+ free(workbook->ordered_charts);
153
+ free(workbook->formats);
154
+ free(workbook->defined_names);
155
+ free(workbook->custom_properties);
156
+ free(workbook);
157
+ }
158
+
159
+ /*
160
+ * Set the default index for each format. This is only used for testing.
161
+ */
162
+ void
163
+ lxw_workbook_set_default_xf_indices(lxw_workbook *self)
164
+ {
165
+ lxw_format *format;
166
+
167
+ STAILQ_FOREACH(format, self->formats, list_pointers) {
168
+ lxw_format_get_xf_index(format);
169
+ }
170
+ }
171
+
172
+ /*
173
+ * Iterate through the XF Format objects and give them an index to non-default
174
+ * font elements.
175
+ */
176
+ STATIC void
177
+ _prepare_fonts(lxw_workbook *self)
178
+ {
179
+
180
+ lxw_hash_table *fonts = lxw_hash_new(128, 1, 1);
181
+ lxw_hash_element *hash_element;
182
+ lxw_hash_element *used_format_element;
183
+ uint16_t index = 0;
184
+
185
+ LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
186
+ lxw_format *format = (lxw_format *) used_format_element->value;
187
+ lxw_font *key = lxw_format_get_font_key(format);
188
+
189
+ if (key) {
190
+ /* Look up the format in the hash table. */
191
+ hash_element = lxw_hash_key_exists(fonts, key, sizeof(lxw_font));
192
+
193
+ if (hash_element) {
194
+ /* Font has already been used. */
195
+ format->font_index = *(uint16_t *) hash_element->value;
196
+ format->has_font = LXW_FALSE;
197
+ free(key);
198
+ }
199
+ else {
200
+ /* This is a new font. */
201
+ uint16_t *font_index = calloc(1, sizeof(uint16_t));
202
+ *font_index = index;
203
+ format->font_index = index;
204
+ format->has_font = 1;
205
+ lxw_insert_hash_element(fonts, key, font_index,
206
+ sizeof(lxw_font));
207
+ index++;
208
+ }
209
+ }
210
+ }
211
+
212
+ lxw_hash_free(fonts);
213
+
214
+ self->font_count = index;
215
+ }
216
+
217
+ /*
218
+ * Iterate through the XF Format objects and give them an index to non-default
219
+ * border elements.
220
+ */
221
+ STATIC void
222
+ _prepare_borders(lxw_workbook *self)
223
+ {
224
+
225
+ lxw_hash_table *borders = lxw_hash_new(128, 1, 1);
226
+ lxw_hash_element *hash_element;
227
+ lxw_hash_element *used_format_element;
228
+ uint16_t index = 0;
229
+
230
+ LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
231
+ lxw_format *format = (lxw_format *) used_format_element->value;
232
+ lxw_border *key = lxw_format_get_border_key(format);
233
+
234
+ if (key) {
235
+ /* Look up the format in the hash table. */
236
+ hash_element =
237
+ lxw_hash_key_exists(borders, key, sizeof(lxw_border));
238
+
239
+ if (hash_element) {
240
+ /* Border has already been used. */
241
+ format->border_index = *(uint16_t *) hash_element->value;
242
+ format->has_border = LXW_FALSE;
243
+ free(key);
244
+ }
245
+ else {
246
+ /* This is a new border. */
247
+ uint16_t *border_index = calloc(1, sizeof(uint16_t));
248
+ *border_index = index;
249
+ format->border_index = index;
250
+ format->has_border = 1;
251
+ lxw_insert_hash_element(borders, key, border_index,
252
+ sizeof(lxw_border));
253
+ index++;
254
+ }
255
+ }
256
+ }
257
+
258
+ lxw_hash_free(borders);
259
+
260
+ self->border_count = index;
261
+ }
262
+
263
+ /*
264
+ * Iterate through the XF Format objects and give them an index to non-default
265
+ * fill elements.
266
+ */
267
+ STATIC void
268
+ _prepare_fills(lxw_workbook *self)
269
+ {
270
+
271
+ lxw_hash_table *fills = lxw_hash_new(128, 1, 1);
272
+ lxw_hash_element *hash_element;
273
+ lxw_hash_element *used_format_element;
274
+ uint16_t index = 2;
275
+ lxw_fill *default_fill_1 = NULL;
276
+ lxw_fill *default_fill_2 = NULL;
277
+ uint16_t *fill_index1 = NULL;
278
+ uint16_t *fill_index2 = NULL;
279
+
280
+ default_fill_1 = calloc(1, sizeof(lxw_fill));
281
+ GOTO_LABEL_ON_MEM_ERROR(default_fill_1, mem_error);
282
+
283
+ default_fill_2 = calloc(1, sizeof(lxw_fill));
284
+ GOTO_LABEL_ON_MEM_ERROR(default_fill_2, mem_error);
285
+
286
+ fill_index1 = calloc(1, sizeof(uint16_t));
287
+ GOTO_LABEL_ON_MEM_ERROR(fill_index1, mem_error);
288
+
289
+ fill_index2 = calloc(1, sizeof(uint16_t));
290
+ GOTO_LABEL_ON_MEM_ERROR(fill_index2, mem_error);
291
+
292
+ /* Add the default fills. */
293
+ default_fill_1->pattern = LXW_PATTERN_NONE;
294
+ default_fill_1->fg_color = LXW_COLOR_UNSET;
295
+ default_fill_1->bg_color = LXW_COLOR_UNSET;
296
+ *fill_index1 = 0;
297
+ lxw_insert_hash_element(fills, default_fill_1, fill_index1,
298
+ sizeof(lxw_fill));
299
+
300
+ default_fill_2->pattern = LXW_PATTERN_GRAY_125;
301
+ default_fill_2->fg_color = LXW_COLOR_UNSET;
302
+ default_fill_2->bg_color = LXW_COLOR_UNSET;
303
+ *fill_index2 = 1;
304
+ lxw_insert_hash_element(fills, default_fill_2, fill_index2,
305
+ sizeof(lxw_fill));
306
+
307
+ LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
308
+ lxw_format *format = (lxw_format *) used_format_element->value;
309
+ lxw_fill *key = lxw_format_get_fill_key(format);
310
+
311
+ /* The following logical statements jointly take care of special */
312
+ /* cases in relation to cell colors and patterns: */
313
+ /* 1. For a solid fill (pattern == 1) Excel reverses the role of */
314
+ /* foreground and background colors, and */
315
+ /* 2. If the user specifies a foreground or background color */
316
+ /* without a pattern they probably wanted a solid fill, so */
317
+ /* we fill in the defaults. */
318
+ if (format->pattern == LXW_PATTERN_SOLID
319
+ && format->bg_color != LXW_COLOR_UNSET
320
+ && format->fg_color != LXW_COLOR_UNSET) {
321
+ lxw_color_t tmp = format->fg_color;
322
+ format->fg_color = format->bg_color;
323
+ format->bg_color = tmp;
324
+ }
325
+
326
+ if (format->pattern <= LXW_PATTERN_SOLID
327
+ && format->bg_color != LXW_COLOR_UNSET
328
+ && format->fg_color == LXW_COLOR_UNSET) {
329
+ format->fg_color = format->bg_color;
330
+ format->bg_color = LXW_COLOR_UNSET;
331
+ format->pattern = LXW_PATTERN_SOLID;
332
+ }
333
+
334
+ if (format->pattern <= LXW_PATTERN_SOLID
335
+ && format->bg_color == LXW_COLOR_UNSET
336
+ && format->fg_color != LXW_COLOR_UNSET) {
337
+ format->bg_color = LXW_COLOR_UNSET;
338
+ format->pattern = LXW_PATTERN_SOLID;
339
+ }
340
+
341
+ if (key) {
342
+ /* Look up the format in the hash table. */
343
+ hash_element = lxw_hash_key_exists(fills, key, sizeof(lxw_fill));
344
+
345
+ if (hash_element) {
346
+ /* Fill has already been used. */
347
+ format->fill_index = *(uint16_t *) hash_element->value;
348
+ format->has_fill = LXW_FALSE;
349
+ free(key);
350
+ }
351
+ else {
352
+ /* This is a new fill. */
353
+ uint16_t *fill_index = calloc(1, sizeof(uint16_t));
354
+ *fill_index = index;
355
+ format->fill_index = index;
356
+ format->has_fill = 1;
357
+ lxw_insert_hash_element(fills, key, fill_index,
358
+ sizeof(lxw_fill));
359
+ index++;
360
+ }
361
+ }
362
+ }
363
+
364
+ lxw_hash_free(fills);
365
+
366
+ self->fill_count = index;
367
+
368
+ return;
369
+
370
+ mem_error:
371
+ free(fill_index2);
372
+ free(fill_index1);
373
+ free(default_fill_2);
374
+ free(default_fill_1);
375
+ lxw_hash_free(fills);
376
+ }
377
+
378
+ /*
379
+ * Iterate through the XF Format objects and give them an index to non-default
380
+ * number format elements. Note, user defined records start from index 0xA4.
381
+ */
382
+ STATIC void
383
+ _prepare_num_formats(lxw_workbook *self)
384
+ {
385
+
386
+ lxw_hash_table *num_formats = lxw_hash_new(128, 0, 1);
387
+ lxw_hash_element *hash_element;
388
+ lxw_hash_element *used_format_element;
389
+ uint16_t index = 0xA4;
390
+ uint16_t num_format_count = 0;
391
+ char *num_format;
392
+ uint16_t *num_format_index;
393
+
394
+ LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
395
+ lxw_format *format = (lxw_format *) used_format_element->value;
396
+
397
+ /* Format already has a number format index. */
398
+ if (format->num_format_index)
399
+ continue;
400
+
401
+ /* Check if there is a user defined number format string. */
402
+ num_format = format->num_format;
403
+
404
+ if (*num_format) {
405
+ /* Look up the num_format in the hash table. */
406
+ hash_element = lxw_hash_key_exists(num_formats, num_format,
407
+ strlen(num_format));
408
+
409
+ if (hash_element) {
410
+ /* Num_Format has already been used. */
411
+ format->num_format_index = *(uint16_t *) hash_element->value;
412
+ }
413
+ else {
414
+ /* This is a new num_format. */
415
+ num_format_index = calloc(1, sizeof(uint16_t));
416
+ *num_format_index = index;
417
+ format->num_format_index = index;
418
+ lxw_insert_hash_element(num_formats, num_format,
419
+ num_format_index, strlen(num_format));
420
+ index++;
421
+ num_format_count++;
422
+ }
423
+ }
424
+ }
425
+
426
+ lxw_hash_free(num_formats);
427
+
428
+ self->num_format_count = num_format_count;
429
+ }
430
+
431
+ /*
432
+ * Prepare workbook and sub-objects for writing.
433
+ */
434
+ STATIC void
435
+ _prepare_workbook(lxw_workbook *self)
436
+ {
437
+ /* Set the font index for the format objects. */
438
+ _prepare_fonts(self);
439
+
440
+ /* Set the number format index for the format objects. */
441
+ _prepare_num_formats(self);
442
+
443
+ /* Set the border index for the format objects. */
444
+ _prepare_borders(self);
445
+
446
+ /* Set the fill index for the format objects. */
447
+ _prepare_fills(self);
448
+
449
+ }
450
+
451
+ /*
452
+ * Compare two defined_name structures.
453
+ */
454
+ static int
455
+ _compare_defined_names(lxw_defined_name *a, lxw_defined_name *b)
456
+ {
457
+ int res = strcmp(a->normalised_name, b->normalised_name);
458
+
459
+ /* Primary comparison based on defined name. */
460
+ if (res)
461
+ return res;
462
+
463
+ /* Secondary comparison based on worksheet name. */
464
+ res = strcmp(a->normalised_sheetname, b->normalised_sheetname);
465
+
466
+ return res;
467
+ }
468
+
469
+ /*
470
+ * Process and store the defined names. The defined names are stored with
471
+ * the Workbook.xml but also with the App.xml if they refer to a sheet
472
+ * range like "Sheet1!:A1". The defined names are store in sorted
473
+ * order for consistency with Excel. The names need to be normalized before
474
+ * sorting.
475
+ */
476
+ STATIC lxw_error
477
+ _store_defined_name(lxw_workbook *self, const char *name,
478
+ const char *app_name, const char *formula, int16_t index,
479
+ uint8_t hidden)
480
+ {
481
+ lxw_worksheet *worksheet;
482
+ lxw_defined_name *defined_name;
483
+ lxw_defined_name *list_defined_name;
484
+ char name_copy[LXW_DEFINED_NAME_LENGTH];
485
+ char *tmp_str;
486
+ char *worksheet_name;
487
+
488
+ /* Do some checks on the input data */
489
+ if (!name || !formula)
490
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
491
+
492
+ if (lxw_utf8_strlen(name) > LXW_DEFINED_NAME_LENGTH ||
493
+ lxw_utf8_strlen(formula) > LXW_DEFINED_NAME_LENGTH) {
494
+ return LXW_ERROR_128_STRING_LENGTH_EXCEEDED;
495
+ }
496
+
497
+ /* Allocate a new defined_name to be added to the linked list of names. */
498
+ defined_name = calloc(1, sizeof(struct lxw_defined_name));
499
+ RETURN_ON_MEM_ERROR(defined_name, LXW_ERROR_MEMORY_MALLOC_FAILED);
500
+
501
+ /* Copy the user input string. */
502
+ lxw_strcpy(name_copy, name);
503
+
504
+ /* Set the worksheet index or -1 for a global defined name. */
505
+ defined_name->index = index;
506
+ defined_name->hidden = hidden;
507
+
508
+ /* Check for local defined names like like "Sheet1!name". */
509
+ tmp_str = strchr(name_copy, '!');
510
+
511
+ if (tmp_str == NULL) {
512
+ /* The name is global. We just store the defined name string. */
513
+ lxw_strcpy(defined_name->name, name_copy);
514
+ }
515
+ else {
516
+ /* The name is worksheet local. We need to extract the sheet name
517
+ * and map it to a sheet index. */
518
+
519
+ /* Split the into the worksheet name and defined name. */
520
+ *tmp_str = '\0';
521
+ tmp_str++;
522
+ worksheet_name = name_copy;
523
+
524
+ /* Remove any worksheet quoting. */
525
+ if (worksheet_name[0] == '\'')
526
+ worksheet_name++;
527
+ if (worksheet_name[strlen(worksheet_name) - 1] == '\'')
528
+ worksheet_name[strlen(worksheet_name) - 1] = '\0';
529
+
530
+ /* Search for worksheet name to get the equivalent worksheet index. */
531
+ STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
532
+ if (strcmp(worksheet_name, worksheet->name) == 0) {
533
+ defined_name->index = worksheet->index;
534
+ lxw_strcpy(defined_name->normalised_sheetname,
535
+ worksheet_name);
536
+ }
537
+ }
538
+
539
+ /* If we didn't find the worksheet name we exit. */
540
+ if (defined_name->index == -1)
541
+ goto mem_error;
542
+
543
+ lxw_strcpy(defined_name->name, tmp_str);
544
+ }
545
+
546
+ /* Print titles and repeat title pass in the name used for App.xml. */
547
+ if (app_name) {
548
+ lxw_strcpy(defined_name->app_name, app_name);
549
+ lxw_strcpy(defined_name->normalised_sheetname, app_name);
550
+ }
551
+ else {
552
+ lxw_strcpy(defined_name->app_name, name);
553
+ }
554
+
555
+ /* We need to normalize the defined names for sorting. This involves
556
+ * removing any _xlnm namespace and converting it to lowercase. */
557
+ tmp_str = strstr(name_copy, "_xlnm.");
558
+
559
+ if (tmp_str)
560
+ lxw_strcpy(defined_name->normalised_name, defined_name->name + 6);
561
+ else
562
+ lxw_strcpy(defined_name->normalised_name, defined_name->name);
563
+
564
+ lxw_str_tolower(defined_name->normalised_name);
565
+ lxw_str_tolower(defined_name->normalised_sheetname);
566
+
567
+ /* Strip leading "=" from the formula. */
568
+ if (formula[0] == '=')
569
+ lxw_strcpy(defined_name->formula, formula + 1);
570
+ else
571
+ lxw_strcpy(defined_name->formula, formula);
572
+
573
+ /* We add the defined name to the list in sorted order. */
574
+ list_defined_name = TAILQ_FIRST(self->defined_names);
575
+
576
+ if (list_defined_name == NULL ||
577
+ _compare_defined_names(defined_name, list_defined_name) < 1) {
578
+ /* List is empty or defined name goes to the head. */
579
+ TAILQ_INSERT_HEAD(self->defined_names, defined_name, list_pointers);
580
+ return LXW_NO_ERROR;
581
+ }
582
+
583
+ TAILQ_FOREACH(list_defined_name, self->defined_names, list_pointers) {
584
+ int res = _compare_defined_names(defined_name, list_defined_name);
585
+
586
+ /* The entry already exists. We exit and don't overwrite. */
587
+ if (res == 0)
588
+ goto mem_error;
589
+
590
+ /* New defined name is inserted in sorted order before other entries. */
591
+ if (res < 0) {
592
+ TAILQ_INSERT_BEFORE(list_defined_name, defined_name,
593
+ list_pointers);
594
+ return LXW_NO_ERROR;
595
+ }
596
+ }
597
+
598
+ /* If the entry wasn't less than any of the entries in the list we add it
599
+ * to the end. */
600
+ TAILQ_INSERT_TAIL(self->defined_names, defined_name, list_pointers);
601
+ return LXW_NO_ERROR;
602
+
603
+ mem_error:
604
+ free(defined_name);
605
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
606
+ }
607
+
608
+ /*
609
+ * Populate the data cache of a chart data series by reading the data from the
610
+ * relevant worksheet and adding it to the cached in the range object as a
611
+ * list of points.
612
+ *
613
+ * Note, the data cache isn't strictly required by Excel but it helps if the
614
+ * chart is embedded in another application such as PowerPoint and it also
615
+ * helps with comparison testing.
616
+ */
617
+ STATIC void
618
+ _populate_range_data_cache(lxw_workbook *self, lxw_series_range *range)
619
+ {
620
+ lxw_worksheet *worksheet;
621
+ lxw_row_t row_num;
622
+ lxw_col_t col_num;
623
+ lxw_row *row_obj;
624
+ lxw_cell *cell_obj;
625
+ struct lxw_series_data_point *data_point;
626
+ uint16_t num_data_points = 0;
627
+
628
+ /* If ignore_cache is set then don't try to populate the cache. This flag
629
+ * may be set manually, for testing, or due to a case where the cache
630
+ * can't be calculated.
631
+ */
632
+ if (range->ignore_cache)
633
+ return;
634
+
635
+ /* Currently we only handle 2D ranges so ensure either the rows or cols
636
+ * are the same.
637
+ */
638
+ if (range->first_row != range->last_row
639
+ && range->first_col != range->last_col) {
640
+ range->ignore_cache = LXW_TRUE;
641
+ return;
642
+ }
643
+
644
+ /* Check that the sheetname exists. */
645
+ worksheet = workbook_get_worksheet_by_name(self, range->sheetname);
646
+ if (!worksheet) {
647
+ LXW_WARN_FORMAT2("workbook_add_chart(): worksheet name '%s' "
648
+ "in chart formula '%s' doesn't exist.",
649
+ range->sheetname, range->formula);
650
+ range->ignore_cache = LXW_TRUE;
651
+ return;
652
+ }
653
+
654
+ /* We can't read the data when worksheet optimization is on. */
655
+ if (worksheet->optimize) {
656
+ range->ignore_cache = LXW_TRUE;
657
+ return;
658
+ }
659
+
660
+ /* Iterate through the worksheet data and populate the range cache. */
661
+ for (row_num = range->first_row; row_num <= range->last_row; row_num++) {
662
+ row_obj = lxw_worksheet_find_row(worksheet, row_num);
663
+
664
+ for (col_num = range->first_col; col_num <= range->last_col;
665
+ col_num++) {
666
+
667
+ data_point = calloc(1, sizeof(struct lxw_series_data_point));
668
+ if (!data_point) {
669
+ range->ignore_cache = LXW_TRUE;
670
+ return;
671
+ }
672
+
673
+ cell_obj = lxw_worksheet_find_cell(row_obj, col_num);
674
+
675
+ if (cell_obj) {
676
+ if (cell_obj->type == NUMBER_CELL) {
677
+ data_point->number = cell_obj->u.number;
678
+ }
679
+
680
+ if (cell_obj->type == STRING_CELL) {
681
+ data_point->string = lxw_strdup(cell_obj->sst_string);
682
+ data_point->is_string = LXW_TRUE;
683
+ range->has_string_cache = LXW_TRUE;
684
+ }
685
+ }
686
+ else {
687
+ data_point->no_data = LXW_TRUE;
688
+ }
689
+
690
+ STAILQ_INSERT_TAIL(range->data_cache, data_point, list_pointers);
691
+ num_data_points++;
692
+ }
693
+ }
694
+
695
+ range->num_data_points = num_data_points;
696
+
697
+ }
698
+
699
+ /* Convert a chart range such as Sheet1!$A$1:$A$5 to a sheet name and row-col
700
+ * dimensions, or vice-versa. This gives us the dimensions to read data back
701
+ * from the worksheet.
702
+ */
703
+ STATIC void
704
+ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
705
+ {
706
+
707
+ char formula[LXW_MAX_FORMULA_RANGE_LENGTH] = { 0 };
708
+ char *tmp_str;
709
+ char *sheetname;
710
+
711
+ /* If neither the range formula or sheetname is defined then this probably
712
+ * isn't a valid range.
713
+ */
714
+ if (!range->formula && !range->sheetname) {
715
+ range->ignore_cache = LXW_TRUE;
716
+ return;
717
+ }
718
+
719
+ /* If the sheetname is already defined it was already set via
720
+ * chart_series_set_categories() or chart_series_set_values().
721
+ */
722
+ if (range->sheetname)
723
+ return;
724
+
725
+ /* Ignore non-contiguous range like (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5) */
726
+ if (range->formula[0] == '(') {
727
+ range->ignore_cache = LXW_TRUE;
728
+ return;
729
+ }
730
+
731
+ /* Create a copy of the formula to modify and parse into parts. */
732
+ lxw_snprintf(formula, LXW_MAX_FORMULA_RANGE_LENGTH, "%s", range->formula);
733
+
734
+ /* Check for valid formula. TODO. This needs stronger validation. */
735
+ tmp_str = strchr(formula, '!');
736
+
737
+ if (tmp_str == NULL) {
738
+ range->ignore_cache = LXW_TRUE;
739
+ return;
740
+ }
741
+ else {
742
+ /* Split the formulas into sheetname and row-col data. */
743
+ *tmp_str = '\0';
744
+ tmp_str++;
745
+ sheetname = formula;
746
+
747
+ /* Remove any worksheet quoting. */
748
+ if (sheetname[0] == '\'')
749
+ sheetname++;
750
+ if (sheetname[strlen(sheetname) - 1] == '\'')
751
+ sheetname[strlen(sheetname) - 1] = '\0';
752
+
753
+ /* Check that the sheetname exists. */
754
+ if (!workbook_get_worksheet_by_name(self, sheetname)) {
755
+ LXW_WARN_FORMAT2("workbook_add_chart(): worksheet name '%s' "
756
+ "in chart formula '%s' doesn't exist.",
757
+ sheetname, range->formula);
758
+ range->ignore_cache = LXW_TRUE;
759
+ return;
760
+ }
761
+
762
+ range->sheetname = lxw_strdup(sheetname);
763
+ range->first_row = lxw_name_to_row(tmp_str);
764
+ range->first_col = lxw_name_to_col(tmp_str);
765
+
766
+ if (strchr(tmp_str, ':')) {
767
+ /* 2D range. */
768
+ range->last_row = lxw_name_to_row_2(tmp_str);
769
+ range->last_col = lxw_name_to_col_2(tmp_str);
770
+ }
771
+ else {
772
+ /* 1D range. */
773
+ range->last_row = range->first_row;
774
+ range->last_col = range->first_col;
775
+ }
776
+
777
+ }
778
+ }
779
+
780
+ /* Set the range dimensions and set the data cache.
781
+ */
782
+ STATIC void
783
+ _populate_range(lxw_workbook *self, lxw_series_range *range)
784
+ {
785
+ _populate_range_dimensions(self, range);
786
+ _populate_range_data_cache(self, range);
787
+ }
788
+
789
+ /*
790
+ * Add "cached" data to charts to provide the numCache and strCache data for
791
+ * series and title/axis ranges.
792
+ */
793
+ STATIC void
794
+ _add_chart_cache_data(lxw_workbook *self)
795
+ {
796
+ lxw_chart *chart;
797
+ lxw_chart_series *series;
798
+
799
+ STAILQ_FOREACH(chart, self->ordered_charts, ordered_list_pointers) {
800
+
801
+ _populate_range(self, chart->title.range);
802
+ _populate_range(self, chart->x_axis->title.range);
803
+ _populate_range(self, chart->y_axis->title.range);
804
+
805
+ if (STAILQ_EMPTY(chart->series_list))
806
+ continue;
807
+
808
+ STAILQ_FOREACH(series, chart->series_list, list_pointers) {
809
+ _populate_range(self, series->categories);
810
+ _populate_range(self, series->values);
811
+ _populate_range(self, series->title.range);
812
+ }
813
+ }
814
+ }
815
+
816
+ /*
817
+ * Iterate through the worksheets and set up any chart or image drawings.
818
+ */
819
+ STATIC void
820
+ _prepare_drawings(lxw_workbook *self)
821
+ {
822
+ lxw_worksheet *worksheet;
823
+ lxw_image_options *image_options;
824
+ uint16_t chart_ref_id = 0;
825
+ uint16_t image_ref_id = 0;
826
+ uint16_t drawing_id = 0;
827
+
828
+ STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
829
+
830
+ if (STAILQ_EMPTY(worksheet->image_data)
831
+ && STAILQ_EMPTY(worksheet->chart_data))
832
+ continue;
833
+
834
+ drawing_id++;
835
+
836
+ STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
837
+ chart_ref_id++;
838
+ lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id,
839
+ image_options);
840
+ if (image_options->chart)
841
+ STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart,
842
+ ordered_list_pointers);
843
+ }
844
+
845
+ STAILQ_FOREACH(image_options, worksheet->image_data, list_pointers) {
846
+
847
+ if (image_options->image_type == LXW_IMAGE_PNG)
848
+ self->has_png = LXW_TRUE;
849
+
850
+ if (image_options->image_type == LXW_IMAGE_JPEG)
851
+ self->has_jpeg = LXW_TRUE;
852
+
853
+ if (image_options->image_type == LXW_IMAGE_BMP)
854
+ self->has_bmp = LXW_TRUE;
855
+
856
+ image_ref_id++;
857
+
858
+ lxw_worksheet_prepare_image(worksheet, image_ref_id, drawing_id,
859
+ image_options);
860
+ }
861
+ }
862
+
863
+ self->drawing_count = drawing_id;
864
+ }
865
+
866
+ /*
867
+ * Iterate through the worksheets and store any defined names used for print
868
+ * ranges or repeat rows/columns.
869
+ */
870
+ STATIC void
871
+ _prepare_defined_names(lxw_workbook *self)
872
+ {
873
+ lxw_worksheet *worksheet;
874
+ char app_name[LXW_DEFINED_NAME_LENGTH];
875
+ char range[LXW_DEFINED_NAME_LENGTH];
876
+ char area[LXW_MAX_CELL_RANGE_LENGTH];
877
+ char first_col[8];
878
+ char last_col[8];
879
+
880
+ STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
881
+
882
+ /*
883
+ * Check for autofilter settings and store them.
884
+ */
885
+ if (worksheet->autofilter.in_use) {
886
+
887
+ lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH,
888
+ "%s!_FilterDatabase", worksheet->quoted_name);
889
+
890
+ lxw_rowcol_to_range_abs(area,
891
+ worksheet->autofilter.first_row,
892
+ worksheet->autofilter.first_col,
893
+ worksheet->autofilter.last_row,
894
+ worksheet->autofilter.last_col);
895
+
896
+ lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, "%s!%s",
897
+ worksheet->quoted_name, area);
898
+
899
+ /* Autofilters are the only defined name to set the hidden flag. */
900
+ _store_defined_name(self, "_xlnm._FilterDatabase", app_name,
901
+ range, worksheet->index, LXW_TRUE);
902
+ }
903
+
904
+ /*
905
+ * Check for Print Area settings and store them.
906
+ */
907
+ if (worksheet->print_area.in_use) {
908
+
909
+ lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH,
910
+ "%s!Print_Area", worksheet->quoted_name);
911
+
912
+ /* Check for print area that is the max row range. */
913
+ if (worksheet->print_area.first_row == 0
914
+ && worksheet->print_area.last_row == LXW_ROW_MAX - 1) {
915
+
916
+ lxw_col_to_name(first_col,
917
+ worksheet->print_area.first_col, LXW_FALSE);
918
+
919
+ lxw_col_to_name(last_col,
920
+ worksheet->print_area.last_col, LXW_FALSE);
921
+
922
+ lxw_snprintf(area, LXW_MAX_CELL_RANGE_LENGTH - 1, "$%s:$%s",
923
+ first_col, last_col);
924
+
925
+ }
926
+ /* Check for print area that is the max column range. */
927
+ else if (worksheet->print_area.first_col == 0
928
+ && worksheet->print_area.last_col == LXW_COL_MAX - 1) {
929
+
930
+ lxw_snprintf(area, LXW_MAX_CELL_RANGE_LENGTH - 1, "$%d:$%d",
931
+ worksheet->print_area.first_row + 1,
932
+ worksheet->print_area.last_row + 1);
933
+
934
+ }
935
+ else {
936
+ lxw_rowcol_to_range_abs(area,
937
+ worksheet->print_area.first_row,
938
+ worksheet->print_area.first_col,
939
+ worksheet->print_area.last_row,
940
+ worksheet->print_area.last_col);
941
+ }
942
+
943
+ lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH, "%s!%s",
944
+ worksheet->quoted_name, area);
945
+
946
+ _store_defined_name(self, "_xlnm.Print_Area", app_name,
947
+ range, worksheet->index, LXW_FALSE);
948
+ }
949
+
950
+ /*
951
+ * Check for repeat rows/cols. aka, Print Titles and store them.
952
+ */
953
+ if (worksheet->repeat_rows.in_use || worksheet->repeat_cols.in_use) {
954
+ if (worksheet->repeat_rows.in_use
955
+ && worksheet->repeat_cols.in_use) {
956
+ lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH,
957
+ "%s!Print_Titles", worksheet->quoted_name);
958
+
959
+ lxw_col_to_name(first_col,
960
+ worksheet->repeat_cols.first_col, LXW_FALSE);
961
+
962
+ lxw_col_to_name(last_col,
963
+ worksheet->repeat_cols.last_col, LXW_FALSE);
964
+
965
+ lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH,
966
+ "%s!$%s:$%s,%s!$%d:$%d",
967
+ worksheet->quoted_name, first_col,
968
+ last_col, worksheet->quoted_name,
969
+ worksheet->repeat_rows.first_row + 1,
970
+ worksheet->repeat_rows.last_row + 1);
971
+
972
+ _store_defined_name(self, "_xlnm.Print_Titles", app_name,
973
+ range, worksheet->index, LXW_FALSE);
974
+ }
975
+ else if (worksheet->repeat_rows.in_use) {
976
+
977
+ lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH,
978
+ "%s!Print_Titles", worksheet->quoted_name);
979
+
980
+ lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH,
981
+ "%s!$%d:$%d", worksheet->quoted_name,
982
+ worksheet->repeat_rows.first_row + 1,
983
+ worksheet->repeat_rows.last_row + 1);
984
+
985
+ _store_defined_name(self, "_xlnm.Print_Titles", app_name,
986
+ range, worksheet->index, LXW_FALSE);
987
+ }
988
+ else if (worksheet->repeat_cols.in_use) {
989
+ lxw_snprintf(app_name, LXW_DEFINED_NAME_LENGTH,
990
+ "%s!Print_Titles", worksheet->quoted_name);
991
+
992
+ lxw_col_to_name(first_col,
993
+ worksheet->repeat_cols.first_col, LXW_FALSE);
994
+
995
+ lxw_col_to_name(last_col,
996
+ worksheet->repeat_cols.last_col, LXW_FALSE);
997
+
998
+ lxw_snprintf(range, LXW_DEFINED_NAME_LENGTH,
999
+ "%s!$%s:$%s", worksheet->quoted_name,
1000
+ first_col, last_col);
1001
+
1002
+ _store_defined_name(self, "_xlnm.Print_Titles", app_name,
1003
+ range, worksheet->index, LXW_FALSE);
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+
1009
+ /*****************************************************************************
1010
+ *
1011
+ * XML functions.
1012
+ *
1013
+ ****************************************************************************/
1014
+
1015
+ /*
1016
+ * Write the XML declaration.
1017
+ */
1018
+ STATIC void
1019
+ _workbook_xml_declaration(lxw_workbook *self)
1020
+ {
1021
+ lxw_xml_declaration(self->file);
1022
+ }
1023
+
1024
+ /*
1025
+ * Write the <workbook> element.
1026
+ */
1027
+ STATIC void
1028
+ _write_workbook(lxw_workbook *self)
1029
+ {
1030
+ struct xml_attribute_list attributes;
1031
+ struct xml_attribute *attribute;
1032
+ char xmlns[] = "http://schemas.openxmlformats.org"
1033
+ "/spreadsheetml/2006/main";
1034
+ char xmlns_r[] = "http://schemas.openxmlformats.org"
1035
+ "/officeDocument/2006/relationships";
1036
+
1037
+ LXW_INIT_ATTRIBUTES();
1038
+ LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns);
1039
+ LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r);
1040
+
1041
+ lxw_xml_start_tag(self->file, "workbook", &attributes);
1042
+
1043
+ LXW_FREE_ATTRIBUTES();
1044
+ }
1045
+
1046
+ /*
1047
+ * Write the <fileVersion> element.
1048
+ */
1049
+ STATIC void
1050
+ _write_file_version(lxw_workbook *self)
1051
+ {
1052
+ struct xml_attribute_list attributes;
1053
+ struct xml_attribute *attribute;
1054
+
1055
+ LXW_INIT_ATTRIBUTES();
1056
+ LXW_PUSH_ATTRIBUTES_STR("appName", "xl");
1057
+ LXW_PUSH_ATTRIBUTES_STR("lastEdited", "4");
1058
+ LXW_PUSH_ATTRIBUTES_STR("lowestEdited", "4");
1059
+ LXW_PUSH_ATTRIBUTES_STR("rupBuild", "4505");
1060
+
1061
+ lxw_xml_empty_tag(self->file, "fileVersion", &attributes);
1062
+
1063
+ LXW_FREE_ATTRIBUTES();
1064
+ }
1065
+
1066
+ /*
1067
+ * Write the <workbookPr> element.
1068
+ */
1069
+ STATIC void
1070
+ _write_workbook_pr(lxw_workbook *self)
1071
+ {
1072
+ struct xml_attribute_list attributes;
1073
+ struct xml_attribute *attribute;
1074
+
1075
+ LXW_INIT_ATTRIBUTES();
1076
+ LXW_PUSH_ATTRIBUTES_STR("defaultThemeVersion", "124226");
1077
+
1078
+ lxw_xml_empty_tag(self->file, "workbookPr", &attributes);
1079
+
1080
+ LXW_FREE_ATTRIBUTES();
1081
+ }
1082
+
1083
+ /*
1084
+ * Write the <workbookView> element.
1085
+ */
1086
+ STATIC void
1087
+ _write_workbook_view(lxw_workbook *self)
1088
+ {
1089
+ struct xml_attribute_list attributes;
1090
+ struct xml_attribute *attribute;
1091
+
1092
+ LXW_INIT_ATTRIBUTES();
1093
+ LXW_PUSH_ATTRIBUTES_STR("xWindow", "240");
1094
+ LXW_PUSH_ATTRIBUTES_STR("yWindow", "15");
1095
+ LXW_PUSH_ATTRIBUTES_STR("windowWidth", "16095");
1096
+ LXW_PUSH_ATTRIBUTES_STR("windowHeight", "9660");
1097
+
1098
+ if (self->first_sheet)
1099
+ LXW_PUSH_ATTRIBUTES_INT("firstSheet", self->first_sheet);
1100
+
1101
+ if (self->active_sheet)
1102
+ LXW_PUSH_ATTRIBUTES_INT("activeTab", self->active_sheet);
1103
+
1104
+ lxw_xml_empty_tag(self->file, "workbookView", &attributes);
1105
+
1106
+ LXW_FREE_ATTRIBUTES();
1107
+ }
1108
+
1109
+ /*
1110
+ * Write the <bookViews> element.
1111
+ */
1112
+ STATIC void
1113
+ _write_book_views(lxw_workbook *self)
1114
+ {
1115
+ lxw_xml_start_tag(self->file, "bookViews", NULL);
1116
+
1117
+ _write_workbook_view(self);
1118
+
1119
+ lxw_xml_end_tag(self->file, "bookViews");
1120
+ }
1121
+
1122
+ /*
1123
+ * Write the <sheet> element.
1124
+ */
1125
+ STATIC void
1126
+ _write_sheet(lxw_workbook *self, const char *name, uint32_t sheet_id,
1127
+ uint8_t hidden)
1128
+ {
1129
+ struct xml_attribute_list attributes;
1130
+ struct xml_attribute *attribute;
1131
+ char r_id[LXW_MAX_ATTRIBUTE_LENGTH] = "rId1";
1132
+
1133
+ lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", sheet_id);
1134
+
1135
+ LXW_INIT_ATTRIBUTES();
1136
+ LXW_PUSH_ATTRIBUTES_STR("name", name);
1137
+ LXW_PUSH_ATTRIBUTES_INT("sheetId", sheet_id);
1138
+
1139
+ if (hidden)
1140
+ LXW_PUSH_ATTRIBUTES_STR("state", "hidden");
1141
+
1142
+ LXW_PUSH_ATTRIBUTES_STR("r:id", r_id);
1143
+
1144
+ lxw_xml_empty_tag(self->file, "sheet", &attributes);
1145
+
1146
+ LXW_FREE_ATTRIBUTES();
1147
+ }
1148
+
1149
+ /*
1150
+ * Write the <sheets> element.
1151
+ */
1152
+ STATIC void
1153
+ _write_sheets(lxw_workbook *self)
1154
+ {
1155
+ lxw_worksheet *worksheet;
1156
+
1157
+ lxw_xml_start_tag(self->file, "sheets", NULL);
1158
+
1159
+ STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
1160
+ _write_sheet(self, worksheet->name, worksheet->index + 1,
1161
+ worksheet->hidden);
1162
+ }
1163
+
1164
+ lxw_xml_end_tag(self->file, "sheets");
1165
+ }
1166
+
1167
+ /*
1168
+ * Write the <calcPr> element.
1169
+ */
1170
+ STATIC void
1171
+ _write_calc_pr(lxw_workbook *self)
1172
+ {
1173
+ struct xml_attribute_list attributes;
1174
+ struct xml_attribute *attribute;
1175
+
1176
+ LXW_INIT_ATTRIBUTES();
1177
+ LXW_PUSH_ATTRIBUTES_STR("calcId", "124519");
1178
+ LXW_PUSH_ATTRIBUTES_STR("fullCalcOnLoad", "1");
1179
+
1180
+ lxw_xml_empty_tag(self->file, "calcPr", &attributes);
1181
+
1182
+ LXW_FREE_ATTRIBUTES();
1183
+ }
1184
+
1185
+ /*
1186
+ * Write the <definedName> element.
1187
+ */
1188
+ STATIC void
1189
+ _write_defined_name(lxw_workbook *self, lxw_defined_name *defined_name)
1190
+ {
1191
+ struct xml_attribute_list attributes;
1192
+ struct xml_attribute *attribute;
1193
+
1194
+ LXW_INIT_ATTRIBUTES();
1195
+ LXW_PUSH_ATTRIBUTES_STR("name", defined_name->name);
1196
+
1197
+ if (defined_name->index != -1)
1198
+ LXW_PUSH_ATTRIBUTES_INT("localSheetId", defined_name->index);
1199
+
1200
+ if (defined_name->hidden)
1201
+ LXW_PUSH_ATTRIBUTES_INT("hidden", 1);
1202
+
1203
+ lxw_xml_data_element(self->file, "definedName", defined_name->formula,
1204
+ &attributes);
1205
+
1206
+ LXW_FREE_ATTRIBUTES();
1207
+ }
1208
+
1209
+ /*
1210
+ * Write the <definedNames> element.
1211
+ */
1212
+ STATIC void
1213
+ _write_defined_names(lxw_workbook *self)
1214
+ {
1215
+ lxw_defined_name *defined_name;
1216
+
1217
+ if (TAILQ_EMPTY(self->defined_names))
1218
+ return;
1219
+
1220
+ lxw_xml_start_tag(self->file, "definedNames", NULL);
1221
+
1222
+ TAILQ_FOREACH(defined_name, self->defined_names, list_pointers) {
1223
+ _write_defined_name(self, defined_name);
1224
+ }
1225
+
1226
+ lxw_xml_end_tag(self->file, "definedNames");
1227
+ }
1228
+
1229
+ /*****************************************************************************
1230
+ *
1231
+ * XML file assembly functions.
1232
+ *
1233
+ ****************************************************************************/
1234
+
1235
+ /*
1236
+ * Assemble and write the XML file.
1237
+ */
1238
+ void
1239
+ lxw_workbook_assemble_xml_file(lxw_workbook *self)
1240
+ {
1241
+ /* Prepare workbook and sub-objects for writing. */
1242
+ _prepare_workbook(self);
1243
+
1244
+ /* Write the XML declaration. */
1245
+ _workbook_xml_declaration(self);
1246
+
1247
+ /* Write the root workbook element. */
1248
+ _write_workbook(self);
1249
+
1250
+ /* Write the XLSX file version. */
1251
+ _write_file_version(self);
1252
+
1253
+ /* Write the workbook properties. */
1254
+ _write_workbook_pr(self);
1255
+
1256
+ /* Write the workbook view properties. */
1257
+ _write_book_views(self);
1258
+
1259
+ /* Write the worksheet names and ids. */
1260
+ _write_sheets(self);
1261
+
1262
+ /* Write the workbook defined names. */
1263
+ _write_defined_names(self);
1264
+
1265
+ /* Write the workbook calculation properties. */
1266
+ _write_calc_pr(self);
1267
+
1268
+ /* Close the workbook tag. */
1269
+ lxw_xml_end_tag(self->file, "workbook");
1270
+ }
1271
+
1272
+ /*****************************************************************************
1273
+ *
1274
+ * Public functions.
1275
+ *
1276
+ ****************************************************************************/
1277
+
1278
+ /*
1279
+ * Create a new workbook object.
1280
+ */
1281
+ lxw_workbook *
1282
+ workbook_new(const char *filename)
1283
+ {
1284
+ return workbook_new_opt(filename, NULL);
1285
+ }
1286
+
1287
+ /* Deprecated function name for backwards compatibility. */
1288
+ lxw_workbook *
1289
+ new_workbook(const char *filename)
1290
+ {
1291
+ return workbook_new_opt(filename, NULL);
1292
+ }
1293
+
1294
+ /* Deprecated function name for backwards compatibility. */
1295
+ lxw_workbook *
1296
+ new_workbook_opt(const char *filename, lxw_workbook_options *options)
1297
+ {
1298
+ return workbook_new_opt(filename, options);
1299
+ }
1300
+
1301
+ /*
1302
+ * Create a new workbook object with options.
1303
+ */
1304
+ lxw_workbook *
1305
+ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1306
+ {
1307
+ lxw_format *format;
1308
+ lxw_workbook *workbook;
1309
+
1310
+ /* Create the workbook object. */
1311
+ workbook = calloc(1, sizeof(lxw_workbook));
1312
+ GOTO_LABEL_ON_MEM_ERROR(workbook, mem_error);
1313
+ workbook->filename = lxw_strdup(filename);
1314
+
1315
+ /* Add the worksheets list. */
1316
+ workbook->worksheets = calloc(1, sizeof(struct lxw_worksheets));
1317
+ GOTO_LABEL_ON_MEM_ERROR(workbook->worksheets, mem_error);
1318
+ STAILQ_INIT(workbook->worksheets);
1319
+
1320
+ /* Add the worksheet names tree. */
1321
+ workbook->worksheet_names = calloc(1, sizeof(struct lxw_worksheet_names));
1322
+ GOTO_LABEL_ON_MEM_ERROR(workbook->worksheet_names, mem_error);
1323
+ RB_INIT(workbook->worksheet_names);
1324
+
1325
+ /* Add the charts list. */
1326
+ workbook->charts = calloc(1, sizeof(struct lxw_charts));
1327
+ GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
1328
+ STAILQ_INIT(workbook->charts);
1329
+
1330
+ /* Add the ordered charts list to track chart insertion order. */
1331
+ workbook->ordered_charts = calloc(1, sizeof(struct lxw_charts));
1332
+ GOTO_LABEL_ON_MEM_ERROR(workbook->ordered_charts, mem_error);
1333
+ STAILQ_INIT(workbook->ordered_charts);
1334
+
1335
+ /* Add the formats list. */
1336
+ workbook->formats = calloc(1, sizeof(struct lxw_formats));
1337
+ GOTO_LABEL_ON_MEM_ERROR(workbook->formats, mem_error);
1338
+ STAILQ_INIT(workbook->formats);
1339
+
1340
+ /* Add the defined_names list. */
1341
+ workbook->defined_names = calloc(1, sizeof(struct lxw_defined_names));
1342
+ GOTO_LABEL_ON_MEM_ERROR(workbook->defined_names, mem_error);
1343
+ TAILQ_INIT(workbook->defined_names);
1344
+
1345
+ /* Add the shared strings table. */
1346
+ workbook->sst = lxw_sst_new();
1347
+ GOTO_LABEL_ON_MEM_ERROR(workbook->sst, mem_error);
1348
+
1349
+ /* Add the default workbook properties. */
1350
+ workbook->properties = calloc(1, sizeof(lxw_doc_properties));
1351
+ GOTO_LABEL_ON_MEM_ERROR(workbook->properties, mem_error);
1352
+
1353
+ /* Add a hash table to track format indices. */
1354
+ workbook->used_xf_formats = lxw_hash_new(128, 1, 0);
1355
+ GOTO_LABEL_ON_MEM_ERROR(workbook->used_xf_formats, mem_error);
1356
+
1357
+ /* Add the worksheets list. */
1358
+ workbook->custom_properties =
1359
+ calloc(1, sizeof(struct lxw_custom_properties));
1360
+ GOTO_LABEL_ON_MEM_ERROR(workbook->custom_properties, mem_error);
1361
+ STAILQ_INIT(workbook->custom_properties);
1362
+
1363
+ /* Add the default cell format. */
1364
+ format = workbook_add_format(workbook);
1365
+ GOTO_LABEL_ON_MEM_ERROR(format, mem_error);
1366
+
1367
+ /* Initialize its index. */
1368
+ lxw_format_get_xf_index(format);
1369
+
1370
+ if (options) {
1371
+ workbook->options.constant_memory = options->constant_memory;
1372
+ workbook->options.tmpdir = lxw_strdup(options->tmpdir);
1373
+ }
1374
+
1375
+ return workbook;
1376
+
1377
+ mem_error:
1378
+ lxw_workbook_free(workbook);
1379
+ workbook = NULL;
1380
+ return NULL;
1381
+ }
1382
+
1383
+ /*
1384
+ * Add a new worksheet to the Excel workbook.
1385
+ */
1386
+ lxw_worksheet *
1387
+ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1388
+ {
1389
+ lxw_worksheet *worksheet;
1390
+ lxw_worksheet_name *worksheet_name = NULL;
1391
+ lxw_error error;
1392
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1393
+ char *new_name = NULL;
1394
+
1395
+ if (sheetname) {
1396
+ /* Use the user supplied name. */
1397
+ init_data.name = lxw_strdup(sheetname);
1398
+ init_data.quoted_name = lxw_quote_sheetname((char *) sheetname);
1399
+ }
1400
+ else {
1401
+ /* Use the default SheetN name. */
1402
+ new_name = malloc(LXW_MAX_SHEETNAME_LENGTH);
1403
+ GOTO_LABEL_ON_MEM_ERROR(new_name, mem_error);
1404
+
1405
+ lxw_snprintf(new_name, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
1406
+ self->num_sheets + 1);
1407
+ init_data.name = new_name;
1408
+ init_data.quoted_name = lxw_strdup(new_name);
1409
+ }
1410
+
1411
+ /* Check that the worksheet name is valid. */
1412
+ error = workbook_validate_worksheet_name(self, init_data.name);
1413
+ if (error) {
1414
+ LXW_WARN_FORMAT2("workbook_add_worksheet(): worksheet name '%s' has "
1415
+ "error: %s", init_data.name, lxw_strerror(error));
1416
+ goto mem_error;
1417
+ }
1418
+
1419
+ /* Create a struct to find/store the worksheet name/pointer. */
1420
+ worksheet_name = calloc(1, sizeof(struct lxw_worksheet_name));
1421
+ GOTO_LABEL_ON_MEM_ERROR(worksheet_name, mem_error);
1422
+
1423
+ /* Initialize the metadata to pass to the worksheet. */
1424
+ init_data.hidden = 0;
1425
+ init_data.index = self->num_sheets;
1426
+ init_data.sst = self->sst;
1427
+ init_data.optimize = self->options.constant_memory;
1428
+ init_data.active_sheet = &self->active_sheet;
1429
+ init_data.first_sheet = &self->first_sheet;
1430
+ init_data.tmpdir = self->options.tmpdir;
1431
+
1432
+ /* Create a new worksheet object. */
1433
+ worksheet = lxw_worksheet_new(&init_data);
1434
+ GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
1435
+
1436
+ self->num_sheets++;
1437
+ STAILQ_INSERT_TAIL(self->worksheets, worksheet, list_pointers);
1438
+
1439
+ /* Store the worksheet so we can look it up by name. */
1440
+ worksheet_name->name = init_data.name;
1441
+ worksheet_name->worksheet = worksheet;
1442
+ RB_INSERT(lxw_worksheet_names, self->worksheet_names, worksheet_name);
1443
+
1444
+ return worksheet;
1445
+
1446
+ mem_error:
1447
+ free(init_data.name);
1448
+ free(init_data.quoted_name);
1449
+ free(worksheet_name);
1450
+ return NULL;
1451
+ }
1452
+
1453
+ /*
1454
+ * Add a new chart to the Excel workbook.
1455
+ */
1456
+ lxw_chart *
1457
+ workbook_add_chart(lxw_workbook *self, uint8_t type)
1458
+ {
1459
+ lxw_chart *chart;
1460
+
1461
+ /* Create a new chart object. */
1462
+ chart = lxw_chart_new(type);
1463
+
1464
+ if (chart)
1465
+ STAILQ_INSERT_TAIL(self->charts, chart, list_pointers);
1466
+
1467
+ return chart;
1468
+ }
1469
+
1470
+ /*
1471
+ * Add a new format to the Excel workbook.
1472
+ */
1473
+ lxw_format *
1474
+ workbook_add_format(lxw_workbook *self)
1475
+ {
1476
+ /* Create a new format object. */
1477
+ lxw_format *format = lxw_format_new();
1478
+ RETURN_ON_MEM_ERROR(format, NULL);
1479
+
1480
+ format->xf_format_indices = self->used_xf_formats;
1481
+ format->num_xf_formats = &self->num_xf_formats;
1482
+
1483
+ STAILQ_INSERT_TAIL(self->formats, format, list_pointers);
1484
+
1485
+ return format;
1486
+ }
1487
+
1488
+ /*
1489
+ * Call finalization code and close file.
1490
+ */
1491
+ lxw_error
1492
+ workbook_close(lxw_workbook *self)
1493
+ {
1494
+ lxw_worksheet *worksheet = NULL;
1495
+ lxw_packager *packager = NULL;
1496
+ uint8_t error = LXW_NO_ERROR;
1497
+
1498
+ /* Add a default worksheet if non have been added. */
1499
+ if (!self->num_sheets)
1500
+ workbook_add_worksheet(self, NULL);
1501
+
1502
+ /* Ensure that at least one worksheet has been selected. */
1503
+ if (self->active_sheet == 0) {
1504
+ worksheet = STAILQ_FIRST(self->worksheets);
1505
+ worksheet->selected = 1;
1506
+ worksheet->hidden = 0;
1507
+ }
1508
+
1509
+ /* Set the active sheet. */
1510
+ STAILQ_FOREACH(worksheet, self->worksheets, list_pointers) {
1511
+ if (worksheet->index == self->active_sheet)
1512
+ worksheet->active = 1;
1513
+ }
1514
+
1515
+ /* Set the defined names for the worksheets such as Print Titles. */
1516
+ _prepare_defined_names(self);
1517
+
1518
+ /* Prepare the drawings, charts and images. */
1519
+ _prepare_drawings(self);
1520
+
1521
+ /* Add cached data to charts. */
1522
+ _add_chart_cache_data(self);
1523
+
1524
+ /* Create a packager object to assemble sub-elements into a zip file. */
1525
+ packager = lxw_packager_new(self->filename, self->options.tmpdir);
1526
+
1527
+ /* If the packager fails it is generally due to a zip permission error. */
1528
+ if (packager == NULL) {
1529
+ fprintf(stderr, "[ERROR] workbook_close(): "
1530
+ "Error creating '%s'. "
1531
+ "Error = %s\n", self->filename, strerror(errno));
1532
+
1533
+ error = LXW_ERROR_CREATING_XLSX_FILE;
1534
+ goto mem_error;
1535
+ }
1536
+
1537
+ /* Set the workbook object in the packager. */
1538
+ packager->workbook = self;
1539
+
1540
+ /* Assemble all the sub-files in the xlsx package. */
1541
+ error = lxw_create_package(packager);
1542
+
1543
+ /* Error and non-error conditions fall through to the cleanup code. */
1544
+ if (error == LXW_ERROR_CREATING_TMPFILE) {
1545
+ fprintf(stderr, "[ERROR] workbook_close(): "
1546
+ "Error creating tmpfile(s) to assemble '%s'. "
1547
+ "Error = %s\n", self->filename, strerror(errno));
1548
+ }
1549
+
1550
+ /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zlib. */
1551
+ if (error == LXW_ERROR_ZIP_FILE_OPERATION) {
1552
+ fprintf(stderr, "[ERROR] workbook_close(): "
1553
+ "Zlib error while creating xlsx file '%s'. "
1554
+ "Error = %s\n", self->filename, strerror(errno));
1555
+ }
1556
+
1557
+ /* The next 2 error conditions don't set errno. */
1558
+ if (error == LXW_ERROR_ZIP_FILE_ADD) {
1559
+ fprintf(stderr, "[ERROR] workbook_close(): "
1560
+ "Zlib error adding file to xlsx file '%s'.\n",
1561
+ self->filename);
1562
+ }
1563
+
1564
+ if (error == LXW_ERROR_ZIP_CLOSE) {
1565
+ fprintf(stderr, "[ERROR] workbook_close(): "
1566
+ "Zlib error closing xlsx file '%s'.\n", self->filename);
1567
+ }
1568
+
1569
+ mem_error:
1570
+ lxw_packager_free(packager);
1571
+ lxw_workbook_free(self);
1572
+ return error;
1573
+ }
1574
+
1575
+ /*
1576
+ * Create a defined name in Excel. We handle global/workbook level names and
1577
+ * local/worksheet names.
1578
+ */
1579
+ lxw_error
1580
+ workbook_define_name(lxw_workbook *self, const char *name,
1581
+ const char *formula)
1582
+ {
1583
+ return _store_defined_name(self, name, NULL, formula, -1, LXW_FALSE);
1584
+ }
1585
+
1586
+ /*
1587
+ * Set the document properties such as Title, Author etc.
1588
+ */
1589
+ lxw_error
1590
+ workbook_set_properties(lxw_workbook *self, lxw_doc_properties *user_props)
1591
+ {
1592
+ lxw_doc_properties *doc_props;
1593
+
1594
+ /* Free any existing properties. */
1595
+ _free_doc_properties(self->properties);
1596
+
1597
+ doc_props = calloc(1, sizeof(lxw_doc_properties));
1598
+ GOTO_LABEL_ON_MEM_ERROR(doc_props, mem_error);
1599
+
1600
+ /* Copy the user properties to an internal structure. */
1601
+ if (user_props->title) {
1602
+ doc_props->title = lxw_strdup(user_props->title);
1603
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->title, mem_error);
1604
+ }
1605
+
1606
+ if (user_props->subject) {
1607
+ doc_props->subject = lxw_strdup(user_props->subject);
1608
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->subject, mem_error);
1609
+ }
1610
+
1611
+ if (user_props->author) {
1612
+ doc_props->author = lxw_strdup(user_props->author);
1613
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->author, mem_error);
1614
+ }
1615
+
1616
+ if (user_props->manager) {
1617
+ doc_props->manager = lxw_strdup(user_props->manager);
1618
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->manager, mem_error);
1619
+ }
1620
+
1621
+ if (user_props->company) {
1622
+ doc_props->company = lxw_strdup(user_props->company);
1623
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->company, mem_error);
1624
+ }
1625
+
1626
+ if (user_props->category) {
1627
+ doc_props->category = lxw_strdup(user_props->category);
1628
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->category, mem_error);
1629
+ }
1630
+
1631
+ if (user_props->keywords) {
1632
+ doc_props->keywords = lxw_strdup(user_props->keywords);
1633
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->keywords, mem_error);
1634
+ }
1635
+
1636
+ if (user_props->comments) {
1637
+ doc_props->comments = lxw_strdup(user_props->comments);
1638
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->comments, mem_error);
1639
+ }
1640
+
1641
+ if (user_props->status) {
1642
+ doc_props->status = lxw_strdup(user_props->status);
1643
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->status, mem_error);
1644
+ }
1645
+
1646
+ if (user_props->hyperlink_base) {
1647
+ doc_props->hyperlink_base = lxw_strdup(user_props->hyperlink_base);
1648
+ GOTO_LABEL_ON_MEM_ERROR(doc_props->hyperlink_base, mem_error);
1649
+ }
1650
+
1651
+ self->properties = doc_props;
1652
+
1653
+ return LXW_NO_ERROR;
1654
+
1655
+ mem_error:
1656
+ _free_doc_properties(doc_props);
1657
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
1658
+ }
1659
+
1660
+ /*
1661
+ * Set a string custom document property.
1662
+ */
1663
+ lxw_error
1664
+ workbook_set_custom_property_string(lxw_workbook *self, const char *name,
1665
+ const char *value)
1666
+ {
1667
+ lxw_custom_property *custom_property;
1668
+
1669
+ if (!name) {
1670
+ LXW_WARN_FORMAT("workbook_set_custom_property_string(): "
1671
+ "parameter 'name' cannot be NULL.");
1672
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1673
+ }
1674
+
1675
+ if (!value) {
1676
+ LXW_WARN_FORMAT("workbook_set_custom_property_string(): "
1677
+ "parameter 'value' cannot be NULL.");
1678
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1679
+ }
1680
+
1681
+ if (lxw_utf8_strlen(name) > 255) {
1682
+ LXW_WARN_FORMAT("workbook_set_custom_property_string(): parameter "
1683
+ "'name' exceeds Excel length limit of 255.");
1684
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
1685
+ }
1686
+
1687
+ if (lxw_utf8_strlen(value) > 255) {
1688
+ LXW_WARN_FORMAT("workbook_set_custom_property_string(): parameter "
1689
+ "'value' exceeds Excel length limit of 255.");
1690
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
1691
+ }
1692
+
1693
+ /* Create a struct to hold the custom property. */
1694
+ custom_property = calloc(1, sizeof(struct lxw_custom_property));
1695
+ RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
1696
+
1697
+ custom_property->name = lxw_strdup(name);
1698
+ custom_property->u.string = lxw_strdup(value);
1699
+ custom_property->type = LXW_CUSTOM_STRING;
1700
+
1701
+ STAILQ_INSERT_TAIL(self->custom_properties, custom_property,
1702
+ list_pointers);
1703
+
1704
+ return LXW_NO_ERROR;
1705
+ }
1706
+
1707
+ /*
1708
+ * Set a double number custom document property.
1709
+ */
1710
+ lxw_error
1711
+ workbook_set_custom_property_number(lxw_workbook *self, const char *name,
1712
+ double value)
1713
+ {
1714
+ lxw_custom_property *custom_property;
1715
+
1716
+ if (!name) {
1717
+ LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter "
1718
+ "'name' cannot be NULL.");
1719
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1720
+ }
1721
+
1722
+ if (lxw_utf8_strlen(name) > 255) {
1723
+ LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter "
1724
+ "'name' exceeds Excel length limit of 255.");
1725
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
1726
+ }
1727
+
1728
+ /* Create a struct to hold the custom property. */
1729
+ custom_property = calloc(1, sizeof(struct lxw_custom_property));
1730
+ RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
1731
+
1732
+ custom_property->name = lxw_strdup(name);
1733
+ custom_property->u.number = value;
1734
+ custom_property->type = LXW_CUSTOM_DOUBLE;
1735
+
1736
+ STAILQ_INSERT_TAIL(self->custom_properties, custom_property,
1737
+ list_pointers);
1738
+
1739
+ return LXW_NO_ERROR;
1740
+ }
1741
+
1742
+ /*
1743
+ * Set a integer number custom document property.
1744
+ */
1745
+ lxw_error
1746
+ workbook_set_custom_property_integer(lxw_workbook *self, const char *name,
1747
+ int32_t value)
1748
+ {
1749
+ lxw_custom_property *custom_property;
1750
+
1751
+ if (!name) {
1752
+ LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter "
1753
+ "'name' cannot be NULL.");
1754
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1755
+ }
1756
+
1757
+ if (strlen(name) > 255) {
1758
+ LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter "
1759
+ "'name' exceeds Excel length limit of 255.");
1760
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
1761
+ }
1762
+
1763
+ /* Create a struct to hold the custom property. */
1764
+ custom_property = calloc(1, sizeof(struct lxw_custom_property));
1765
+ RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
1766
+
1767
+ custom_property->name = lxw_strdup(name);
1768
+ custom_property->u.integer = value;
1769
+ custom_property->type = LXW_CUSTOM_INTEGER;
1770
+
1771
+ STAILQ_INSERT_TAIL(self->custom_properties, custom_property,
1772
+ list_pointers);
1773
+
1774
+ return LXW_NO_ERROR;
1775
+ }
1776
+
1777
+ /*
1778
+ * Set a boolean custom document property.
1779
+ */
1780
+ lxw_error
1781
+ workbook_set_custom_property_boolean(lxw_workbook *self, const char *name,
1782
+ uint8_t value)
1783
+ {
1784
+ lxw_custom_property *custom_property;
1785
+
1786
+ if (!name) {
1787
+ LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter "
1788
+ "'name' cannot be NULL.");
1789
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1790
+ }
1791
+
1792
+ if (lxw_utf8_strlen(name) > 255) {
1793
+ LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter "
1794
+ "'name' exceeds Excel length limit of 255.");
1795
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
1796
+ }
1797
+
1798
+ /* Create a struct to hold the custom property. */
1799
+ custom_property = calloc(1, sizeof(struct lxw_custom_property));
1800
+ RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
1801
+
1802
+ custom_property->name = lxw_strdup(name);
1803
+ custom_property->u.boolean = value;
1804
+ custom_property->type = LXW_CUSTOM_BOOLEAN;
1805
+
1806
+ STAILQ_INSERT_TAIL(self->custom_properties, custom_property,
1807
+ list_pointers);
1808
+
1809
+ return LXW_NO_ERROR;
1810
+ }
1811
+
1812
+ /*
1813
+ * Set a datetime custom document property.
1814
+ */
1815
+ lxw_error
1816
+ workbook_set_custom_property_datetime(lxw_workbook *self, const char *name,
1817
+ lxw_datetime *datetime)
1818
+ {
1819
+ lxw_custom_property *custom_property;
1820
+
1821
+ if (!name) {
1822
+ LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter "
1823
+ "'name' cannot be NULL.");
1824
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1825
+ }
1826
+
1827
+ if (lxw_utf8_strlen(name) > 255) {
1828
+ LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter "
1829
+ "'name' exceeds Excel length limit of 255.");
1830
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1831
+ }
1832
+
1833
+ if (!datetime) {
1834
+ LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter "
1835
+ "'datetime' cannot be NULL.");
1836
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
1837
+ }
1838
+
1839
+ /* Create a struct to hold the custom property. */
1840
+ custom_property = calloc(1, sizeof(struct lxw_custom_property));
1841
+ RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
1842
+
1843
+ custom_property->name = lxw_strdup(name);
1844
+
1845
+ memcpy(&custom_property->u.datetime, datetime, sizeof(lxw_datetime));
1846
+ custom_property->type = LXW_CUSTOM_DATETIME;
1847
+
1848
+ STAILQ_INSERT_TAIL(self->custom_properties, custom_property,
1849
+ list_pointers);
1850
+
1851
+ return LXW_NO_ERROR;
1852
+ }
1853
+
1854
+ /*
1855
+ * Get a worksheet object from its name.
1856
+ */
1857
+ lxw_worksheet *
1858
+ workbook_get_worksheet_by_name(lxw_workbook *self, const char *name)
1859
+ {
1860
+ lxw_worksheet_name worksheet_name;
1861
+ lxw_worksheet_name *found;
1862
+
1863
+ if (!name)
1864
+ return NULL;
1865
+
1866
+ worksheet_name.name = name;
1867
+ found = RB_FIND(lxw_worksheet_names,
1868
+ self->worksheet_names, &worksheet_name);
1869
+
1870
+ if (found)
1871
+ return found->worksheet;
1872
+ else
1873
+ return NULL;
1874
+ }
1875
+
1876
+ /*
1877
+ * Validate the worksheet name based on Excel's rules.
1878
+ */
1879
+ lxw_error
1880
+ workbook_validate_worksheet_name(lxw_workbook *self, const char *sheetname)
1881
+ {
1882
+ /* Check the UTF-8 length of the worksheet name. */
1883
+ if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX)
1884
+ return LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED;
1885
+
1886
+ /* Check that the worksheet name doesn't contain invalid characters. */
1887
+ if (strpbrk(sheetname, "[]:*?/\\"))
1888
+ return LXW_ERROR_INVALID_SHEETNAME_CHARACTER;
1889
+
1890
+ /* Check if the worksheet name is already in use. */
1891
+ if (workbook_get_worksheet_by_name(self, sheetname))
1892
+ return LXW_ERROR_SHEETNAME_ALREADY_USED;
1893
+
1894
+ return LXW_NO_ERROR;
1895
+ }