kmadej_fast_excel_fork 0.2.2

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