kmadej_fast_excel_fork 0.2.2

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