xlsxwriter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +40 -0
  3. data/ext/xlsxwriter/chart.c +105 -0
  4. data/ext/xlsxwriter/chart.h +27 -0
  5. data/ext/xlsxwriter/extconf.rb +14 -0
  6. data/ext/xlsxwriter/format.c +67 -0
  7. data/ext/xlsxwriter/format.h +9 -0
  8. data/ext/xlsxwriter/libxlsxwriter/LICENSE.txt +89 -0
  9. data/ext/xlsxwriter/libxlsxwriter/Makefile +141 -0
  10. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter.h +23 -0
  11. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/app.h +79 -0
  12. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chart.h +1093 -0
  13. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/common.h +336 -0
  14. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/content_types.h +74 -0
  15. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/core.h +51 -0
  16. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/custom.h +52 -0
  17. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/drawing.h +111 -0
  18. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/format.h +1214 -0
  19. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/hash_table.h +76 -0
  20. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/packager.h +80 -0
  21. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/relationships.h +77 -0
  22. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/shared_strings.h +83 -0
  23. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/styles.h +77 -0
  24. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/theme.h +47 -0
  25. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +215 -0
  26. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/queue.h +694 -0
  27. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/tmpfileplus.h +53 -0
  28. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/tree.h +801 -0
  29. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/zip.h +375 -0
  30. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/utility.h +166 -0
  31. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/workbook.h +751 -0
  32. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/worksheet.h +2641 -0
  33. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/xmlwriter.h +178 -0
  34. data/ext/xlsxwriter/libxlsxwriter/lib/.gitignore +0 -0
  35. data/ext/xlsxwriter/libxlsxwriter/src/Makefile +125 -0
  36. data/ext/xlsxwriter/libxlsxwriter/src/app.c +439 -0
  37. data/ext/xlsxwriter/libxlsxwriter/src/chart.c +3420 -0
  38. data/ext/xlsxwriter/libxlsxwriter/src/content_types.c +341 -0
  39. data/ext/xlsxwriter/libxlsxwriter/src/core.c +293 -0
  40. data/ext/xlsxwriter/libxlsxwriter/src/custom.c +224 -0
  41. data/ext/xlsxwriter/libxlsxwriter/src/drawing.c +746 -0
  42. data/ext/xlsxwriter/libxlsxwriter/src/format.c +728 -0
  43. data/ext/xlsxwriter/libxlsxwriter/src/hash_table.c +223 -0
  44. data/ext/xlsxwriter/libxlsxwriter/src/packager.c +877 -0
  45. data/ext/xlsxwriter/libxlsxwriter/src/relationships.c +242 -0
  46. data/ext/xlsxwriter/libxlsxwriter/src/shared_strings.c +264 -0
  47. data/ext/xlsxwriter/libxlsxwriter/src/styles.c +1086 -0
  48. data/ext/xlsxwriter/libxlsxwriter/src/theme.c +348 -0
  49. data/ext/xlsxwriter/libxlsxwriter/src/utility.c +512 -0
  50. data/ext/xlsxwriter/libxlsxwriter/src/workbook.c +1895 -0
  51. data/ext/xlsxwriter/libxlsxwriter/src/worksheet.c +4992 -0
  52. data/ext/xlsxwriter/libxlsxwriter/src/xmlwriter.c +355 -0
  53. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/Makefile +44 -0
  54. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/crypt.h +131 -0
  55. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/ioapi.c +247 -0
  56. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/ioapi.h +209 -0
  57. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/iowin32.c +456 -0
  58. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/iowin32.h +28 -0
  59. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/miniunz.c +660 -0
  60. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/minizip.c +520 -0
  61. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/mztools.c +291 -0
  62. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/mztools.h +37 -0
  63. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/unzip.c +2125 -0
  64. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/unzip.h +437 -0
  65. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/zip.c +2007 -0
  66. data/ext/xlsxwriter/libxlsxwriter/third_party/minizip/zip.h +367 -0
  67. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/Makefile +42 -0
  68. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +342 -0
  69. data/ext/xlsxwriter/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.h +53 -0
  70. data/ext/xlsxwriter/workbook.c +257 -0
  71. data/ext/xlsxwriter/workbook.h +42 -0
  72. data/ext/xlsxwriter/workbook_properties.c +103 -0
  73. data/ext/xlsxwriter/workbook_properties.h +10 -0
  74. data/ext/xlsxwriter/worksheet.c +1064 -0
  75. data/ext/xlsxwriter/worksheet.h +74 -0
  76. data/ext/xlsxwriter/xlsxwriter.c +239 -0
  77. data/lib/xlsxwriter.rb +6 -0
  78. data/lib/xlsxwriter/version.rb +3 -0
  79. data/lib/xlsxwriter/worksheet.rb +72 -0
  80. data/test/run-test.rb +11 -0
  81. data/test/support/xlsx_comparable.rb +109 -0
  82. data/test/test-array-formula.rb +33 -0
  83. data/test/test-autofilter.rb +70 -0
  84. data/test/test-chart-area.rb +25 -0
  85. data/test/test-data.rb +65 -0
  86. data/test/test-default-row.rb +25 -0
  87. data/test/test-defined-name.rb +46 -0
  88. data/test/test-escapes.rb +33 -0
  89. data/test/test-fit-to-pages.rb +21 -0
  90. data/test/test-formatting.rb +137 -0
  91. data/test/test-gridlines.rb +15 -0
  92. data/test/test-hyperlink.rb +67 -0
  93. data/test/test-image.rb +84 -0
  94. data/test/test-merge-range.rb +18 -0
  95. data/test/test-misc.rb +29 -0
  96. data/test/test-optimize.rb +32 -0
  97. data/test/test-page-breaks.rb +13 -0
  98. data/test/test-page-setup.rb +28 -0
  99. data/test/test-panes.rb +45 -0
  100. data/test/test-print-area.rb +19 -0
  101. data/test/test-print-options.rb +61 -0
  102. data/test/test-print-scale.rb +12 -0
  103. data/test/test-properties.rb +51 -0
  104. data/test/test-protect.rb +27 -0
  105. data/test/test-repeat.rb +23 -0
  106. data/test/test-row-col-format.rb +35 -0
  107. data/test/test-set-selection.rb +13 -0
  108. data/test/test-set-start-page.rb +13 -0
  109. data/test/test-simple.rb +62 -0
  110. data/test/test-types.rb +17 -0
  111. data/test/xlsx-func-testcase.rb +36 -0
  112. metadata +228 -0
@@ -0,0 +1,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,877 @@
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 uint8_t _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/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 uint8_t
136
+ _write_workbook_file(lxw_packager *self)
137
+ {
138
+ lxw_workbook *workbook = self->workbook;
139
+ int 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 0;
153
+ }
154
+
155
+ /*
156
+ * Write the worksheet files.
157
+ */
158
+ STATIC uint8_t
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
+ int 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 0;
187
+ }
188
+
189
+ /*
190
+ * Write the /xl/media/image?.xml files.
191
+ */
192
+ STATIC uint8_t
193
+ _write_image_files(lxw_packager *self)
194
+ {
195
+ lxw_workbook *workbook = self->workbook;
196
+ lxw_worksheet *worksheet;
197
+ lxw_image_options *image;
198
+ int 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 0;
223
+ }
224
+
225
+ /*
226
+ * Write the chart files.
227
+ */
228
+ STATIC uint8_t
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
+ int 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 0;
257
+ }
258
+
259
+ /*
260
+ * Write the drawing files.
261
+ */
262
+ STATIC uint8_t
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
+ int 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 0;
294
+ }
295
+
296
+ /*
297
+ * Write the sharedStrings.xml file.
298
+ */
299
+ STATIC uint8_t
300
+ _write_shared_strings_file(lxw_packager *self)
301
+ {
302
+ lxw_sst *sst = self->workbook->sst;
303
+ int err;
304
+
305
+ /* Skip the sharedStrings file if there are no shared strings. */
306
+ if (!sst->string_count)
307
+ return 0;
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 0;
321
+ }
322
+
323
+ /*
324
+ * Write the app.xml file.
325
+ */
326
+ STATIC uint8_t
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 = lxw_app_new();
333
+ uint16_t named_range_count = 0;
334
+ char *autofilter;
335
+ char *has_range;
336
+ char number[LXW_ATTR_32] = { 0 };
337
+ int err;
338
+
339
+ app->file = lxw_tmpfile(self->tmpdir);
340
+ if (!app->file)
341
+ return LXW_ERROR_CREATING_TMPFILE;
342
+
343
+ lxw_snprintf(number, LXW_ATTR_32, "%d", self->workbook->num_sheets);
344
+
345
+ lxw_app_add_heading_pair(app, "Worksheets", number);
346
+
347
+ STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
348
+ lxw_app_add_part_name(app, worksheet->name);
349
+ }
350
+
351
+ /* Add the Named Ranges parts. */
352
+ TAILQ_FOREACH(defined_name, workbook->defined_names, list_pointers) {
353
+
354
+ has_range = strchr(defined_name->formula, '!');
355
+ autofilter = strstr(defined_name->app_name, "_FilterDatabase");
356
+
357
+ /* Only store defined names with ranges (except for autofilters). */
358
+ if (has_range && !autofilter) {
359
+ lxw_app_add_part_name(app, defined_name->app_name);
360
+ named_range_count++;
361
+ }
362
+ }
363
+
364
+ /* Add the Named Range heading pairs. */
365
+ if (named_range_count) {
366
+ lxw_snprintf(number, LXW_ATTR_32, "%d", named_range_count);
367
+ lxw_app_add_heading_pair(app, "Named Ranges", number);
368
+ }
369
+
370
+ /* Set the app/doc properties. */
371
+ app->properties = workbook->properties;
372
+
373
+ lxw_app_assemble_xml_file(app);
374
+
375
+ err = _add_file_to_zip(self, app->file, "docProps/app.xml");
376
+ RETURN_ON_ERROR(err);
377
+
378
+ fclose(app->file);
379
+
380
+ lxw_app_free(app);
381
+
382
+ return 0;
383
+ }
384
+
385
+ /*
386
+ * Write the core.xml file.
387
+ */
388
+ STATIC uint8_t
389
+ _write_core_file(lxw_packager *self)
390
+ {
391
+ lxw_core *core = lxw_core_new();
392
+ int err;
393
+
394
+ core->file = lxw_tmpfile(self->tmpdir);
395
+ if (!core->file)
396
+ return LXW_ERROR_CREATING_TMPFILE;
397
+
398
+ core->properties = self->workbook->properties;
399
+
400
+ lxw_core_assemble_xml_file(core);
401
+
402
+ err = _add_file_to_zip(self, core->file, "docProps/core.xml");
403
+ RETURN_ON_ERROR(err);
404
+
405
+ fclose(core->file);
406
+
407
+ lxw_core_free(core);
408
+
409
+ return 0;
410
+ }
411
+
412
+ /*
413
+ * Write the custom.xml file.
414
+ */
415
+ STATIC uint8_t
416
+ _write_custom_file(lxw_packager *self)
417
+ {
418
+ lxw_custom *custom;
419
+ int err;
420
+
421
+ if (STAILQ_EMPTY(self->workbook->custom_properties))
422
+ return 0;
423
+
424
+ custom = lxw_custom_new();
425
+
426
+ custom->file = lxw_tmpfile(self->tmpdir);
427
+ if (!custom->file)
428
+ return LXW_ERROR_CREATING_TMPFILE;
429
+
430
+ custom->custom_properties = self->workbook->custom_properties;
431
+
432
+ lxw_custom_assemble_xml_file(custom);
433
+
434
+ err = _add_file_to_zip(self, custom->file, "docProps/custom.xml");
435
+ RETURN_ON_ERROR(err);
436
+
437
+ fclose(custom->file);
438
+
439
+ lxw_custom_free(custom);
440
+
441
+ return 0;
442
+ }
443
+
444
+ /*
445
+ * Write the theme.xml file.
446
+ */
447
+ STATIC uint8_t
448
+ _write_theme_file(lxw_packager *self)
449
+ {
450
+ lxw_theme *theme = lxw_theme_new();
451
+ int err;
452
+
453
+ theme->file = lxw_tmpfile(self->tmpdir);
454
+ if (!theme->file)
455
+ return LXW_ERROR_CREATING_TMPFILE;
456
+
457
+ lxw_theme_assemble_xml_file(theme);
458
+
459
+ err = _add_file_to_zip(self, theme->file, "xl/theme/theme1.xml");
460
+ RETURN_ON_ERROR(err);
461
+
462
+ fclose(theme->file);
463
+
464
+ lxw_theme_free(theme);
465
+
466
+ return 0;
467
+ }
468
+
469
+ /*
470
+ * Write the styles.xml file.
471
+ */
472
+ STATIC uint8_t
473
+ _write_styles_file(lxw_packager *self)
474
+ {
475
+ lxw_styles *styles = lxw_styles_new();
476
+ lxw_hash_element *hash_element;
477
+ int err;
478
+
479
+ /* Copy the unique and in-use formats from the workbook to the styles
480
+ * xf_format list. */
481
+ LXW_FOREACH_ORDERED(hash_element, self->workbook->used_xf_formats) {
482
+ lxw_format *workbook_format = (lxw_format *) hash_element->value;
483
+ lxw_format *style_format = lxw_format_new();
484
+ memcpy(style_format, workbook_format, sizeof(lxw_format));
485
+ STAILQ_INSERT_TAIL(styles->xf_formats, style_format, list_pointers);
486
+ }
487
+
488
+ styles->font_count = self->workbook->font_count;
489
+ styles->border_count = self->workbook->border_count;
490
+ styles->fill_count = self->workbook->fill_count;
491
+ styles->num_format_count = self->workbook->num_format_count;
492
+ styles->xf_count = self->workbook->used_xf_formats->unique_count;
493
+
494
+ styles->file = lxw_tmpfile(self->tmpdir);
495
+ if (!styles->file)
496
+ return LXW_ERROR_CREATING_TMPFILE;
497
+
498
+ lxw_styles_assemble_xml_file(styles);
499
+
500
+ err = _add_file_to_zip(self, styles->file, "xl/styles.xml");
501
+ RETURN_ON_ERROR(err);
502
+
503
+ fclose(styles->file);
504
+
505
+ lxw_styles_free(styles);
506
+
507
+ return 0;
508
+ }
509
+
510
+ /*
511
+ * Write the ContentTypes.xml file.
512
+ */
513
+ STATIC uint8_t
514
+ _write_content_types_file(lxw_packager *self)
515
+ {
516
+ lxw_content_types *content_types = lxw_content_types_new();
517
+ lxw_workbook *workbook = self->workbook;
518
+ lxw_worksheet *worksheet;
519
+ char filename[LXW_MAX_ATTRIBUTE_LENGTH] = { 0 };
520
+ uint16_t index = 1;
521
+ int err;
522
+
523
+ content_types->file = lxw_tmpfile(self->tmpdir);
524
+ if (!content_types->file)
525
+ return LXW_ERROR_CREATING_TMPFILE;
526
+
527
+ if (workbook->has_png)
528
+ lxw_ct_add_default(content_types, "png", "image/png");
529
+
530
+ if (workbook->has_jpeg)
531
+ lxw_ct_add_default(content_types, "jpeg", "image/jpeg");
532
+
533
+ if (workbook->has_bmp)
534
+ lxw_ct_add_default(content_types, "bmp", "image/bmp");
535
+
536
+ STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
537
+ lxw_snprintf(filename, LXW_FILENAME_LENGTH,
538
+ "/xl/worksheets/sheet%d.xml", index++);
539
+ lxw_ct_add_worksheet_name(content_types, filename);
540
+ }
541
+
542
+ for (index = 1; index <= self->chart_count; index++) {
543
+ lxw_snprintf(filename, LXW_FILENAME_LENGTH, "/xl/charts/chart%d.xml",
544
+ index);
545
+ lxw_ct_add_chart_name(content_types, filename);
546
+ }
547
+
548
+ for (index = 1; index <= self->drawing_count; index++) {
549
+ lxw_snprintf(filename, LXW_FILENAME_LENGTH,
550
+ "/xl/drawings/drawing%d.xml", index);
551
+ lxw_ct_add_drawing_name(content_types, filename);
552
+ }
553
+
554
+ if (workbook->sst->string_count)
555
+ lxw_ct_add_shared_strings(content_types);
556
+
557
+ if (!STAILQ_EMPTY(self->workbook->custom_properties))
558
+ lxw_ct_add_custom_properties(content_types);
559
+
560
+ lxw_content_types_assemble_xml_file(content_types);
561
+
562
+ err = _add_file_to_zip(self, content_types->file, "[Content_Types].xml");
563
+ RETURN_ON_ERROR(err);
564
+
565
+ fclose(content_types->file);
566
+
567
+ lxw_content_types_free(content_types);
568
+
569
+ return 0;
570
+ }
571
+
572
+ /*
573
+ * Write the workbook .rels xml file.
574
+ */
575
+ STATIC uint8_t
576
+ _write_workbook_rels_file(lxw_packager *self)
577
+ {
578
+ lxw_relationships *rels = lxw_relationships_new();
579
+ lxw_workbook *workbook = self->workbook;
580
+ lxw_worksheet *worksheet;
581
+ char sheetname[LXW_FILENAME_LENGTH] = { 0 };
582
+ uint16_t index = 1;
583
+ int err;
584
+
585
+ rels->file = lxw_tmpfile(self->tmpdir);
586
+ if (!rels->file)
587
+ return LXW_ERROR_CREATING_TMPFILE;
588
+
589
+ STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
590
+ lxw_snprintf(sheetname, LXW_FILENAME_LENGTH, "worksheets/sheet%d.xml",
591
+ index++);
592
+ lxw_add_document_relationship(rels, "/worksheet", sheetname);
593
+ }
594
+
595
+ lxw_add_document_relationship(rels, "/theme", "theme/theme1.xml");
596
+ lxw_add_document_relationship(rels, "/styles", "styles.xml");
597
+
598
+ if (workbook->sst->string_count)
599
+ lxw_add_document_relationship(rels, "/sharedStrings",
600
+ "sharedStrings.xml");
601
+
602
+ lxw_relationships_assemble_xml_file(rels);
603
+
604
+ err = _add_file_to_zip(self, rels->file, "xl/_rels/workbook.xml.rels");
605
+ RETURN_ON_ERROR(err);
606
+
607
+ fclose(rels->file);
608
+ lxw_free_relationships(rels);
609
+
610
+ return 0;
611
+ }
612
+
613
+ /*
614
+ * Write the worksheet .rels files for worksheets that contain links to
615
+ * external data such as hyperlinks or drawings.
616
+ */
617
+ STATIC uint8_t
618
+ _write_worksheet_rels_file(lxw_packager *self)
619
+ {
620
+ lxw_relationships *rels;
621
+ lxw_rel_tuple *rel;
622
+ lxw_workbook *workbook = self->workbook;
623
+ lxw_worksheet *worksheet;
624
+ char sheetname[LXW_FILENAME_LENGTH] = { 0 };
625
+ uint16_t index = 0;
626
+ int err;
627
+
628
+ STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
629
+
630
+ index++;
631
+
632
+ if (STAILQ_EMPTY(worksheet->external_hyperlinks) &&
633
+ STAILQ_EMPTY(worksheet->external_drawing_links))
634
+ continue;
635
+
636
+ rels = lxw_relationships_new();
637
+ rels->file = lxw_tmpfile(self->tmpdir);
638
+ if (!rels->file)
639
+ return LXW_ERROR_CREATING_TMPFILE;
640
+
641
+ STAILQ_FOREACH(rel, worksheet->external_hyperlinks, list_pointers) {
642
+ lxw_add_worksheet_relationship(rels, rel->type, rel->target,
643
+ rel->target_mode);
644
+ }
645
+
646
+ STAILQ_FOREACH(rel, worksheet->external_drawing_links, list_pointers) {
647
+ lxw_add_worksheet_relationship(rels, rel->type, rel->target,
648
+ rel->target_mode);
649
+ }
650
+
651
+ lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
652
+ "xl/worksheets/_rels/sheet%d.xml.rels", index);
653
+
654
+ lxw_relationships_assemble_xml_file(rels);
655
+
656
+ err = _add_file_to_zip(self, rels->file, sheetname);
657
+ RETURN_ON_ERROR(err);
658
+
659
+ fclose(rels->file);
660
+ lxw_free_relationships(rels);
661
+ }
662
+
663
+ return 0;
664
+ }
665
+
666
+ /*
667
+ * Write the drawing .rels files for worksheets that contain charts or
668
+ * drawings.
669
+ */
670
+ STATIC uint8_t
671
+ _write_drawing_rels_file(lxw_packager *self)
672
+ {
673
+ lxw_relationships *rels;
674
+ lxw_rel_tuple *rel;
675
+ lxw_workbook *workbook = self->workbook;
676
+ lxw_worksheet *worksheet;
677
+ char sheetname[LXW_FILENAME_LENGTH] = { 0 };
678
+ uint16_t index = 1;
679
+ int err;
680
+
681
+ STAILQ_FOREACH(worksheet, workbook->worksheets, list_pointers) {
682
+
683
+ if (STAILQ_EMPTY(worksheet->drawing_links))
684
+ continue;
685
+
686
+ rels = lxw_relationships_new();
687
+ rels->file = lxw_tmpfile(self->tmpdir);
688
+ if (!rels->file)
689
+ return LXW_ERROR_CREATING_TMPFILE;
690
+
691
+ STAILQ_FOREACH(rel, worksheet->drawing_links, list_pointers) {
692
+ lxw_add_worksheet_relationship(rels, rel->type, rel->target,
693
+ rel->target_mode);
694
+
695
+ }
696
+
697
+ lxw_snprintf(sheetname, LXW_FILENAME_LENGTH,
698
+ "xl/drawings/_rels/drawing%d.xml.rels", index++);
699
+
700
+ lxw_relationships_assemble_xml_file(rels);
701
+
702
+ err = _add_file_to_zip(self, rels->file, sheetname);
703
+ RETURN_ON_ERROR(err);
704
+
705
+ fclose(rels->file);
706
+ lxw_free_relationships(rels);
707
+ }
708
+
709
+ return 0;
710
+ }
711
+
712
+ /*
713
+ * Write the _rels/.rels xml file.
714
+ */
715
+ STATIC uint8_t
716
+ _write_root_rels_file(lxw_packager *self)
717
+ {
718
+ lxw_relationships *rels = lxw_relationships_new();
719
+ int err;
720
+
721
+ rels->file = lxw_tmpfile(self->tmpdir);
722
+ if (!rels->file)
723
+ return LXW_ERROR_CREATING_TMPFILE;
724
+
725
+ lxw_add_document_relationship(rels, "/officeDocument", "xl/workbook.xml");
726
+
727
+ lxw_add_package_relationship(rels,
728
+ "/metadata/core-properties",
729
+ "docProps/core.xml");
730
+
731
+ lxw_add_document_relationship(rels,
732
+ "/extended-properties", "docProps/app.xml");
733
+
734
+ if (!STAILQ_EMPTY(self->workbook->custom_properties))
735
+ lxw_add_document_relationship(rels,
736
+ "/custom-properties",
737
+ "docProps/custom.xml");
738
+
739
+ lxw_relationships_assemble_xml_file(rels);
740
+
741
+ err = _add_file_to_zip(self, rels->file, "_rels/.rels");
742
+ RETURN_ON_ERROR(err);
743
+
744
+ fclose(rels->file);
745
+
746
+ lxw_free_relationships(rels);
747
+
748
+ return 0;
749
+ }
750
+
751
+ /*****************************************************************************
752
+ *
753
+ * Public functions.
754
+ *
755
+ ****************************************************************************/
756
+
757
+ STATIC uint8_t
758
+ _add_file_to_zip(lxw_packager *self, FILE * file, const char *filename)
759
+ {
760
+ int16_t error = ZIP_OK;
761
+ size_t size_read;
762
+
763
+ error = zipOpenNewFileInZip4_64(self->zipfile,
764
+ filename,
765
+ &self->zipfile_info,
766
+ NULL, 0, NULL, 0, NULL,
767
+ Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
768
+ -MAX_WBITS, DEF_MEM_LEVEL,
769
+ Z_DEFAULT_STRATEGY, NULL, 0, 0, 0, 0);
770
+
771
+ if (error != ZIP_OK) {
772
+ LXW_ERROR("Error adding member to zipfile");
773
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
774
+ }
775
+
776
+ fflush(file);
777
+ rewind(file);
778
+
779
+ size_read = fread(self->buffer, 1, self->buffer_size, file);
780
+
781
+ while (size_read) {
782
+
783
+ if (size_read < self->buffer_size) {
784
+ if (feof(file) == 0) {
785
+ LXW_ERROR("Error reading member file data");
786
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
787
+ }
788
+ }
789
+
790
+ error = zipWriteInFileInZip(self->zipfile,
791
+ self->buffer, (unsigned int) size_read);
792
+
793
+ if (error < 0) {
794
+ LXW_ERROR("Error in writing member in the zipfile");
795
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
796
+ }
797
+
798
+ size_read = fread(self->buffer, 1, self->buffer_size, file);
799
+ }
800
+
801
+ if (error < 0) {
802
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
803
+ }
804
+ else {
805
+ error = zipCloseFileInZip(self->zipfile);
806
+ if (error != ZIP_OK) {
807
+ LXW_ERROR("Error in closing member in the zipfile");
808
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_FILE_ADD);
809
+ }
810
+ }
811
+
812
+ return 0;
813
+ }
814
+
815
+ /*
816
+ * Write the xml files that make up the XLXS OPC package.
817
+ */
818
+ uint8_t
819
+ lxw_create_package(lxw_packager *self)
820
+ {
821
+ int8_t error;
822
+
823
+ error = _write_worksheet_files(self);
824
+ RETURN_ON_ERROR(error);
825
+
826
+ error = _write_workbook_file(self);
827
+ RETURN_ON_ERROR(error);
828
+
829
+ error = _write_chart_files(self);
830
+ RETURN_ON_ERROR(error);
831
+
832
+ error = _write_drawing_files(self);
833
+ RETURN_ON_ERROR(error);
834
+
835
+ error = _write_shared_strings_file(self);
836
+ RETURN_ON_ERROR(error);
837
+
838
+ error = _write_app_file(self);
839
+ RETURN_ON_ERROR(error);
840
+
841
+ error = _write_core_file(self);
842
+ RETURN_ON_ERROR(error);
843
+
844
+ error = _write_custom_file(self);
845
+ RETURN_ON_ERROR(error);
846
+
847
+ error = _write_theme_file(self);
848
+ RETURN_ON_ERROR(error);
849
+
850
+ error = _write_styles_file(self);
851
+ RETURN_ON_ERROR(error);
852
+
853
+ error = _write_content_types_file(self);
854
+ RETURN_ON_ERROR(error);
855
+
856
+ error = _write_workbook_rels_file(self);
857
+ RETURN_ON_ERROR(error);
858
+
859
+ error = _write_worksheet_rels_file(self);
860
+ RETURN_ON_ERROR(error);
861
+
862
+ error = _write_drawing_rels_file(self);
863
+ RETURN_ON_ERROR(error);
864
+
865
+ error = _write_image_files(self);
866
+ RETURN_ON_ERROR(error);;
867
+
868
+ error = _write_root_rels_file(self);
869
+ RETURN_ON_ERROR(error);
870
+
871
+ error = zipClose(self->zipfile, NULL);
872
+ if (error) {
873
+ RETURN_ON_ZIP_ERROR(error, LXW_ERROR_ZIP_CLOSE);
874
+ }
875
+
876
+ return 0;
877
+ }