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,223 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
* hash_table - Hash table functions for libxlsxwriter.
|
|
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 <stdlib.h>
|
|
11
|
+
#include <stdio.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
#include <stdint.h>
|
|
14
|
+
#include "xlsxwriter/hash_table.h"
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Calculate the hash key using the FNV function. See:
|
|
18
|
+
* http://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function
|
|
19
|
+
*/
|
|
20
|
+
STATIC size_t
|
|
21
|
+
_generate_hash_key(void *data, size_t data_len, size_t num_buckets)
|
|
22
|
+
{
|
|
23
|
+
unsigned char *p = data;
|
|
24
|
+
size_t hash = 2166136261U;
|
|
25
|
+
size_t i;
|
|
26
|
+
|
|
27
|
+
for (i = 0; i < data_len; i++)
|
|
28
|
+
hash = (hash * 16777619) ^ p[i];
|
|
29
|
+
|
|
30
|
+
return hash % num_buckets;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Check if an element exists in the hash table and return a pointer
|
|
35
|
+
* to it if it does.
|
|
36
|
+
*/
|
|
37
|
+
lxw_hash_element *
|
|
38
|
+
lxw_hash_key_exists(lxw_hash_table *lxw_hash, void *key, size_t key_len)
|
|
39
|
+
{
|
|
40
|
+
size_t hash_key = _generate_hash_key(key, key_len, lxw_hash->num_buckets);
|
|
41
|
+
struct lxw_hash_bucket_list *list;
|
|
42
|
+
lxw_hash_element *element;
|
|
43
|
+
|
|
44
|
+
if (!lxw_hash->buckets[hash_key]) {
|
|
45
|
+
/* The key isn't in the LXW_HASH hash table. */
|
|
46
|
+
return NULL;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
/* The key is already in the table or there is a hash collision. */
|
|
50
|
+
list = lxw_hash->buckets[hash_key];
|
|
51
|
+
|
|
52
|
+
/* Iterate over the keys in the bucket's linked list. */
|
|
53
|
+
SLIST_FOREACH(element, list, lxw_hash_list_pointers) {
|
|
54
|
+
if (memcmp(element->key, key, key_len) == 0) {
|
|
55
|
+
/* The key already exists in the table. */
|
|
56
|
+
return element;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Key doesn't exist in the list so this is a hash collision. */
|
|
61
|
+
return NULL;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/*
|
|
66
|
+
* Insert or update a value in the LXW_HASH table based on a key
|
|
67
|
+
* and return a pointer to the new or updated element.
|
|
68
|
+
*/
|
|
69
|
+
lxw_hash_element *
|
|
70
|
+
lxw_insert_hash_element(lxw_hash_table *lxw_hash, void *key, void *value,
|
|
71
|
+
size_t key_len)
|
|
72
|
+
{
|
|
73
|
+
size_t hash_key = _generate_hash_key(key, key_len, lxw_hash->num_buckets);
|
|
74
|
+
struct lxw_hash_bucket_list *list = NULL;
|
|
75
|
+
lxw_hash_element *element = NULL;
|
|
76
|
+
|
|
77
|
+
if (!lxw_hash->buckets[hash_key]) {
|
|
78
|
+
/* The key isn't in the LXW_HASH hash table. */
|
|
79
|
+
|
|
80
|
+
/* Create a linked list in the bucket to hold the lxw_hash keys. */
|
|
81
|
+
list = calloc(1, sizeof(struct lxw_hash_bucket_list));
|
|
82
|
+
GOTO_LABEL_ON_MEM_ERROR(list, mem_error1);
|
|
83
|
+
|
|
84
|
+
/* Initialize the bucket linked list. */
|
|
85
|
+
SLIST_INIT(list);
|
|
86
|
+
|
|
87
|
+
/* Create an lxw_hash element to add to the linked list. */
|
|
88
|
+
element = calloc(1, sizeof(lxw_hash_element));
|
|
89
|
+
GOTO_LABEL_ON_MEM_ERROR(element, mem_error1);
|
|
90
|
+
|
|
91
|
+
/* Store the key and value. */
|
|
92
|
+
element->key = key;
|
|
93
|
+
element->value = value;
|
|
94
|
+
|
|
95
|
+
/* Add the lxw_hash element to the bucket's linked list. */
|
|
96
|
+
SLIST_INSERT_HEAD(list, element, lxw_hash_list_pointers);
|
|
97
|
+
|
|
98
|
+
/* Also add it to the insertion order linked list. */
|
|
99
|
+
STAILQ_INSERT_TAIL(lxw_hash->order_list, element,
|
|
100
|
+
lxw_hash_order_pointers);
|
|
101
|
+
|
|
102
|
+
/* Store the bucket list at the hash index. */
|
|
103
|
+
lxw_hash->buckets[hash_key] = list;
|
|
104
|
+
|
|
105
|
+
lxw_hash->used_buckets++;
|
|
106
|
+
lxw_hash->unique_count++;
|
|
107
|
+
|
|
108
|
+
return element;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
/* The key is already in the table or there is a hash collision. */
|
|
112
|
+
list = lxw_hash->buckets[hash_key];
|
|
113
|
+
|
|
114
|
+
/* Iterate over the keys in the bucket's linked list. */
|
|
115
|
+
SLIST_FOREACH(element, list, lxw_hash_list_pointers) {
|
|
116
|
+
if (memcmp(element->key, key, key_len) == 0) {
|
|
117
|
+
/* The key already exists in the table. Update the value. */
|
|
118
|
+
if (lxw_hash->free_value)
|
|
119
|
+
free(element->value);
|
|
120
|
+
|
|
121
|
+
element->value = value;
|
|
122
|
+
return element;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Key doesn't exist in the list so this is a hash collision.
|
|
127
|
+
* Create an lxw_hash element to add to the linked list. */
|
|
128
|
+
element = calloc(1, sizeof(lxw_hash_element));
|
|
129
|
+
GOTO_LABEL_ON_MEM_ERROR(element, mem_error2);
|
|
130
|
+
|
|
131
|
+
/* Store the key and value. */
|
|
132
|
+
element->key = key;
|
|
133
|
+
element->value = value;
|
|
134
|
+
|
|
135
|
+
/* Add the lxw_hash element to the bucket linked list. */
|
|
136
|
+
SLIST_INSERT_HEAD(list, element, lxw_hash_list_pointers);
|
|
137
|
+
|
|
138
|
+
/* Also add it to the insertion order linked list. */
|
|
139
|
+
STAILQ_INSERT_TAIL(lxw_hash->order_list, element,
|
|
140
|
+
lxw_hash_order_pointers);
|
|
141
|
+
|
|
142
|
+
lxw_hash->unique_count++;
|
|
143
|
+
|
|
144
|
+
return element;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
mem_error1:
|
|
148
|
+
free(list);
|
|
149
|
+
|
|
150
|
+
mem_error2:
|
|
151
|
+
free(element);
|
|
152
|
+
return NULL;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* Create a new LXW_HASH hash table object.
|
|
157
|
+
*/
|
|
158
|
+
lxw_hash_table *
|
|
159
|
+
lxw_hash_new(uint32_t num_buckets, uint8_t free_key, uint8_t free_value)
|
|
160
|
+
{
|
|
161
|
+
/* Create the new hash table. */
|
|
162
|
+
lxw_hash_table *lxw_hash = calloc(1, sizeof(lxw_hash_table));
|
|
163
|
+
RETURN_ON_MEM_ERROR(lxw_hash, NULL);
|
|
164
|
+
|
|
165
|
+
lxw_hash->free_key = free_key;
|
|
166
|
+
lxw_hash->free_value = free_value;
|
|
167
|
+
|
|
168
|
+
/* Add the lxw_hash element buckets. */
|
|
169
|
+
lxw_hash->buckets =
|
|
170
|
+
calloc(num_buckets, sizeof(struct lxw_hash_bucket_list *));
|
|
171
|
+
GOTO_LABEL_ON_MEM_ERROR(lxw_hash->buckets, mem_error);
|
|
172
|
+
|
|
173
|
+
/* Add a list for tracking the insertion order. */
|
|
174
|
+
lxw_hash->order_list = calloc(1, sizeof(struct lxw_hash_order_list));
|
|
175
|
+
GOTO_LABEL_ON_MEM_ERROR(lxw_hash->order_list, mem_error);
|
|
176
|
+
|
|
177
|
+
/* Initialize the order list. */
|
|
178
|
+
STAILQ_INIT(lxw_hash->order_list);
|
|
179
|
+
|
|
180
|
+
/* Store the number of buckets to calculate the load factor. */
|
|
181
|
+
lxw_hash->num_buckets = num_buckets;
|
|
182
|
+
|
|
183
|
+
return lxw_hash;
|
|
184
|
+
|
|
185
|
+
mem_error:
|
|
186
|
+
lxw_hash_free(lxw_hash);
|
|
187
|
+
return NULL;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/*
|
|
191
|
+
* Free the LXW_HASH hash table object.
|
|
192
|
+
*/
|
|
193
|
+
void
|
|
194
|
+
lxw_hash_free(lxw_hash_table *lxw_hash)
|
|
195
|
+
{
|
|
196
|
+
size_t i;
|
|
197
|
+
lxw_hash_element *element;
|
|
198
|
+
lxw_hash_element *element_temp;
|
|
199
|
+
|
|
200
|
+
if (!lxw_hash)
|
|
201
|
+
return;
|
|
202
|
+
|
|
203
|
+
/* Free the lxw_hash_elements and data using the ordered linked list. */
|
|
204
|
+
if (lxw_hash->order_list) {
|
|
205
|
+
STAILQ_FOREACH_SAFE(element, lxw_hash->order_list,
|
|
206
|
+
lxw_hash_order_pointers, element_temp) {
|
|
207
|
+
if (lxw_hash->free_key)
|
|
208
|
+
free(element->key);
|
|
209
|
+
if (lxw_hash->free_value)
|
|
210
|
+
free(element->value);
|
|
211
|
+
free(element);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Free the buckets from the hash table. */
|
|
216
|
+
for (i = 0; i < lxw_hash->num_buckets; i++) {
|
|
217
|
+
free(lxw_hash->buckets[i]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
free(lxw_hash->order_list);
|
|
221
|
+
free(lxw_hash->buckets);
|
|
222
|
+
free(lxw_hash);
|
|
223
|
+
}
|
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
* packager - A library for creating Excel XLSX packager 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/packager.h"
|
|
12
|
+
#include "xlsxwriter/hash_table.h"
|
|
13
|
+
#include "xlsxwriter/utility.h"
|
|
14
|
+
|
|
15
|
+
STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file,
|
|
16
|
+
const char *filename);
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Forward declarations.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/*****************************************************************************
|
|
23
|
+
*
|
|
24
|
+
* Private functions.
|
|
25
|
+
*
|
|
26
|
+
****************************************************************************/
|
|
27
|
+
/* Avoid non MSVC definition of _WIN32 in MinGW. */
|
|
28
|
+
|
|
29
|
+
#ifdef __MINGW32__
|
|
30
|
+
#undef _WIN32
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
#ifdef _WIN32
|
|
34
|
+
|
|
35
|
+
/* Silence Windows warning with duplicate symbol for SLIST_ENTRY in local
|
|
36
|
+
* queue.h and widows.h. */
|
|
37
|
+
#undef SLIST_ENTRY
|
|
38
|
+
|
|
39
|
+
#include <windows.h>
|
|
40
|
+
#include "../third_party/minizip/iowin32.h"
|
|
41
|
+
|
|
42
|
+
zipFile
|
|
43
|
+
_open_zipfile_win32(const char *filename)
|
|
44
|
+
{
|
|
45
|
+
int n;
|
|
46
|
+
zlib_filefunc64_def filefunc;
|
|
47
|
+
|
|
48
|
+
wchar_t wide_filename[_MAX_PATH + 1] = L"";
|
|
49
|
+
|
|
50
|
+
/* Build a UTF-16 filename for Win32. */
|
|
51
|
+
n = MultiByteToWideChar(CP_UTF8, 0, filename, (int) strlen(filename),
|
|
52
|
+
wide_filename, _MAX_PATH);
|
|
53
|
+
|
|
54
|
+
if (n == 0) {
|
|
55
|
+
LXW_ERROR("MultiByteToWideChar error");
|
|
56
|
+
return NULL;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Use the native Win32 file handling functions with minizip. */
|
|
60
|
+
fill_win32_filefunc64(&filefunc);
|
|
61
|
+
|
|
62
|
+
return zipOpen2_64(wide_filename, 0, NULL, &filefunc);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#endif
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Create a new packager object.
|
|
69
|
+
*/
|
|
70
|
+
lxw_packager *
|
|
71
|
+
lxw_packager_new(const char *filename, char *tmpdir)
|
|
72
|
+
{
|
|
73
|
+
lxw_packager *packager = calloc(1, sizeof(lxw_packager));
|
|
74
|
+
GOTO_LABEL_ON_MEM_ERROR(packager, mem_error);
|
|
75
|
+
|
|
76
|
+
packager->buffer = calloc(1, LXW_ZIP_BUFFER_SIZE);
|
|
77
|
+
GOTO_LABEL_ON_MEM_ERROR(packager->buffer, mem_error);
|
|
78
|
+
|
|
79
|
+
packager->filename = lxw_strdup(filename);
|
|
80
|
+
packager->tmpdir = tmpdir;
|
|
81
|
+
GOTO_LABEL_ON_MEM_ERROR(packager->filename, mem_error);
|
|
82
|
+
|
|
83
|
+
packager->buffer_size = LXW_ZIP_BUFFER_SIZE;
|
|
84
|
+
|
|
85
|
+
/* Initialize the zip_fileinfo struct to Jan 1 1980 like Excel. */
|
|
86
|
+
packager->zipfile_info.tmz_date.tm_sec = 0;
|
|
87
|
+
packager->zipfile_info.tmz_date.tm_min = 0;
|
|
88
|
+
packager->zipfile_info.tmz_date.tm_hour = 0;
|
|
89
|
+
packager->zipfile_info.tmz_date.tm_mday = 1;
|
|
90
|
+
packager->zipfile_info.tmz_date.tm_mon = 0;
|
|
91
|
+
packager->zipfile_info.tmz_date.tm_year = 1980;
|
|
92
|
+
packager->zipfile_info.dosDate = 0;
|
|
93
|
+
packager->zipfile_info.internal_fa = 0;
|
|
94
|
+
packager->zipfile_info.external_fa = 0;
|
|
95
|
+
|
|
96
|
+
/* Create a zip container for the xlsx file. */
|
|
97
|
+
#ifdef _WIN32
|
|
98
|
+
packager->zipfile = _open_zipfile_win32(packager->filename);
|
|
99
|
+
#else
|
|
100
|
+
packager->zipfile = zipOpen(packager->filename, 0);
|
|
101
|
+
#endif
|
|
102
|
+
|
|
103
|
+
if (packager->zipfile == NULL)
|
|
104
|
+
goto mem_error;
|
|
105
|
+
|
|
106
|
+
return packager;
|
|
107
|
+
|
|
108
|
+
mem_error:
|
|
109
|
+
lxw_packager_free(packager);
|
|
110
|
+
return NULL;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/*
|
|
114
|
+
* Free a packager object.
|
|
115
|
+
*/
|
|
116
|
+
void
|
|
117
|
+
lxw_packager_free(lxw_packager *packager)
|
|
118
|
+
{
|
|
119
|
+
if (!packager)
|
|
120
|
+
return;
|
|
121
|
+
|
|
122
|
+
free(packager->buffer);
|
|
123
|
+
free(packager->filename);
|
|
124
|
+
free(packager);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*****************************************************************************
|
|
128
|
+
*
|
|
129
|
+
* File assembly functions.
|
|
130
|
+
*
|
|
131
|
+
****************************************************************************/
|
|
132
|
+
/*
|
|
133
|
+
* Write the workbook.xml file.
|
|
134
|
+
*/
|
|
135
|
+
STATIC lxw_error
|
|
136
|
+
_write_workbook_file(lxw_packager *self)
|
|
137
|
+
{
|
|
138
|
+
lxw_workbook *workbook = self->workbook;
|
|
139
|
+
lxw_error err;
|
|
140
|
+
|
|
141
|
+
workbook->file = lxw_tmpfile(self->tmpdir);
|
|
142
|
+
if (!workbook->file)
|
|
143
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
144
|
+
|
|
145
|
+
lxw_workbook_assemble_xml_file(workbook);
|
|
146
|
+
|
|
147
|
+
err = _add_file_to_zip(self, workbook->file, "xl/workbook.xml");
|
|
148
|
+
RETURN_ON_ERROR(err);
|
|
149
|
+
|
|
150
|
+
fclose(workbook->file);
|
|
151
|
+
|
|
152
|
+
return LXW_NO_ERROR;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* Write the worksheet files.
|
|
157
|
+
*/
|
|
158
|
+
STATIC lxw_error
|
|
159
|
+
_write_worksheet_files(lxw_packager *self)
|
|
160
|
+
{
|
|
161
|
+
lxw_workbook *workbook = self->workbook;
|
|
162
|
+
lxw_worksheet *worksheet;
|
|
163
|
+
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
164
|
+
uint16_t index = 1;
|
|
165
|
+
lxw_error err;
|
|
166
|
+
|
|
167
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
168
|
+
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
169
|
+
"xl/worksheets/sheet%d.xml", index++);
|
|
170
|
+
|
|
171
|
+
if (worksheet->optimize_row)
|
|
172
|
+
lxw_worksheet_write_single_row(worksheet);
|
|
173
|
+
|
|
174
|
+
worksheet->file = lxw_tmpfile(self->tmpdir);
|
|
175
|
+
if (!worksheet->file)
|
|
176
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
177
|
+
|
|
178
|
+
lxw_worksheet_assemble_xml_file(worksheet);
|
|
179
|
+
|
|
180
|
+
err = _add_file_to_zip(self, worksheet->file, sheetname);
|
|
181
|
+
RETURN_ON_ERROR(err);
|
|
182
|
+
|
|
183
|
+
fclose(worksheet->file);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return LXW_NO_ERROR;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* Write the /xl/media/image?.xml files.
|
|
191
|
+
*/
|
|
192
|
+
STATIC lxw_error
|
|
193
|
+
_write_image_files(lxw_packager *self)
|
|
194
|
+
{
|
|
195
|
+
lxw_workbook *workbook = self->workbook;
|
|
196
|
+
lxw_worksheet *worksheet;
|
|
197
|
+
lxw_image_options *image;
|
|
198
|
+
lxw_error err;
|
|
199
|
+
|
|
200
|
+
char filename[LXW_FILENAME_LENGTH] = { 0 };
|
|
201
|
+
uint16_t index = 1;
|
|
202
|
+
|
|
203
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
204
|
+
|
|
205
|
+
if (STAILQ_EMPTY(worksheet->image_data))
|
|
206
|
+
continue;
|
|
207
|
+
|
|
208
|
+
STAILQ_FOREACH(image, worksheet->image_data, list_pointers) {
|
|
209
|
+
|
|
210
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
211
|
+
"xl/media/image%d.%s", index++, image->extension);
|
|
212
|
+
|
|
213
|
+
rewind(image->stream);
|
|
214
|
+
|
|
215
|
+
err = _add_file_to_zip(self, image->stream, filename);
|
|
216
|
+
RETURN_ON_ERROR(err);
|
|
217
|
+
|
|
218
|
+
fclose(image->stream);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return LXW_NO_ERROR;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
* Write the chart files.
|
|
227
|
+
*/
|
|
228
|
+
STATIC lxw_error
|
|
229
|
+
_write_chart_files(lxw_packager *self)
|
|
230
|
+
{
|
|
231
|
+
lxw_workbook *workbook = self->workbook;
|
|
232
|
+
lxw_chart *chart;
|
|
233
|
+
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
234
|
+
uint16_t index = 1;
|
|
235
|
+
lxw_error err;
|
|
236
|
+
|
|
237
|
+
STAILQ_FOREACH(chart, workbook->ordered_charts, ordered_list_pointers) {
|
|
238
|
+
|
|
239
|
+
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
240
|
+
"xl/charts/chart%d.xml", index++);
|
|
241
|
+
|
|
242
|
+
chart->file = lxw_tmpfile(self->tmpdir);
|
|
243
|
+
if (!chart->file)
|
|
244
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
245
|
+
|
|
246
|
+
lxw_chart_assemble_xml_file(chart);
|
|
247
|
+
|
|
248
|
+
err = _add_file_to_zip(self, chart->file, sheetname);
|
|
249
|
+
RETURN_ON_ERROR(err);
|
|
250
|
+
|
|
251
|
+
self->chart_count++;
|
|
252
|
+
|
|
253
|
+
fclose(chart->file);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return LXW_NO_ERROR;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/*
|
|
260
|
+
* Write the drawing files.
|
|
261
|
+
*/
|
|
262
|
+
STATIC lxw_error
|
|
263
|
+
_write_drawing_files(lxw_packager *self)
|
|
264
|
+
{
|
|
265
|
+
lxw_workbook *workbook = self->workbook;
|
|
266
|
+
lxw_worksheet *worksheet;
|
|
267
|
+
lxw_drawing *drawing;
|
|
268
|
+
char filename[LXW_FILENAME_LENGTH] = { 0 };
|
|
269
|
+
uint16_t index = 1;
|
|
270
|
+
lxw_error err;
|
|
271
|
+
|
|
272
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
273
|
+
drawing = worksheet->drawing;
|
|
274
|
+
|
|
275
|
+
if (drawing) {
|
|
276
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
277
|
+
"xl/drawings/drawing%d.xml", index++);
|
|
278
|
+
|
|
279
|
+
drawing->file = lxw_tmpfile(self->tmpdir);
|
|
280
|
+
if (!drawing->file)
|
|
281
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
282
|
+
|
|
283
|
+
lxw_drawing_assemble_xml_file(drawing);
|
|
284
|
+
err = _add_file_to_zip(self, drawing->file, filename);
|
|
285
|
+
RETURN_ON_ERROR(err);
|
|
286
|
+
|
|
287
|
+
fclose(drawing->file);
|
|
288
|
+
|
|
289
|
+
self->drawing_count++;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return LXW_NO_ERROR;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/*
|
|
297
|
+
* Write the sharedStrings.xml file.
|
|
298
|
+
*/
|
|
299
|
+
STATIC lxw_error
|
|
300
|
+
_write_shared_strings_file(lxw_packager *self)
|
|
301
|
+
{
|
|
302
|
+
lxw_sst *sst = self->workbook->sst;
|
|
303
|
+
lxw_error err;
|
|
304
|
+
|
|
305
|
+
/* Skip the sharedStrings file if there are no shared strings. */
|
|
306
|
+
if (!sst->string_count)
|
|
307
|
+
return LXW_NO_ERROR;
|
|
308
|
+
|
|
309
|
+
sst->file = lxw_tmpfile(self->tmpdir);
|
|
310
|
+
if (!sst->file)
|
|
311
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
312
|
+
|
|
313
|
+
lxw_sst_assemble_xml_file(sst);
|
|
314
|
+
|
|
315
|
+
err = _add_file_to_zip(self, sst->file, "xl/sharedStrings.xml");
|
|
316
|
+
RETURN_ON_ERROR(err);
|
|
317
|
+
|
|
318
|
+
fclose(sst->file);
|
|
319
|
+
|
|
320
|
+
return LXW_NO_ERROR;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/*
|
|
324
|
+
* Write the app.xml file.
|
|
325
|
+
*/
|
|
326
|
+
STATIC lxw_error
|
|
327
|
+
_write_app_file(lxw_packager *self)
|
|
328
|
+
{
|
|
329
|
+
lxw_workbook *workbook = self->workbook;
|
|
330
|
+
lxw_worksheet *worksheet;
|
|
331
|
+
lxw_defined_name *defined_name;
|
|
332
|
+
lxw_app *app;
|
|
333
|
+
uint16_t named_range_count = 0;
|
|
334
|
+
char *autofilter;
|
|
335
|
+
char *has_range;
|
|
336
|
+
char number[LXW_ATTR_32] = { 0 };
|
|
337
|
+
lxw_error err = LXW_NO_ERROR;
|
|
338
|
+
|
|
339
|
+
app = lxw_app_new();
|
|
340
|
+
if (!app) {
|
|
341
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
342
|
+
goto mem_error;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
app->file = lxw_tmpfile(self->tmpdir);
|
|
346
|
+
if (!app->file) {
|
|
347
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
348
|
+
goto mem_error;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_sheets);
|
|
352
|
+
|
|
353
|
+
lxw_app_add_heading_pair(app, "Worksheets", number);
|
|
354
|
+
|
|
355
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
356
|
+
lxw_app_add_part_name(app, worksheet->name);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Add the Named Ranges parts. */
|
|
360
|
+
TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) {
|
|
361
|
+
|
|
362
|
+
has_range = strchr(defined_name->formula, '!');
|
|
363
|
+
autofilter = strstr(defined_name->app_name, "_FilterDatabase");
|
|
364
|
+
|
|
365
|
+
/* Only store defined names with ranges (except for autofilters). */
|
|
366
|
+
if (has_range && !autofilter) {
|
|
367
|
+
lxw_app_add_part_name(app, defined_name->app_name);
|
|
368
|
+
named_range_count++;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Add the Named Range heading pairs. */
|
|
373
|
+
if (named_range_count) {
|
|
374
|
+
lxw_snprintf(number, LXW_ATTR_32, "%d", named_range_count);
|
|
375
|
+
lxw_app_add_heading_pair(app, "Named Ranges", number);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* Set the app/doc properties. */
|
|
379
|
+
app->properties = workbook->properties;
|
|
380
|
+
|
|
381
|
+
lxw_app_assemble_xml_file(app);
|
|
382
|
+
|
|
383
|
+
err = _add_file_to_zip(self, app->file, "docProps/app.xml");
|
|
384
|
+
|
|
385
|
+
fclose(app->file);
|
|
386
|
+
|
|
387
|
+
mem_error:
|
|
388
|
+
lxw_app_free(app);
|
|
389
|
+
|
|
390
|
+
return err;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/*
|
|
394
|
+
* Write the core.xml file.
|
|
395
|
+
*/
|
|
396
|
+
STATIC lxw_error
|
|
397
|
+
_write_core_file(lxw_packager *self)
|
|
398
|
+
{
|
|
399
|
+
lxw_error err = LXW_NO_ERROR;
|
|
400
|
+
lxw_core *core = lxw_core_new();
|
|
401
|
+
|
|
402
|
+
if (!core) {
|
|
403
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
404
|
+
goto mem_error;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
core->file = lxw_tmpfile(self->tmpdir);
|
|
408
|
+
if (!core->file) {
|
|
409
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
410
|
+
goto mem_error;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
core->properties = self->workbook->properties;
|
|
414
|
+
|
|
415
|
+
lxw_core_assemble_xml_file(core);
|
|
416
|
+
|
|
417
|
+
err = _add_file_to_zip(self, core->file, "docProps/core.xml");
|
|
418
|
+
|
|
419
|
+
fclose(core->file);
|
|
420
|
+
|
|
421
|
+
mem_error:
|
|
422
|
+
lxw_core_free(core);
|
|
423
|
+
|
|
424
|
+
return err;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/*
|
|
428
|
+
* Write the custom.xml file.
|
|
429
|
+
*/
|
|
430
|
+
STATIC lxw_error
|
|
431
|
+
_write_custom_file(lxw_packager *self)
|
|
432
|
+
{
|
|
433
|
+
lxw_custom *custom;
|
|
434
|
+
lxw_error err = LXW_NO_ERROR;
|
|
435
|
+
|
|
436
|
+
if (STAILQ_EMPTY(self->workbook->custom_properties))
|
|
437
|
+
return LXW_NO_ERROR;
|
|
438
|
+
|
|
439
|
+
custom = lxw_custom_new();
|
|
440
|
+
if (!custom) {
|
|
441
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
442
|
+
goto mem_error;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
custom->file = lxw_tmpfile(self->tmpdir);
|
|
446
|
+
if (!custom->file) {
|
|
447
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
448
|
+
goto mem_error;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
custom->custom_properties = self->workbook->custom_properties;
|
|
452
|
+
|
|
453
|
+
lxw_custom_assemble_xml_file(custom);
|
|
454
|
+
|
|
455
|
+
err = _add_file_to_zip(self, custom->file, "docProps/custom.xml");
|
|
456
|
+
|
|
457
|
+
fclose(custom->file);
|
|
458
|
+
|
|
459
|
+
mem_error:
|
|
460
|
+
lxw_custom_free(custom);
|
|
461
|
+
return err;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/*
|
|
465
|
+
* Write the theme.xml file.
|
|
466
|
+
*/
|
|
467
|
+
STATIC lxw_error
|
|
468
|
+
_write_theme_file(lxw_packager *self)
|
|
469
|
+
{
|
|
470
|
+
lxw_error err = LXW_NO_ERROR;
|
|
471
|
+
lxw_theme *theme = lxw_theme_new();
|
|
472
|
+
|
|
473
|
+
if (!theme) {
|
|
474
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
475
|
+
goto mem_error;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
theme->file = lxw_tmpfile(self->tmpdir);
|
|
479
|
+
if (!theme->file) {
|
|
480
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
481
|
+
goto mem_error;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
lxw_theme_assemble_xml_file(theme);
|
|
485
|
+
|
|
486
|
+
err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml");
|
|
487
|
+
|
|
488
|
+
fclose(theme->file);
|
|
489
|
+
|
|
490
|
+
mem_error:
|
|
491
|
+
lxw_theme_free(theme);
|
|
492
|
+
|
|
493
|
+
return err;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/*
|
|
497
|
+
* Write the styles.xml file.
|
|
498
|
+
*/
|
|
499
|
+
STATIC lxw_error
|
|
500
|
+
_write_styles_file(lxw_packager *self)
|
|
501
|
+
{
|
|
502
|
+
lxw_styles *styles = lxw_styles_new();
|
|
503
|
+
lxw_hash_element *hash_element;
|
|
504
|
+
lxw_error err = LXW_NO_ERROR;
|
|
505
|
+
|
|
506
|
+
if (!styles) {
|
|
507
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
508
|
+
goto mem_error;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* Copy the unique and in-use formats from the workbook to the styles
|
|
512
|
+
* xf_format list. */
|
|
513
|
+
LXW_FOREACH_ORDERED(hash_element, self->workbook->used_xf_formats) {
|
|
514
|
+
lxw_format *workbook_format = (lxw_format *) hash_element->value;
|
|
515
|
+
lxw_format *style_format = lxw_format_new();
|
|
516
|
+
|
|
517
|
+
if (!style_format) {
|
|
518
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
519
|
+
goto mem_error;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
memcpy(style_format, workbook_format, sizeof(lxw_format));
|
|
523
|
+
STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
styles->font_count = self->workbook->font_count;
|
|
527
|
+
styles->border_count = self->workbook->border_count;
|
|
528
|
+
styles->fill_count = self->workbook->fill_count;
|
|
529
|
+
styles->num_format_count = self->workbook->num_format_count;
|
|
530
|
+
styles->xf_count = self->workbook->used_xf_formats->unique_count;
|
|
531
|
+
|
|
532
|
+
styles->file = lxw_tmpfile(self->tmpdir);
|
|
533
|
+
if (!styles->file) {
|
|
534
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
535
|
+
goto mem_error;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
lxw_styles_assemble_xml_file(styles);
|
|
539
|
+
|
|
540
|
+
err = _add_file_to_zip(self, styles->file, "xl/styles.xml");
|
|
541
|
+
|
|
542
|
+
fclose(styles->file);
|
|
543
|
+
|
|
544
|
+
mem_error:
|
|
545
|
+
lxw_styles_free(styles);
|
|
546
|
+
|
|
547
|
+
return err;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/*
|
|
551
|
+
* Write the ContentTypes.xml file.
|
|
552
|
+
*/
|
|
553
|
+
STATIC lxw_error
|
|
554
|
+
_write_content_types_file(lxw_packager *self)
|
|
555
|
+
{
|
|
556
|
+
lxw_content_types *content_types = lxw_content_types_new();
|
|
557
|
+
lxw_workbook *workbook = self->workbook;
|
|
558
|
+
lxw_worksheet *worksheet;
|
|
559
|
+
char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
|
|
560
|
+
uint16_t index = 1;
|
|
561
|
+
lxw_error err = LXW_NO_ERROR;
|
|
562
|
+
|
|
563
|
+
if (!content_types) {
|
|
564
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
565
|
+
goto mem_error;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
content_types->file = lxw_tmpfile(self->tmpdir);
|
|
569
|
+
if (!content_types->file) {
|
|
570
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
571
|
+
goto mem_error;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (workbook->has_png)
|
|
575
|
+
lxw_ct_add_default(content_types, "png", "image/png");
|
|
576
|
+
|
|
577
|
+
if (workbook->has_jpeg)
|
|
578
|
+
lxw_ct_add_default(content_types, "jpeg", "image/jpeg");
|
|
579
|
+
|
|
580
|
+
if (workbook->has_bmp)
|
|
581
|
+
lxw_ct_add_default(content_types, "bmp", "image/bmp");
|
|
582
|
+
|
|
583
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
584
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
585
|
+
"/xl/worksheets/sheet%d.xml", index++);
|
|
586
|
+
lxw_ct_add_worksheet_name(content_types, filename);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
for (index = 1; index <= self->chart_count; index++) {
|
|
590
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/charts/chart%d.xml",
|
|
591
|
+
index);
|
|
592
|
+
lxw_ct_add_chart_name(content_types, filename);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
for (index = 1; index <= self->drawing_count; index++) {
|
|
596
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
597
|
+
"/xl/drawings/drawing%d.xml", index);
|
|
598
|
+
lxw_ct_add_drawing_name(content_types, filename);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (workbook->sst->string_count)
|
|
602
|
+
lxw_ct_add_shared_strings(content_types);
|
|
603
|
+
|
|
604
|
+
if (!STAILQ_EMPTY(self->workbook->custom_properties))
|
|
605
|
+
lxw_ct_add_custom_properties(content_types);
|
|
606
|
+
|
|
607
|
+
lxw_content_types_assemble_xml_file(content_types);
|
|
608
|
+
|
|
609
|
+
err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml");
|
|
610
|
+
|
|
611
|
+
fclose(content_types->file);
|
|
612
|
+
|
|
613
|
+
mem_error:
|
|
614
|
+
lxw_content_types_free(content_types);
|
|
615
|
+
|
|
616
|
+
return err;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/*
|
|
620
|
+
* Write the workbook .rels xml file.
|
|
621
|
+
*/
|
|
622
|
+
STATIC lxw_error
|
|
623
|
+
_write_workbook_rels_file(lxw_packager *self)
|
|
624
|
+
{
|
|
625
|
+
lxw_relationships *rels = lxw_relationships_new();
|
|
626
|
+
lxw_workbook *workbook = self->workbook;
|
|
627
|
+
lxw_worksheet *worksheet;
|
|
628
|
+
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
629
|
+
uint16_t index = 1;
|
|
630
|
+
lxw_error err = LXW_NO_ERROR;
|
|
631
|
+
|
|
632
|
+
if (!rels) {
|
|
633
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
634
|
+
goto mem_error;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
rels->file = lxw_tmpfile(self->tmpdir);
|
|
638
|
+
if (!rels->file) {
|
|
639
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
640
|
+
goto mem_error;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
644
|
+
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml",
|
|
645
|
+
index++);
|
|
646
|
+
lxw_add_document_relationship(rels, "/worksheet", sheetname);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml");
|
|
650
|
+
lxw_add_document_relationship(rels, "/styles", "styles.xml");
|
|
651
|
+
|
|
652
|
+
if (workbook->sst->string_count)
|
|
653
|
+
lxw_add_document_relationship(rels, "/sharedStrings",
|
|
654
|
+
"sharedStrings.xml");
|
|
655
|
+
|
|
656
|
+
lxw_relationships_assemble_xml_file(rels);
|
|
657
|
+
|
|
658
|
+
err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels");
|
|
659
|
+
|
|
660
|
+
fclose(rels->file);
|
|
661
|
+
|
|
662
|
+
mem_error:
|
|
663
|
+
lxw_free_relationships(rels);
|
|
664
|
+
|
|
665
|
+
return err;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/*
|
|
669
|
+
* Write the worksheet .rels files for worksheets that contain links to
|
|
670
|
+
* external data such as hyperlinks or drawings.
|
|
671
|
+
*/
|
|
672
|
+
STATIC lxw_error
|
|
673
|
+
_write_worksheet_rels_file(lxw_packager *self)
|
|
674
|
+
{
|
|
675
|
+
lxw_relationships *rels;
|
|
676
|
+
lxw_rel_tuple *rel;
|
|
677
|
+
lxw_workbook *workbook = self->workbook;
|
|
678
|
+
lxw_worksheet *worksheet;
|
|
679
|
+
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
680
|
+
uint16_t index = 0;
|
|
681
|
+
lxw_error err;
|
|
682
|
+
|
|
683
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
684
|
+
|
|
685
|
+
index++;
|
|
686
|
+
|
|
687
|
+
if (STAILQ_EMPTY(worksheet->external_hyperlinks) &&
|
|
688
|
+
STAILQ_EMPTY(worksheet->external_drawing_links))
|
|
689
|
+
continue;
|
|
690
|
+
|
|
691
|
+
rels = lxw_relationships_new();
|
|
692
|
+
|
|
693
|
+
rels->file = lxw_tmpfile(self->tmpdir);
|
|
694
|
+
if (!rels->file) {
|
|
695
|
+
lxw_free_relationships(rels);
|
|
696
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
|
|
700
|
+
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
701
|
+
rel->target_mode);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
|
|
705
|
+
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
706
|
+
rel->target_mode);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
710
|
+
"xl/worksheets/_rels/sheet%d.xml.rels", index);
|
|
711
|
+
|
|
712
|
+
lxw_relationships_assemble_xml_file(rels);
|
|
713
|
+
|
|
714
|
+
err = _add_file_to_zip(self, rels->file, sheetname);
|
|
715
|
+
|
|
716
|
+
fclose(rels->file);
|
|
717
|
+
lxw_free_relationships(rels);
|
|
718
|
+
|
|
719
|
+
RETURN_ON_ERROR(err);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return LXW_NO_ERROR;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/*
|
|
726
|
+
* Write the drawing .rels files for worksheets that contain charts or
|
|
727
|
+
* drawings.
|
|
728
|
+
*/
|
|
729
|
+
STATIC lxw_error
|
|
730
|
+
_write_drawing_rels_file(lxw_packager *self)
|
|
731
|
+
{
|
|
732
|
+
lxw_relationships *rels;
|
|
733
|
+
lxw_rel_tuple *rel;
|
|
734
|
+
lxw_workbook *workbook = self->workbook;
|
|
735
|
+
lxw_worksheet *worksheet;
|
|
736
|
+
char sheetname[LXW_FILENAME_LENGTH] = { 0 };
|
|
737
|
+
uint16_t index = 1;
|
|
738
|
+
lxw_error err;
|
|
739
|
+
|
|
740
|
+
STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
|
|
741
|
+
|
|
742
|
+
if (STAILQ_EMPTY(worksheet->drawing_links))
|
|
743
|
+
continue;
|
|
744
|
+
|
|
745
|
+
rels = lxw_relationships_new();
|
|
746
|
+
|
|
747
|
+
rels->file = lxw_tmpfile(self->tmpdir);
|
|
748
|
+
if (!rels->file) {
|
|
749
|
+
lxw_free_relationships(rels);
|
|
750
|
+
return LXW_ERROR_CREATING_TMPFILE;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) {
|
|
754
|
+
lxw_add_worksheet_relationship(rels, rel->type, rel->target,
|
|
755
|
+
rel->target_mode);
|
|
756
|
+
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
|
|
760
|
+
"xl/drawings/_rels/drawing%d.xml.rels", index++);
|
|
761
|
+
|
|
762
|
+
lxw_relationships_assemble_xml_file(rels);
|
|
763
|
+
|
|
764
|
+
err = _add_file_to_zip(self, rels->file, sheetname);
|
|
765
|
+
|
|
766
|
+
fclose(rels->file);
|
|
767
|
+
lxw_free_relationships(rels);
|
|
768
|
+
|
|
769
|
+
RETURN_ON_ERROR(err);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return LXW_NO_ERROR;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/*
|
|
776
|
+
* Write the _rels/.rels xml file.
|
|
777
|
+
*/
|
|
778
|
+
STATIC lxw_error
|
|
779
|
+
_write_root_rels_file(lxw_packager *self)
|
|
780
|
+
{
|
|
781
|
+
lxw_relationships *rels = lxw_relationships_new();
|
|
782
|
+
lxw_error err = LXW_NO_ERROR;
|
|
783
|
+
|
|
784
|
+
if (!rels) {
|
|
785
|
+
err = LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
786
|
+
goto mem_error;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
rels->file = lxw_tmpfile(self->tmpdir);
|
|
790
|
+
if (!rels->file) {
|
|
791
|
+
err = LXW_ERROR_CREATING_TMPFILE;
|
|
792
|
+
goto mem_error;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
lxw_add_document_relationship(rels, "/officeDocument", "xl/workbook.xml");
|
|
796
|
+
|
|
797
|
+
lxw_add_package_relationship(rels,
|
|
798
|
+
"/metadata/core-properties",
|
|
799
|
+
"docProps/core.xml");
|
|
800
|
+
|
|
801
|
+
lxw_add_document_relationship(rels,
|
|
802
|
+
"/extended-properties", "docProps/app.xml");
|
|
803
|
+
|
|
804
|
+
if (!STAILQ_EMPTY(self->workbook->custom_properties))
|
|
805
|
+
lxw_add_document_relationship(rels,
|
|
806
|
+
"/custom-properties",
|
|
807
|
+
"docProps/custom.xml");
|
|
808
|
+
|
|
809
|
+
lxw_relationships_assemble_xml_file(rels);
|
|
810
|
+
|
|
811
|
+
err = _add_file_to_zip(self, rels->file, "_rels/.rels");
|
|
812
|
+
|
|
813
|
+
fclose(rels->file);
|
|
814
|
+
|
|
815
|
+
mem_error:
|
|
816
|
+
lxw_free_relationships(rels);
|
|
817
|
+
|
|
818
|
+
return err;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/*****************************************************************************
|
|
822
|
+
*
|
|
823
|
+
* Public functions.
|
|
824
|
+
*
|
|
825
|
+
****************************************************************************/
|
|
826
|
+
|
|
827
|
+
STATIC lxw_error
|
|
828
|
+
_add_file_to_zip(lxw_packager *self, FILE * file, const char *filename)
|
|
829
|
+
{
|
|
830
|
+
int16_t error = ZIP_OK;
|
|
831
|
+
size_t size_read;
|
|
832
|
+
|
|
833
|
+
error = zipOpenNewFileInZip4_64(self->zipfile,
|
|
834
|
+
filename,
|
|
835
|
+
&self->zipfile_info,
|
|
836
|
+
NULL, 0, NULL, 0, NULL,
|
|
837
|
+
Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
|
|
838
|
+
-MAX_WBITS, DEF_MEM_LEVEL,
|
|
839
|
+
Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0);
|
|
840
|
+
|
|
841
|
+
if (error != ZIP_OK) {
|
|
842
|
+
LXW_ERROR("Error adding member to zipfile");
|
|
843
|
+
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
fflush(file);
|
|
847
|
+
rewind(file);
|
|
848
|
+
|
|
849
|
+
size_read = fread(self->buffer, 1, self->buffer_size, file);
|
|
850
|
+
|
|
851
|
+
while (size_read) {
|
|
852
|
+
|
|
853
|
+
if (size_read < self->buffer_size) {
|
|
854
|
+
if (feof(file) == 0) {
|
|
855
|
+
LXW_ERROR("Error reading member file data");
|
|
856
|
+
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
error = zipWriteInFileInZip(self->zipfile,
|
|
861
|
+
self->buffer, (unsigned int) size_read);
|
|
862
|
+
|
|
863
|
+
if (error < 0) {
|
|
864
|
+
LXW_ERROR("Error in writing member in the zipfile");
|
|
865
|
+
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
size_read = fread(self->buffer, 1, self->buffer_size, file);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (error < 0) {
|
|
872
|
+
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
error = zipCloseFileInZip(self->zipfile);
|
|
876
|
+
if (error != ZIP_OK) {
|
|
877
|
+
LXW_ERROR("Error in closing member in the zipfile");
|
|
878
|
+
RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return LXW_NO_ERROR;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/*
|
|
886
|
+
* Write the xml files that make up the XLXS OPC package.
|
|
887
|
+
*/
|
|
888
|
+
lxw_error
|
|
889
|
+
lxw_create_package(lxw_packager *self)
|
|
890
|
+
{
|
|
891
|
+
lxw_error error;
|
|
892
|
+
int8_t zip_error;
|
|
893
|
+
|
|
894
|
+
error = _write_worksheet_files(self);
|
|
895
|
+
RETURN_ON_ERROR(error);
|
|
896
|
+
|
|
897
|
+
error = _write_workbook_file(self);
|
|
898
|
+
RETURN_ON_ERROR(error);
|
|
899
|
+
|
|
900
|
+
error = _write_chart_files(self);
|
|
901
|
+
RETURN_ON_ERROR(error);
|
|
902
|
+
|
|
903
|
+
error = _write_drawing_files(self);
|
|
904
|
+
RETURN_ON_ERROR(error);
|
|
905
|
+
|
|
906
|
+
error = _write_shared_strings_file(self);
|
|
907
|
+
RETURN_ON_ERROR(error);
|
|
908
|
+
|
|
909
|
+
error = _write_app_file(self);
|
|
910
|
+
RETURN_ON_ERROR(error);
|
|
911
|
+
|
|
912
|
+
error = _write_core_file(self);
|
|
913
|
+
RETURN_ON_ERROR(error);
|
|
914
|
+
|
|
915
|
+
error = _write_custom_file(self);
|
|
916
|
+
RETURN_ON_ERROR(error);
|
|
917
|
+
|
|
918
|
+
error = _write_theme_file(self);
|
|
919
|
+
RETURN_ON_ERROR(error);
|
|
920
|
+
|
|
921
|
+
error = _write_styles_file(self);
|
|
922
|
+
RETURN_ON_ERROR(error);
|
|
923
|
+
|
|
924
|
+
error = _write_content_types_file(self);
|
|
925
|
+
RETURN_ON_ERROR(error);
|
|
926
|
+
|
|
927
|
+
error = _write_workbook_rels_file(self);
|
|
928
|
+
RETURN_ON_ERROR(error);
|
|
929
|
+
|
|
930
|
+
error = _write_worksheet_rels_file(self);
|
|
931
|
+
RETURN_ON_ERROR(error);
|
|
932
|
+
|
|
933
|
+
error = _write_drawing_rels_file(self);
|
|
934
|
+
RETURN_ON_ERROR(error);
|
|
935
|
+
|
|
936
|
+
error = _write_image_files(self);
|
|
937
|
+
RETURN_ON_ERROR(error);;
|
|
938
|
+
|
|
939
|
+
error = _write_root_rels_file(self);
|
|
940
|
+
RETURN_ON_ERROR(error);
|
|
941
|
+
|
|
942
|
+
zip_error = zipClose(self->zipfile, NULL);
|
|
943
|
+
if (zip_error) {
|
|
944
|
+
RETURN_ON_ZIP_ERROR(zip_error, LXW_ERROR_ZIP_CLOSE);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return LXW_NO_ERROR;
|
|
948
|
+
}
|