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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +70 -0
- data/Makefile +14 -0
- data/README.md +95 -0
- data/Rakefile +24 -0
- data/appveyor.yml +25 -0
- data/benchmarks/1k_rows.rb +59 -0
- data/benchmarks/20k_rows.rb +26 -0
- data/benchmarks/init.rb +59 -0
- data/benchmarks/memory.rb +49 -0
- data/examples/example.rb +42 -0
- data/examples/example_align.rb +23 -0
- data/examples/example_chart.rb +21 -0
- data/examples/example_colors.rb +37 -0
- data/examples/example_formula.rb +18 -0
- data/examples/example_image.rb +13 -0
- data/examples/example_styles.rb +27 -0
- data/examples/logo.png +0 -0
- data/extconf.rb +0 -0
- data/fast_excel.gemspec +20 -0
- data/lib/fast_excel.rb +600 -0
- data/lib/fast_excel/binding.rb +2819 -0
- data/lib/fast_excel/binding/chart.rb +2666 -0
- data/lib/fast_excel/binding/format.rb +1177 -0
- data/lib/fast_excel/binding/workbook.rb +338 -0
- data/lib/fast_excel/binding/worksheet.rb +1555 -0
- data/libxlsxwriter/.gitignore +49 -0
- data/libxlsxwriter/.indent.pro +125 -0
- data/libxlsxwriter/.travis.yml +25 -0
- data/libxlsxwriter/CONTRIBUTING.md +226 -0
- data/libxlsxwriter/Changes.txt +557 -0
- data/libxlsxwriter/LICENSE.txt +89 -0
- data/libxlsxwriter/Makefile +156 -0
- data/libxlsxwriter/Readme.md +78 -0
- data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +30 -0
- data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +7 -0
- data/libxlsxwriter/include/xlsxwriter.h +23 -0
- data/libxlsxwriter/include/xlsxwriter/app.h +79 -0
- data/libxlsxwriter/include/xlsxwriter/chart.h +3476 -0
- data/libxlsxwriter/include/xlsxwriter/common.h +372 -0
- data/libxlsxwriter/include/xlsxwriter/content_types.h +74 -0
- data/libxlsxwriter/include/xlsxwriter/core.h +51 -0
- data/libxlsxwriter/include/xlsxwriter/custom.h +52 -0
- data/libxlsxwriter/include/xlsxwriter/drawing.h +111 -0
- data/libxlsxwriter/include/xlsxwriter/format.h +1214 -0
- data/libxlsxwriter/include/xlsxwriter/hash_table.h +76 -0
- data/libxlsxwriter/include/xlsxwriter/packager.h +80 -0
- data/libxlsxwriter/include/xlsxwriter/relationships.h +77 -0
- data/libxlsxwriter/include/xlsxwriter/shared_strings.h +83 -0
- data/libxlsxwriter/include/xlsxwriter/styles.h +77 -0
- data/libxlsxwriter/include/xlsxwriter/theme.h +47 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +214 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/queue.h +694 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/tmpfileplus.h +53 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/tree.h +801 -0
- data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +375 -0
- data/libxlsxwriter/include/xlsxwriter/utility.h +166 -0
- data/libxlsxwriter/include/xlsxwriter/workbook.h +757 -0
- data/libxlsxwriter/include/xlsxwriter/worksheet.h +2641 -0
- data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +178 -0
- data/libxlsxwriter/lib/.gitignore +0 -0
- data/libxlsxwriter/libxlsxwriter.podspec +47 -0
- data/libxlsxwriter/src/Makefile +130 -0
- data/libxlsxwriter/src/app.c +443 -0
- data/libxlsxwriter/src/chart.c +6346 -0
- data/libxlsxwriter/src/content_types.c +345 -0
- data/libxlsxwriter/src/core.c +293 -0
- data/libxlsxwriter/src/custom.c +224 -0
- data/libxlsxwriter/src/drawing.c +746 -0
- data/libxlsxwriter/src/format.c +729 -0
- data/libxlsxwriter/src/hash_table.c +223 -0
- data/libxlsxwriter/src/packager.c +948 -0
- data/libxlsxwriter/src/relationships.c +245 -0
- data/libxlsxwriter/src/shared_strings.c +266 -0
- data/libxlsxwriter/src/styles.c +1088 -0
- data/libxlsxwriter/src/theme.c +348 -0
- data/libxlsxwriter/src/utility.c +515 -0
- data/libxlsxwriter/src/workbook.c +1930 -0
- data/libxlsxwriter/src/worksheet.c +5022 -0
- data/libxlsxwriter/src/xmlwriter.c +355 -0
- data/libxlsxwriter/third_party/minizip/Makefile +44 -0
- data/libxlsxwriter/third_party/minizip/Makefile.am +45 -0
- data/libxlsxwriter/third_party/minizip/Makefile.orig +25 -0
- data/libxlsxwriter/third_party/minizip/MiniZip64_Changes.txt +6 -0
- data/libxlsxwriter/third_party/minizip/MiniZip64_info.txt +74 -0
- data/libxlsxwriter/third_party/minizip/README.txt +5 -0
- data/libxlsxwriter/third_party/minizip/configure.ac +32 -0
- data/libxlsxwriter/third_party/minizip/crypt.h +131 -0
- data/libxlsxwriter/third_party/minizip/ioapi.c +247 -0
- data/libxlsxwriter/third_party/minizip/ioapi.h +208 -0
- data/libxlsxwriter/third_party/minizip/iowin32.c +456 -0
- data/libxlsxwriter/third_party/minizip/iowin32.h +28 -0
- data/libxlsxwriter/third_party/minizip/make_vms.com +25 -0
- data/libxlsxwriter/third_party/minizip/miniunz.c +660 -0
- data/libxlsxwriter/third_party/minizip/miniunzip.1 +63 -0
- data/libxlsxwriter/third_party/minizip/minizip.1 +46 -0
- data/libxlsxwriter/third_party/minizip/minizip.c +520 -0
- data/libxlsxwriter/third_party/minizip/minizip.pc.in +12 -0
- data/libxlsxwriter/third_party/minizip/mztools.c +291 -0
- data/libxlsxwriter/third_party/minizip/mztools.h +37 -0
- data/libxlsxwriter/third_party/minizip/unzip.c +2125 -0
- data/libxlsxwriter/third_party/minizip/unzip.h +437 -0
- data/libxlsxwriter/third_party/minizip/zip.c +2007 -0
- data/libxlsxwriter/third_party/minizip/zip.h +367 -0
- data/libxlsxwriter/third_party/tmpfileplus/Makefile +42 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +342 -0
- data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.h +53 -0
- data/libxlsxwriter/version.txt +1 -0
- data/test/date_test.rb +22 -0
- data/test/default_format_test.rb +19 -0
- data/test/format_test.rb +171 -0
- data/test/test_helper.rb +52 -0
- data/test/tmpfile_test.rb +23 -0
- data/test/worksheet_test.rb +86 -0
- 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
|
+
}
|