fast_excel 0.1.4 → 0.1.5
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 +4 -4
- data/.gitignore +5 -2
- data/Makefile +14 -0
- data/README.md +7 -2
- data/Rakefile +10 -0
- data/extconf.rb +0 -0
- data/fast_excel.gemspec +3 -1
- data/lib/fast_excel/binding.rb +3 -7
- data/lib/rubygems_plugin.rb +3 -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/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/include/xlsxwriter.h +23 -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
- metadata +89 -6
- data/binaries/libxlsxwriter-alpine.so +0 -0
- data/binaries/libxlsxwriter-darwin.dylib +0 -0
- data/binaries/libxlsxwriter-glibc.so +0 -0
|
@@ -0,0 +1,5022 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
* worksheet - A library for creating Excel XLSX worksheet 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 <ctype.h>
|
|
11
|
+
|
|
12
|
+
#include "xlsxwriter/xmlwriter.h"
|
|
13
|
+
#include "xlsxwriter/worksheet.h"
|
|
14
|
+
#include "xlsxwriter/format.h"
|
|
15
|
+
#include "xlsxwriter/utility.h"
|
|
16
|
+
#include "xlsxwriter/relationships.h"
|
|
17
|
+
|
|
18
|
+
#define LXW_STR_MAX 32767
|
|
19
|
+
#define LXW_BUFFER_SIZE 4096
|
|
20
|
+
#define LXW_PORTRAIT 1
|
|
21
|
+
#define LXW_LANDSCAPE 0
|
|
22
|
+
#define LXW_PRINT_ACROSS 1
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Forward declarations.
|
|
26
|
+
*/
|
|
27
|
+
STATIC void _worksheet_write_rows(lxw_worksheet *self);
|
|
28
|
+
STATIC int _row_cmp(lxw_row *row1, lxw_row *row2);
|
|
29
|
+
STATIC int _cell_cmp(lxw_cell *cell1, lxw_cell *cell2);
|
|
30
|
+
|
|
31
|
+
#ifndef __clang_analyzer__
|
|
32
|
+
LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp);
|
|
33
|
+
LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp);
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
/*****************************************************************************
|
|
37
|
+
*
|
|
38
|
+
* Private functions.
|
|
39
|
+
*
|
|
40
|
+
****************************************************************************/
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* Find but don't create a row object for a given row number.
|
|
44
|
+
*/
|
|
45
|
+
lxw_row *
|
|
46
|
+
lxw_worksheet_find_row(lxw_worksheet *self, lxw_row_t row_num)
|
|
47
|
+
{
|
|
48
|
+
lxw_row row;
|
|
49
|
+
|
|
50
|
+
row.row_num = row_num;
|
|
51
|
+
|
|
52
|
+
return RB_FIND(lxw_table_rows, self->table, &row);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Find but don't create a cell object for a given row object and col number.
|
|
57
|
+
*/
|
|
58
|
+
lxw_cell *
|
|
59
|
+
lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num)
|
|
60
|
+
{
|
|
61
|
+
lxw_cell cell;
|
|
62
|
+
|
|
63
|
+
if (!row)
|
|
64
|
+
return NULL;
|
|
65
|
+
|
|
66
|
+
cell.col_num = col_num;
|
|
67
|
+
|
|
68
|
+
return RB_FIND(lxw_table_cells, row->cells, &cell);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* Create a new worksheet object.
|
|
73
|
+
*/
|
|
74
|
+
lxw_worksheet *
|
|
75
|
+
lxw_worksheet_new(lxw_worksheet_init_data *init_data)
|
|
76
|
+
{
|
|
77
|
+
lxw_worksheet *worksheet = calloc(1, sizeof(lxw_worksheet));
|
|
78
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
|
|
79
|
+
|
|
80
|
+
worksheet->table = calloc(1, sizeof(struct lxw_table_rows));
|
|
81
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->table, mem_error);
|
|
82
|
+
RB_INIT(worksheet->table);
|
|
83
|
+
|
|
84
|
+
worksheet->hyperlinks = calloc(1, sizeof(struct lxw_table_rows));
|
|
85
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->hyperlinks, mem_error);
|
|
86
|
+
RB_INIT(worksheet->hyperlinks);
|
|
87
|
+
|
|
88
|
+
/* Initialize the cached rows. */
|
|
89
|
+
worksheet->table->cached_row_num = LXW_ROW_MAX + 1;
|
|
90
|
+
worksheet->hyperlinks->cached_row_num = LXW_ROW_MAX + 1;
|
|
91
|
+
|
|
92
|
+
if (init_data && init_data->optimize) {
|
|
93
|
+
worksheet->array = calloc(LXW_COL_MAX, sizeof(struct lxw_cell *));
|
|
94
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->array, mem_error);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
worksheet->col_options =
|
|
98
|
+
calloc(LXW_COL_META_MAX, sizeof(lxw_col_options *));
|
|
99
|
+
worksheet->col_options_max = LXW_COL_META_MAX;
|
|
100
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->col_options, mem_error);
|
|
101
|
+
|
|
102
|
+
worksheet->col_formats = calloc(LXW_COL_META_MAX, sizeof(lxw_format *));
|
|
103
|
+
worksheet->col_formats_max = LXW_COL_META_MAX;
|
|
104
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->col_formats, mem_error);
|
|
105
|
+
|
|
106
|
+
worksheet->optimize_row = calloc(1, sizeof(struct lxw_row));
|
|
107
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_row, mem_error);
|
|
108
|
+
worksheet->optimize_row->height = LXW_DEF_ROW_HEIGHT;
|
|
109
|
+
|
|
110
|
+
worksheet->merged_ranges = calloc(1, sizeof(struct lxw_merged_ranges));
|
|
111
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->merged_ranges, mem_error);
|
|
112
|
+
STAILQ_INIT(worksheet->merged_ranges);
|
|
113
|
+
|
|
114
|
+
worksheet->image_data = calloc(1, sizeof(struct lxw_image_data));
|
|
115
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->image_data, mem_error);
|
|
116
|
+
STAILQ_INIT(worksheet->image_data);
|
|
117
|
+
|
|
118
|
+
worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_data));
|
|
119
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error);
|
|
120
|
+
STAILQ_INIT(worksheet->chart_data);
|
|
121
|
+
|
|
122
|
+
worksheet->selections = calloc(1, sizeof(struct lxw_selections));
|
|
123
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error);
|
|
124
|
+
STAILQ_INIT(worksheet->selections);
|
|
125
|
+
|
|
126
|
+
worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
|
|
127
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
|
|
128
|
+
STAILQ_INIT(worksheet->external_hyperlinks);
|
|
129
|
+
|
|
130
|
+
worksheet->external_drawing_links =
|
|
131
|
+
calloc(1, sizeof(struct lxw_rel_tuples));
|
|
132
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->external_drawing_links, mem_error);
|
|
133
|
+
STAILQ_INIT(worksheet->external_drawing_links);
|
|
134
|
+
|
|
135
|
+
worksheet->drawing_links = calloc(1, sizeof(struct lxw_rel_tuples));
|
|
136
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_links, mem_error);
|
|
137
|
+
STAILQ_INIT(worksheet->drawing_links);
|
|
138
|
+
|
|
139
|
+
if (init_data && init_data->optimize) {
|
|
140
|
+
FILE *tmpfile;
|
|
141
|
+
|
|
142
|
+
tmpfile = lxw_tmpfile(init_data->tmpdir);
|
|
143
|
+
if (!tmpfile) {
|
|
144
|
+
LXW_ERROR("Error creating tmpfile() for worksheet in "
|
|
145
|
+
"'constant_memory' mode.");
|
|
146
|
+
goto mem_error;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
worksheet->optimize_tmpfile = tmpfile;
|
|
150
|
+
GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_tmpfile, mem_error);
|
|
151
|
+
worksheet->file = worksheet->optimize_tmpfile;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Initialize the worksheet dimensions. */
|
|
155
|
+
worksheet->dim_rowmax = 0;
|
|
156
|
+
worksheet->dim_colmax = 0;
|
|
157
|
+
worksheet->dim_rowmin = LXW_ROW_MAX;
|
|
158
|
+
worksheet->dim_colmin = LXW_COL_MAX;
|
|
159
|
+
|
|
160
|
+
worksheet->default_row_height = LXW_DEF_ROW_HEIGHT;
|
|
161
|
+
worksheet->default_row_pixels = 20;
|
|
162
|
+
worksheet->default_col_pixels = 64;
|
|
163
|
+
|
|
164
|
+
/* Initialize the page setup properties. */
|
|
165
|
+
worksheet->fit_height = 0;
|
|
166
|
+
worksheet->fit_width = 0;
|
|
167
|
+
worksheet->page_start = 0;
|
|
168
|
+
worksheet->print_scale = 100;
|
|
169
|
+
worksheet->fit_page = 0;
|
|
170
|
+
worksheet->orientation = LXW_TRUE;
|
|
171
|
+
worksheet->page_order = 0;
|
|
172
|
+
worksheet->page_setup_changed = LXW_FALSE;
|
|
173
|
+
worksheet->page_view = LXW_FALSE;
|
|
174
|
+
worksheet->paper_size = 0;
|
|
175
|
+
worksheet->vertical_dpi = 0;
|
|
176
|
+
worksheet->horizontal_dpi = 0;
|
|
177
|
+
worksheet->margin_left = 0.7;
|
|
178
|
+
worksheet->margin_right = 0.7;
|
|
179
|
+
worksheet->margin_top = 0.75;
|
|
180
|
+
worksheet->margin_bottom = 0.75;
|
|
181
|
+
worksheet->margin_header = 0.3;
|
|
182
|
+
worksheet->margin_footer = 0.3;
|
|
183
|
+
worksheet->print_gridlines = 0;
|
|
184
|
+
worksheet->screen_gridlines = 1;
|
|
185
|
+
worksheet->print_options_changed = 0;
|
|
186
|
+
worksheet->zoom = 100;
|
|
187
|
+
worksheet->zoom_scale_normal = LXW_TRUE;
|
|
188
|
+
worksheet->show_zeros = LXW_TRUE;
|
|
189
|
+
worksheet->outline_on = LXW_TRUE;
|
|
190
|
+
worksheet->tab_color = LXW_COLOR_UNSET;
|
|
191
|
+
|
|
192
|
+
if (init_data) {
|
|
193
|
+
worksheet->name = init_data->name;
|
|
194
|
+
worksheet->quoted_name = init_data->quoted_name;
|
|
195
|
+
worksheet->tmpdir = init_data->tmpdir;
|
|
196
|
+
worksheet->index = init_data->index;
|
|
197
|
+
worksheet->hidden = init_data->hidden;
|
|
198
|
+
worksheet->sst = init_data->sst;
|
|
199
|
+
worksheet->optimize = init_data->optimize;
|
|
200
|
+
worksheet->active_sheet = init_data->active_sheet;
|
|
201
|
+
worksheet->first_sheet = init_data->first_sheet;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return worksheet;
|
|
205
|
+
|
|
206
|
+
mem_error:
|
|
207
|
+
lxw_worksheet_free(worksheet);
|
|
208
|
+
return NULL;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* Free a worksheet cell.
|
|
213
|
+
*/
|
|
214
|
+
STATIC void
|
|
215
|
+
_free_cell(lxw_cell *cell)
|
|
216
|
+
{
|
|
217
|
+
if (!cell)
|
|
218
|
+
return;
|
|
219
|
+
|
|
220
|
+
if (cell->type != NUMBER_CELL && cell->type != STRING_CELL
|
|
221
|
+
&& cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) {
|
|
222
|
+
|
|
223
|
+
free(cell->u.string);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
free(cell->user_data1);
|
|
227
|
+
free(cell->user_data2);
|
|
228
|
+
|
|
229
|
+
free(cell);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* Free a worksheet row.
|
|
234
|
+
*/
|
|
235
|
+
STATIC void
|
|
236
|
+
_free_row(lxw_row *row)
|
|
237
|
+
{
|
|
238
|
+
lxw_cell *cell;
|
|
239
|
+
lxw_cell *next_cell;
|
|
240
|
+
|
|
241
|
+
if (!row)
|
|
242
|
+
return;
|
|
243
|
+
|
|
244
|
+
for (cell = RB_MIN(lxw_table_cells, row->cells); cell; cell = next_cell) {
|
|
245
|
+
next_cell = RB_NEXT(lxw_table_cells, row->cells, cell);
|
|
246
|
+
RB_REMOVE(lxw_table_cells, row->cells, cell);
|
|
247
|
+
_free_cell(cell);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
free(row->cells);
|
|
251
|
+
free(row);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/*
|
|
255
|
+
* Free a worksheet image_options.
|
|
256
|
+
*/
|
|
257
|
+
STATIC void
|
|
258
|
+
_free_image_options(lxw_image_options *image)
|
|
259
|
+
{
|
|
260
|
+
if (!image)
|
|
261
|
+
return;
|
|
262
|
+
|
|
263
|
+
free(image->filename);
|
|
264
|
+
free(image->short_name);
|
|
265
|
+
free(image->extension);
|
|
266
|
+
free(image->url);
|
|
267
|
+
free(image->tip);
|
|
268
|
+
free(image);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/*
|
|
272
|
+
* Free a worksheet object.
|
|
273
|
+
*/
|
|
274
|
+
void
|
|
275
|
+
lxw_worksheet_free(lxw_worksheet *worksheet)
|
|
276
|
+
{
|
|
277
|
+
lxw_row *row;
|
|
278
|
+
lxw_row *next_row;
|
|
279
|
+
lxw_col_t col;
|
|
280
|
+
lxw_merged_range *merged_range;
|
|
281
|
+
lxw_image_options *image_options;
|
|
282
|
+
lxw_selection *selection;
|
|
283
|
+
lxw_rel_tuple *relationship;
|
|
284
|
+
|
|
285
|
+
if (!worksheet)
|
|
286
|
+
return;
|
|
287
|
+
|
|
288
|
+
if (worksheet->col_options) {
|
|
289
|
+
for (col = 0; col < worksheet->col_options_max; col++) {
|
|
290
|
+
if (worksheet->col_options[col])
|
|
291
|
+
free(worksheet->col_options[col]);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
free(worksheet->col_options);
|
|
296
|
+
free(worksheet->col_sizes);
|
|
297
|
+
free(worksheet->col_formats);
|
|
298
|
+
|
|
299
|
+
if (worksheet->table) {
|
|
300
|
+
for (row = RB_MIN(lxw_table_rows, worksheet->table); row;
|
|
301
|
+
row = next_row) {
|
|
302
|
+
|
|
303
|
+
next_row = RB_NEXT(lxw_table_rows, worksheet->table, row);
|
|
304
|
+
RB_REMOVE(lxw_table_rows, worksheet->table, row);
|
|
305
|
+
_free_row(row);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
free(worksheet->table);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (worksheet->hyperlinks) {
|
|
312
|
+
|
|
313
|
+
for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row;
|
|
314
|
+
row = next_row) {
|
|
315
|
+
|
|
316
|
+
next_row = RB_NEXT(lxw_table_rows, worksheet->hyperlinks, row);
|
|
317
|
+
RB_REMOVE(lxw_table_rows, worksheet->hyperlinks, row);
|
|
318
|
+
_free_row(row);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
free(worksheet->hyperlinks);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (worksheet->merged_ranges) {
|
|
325
|
+
while (!STAILQ_EMPTY(worksheet->merged_ranges)) {
|
|
326
|
+
merged_range = STAILQ_FIRST(worksheet->merged_ranges);
|
|
327
|
+
STAILQ_REMOVE_HEAD(worksheet->merged_ranges, list_pointers);
|
|
328
|
+
free(merged_range);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
free(worksheet->merged_ranges);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (worksheet->image_data) {
|
|
335
|
+
while (!STAILQ_EMPTY(worksheet->image_data)) {
|
|
336
|
+
image_options = STAILQ_FIRST(worksheet->image_data);
|
|
337
|
+
STAILQ_REMOVE_HEAD(worksheet->image_data, list_pointers);
|
|
338
|
+
_free_image_options(image_options);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
free(worksheet->image_data);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (worksheet->chart_data) {
|
|
345
|
+
while (!STAILQ_EMPTY(worksheet->chart_data)) {
|
|
346
|
+
image_options = STAILQ_FIRST(worksheet->chart_data);
|
|
347
|
+
STAILQ_REMOVE_HEAD(worksheet->chart_data, list_pointers);
|
|
348
|
+
_free_image_options(image_options);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
free(worksheet->chart_data);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (worksheet->selections) {
|
|
355
|
+
while (!STAILQ_EMPTY(worksheet->selections)) {
|
|
356
|
+
selection = STAILQ_FIRST(worksheet->selections);
|
|
357
|
+
STAILQ_REMOVE_HEAD(worksheet->selections, list_pointers);
|
|
358
|
+
free(selection);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
free(worksheet->selections);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* TODO. Add function for freeing the relationship lists. */
|
|
365
|
+
while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
|
|
366
|
+
relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
|
|
367
|
+
STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers);
|
|
368
|
+
free(relationship->type);
|
|
369
|
+
free(relationship->target);
|
|
370
|
+
free(relationship->target_mode);
|
|
371
|
+
free(relationship);
|
|
372
|
+
}
|
|
373
|
+
free(worksheet->external_hyperlinks);
|
|
374
|
+
|
|
375
|
+
while (!STAILQ_EMPTY(worksheet->external_drawing_links)) {
|
|
376
|
+
relationship = STAILQ_FIRST(worksheet->external_drawing_links);
|
|
377
|
+
STAILQ_REMOVE_HEAD(worksheet->external_drawing_links, list_pointers);
|
|
378
|
+
free(relationship->type);
|
|
379
|
+
free(relationship->target);
|
|
380
|
+
free(relationship->target_mode);
|
|
381
|
+
free(relationship);
|
|
382
|
+
}
|
|
383
|
+
free(worksheet->external_drawing_links);
|
|
384
|
+
|
|
385
|
+
while (!STAILQ_EMPTY(worksheet->drawing_links)) {
|
|
386
|
+
relationship = STAILQ_FIRST(worksheet->drawing_links);
|
|
387
|
+
STAILQ_REMOVE_HEAD(worksheet->drawing_links, list_pointers);
|
|
388
|
+
free(relationship->type);
|
|
389
|
+
free(relationship->target);
|
|
390
|
+
free(relationship->target_mode);
|
|
391
|
+
free(relationship);
|
|
392
|
+
}
|
|
393
|
+
free(worksheet->drawing_links);
|
|
394
|
+
|
|
395
|
+
if (worksheet->array) {
|
|
396
|
+
for (col = 0; col < LXW_COL_MAX; col++) {
|
|
397
|
+
_free_cell(worksheet->array[col]);
|
|
398
|
+
}
|
|
399
|
+
free(worksheet->array);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (worksheet->optimize_row)
|
|
403
|
+
free(worksheet->optimize_row);
|
|
404
|
+
|
|
405
|
+
if (worksheet->drawing)
|
|
406
|
+
lxw_drawing_free(worksheet->drawing);
|
|
407
|
+
|
|
408
|
+
free(worksheet->hbreaks);
|
|
409
|
+
free(worksheet->vbreaks);
|
|
410
|
+
free(worksheet->name);
|
|
411
|
+
free(worksheet->quoted_name);
|
|
412
|
+
|
|
413
|
+
free(worksheet);
|
|
414
|
+
worksheet = NULL;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/*
|
|
418
|
+
* Create a new worksheet row object.
|
|
419
|
+
*/
|
|
420
|
+
STATIC lxw_row *
|
|
421
|
+
_new_row(lxw_row_t row_num)
|
|
422
|
+
{
|
|
423
|
+
lxw_row *row = calloc(1, sizeof(lxw_row));
|
|
424
|
+
|
|
425
|
+
if (row) {
|
|
426
|
+
row->row_num = row_num;
|
|
427
|
+
row->cells = calloc(1, sizeof(struct lxw_table_cells));
|
|
428
|
+
row->height = LXW_DEF_ROW_HEIGHT;
|
|
429
|
+
|
|
430
|
+
if (row->cells)
|
|
431
|
+
RB_INIT(row->cells);
|
|
432
|
+
else
|
|
433
|
+
LXW_MEM_ERROR();
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
LXW_MEM_ERROR();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return row;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/*
|
|
443
|
+
* Create a new worksheet number cell object.
|
|
444
|
+
*/
|
|
445
|
+
STATIC lxw_cell *
|
|
446
|
+
_new_number_cell(lxw_row_t row_num,
|
|
447
|
+
lxw_col_t col_num, double value, lxw_format *format)
|
|
448
|
+
{
|
|
449
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
450
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
451
|
+
|
|
452
|
+
cell->row_num = row_num;
|
|
453
|
+
cell->col_num = col_num;
|
|
454
|
+
cell->type = NUMBER_CELL;
|
|
455
|
+
cell->format = format;
|
|
456
|
+
cell->u.number = value;
|
|
457
|
+
|
|
458
|
+
return cell;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/*
|
|
462
|
+
* Create a new worksheet string cell object.
|
|
463
|
+
*/
|
|
464
|
+
STATIC lxw_cell *
|
|
465
|
+
_new_string_cell(lxw_row_t row_num,
|
|
466
|
+
lxw_col_t col_num, int32_t string_id, char *sst_string,
|
|
467
|
+
lxw_format *format)
|
|
468
|
+
{
|
|
469
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
470
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
471
|
+
|
|
472
|
+
cell->row_num = row_num;
|
|
473
|
+
cell->col_num = col_num;
|
|
474
|
+
cell->type = STRING_CELL;
|
|
475
|
+
cell->format = format;
|
|
476
|
+
cell->u.string_id = string_id;
|
|
477
|
+
cell->sst_string = sst_string;
|
|
478
|
+
|
|
479
|
+
return cell;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/*
|
|
483
|
+
* Create a new worksheet inline_string cell object.
|
|
484
|
+
*/
|
|
485
|
+
STATIC lxw_cell *
|
|
486
|
+
_new_inline_string_cell(lxw_row_t row_num,
|
|
487
|
+
lxw_col_t col_num, char *string, lxw_format *format)
|
|
488
|
+
{
|
|
489
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
490
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
491
|
+
|
|
492
|
+
cell->row_num = row_num;
|
|
493
|
+
cell->col_num = col_num;
|
|
494
|
+
cell->type = INLINE_STRING_CELL;
|
|
495
|
+
cell->format = format;
|
|
496
|
+
cell->u.string = string;
|
|
497
|
+
|
|
498
|
+
return cell;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/*
|
|
502
|
+
* Create a new worksheet formula cell object.
|
|
503
|
+
*/
|
|
504
|
+
STATIC lxw_cell *
|
|
505
|
+
_new_formula_cell(lxw_row_t row_num,
|
|
506
|
+
lxw_col_t col_num, char *formula, lxw_format *format)
|
|
507
|
+
{
|
|
508
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
509
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
510
|
+
|
|
511
|
+
cell->row_num = row_num;
|
|
512
|
+
cell->col_num = col_num;
|
|
513
|
+
cell->type = FORMULA_CELL;
|
|
514
|
+
cell->format = format;
|
|
515
|
+
cell->u.string = formula;
|
|
516
|
+
|
|
517
|
+
return cell;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/*
|
|
521
|
+
* Create a new worksheet array formula cell object.
|
|
522
|
+
*/
|
|
523
|
+
STATIC lxw_cell *
|
|
524
|
+
_new_array_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula,
|
|
525
|
+
char *range, lxw_format *format)
|
|
526
|
+
{
|
|
527
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
528
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
529
|
+
|
|
530
|
+
cell->row_num = row_num;
|
|
531
|
+
cell->col_num = col_num;
|
|
532
|
+
cell->type = ARRAY_FORMULA_CELL;
|
|
533
|
+
cell->format = format;
|
|
534
|
+
cell->u.string = formula;
|
|
535
|
+
cell->user_data1 = range;
|
|
536
|
+
|
|
537
|
+
return cell;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/*
|
|
541
|
+
* Create a new worksheet blank cell object.
|
|
542
|
+
*/
|
|
543
|
+
STATIC lxw_cell *
|
|
544
|
+
_new_blank_cell(lxw_row_t row_num, lxw_col_t col_num, lxw_format *format)
|
|
545
|
+
{
|
|
546
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
547
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
548
|
+
|
|
549
|
+
cell->row_num = row_num;
|
|
550
|
+
cell->col_num = col_num;
|
|
551
|
+
cell->type = BLANK_CELL;
|
|
552
|
+
cell->format = format;
|
|
553
|
+
|
|
554
|
+
return cell;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/*
|
|
558
|
+
* Create a new worksheet boolean cell object.
|
|
559
|
+
*/
|
|
560
|
+
STATIC lxw_cell *
|
|
561
|
+
_new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value,
|
|
562
|
+
lxw_format *format)
|
|
563
|
+
{
|
|
564
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
565
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
566
|
+
|
|
567
|
+
cell->row_num = row_num;
|
|
568
|
+
cell->col_num = col_num;
|
|
569
|
+
cell->type = BOOLEAN_CELL;
|
|
570
|
+
cell->format = format;
|
|
571
|
+
cell->u.number = value;
|
|
572
|
+
|
|
573
|
+
return cell;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* Create a new worksheet hyperlink cell object.
|
|
578
|
+
*/
|
|
579
|
+
STATIC lxw_cell *
|
|
580
|
+
_new_hyperlink_cell(lxw_row_t row_num, lxw_col_t col_num,
|
|
581
|
+
enum cell_types link_type, char *url, char *string,
|
|
582
|
+
char *tooltip)
|
|
583
|
+
{
|
|
584
|
+
lxw_cell *cell = calloc(1, sizeof(lxw_cell));
|
|
585
|
+
RETURN_ON_MEM_ERROR(cell, cell);
|
|
586
|
+
|
|
587
|
+
cell->row_num = row_num;
|
|
588
|
+
cell->col_num = col_num;
|
|
589
|
+
cell->type = link_type;
|
|
590
|
+
cell->u.string = url;
|
|
591
|
+
cell->user_data1 = string;
|
|
592
|
+
cell->user_data2 = tooltip;
|
|
593
|
+
|
|
594
|
+
return cell;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/*
|
|
598
|
+
* Get or create the row object for a given row number.
|
|
599
|
+
*/
|
|
600
|
+
STATIC lxw_row *
|
|
601
|
+
_get_row_list(struct lxw_table_rows *table, lxw_row_t row_num)
|
|
602
|
+
{
|
|
603
|
+
lxw_row *row;
|
|
604
|
+
lxw_row *existing_row;
|
|
605
|
+
|
|
606
|
+
if (table->cached_row_num == row_num)
|
|
607
|
+
return table->cached_row;
|
|
608
|
+
|
|
609
|
+
/* Create a new row and try and insert it. */
|
|
610
|
+
row = _new_row(row_num);
|
|
611
|
+
existing_row = RB_INSERT(lxw_table_rows, table, row);
|
|
612
|
+
|
|
613
|
+
/* If existing_row is not NULL, then it already existed. Free new row */
|
|
614
|
+
/* and return existing_row. */
|
|
615
|
+
if (existing_row) {
|
|
616
|
+
_free_row(row);
|
|
617
|
+
row = existing_row;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
table->cached_row = row;
|
|
621
|
+
table->cached_row_num = row_num;
|
|
622
|
+
|
|
623
|
+
return row;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/*
|
|
627
|
+
* Get or create the row object for a given row number.
|
|
628
|
+
*/
|
|
629
|
+
STATIC lxw_row *
|
|
630
|
+
_get_row(lxw_worksheet *self, lxw_row_t row_num)
|
|
631
|
+
{
|
|
632
|
+
lxw_row *row;
|
|
633
|
+
|
|
634
|
+
if (!self->optimize) {
|
|
635
|
+
row = _get_row_list(self->table, row_num);
|
|
636
|
+
return row;
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
if (row_num < self->optimize_row->row_num) {
|
|
640
|
+
return NULL;
|
|
641
|
+
}
|
|
642
|
+
else if (row_num == self->optimize_row->row_num) {
|
|
643
|
+
return self->optimize_row;
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
/* Flush row. */
|
|
647
|
+
lxw_worksheet_write_single_row(self);
|
|
648
|
+
row = self->optimize_row;
|
|
649
|
+
row->row_num = row_num;
|
|
650
|
+
return row;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/*
|
|
656
|
+
* Insert a cell object in the cell list of a row object.
|
|
657
|
+
*/
|
|
658
|
+
STATIC void
|
|
659
|
+
_insert_cell_list(struct lxw_table_cells *cell_list,
|
|
660
|
+
lxw_cell *cell, lxw_col_t col_num)
|
|
661
|
+
{
|
|
662
|
+
lxw_cell *existing_cell;
|
|
663
|
+
|
|
664
|
+
cell->col_num = col_num;
|
|
665
|
+
|
|
666
|
+
existing_cell = RB_INSERT(lxw_table_cells, cell_list, cell);
|
|
667
|
+
|
|
668
|
+
/* If existing_cell is not NULL, then that cell already existed. */
|
|
669
|
+
/* Remove existing_cell and add new one in again. */
|
|
670
|
+
if (existing_cell) {
|
|
671
|
+
RB_REMOVE(lxw_table_cells, cell_list, existing_cell);
|
|
672
|
+
|
|
673
|
+
/* Add it in again. */
|
|
674
|
+
RB_INSERT(lxw_table_cells, cell_list, cell);
|
|
675
|
+
_free_cell(existing_cell);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/*
|
|
682
|
+
* Insert a cell object into the cell list or array.
|
|
683
|
+
*/
|
|
684
|
+
STATIC void
|
|
685
|
+
_insert_cell(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num,
|
|
686
|
+
lxw_cell *cell)
|
|
687
|
+
{
|
|
688
|
+
lxw_row *row = _get_row(self, row_num);
|
|
689
|
+
|
|
690
|
+
if (!self->optimize) {
|
|
691
|
+
row->data_changed = LXW_TRUE;
|
|
692
|
+
_insert_cell_list(row->cells, cell, col_num);
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
if (row) {
|
|
696
|
+
row->data_changed = LXW_TRUE;
|
|
697
|
+
|
|
698
|
+
/* Overwrite an existing cell if necessary. */
|
|
699
|
+
if (self->array[col_num])
|
|
700
|
+
_free_cell(self->array[col_num]);
|
|
701
|
+
|
|
702
|
+
self->array[col_num] = cell;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/*
|
|
708
|
+
* Insert a hyperlink object into the hyperlink list.
|
|
709
|
+
*/
|
|
710
|
+
STATIC void
|
|
711
|
+
_insert_hyperlink(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num,
|
|
712
|
+
lxw_cell *link)
|
|
713
|
+
{
|
|
714
|
+
lxw_row *row = _get_row_list(self->hyperlinks, row_num);
|
|
715
|
+
|
|
716
|
+
_insert_cell_list(row->cells, link, col_num);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/*
|
|
720
|
+
* Next power of two for column reallocs. Taken from bithacks in the public
|
|
721
|
+
* domain.
|
|
722
|
+
*/
|
|
723
|
+
STATIC lxw_col_t
|
|
724
|
+
_next_power_of_two(uint16_t col)
|
|
725
|
+
{
|
|
726
|
+
col--;
|
|
727
|
+
col |= col >> 1;
|
|
728
|
+
col |= col >> 2;
|
|
729
|
+
col |= col >> 4;
|
|
730
|
+
col |= col >> 8;
|
|
731
|
+
col++;
|
|
732
|
+
|
|
733
|
+
return col;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/*
|
|
737
|
+
* Check that row and col are within the allowed Excel range and store max
|
|
738
|
+
* and min values for use in other methods/elements.
|
|
739
|
+
*
|
|
740
|
+
* The ignore_row/ignore_col flags are used to indicate that we wish to
|
|
741
|
+
* perform the dimension check without storing the value.
|
|
742
|
+
*/
|
|
743
|
+
STATIC lxw_error
|
|
744
|
+
_check_dimensions(lxw_worksheet *self,
|
|
745
|
+
lxw_row_t row_num,
|
|
746
|
+
lxw_col_t col_num, int8_t ignore_row, int8_t ignore_col)
|
|
747
|
+
{
|
|
748
|
+
if (row_num >= LXW_ROW_MAX)
|
|
749
|
+
return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
|
|
750
|
+
|
|
751
|
+
if (col_num >= LXW_COL_MAX)
|
|
752
|
+
return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
|
|
753
|
+
|
|
754
|
+
/* In optimization mode we don't change dimensions for rows that are */
|
|
755
|
+
/* already written. */
|
|
756
|
+
if (!ignore_row && !ignore_col && self->optimize) {
|
|
757
|
+
if (row_num < self->optimize_row->row_num)
|
|
758
|
+
return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (!ignore_row) {
|
|
762
|
+
if (row_num < self->dim_rowmin)
|
|
763
|
+
self->dim_rowmin = row_num;
|
|
764
|
+
if (row_num > self->dim_rowmax)
|
|
765
|
+
self->dim_rowmax = row_num;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (!ignore_col) {
|
|
769
|
+
if (col_num < self->dim_colmin)
|
|
770
|
+
self->dim_colmin = col_num;
|
|
771
|
+
if (col_num > self->dim_colmax)
|
|
772
|
+
self->dim_colmax = col_num;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
return LXW_NO_ERROR;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/*
|
|
779
|
+
* Comparator for the row structure red/black tree.
|
|
780
|
+
*/
|
|
781
|
+
STATIC int
|
|
782
|
+
_row_cmp(lxw_row *row1, lxw_row *row2)
|
|
783
|
+
{
|
|
784
|
+
if (row1->row_num > row2->row_num)
|
|
785
|
+
return 1;
|
|
786
|
+
if (row1->row_num < row2->row_num)
|
|
787
|
+
return -1;
|
|
788
|
+
return 0;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/*
|
|
792
|
+
* Comparator for the cell structure red/black tree.
|
|
793
|
+
*/
|
|
794
|
+
STATIC int
|
|
795
|
+
_cell_cmp(lxw_cell *cell1, lxw_cell *cell2)
|
|
796
|
+
{
|
|
797
|
+
if (cell1->col_num > cell2->col_num)
|
|
798
|
+
return 1;
|
|
799
|
+
if (cell1->col_num < cell2->col_num)
|
|
800
|
+
return -1;
|
|
801
|
+
return 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/*
|
|
805
|
+
* Hash a worksheet password. Based on the algorithm provided by Daniel Rentz
|
|
806
|
+
* of OpenOffice.
|
|
807
|
+
*/
|
|
808
|
+
STATIC uint16_t
|
|
809
|
+
_hash_password(const char *password)
|
|
810
|
+
{
|
|
811
|
+
size_t count;
|
|
812
|
+
uint8_t i;
|
|
813
|
+
uint16_t hash = 0x0000;
|
|
814
|
+
|
|
815
|
+
count = strlen(password);
|
|
816
|
+
|
|
817
|
+
for (i = 0; i < count; i++) {
|
|
818
|
+
uint32_t low_15;
|
|
819
|
+
uint32_t high_15;
|
|
820
|
+
uint32_t letter = password[i] << (i + 1);
|
|
821
|
+
|
|
822
|
+
low_15 = letter & 0x7fff;
|
|
823
|
+
high_15 = letter & (0x7fff << 15);
|
|
824
|
+
high_15 = high_15 >> 15;
|
|
825
|
+
letter = low_15 | high_15;
|
|
826
|
+
|
|
827
|
+
hash ^= letter;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
hash ^= count;
|
|
831
|
+
hash ^= 0xCE4B;
|
|
832
|
+
|
|
833
|
+
return hash;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/*
|
|
837
|
+
* Simple replacement for libgen.h basename() for compatibility with MSVC. It
|
|
838
|
+
* handles forward and back slashes. It doesn't copy exactly the return
|
|
839
|
+
* format of basename().
|
|
840
|
+
*/
|
|
841
|
+
char *
|
|
842
|
+
lxw_basename(const char *path)
|
|
843
|
+
{
|
|
844
|
+
|
|
845
|
+
char *forward_slash;
|
|
846
|
+
char *back_slash;
|
|
847
|
+
|
|
848
|
+
if (!path)
|
|
849
|
+
return NULL;
|
|
850
|
+
|
|
851
|
+
forward_slash = strrchr(path, '/');
|
|
852
|
+
back_slash = strrchr(path, '\\');
|
|
853
|
+
|
|
854
|
+
if (!forward_slash && !back_slash)
|
|
855
|
+
return (char *) path;
|
|
856
|
+
|
|
857
|
+
if (forward_slash > back_slash)
|
|
858
|
+
return forward_slash + 1;
|
|
859
|
+
else
|
|
860
|
+
return back_slash + 1;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/*****************************************************************************
|
|
864
|
+
*
|
|
865
|
+
* XML functions.
|
|
866
|
+
*
|
|
867
|
+
****************************************************************************/
|
|
868
|
+
/*
|
|
869
|
+
* Write the XML declaration.
|
|
870
|
+
*/
|
|
871
|
+
STATIC void
|
|
872
|
+
_worksheet_xml_declaration(lxw_worksheet *self)
|
|
873
|
+
{
|
|
874
|
+
lxw_xml_declaration(self->file);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/*
|
|
878
|
+
* Write the <worksheet> element.
|
|
879
|
+
*/
|
|
880
|
+
STATIC void
|
|
881
|
+
_worksheet_write_worksheet(lxw_worksheet *self)
|
|
882
|
+
{
|
|
883
|
+
struct xml_attribute_list attributes;
|
|
884
|
+
struct xml_attribute *attribute;
|
|
885
|
+
char xmlns[] = "http://schemas.openxmlformats.org/"
|
|
886
|
+
"spreadsheetml/2006/main";
|
|
887
|
+
char xmlns_r[] = "http://schemas.openxmlformats.org/"
|
|
888
|
+
"officeDocument/2006/relationships";
|
|
889
|
+
|
|
890
|
+
LXW_INIT_ATTRIBUTES();
|
|
891
|
+
LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns);
|
|
892
|
+
LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r);
|
|
893
|
+
|
|
894
|
+
lxw_xml_start_tag(self->file, "worksheet", &attributes);
|
|
895
|
+
LXW_FREE_ATTRIBUTES();
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/*
|
|
899
|
+
* Write the <dimension> element.
|
|
900
|
+
*/
|
|
901
|
+
STATIC void
|
|
902
|
+
_worksheet_write_dimension(lxw_worksheet *self)
|
|
903
|
+
{
|
|
904
|
+
struct xml_attribute_list attributes;
|
|
905
|
+
struct xml_attribute *attribute;
|
|
906
|
+
char ref[LXW_MAX_CELL_RANGE_LENGTH];
|
|
907
|
+
lxw_row_t dim_rowmin = self->dim_rowmin;
|
|
908
|
+
lxw_row_t dim_rowmax = self->dim_rowmax;
|
|
909
|
+
lxw_col_t dim_colmin = self->dim_colmin;
|
|
910
|
+
lxw_col_t dim_colmax = self->dim_colmax;
|
|
911
|
+
|
|
912
|
+
if (dim_rowmin == LXW_ROW_MAX && dim_colmin == LXW_COL_MAX) {
|
|
913
|
+
/* If the rows and cols are still the defaults then no dimensions have
|
|
914
|
+
* been set and we use the default range "A1". */
|
|
915
|
+
lxw_rowcol_to_range(ref, 0, 0, 0, 0);
|
|
916
|
+
}
|
|
917
|
+
else if (dim_rowmin == LXW_ROW_MAX && dim_colmin != LXW_COL_MAX) {
|
|
918
|
+
/* If the rows aren't set but the columns are then the dimensions have
|
|
919
|
+
* been changed via set_column(). */
|
|
920
|
+
lxw_rowcol_to_range(ref, 0, dim_colmin, 0, dim_colmax);
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
lxw_rowcol_to_range(ref, dim_rowmin, dim_colmin, dim_rowmax,
|
|
924
|
+
dim_colmax);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
LXW_INIT_ATTRIBUTES();
|
|
928
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", ref);
|
|
929
|
+
|
|
930
|
+
lxw_xml_empty_tag(self->file, "dimension", &attributes);
|
|
931
|
+
|
|
932
|
+
LXW_FREE_ATTRIBUTES();
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/*
|
|
936
|
+
* Write the <pane> element for freeze panes.
|
|
937
|
+
*/
|
|
938
|
+
STATIC void
|
|
939
|
+
_worksheet_write_freeze_panes(lxw_worksheet *self)
|
|
940
|
+
{
|
|
941
|
+
struct xml_attribute_list attributes;
|
|
942
|
+
struct xml_attribute *attribute;
|
|
943
|
+
|
|
944
|
+
lxw_selection *selection;
|
|
945
|
+
lxw_selection *user_selection;
|
|
946
|
+
lxw_row_t row = self->panes.first_row;
|
|
947
|
+
lxw_col_t col = self->panes.first_col;
|
|
948
|
+
lxw_row_t top_row = self->panes.top_row;
|
|
949
|
+
lxw_col_t left_col = self->panes.left_col;
|
|
950
|
+
|
|
951
|
+
char row_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
952
|
+
char col_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
953
|
+
char top_left_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
954
|
+
char active_pane[LXW_PANE_NAME_LENGTH];
|
|
955
|
+
|
|
956
|
+
/* If there is a user selection we remove it from the list and use it. */
|
|
957
|
+
if (!STAILQ_EMPTY(self->selections)) {
|
|
958
|
+
user_selection = STAILQ_FIRST(self->selections);
|
|
959
|
+
STAILQ_REMOVE_HEAD(self->selections, list_pointers);
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
/* or else create a new blank selection. */
|
|
963
|
+
user_selection = calloc(1, sizeof(lxw_selection));
|
|
964
|
+
RETURN_VOID_ON_MEM_ERROR(user_selection);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
LXW_INIT_ATTRIBUTES();
|
|
968
|
+
|
|
969
|
+
lxw_rowcol_to_cell(top_left_cell, top_row, left_col);
|
|
970
|
+
|
|
971
|
+
/* Set the active pane. */
|
|
972
|
+
if (row && col) {
|
|
973
|
+
lxw_strcpy(active_pane, "bottomRight");
|
|
974
|
+
|
|
975
|
+
lxw_rowcol_to_cell(row_cell, row, 0);
|
|
976
|
+
lxw_rowcol_to_cell(col_cell, 0, col);
|
|
977
|
+
|
|
978
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
979
|
+
if (selection) {
|
|
980
|
+
lxw_strcpy(selection->pane, "topRight");
|
|
981
|
+
lxw_strcpy(selection->active_cell, col_cell);
|
|
982
|
+
lxw_strcpy(selection->sqref, col_cell);
|
|
983
|
+
|
|
984
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
988
|
+
if (selection) {
|
|
989
|
+
lxw_strcpy(selection->pane, "bottomLeft");
|
|
990
|
+
lxw_strcpy(selection->active_cell, row_cell);
|
|
991
|
+
lxw_strcpy(selection->sqref, row_cell);
|
|
992
|
+
|
|
993
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
997
|
+
if (selection) {
|
|
998
|
+
lxw_strcpy(selection->pane, "bottomRight");
|
|
999
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1000
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1001
|
+
|
|
1002
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
else if (col) {
|
|
1006
|
+
lxw_strcpy(active_pane, "topRight");
|
|
1007
|
+
|
|
1008
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1009
|
+
if (selection) {
|
|
1010
|
+
lxw_strcpy(selection->pane, "topRight");
|
|
1011
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1012
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1013
|
+
|
|
1014
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
lxw_strcpy(active_pane, "bottomLeft");
|
|
1019
|
+
|
|
1020
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1021
|
+
if (selection) {
|
|
1022
|
+
lxw_strcpy(selection->pane, "bottomLeft");
|
|
1023
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1024
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1025
|
+
|
|
1026
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
if (col)
|
|
1031
|
+
LXW_PUSH_ATTRIBUTES_INT("xSplit", col);
|
|
1032
|
+
|
|
1033
|
+
if (row)
|
|
1034
|
+
LXW_PUSH_ATTRIBUTES_INT("ySplit", row);
|
|
1035
|
+
|
|
1036
|
+
LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell);
|
|
1037
|
+
LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane);
|
|
1038
|
+
|
|
1039
|
+
if (self->panes.type == FREEZE_PANES)
|
|
1040
|
+
LXW_PUSH_ATTRIBUTES_STR("state", "frozen");
|
|
1041
|
+
else if (self->panes.type == FREEZE_SPLIT_PANES)
|
|
1042
|
+
LXW_PUSH_ATTRIBUTES_STR("state", "frozenSplit");
|
|
1043
|
+
|
|
1044
|
+
lxw_xml_empty_tag(self->file, "pane", &attributes);
|
|
1045
|
+
|
|
1046
|
+
free(user_selection);
|
|
1047
|
+
|
|
1048
|
+
LXW_FREE_ATTRIBUTES();
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/*
|
|
1052
|
+
* Convert column width from user units to pane split width.
|
|
1053
|
+
*/
|
|
1054
|
+
STATIC uint32_t
|
|
1055
|
+
_worksheet_calculate_x_split_width(double x_split)
|
|
1056
|
+
{
|
|
1057
|
+
uint32_t width;
|
|
1058
|
+
uint32_t pixels;
|
|
1059
|
+
uint32_t points;
|
|
1060
|
+
uint32_t twips;
|
|
1061
|
+
double max_digit_width = 7.0; /* For Calabri 11. */
|
|
1062
|
+
double padding = 5.0;
|
|
1063
|
+
|
|
1064
|
+
/* Convert to pixels. */
|
|
1065
|
+
if (x_split < 1.0) {
|
|
1066
|
+
pixels = (uint32_t) (x_split * (max_digit_width + padding) + 0.5);
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
pixels = (uint32_t) (x_split * max_digit_width + 0.5) + 5;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* Convert to points. */
|
|
1073
|
+
points = (pixels * 3) / 4;
|
|
1074
|
+
|
|
1075
|
+
/* Convert to twips (twentieths of a point). */
|
|
1076
|
+
twips = points * 20;
|
|
1077
|
+
|
|
1078
|
+
/* Add offset/padding. */
|
|
1079
|
+
width = twips + 390;
|
|
1080
|
+
|
|
1081
|
+
return width;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/*
|
|
1085
|
+
* Write the <pane> element for split panes.
|
|
1086
|
+
*/
|
|
1087
|
+
STATIC void
|
|
1088
|
+
_worksheet_write_split_panes(lxw_worksheet *self)
|
|
1089
|
+
{
|
|
1090
|
+
struct xml_attribute_list attributes;
|
|
1091
|
+
struct xml_attribute *attribute;
|
|
1092
|
+
|
|
1093
|
+
lxw_selection *selection;
|
|
1094
|
+
lxw_selection *user_selection;
|
|
1095
|
+
lxw_row_t row = self->panes.first_row;
|
|
1096
|
+
lxw_col_t col = self->panes.first_col;
|
|
1097
|
+
lxw_row_t top_row = self->panes.top_row;
|
|
1098
|
+
lxw_col_t left_col = self->panes.left_col;
|
|
1099
|
+
double x_split = self->panes.x_split;
|
|
1100
|
+
double y_split = self->panes.y_split;
|
|
1101
|
+
uint8_t has_selection = LXW_FALSE;
|
|
1102
|
+
|
|
1103
|
+
char row_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
1104
|
+
char col_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
1105
|
+
char top_left_cell[LXW_MAX_CELL_NAME_LENGTH];
|
|
1106
|
+
char active_pane[LXW_PANE_NAME_LENGTH];
|
|
1107
|
+
|
|
1108
|
+
/* If there is a user selection we remove it from the list and use it. */
|
|
1109
|
+
if (!STAILQ_EMPTY(self->selections)) {
|
|
1110
|
+
user_selection = STAILQ_FIRST(self->selections);
|
|
1111
|
+
STAILQ_REMOVE_HEAD(self->selections, list_pointers);
|
|
1112
|
+
has_selection = LXW_TRUE;
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
/* or else create a new blank selection. */
|
|
1116
|
+
user_selection = calloc(1, sizeof(lxw_selection));
|
|
1117
|
+
RETURN_VOID_ON_MEM_ERROR(user_selection);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
LXW_INIT_ATTRIBUTES();
|
|
1121
|
+
|
|
1122
|
+
/* Convert the row and col to 1/20 twip units with padding. */
|
|
1123
|
+
if (y_split > 0.0)
|
|
1124
|
+
y_split = (uint32_t) (20 * y_split + 300);
|
|
1125
|
+
|
|
1126
|
+
if (x_split > 0.0)
|
|
1127
|
+
x_split = _worksheet_calculate_x_split_width(x_split);
|
|
1128
|
+
|
|
1129
|
+
/* For non-explicit topLeft definitions, estimate the cell offset based on
|
|
1130
|
+
* the pixels dimensions. This is only a workaround and doesn't take
|
|
1131
|
+
* adjusted cell dimensions into account.
|
|
1132
|
+
*/
|
|
1133
|
+
if (top_row == row && left_col == col) {
|
|
1134
|
+
top_row = (lxw_row_t) (0.5 + (y_split - 300.0) / 20.0 / 15.0);
|
|
1135
|
+
left_col = (lxw_col_t) (0.5 + (x_split - 390.0) / 20.0 / 3.0 / 16.0);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
lxw_rowcol_to_cell(top_left_cell, top_row, left_col);
|
|
1139
|
+
|
|
1140
|
+
/* If there is no selection set the active cell to the top left cell. */
|
|
1141
|
+
if (!has_selection) {
|
|
1142
|
+
lxw_strcpy(user_selection->active_cell, top_left_cell);
|
|
1143
|
+
lxw_strcpy(user_selection->sqref, top_left_cell);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Set the active pane. */
|
|
1147
|
+
if (y_split > 0.0 && x_split > 0.0) {
|
|
1148
|
+
lxw_strcpy(active_pane, "bottomRight");
|
|
1149
|
+
|
|
1150
|
+
lxw_rowcol_to_cell(row_cell, top_row, 0);
|
|
1151
|
+
lxw_rowcol_to_cell(col_cell, 0, left_col);
|
|
1152
|
+
|
|
1153
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1154
|
+
if (selection) {
|
|
1155
|
+
lxw_strcpy(selection->pane, "topRight");
|
|
1156
|
+
lxw_strcpy(selection->active_cell, col_cell);
|
|
1157
|
+
lxw_strcpy(selection->sqref, col_cell);
|
|
1158
|
+
|
|
1159
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1163
|
+
if (selection) {
|
|
1164
|
+
lxw_strcpy(selection->pane, "bottomLeft");
|
|
1165
|
+
lxw_strcpy(selection->active_cell, row_cell);
|
|
1166
|
+
lxw_strcpy(selection->sqref, row_cell);
|
|
1167
|
+
|
|
1168
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1172
|
+
if (selection) {
|
|
1173
|
+
lxw_strcpy(selection->pane, "bottomRight");
|
|
1174
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1175
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1176
|
+
|
|
1177
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
else if (x_split > 0.0) {
|
|
1181
|
+
lxw_strcpy(active_pane, "topRight");
|
|
1182
|
+
|
|
1183
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1184
|
+
if (selection) {
|
|
1185
|
+
lxw_strcpy(selection->pane, "topRight");
|
|
1186
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1187
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1188
|
+
|
|
1189
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
lxw_strcpy(active_pane, "bottomLeft");
|
|
1194
|
+
|
|
1195
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
1196
|
+
if (selection) {
|
|
1197
|
+
lxw_strcpy(selection->pane, "bottomLeft");
|
|
1198
|
+
lxw_strcpy(selection->active_cell, user_selection->active_cell);
|
|
1199
|
+
lxw_strcpy(selection->sqref, user_selection->sqref);
|
|
1200
|
+
|
|
1201
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
if (x_split > 0.0)
|
|
1206
|
+
LXW_PUSH_ATTRIBUTES_DBL("xSplit", x_split);
|
|
1207
|
+
|
|
1208
|
+
if (y_split > 0.0)
|
|
1209
|
+
LXW_PUSH_ATTRIBUTES_DBL("ySplit", y_split);
|
|
1210
|
+
|
|
1211
|
+
LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell);
|
|
1212
|
+
|
|
1213
|
+
if (has_selection)
|
|
1214
|
+
LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane);
|
|
1215
|
+
|
|
1216
|
+
lxw_xml_empty_tag(self->file, "pane", &attributes);
|
|
1217
|
+
|
|
1218
|
+
free(user_selection);
|
|
1219
|
+
|
|
1220
|
+
LXW_FREE_ATTRIBUTES();
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/*
|
|
1224
|
+
* Write the <selection> element.
|
|
1225
|
+
*/
|
|
1226
|
+
STATIC void
|
|
1227
|
+
_worksheet_write_selection(lxw_worksheet *self, lxw_selection *selection)
|
|
1228
|
+
{
|
|
1229
|
+
struct xml_attribute_list attributes;
|
|
1230
|
+
struct xml_attribute *attribute;
|
|
1231
|
+
|
|
1232
|
+
LXW_INIT_ATTRIBUTES();
|
|
1233
|
+
|
|
1234
|
+
if (*selection->pane)
|
|
1235
|
+
LXW_PUSH_ATTRIBUTES_STR("pane", selection->pane);
|
|
1236
|
+
|
|
1237
|
+
if (*selection->active_cell)
|
|
1238
|
+
LXW_PUSH_ATTRIBUTES_STR("activeCell", selection->active_cell);
|
|
1239
|
+
|
|
1240
|
+
if (*selection->sqref)
|
|
1241
|
+
LXW_PUSH_ATTRIBUTES_STR("sqref", selection->sqref);
|
|
1242
|
+
|
|
1243
|
+
lxw_xml_empty_tag(self->file, "selection", &attributes);
|
|
1244
|
+
|
|
1245
|
+
LXW_FREE_ATTRIBUTES();
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/*
|
|
1249
|
+
* Write the <selection> elements.
|
|
1250
|
+
*/
|
|
1251
|
+
STATIC void
|
|
1252
|
+
_worksheet_write_selections(lxw_worksheet *self)
|
|
1253
|
+
{
|
|
1254
|
+
lxw_selection *selection;
|
|
1255
|
+
|
|
1256
|
+
STAILQ_FOREACH(selection, self->selections, list_pointers) {
|
|
1257
|
+
_worksheet_write_selection(self, selection);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/*
|
|
1262
|
+
* Write the frozen or split <pane> elements.
|
|
1263
|
+
*/
|
|
1264
|
+
STATIC void
|
|
1265
|
+
_worksheet_write_panes(lxw_worksheet *self)
|
|
1266
|
+
{
|
|
1267
|
+
if (self->panes.type == NO_PANES)
|
|
1268
|
+
return;
|
|
1269
|
+
|
|
1270
|
+
else if (self->panes.type == FREEZE_PANES)
|
|
1271
|
+
_worksheet_write_freeze_panes(self);
|
|
1272
|
+
|
|
1273
|
+
else if (self->panes.type == FREEZE_SPLIT_PANES)
|
|
1274
|
+
_worksheet_write_freeze_panes(self);
|
|
1275
|
+
|
|
1276
|
+
else if (self->panes.type == SPLIT_PANES)
|
|
1277
|
+
_worksheet_write_split_panes(self);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/*
|
|
1281
|
+
* Write the <sheetView> element.
|
|
1282
|
+
*/
|
|
1283
|
+
STATIC void
|
|
1284
|
+
_worksheet_write_sheet_view(lxw_worksheet *self)
|
|
1285
|
+
{
|
|
1286
|
+
struct xml_attribute_list attributes;
|
|
1287
|
+
struct xml_attribute *attribute;
|
|
1288
|
+
|
|
1289
|
+
LXW_INIT_ATTRIBUTES();
|
|
1290
|
+
|
|
1291
|
+
/* Hide screen gridlines if required */
|
|
1292
|
+
if (!self->screen_gridlines)
|
|
1293
|
+
LXW_PUSH_ATTRIBUTES_STR("showGridLines", "0");
|
|
1294
|
+
|
|
1295
|
+
/* Hide zeroes in cells. */
|
|
1296
|
+
if (!self->show_zeros) {
|
|
1297
|
+
LXW_PUSH_ATTRIBUTES_STR("showZeros", "0");
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/* Display worksheet right to left for Hebrew, Arabic and others. */
|
|
1301
|
+
if (self->right_to_left) {
|
|
1302
|
+
LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1");
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/* Show that the sheet tab is selected. */
|
|
1306
|
+
if (self->selected)
|
|
1307
|
+
LXW_PUSH_ATTRIBUTES_STR("tabSelected", "1");
|
|
1308
|
+
|
|
1309
|
+
/* Turn outlines off. Also required in the outlinePr element. */
|
|
1310
|
+
if (!self->outline_on) {
|
|
1311
|
+
LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0");
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/* Set the page view/layout mode if required. */
|
|
1315
|
+
if (self->page_view)
|
|
1316
|
+
LXW_PUSH_ATTRIBUTES_STR("view", "pageLayout");
|
|
1317
|
+
|
|
1318
|
+
/* Set the zoom level. */
|
|
1319
|
+
if (self->zoom != 100) {
|
|
1320
|
+
if (!self->page_view) {
|
|
1321
|
+
LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom);
|
|
1322
|
+
|
|
1323
|
+
if (self->zoom_scale_normal)
|
|
1324
|
+
LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
LXW_PUSH_ATTRIBUTES_STR("workbookViewId", "0");
|
|
1329
|
+
|
|
1330
|
+
if (self->panes.type != NO_PANES || !STAILQ_EMPTY(self->selections)) {
|
|
1331
|
+
lxw_xml_start_tag(self->file, "sheetView", &attributes);
|
|
1332
|
+
_worksheet_write_panes(self);
|
|
1333
|
+
_worksheet_write_selections(self);
|
|
1334
|
+
lxw_xml_end_tag(self->file, "sheetView");
|
|
1335
|
+
}
|
|
1336
|
+
else {
|
|
1337
|
+
lxw_xml_empty_tag(self->file, "sheetView", &attributes);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
LXW_FREE_ATTRIBUTES();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/*
|
|
1344
|
+
* Write the <sheetViews> element.
|
|
1345
|
+
*/
|
|
1346
|
+
STATIC void
|
|
1347
|
+
_worksheet_write_sheet_views(lxw_worksheet *self)
|
|
1348
|
+
{
|
|
1349
|
+
lxw_xml_start_tag(self->file, "sheetViews", NULL);
|
|
1350
|
+
|
|
1351
|
+
/* Write the sheetView element. */
|
|
1352
|
+
_worksheet_write_sheet_view(self);
|
|
1353
|
+
|
|
1354
|
+
lxw_xml_end_tag(self->file, "sheetViews");
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/*
|
|
1358
|
+
* Write the <sheetFormatPr> element.
|
|
1359
|
+
*/
|
|
1360
|
+
STATIC void
|
|
1361
|
+
_worksheet_write_sheet_format_pr(lxw_worksheet *self)
|
|
1362
|
+
{
|
|
1363
|
+
struct xml_attribute_list attributes;
|
|
1364
|
+
struct xml_attribute *attribute;
|
|
1365
|
+
|
|
1366
|
+
LXW_INIT_ATTRIBUTES();
|
|
1367
|
+
LXW_PUSH_ATTRIBUTES_DBL("defaultRowHeight", self->default_row_height);
|
|
1368
|
+
|
|
1369
|
+
if (self->default_row_height != LXW_DEF_ROW_HEIGHT)
|
|
1370
|
+
LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
|
|
1371
|
+
|
|
1372
|
+
if (self->default_row_zeroed)
|
|
1373
|
+
LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1");
|
|
1374
|
+
|
|
1375
|
+
lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes);
|
|
1376
|
+
|
|
1377
|
+
LXW_FREE_ATTRIBUTES();
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/*
|
|
1381
|
+
* Write the <sheetData> element.
|
|
1382
|
+
*/
|
|
1383
|
+
STATIC void
|
|
1384
|
+
_worksheet_write_sheet_data(lxw_worksheet *self)
|
|
1385
|
+
{
|
|
1386
|
+
if (RB_EMPTY(self->table)) {
|
|
1387
|
+
lxw_xml_empty_tag(self->file, "sheetData", NULL);
|
|
1388
|
+
}
|
|
1389
|
+
else {
|
|
1390
|
+
lxw_xml_start_tag(self->file, "sheetData", NULL);
|
|
1391
|
+
_worksheet_write_rows(self);
|
|
1392
|
+
lxw_xml_end_tag(self->file, "sheetData");
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
/*
|
|
1397
|
+
* Write the <sheetData> element when the memory optimization is on. In which
|
|
1398
|
+
* case we read the data stored in the temp file and rewrite it to the XML
|
|
1399
|
+
* sheet file.
|
|
1400
|
+
*/
|
|
1401
|
+
STATIC void
|
|
1402
|
+
_worksheet_write_optimized_sheet_data(lxw_worksheet *self)
|
|
1403
|
+
{
|
|
1404
|
+
size_t read_size = 1;
|
|
1405
|
+
char buffer[LXW_BUFFER_SIZE];
|
|
1406
|
+
|
|
1407
|
+
if (self->dim_rowmin == LXW_ROW_MAX) {
|
|
1408
|
+
/* If the dimensions aren't defined then there is no data to write. */
|
|
1409
|
+
lxw_xml_empty_tag(self->file, "sheetData", NULL);
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
|
|
1413
|
+
lxw_xml_start_tag(self->file, "sheetData", NULL);
|
|
1414
|
+
|
|
1415
|
+
/* Flush and rewind the temp file. */
|
|
1416
|
+
fflush(self->optimize_tmpfile);
|
|
1417
|
+
rewind(self->optimize_tmpfile);
|
|
1418
|
+
|
|
1419
|
+
while (read_size) {
|
|
1420
|
+
read_size =
|
|
1421
|
+
fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile);
|
|
1422
|
+
fwrite(buffer, 1, read_size, self->file);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
fclose(self->optimize_tmpfile);
|
|
1426
|
+
|
|
1427
|
+
lxw_xml_end_tag(self->file, "sheetData");
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/*
|
|
1432
|
+
* Write the <pageMargins> element.
|
|
1433
|
+
*/
|
|
1434
|
+
STATIC void
|
|
1435
|
+
_worksheet_write_page_margins(lxw_worksheet *self)
|
|
1436
|
+
{
|
|
1437
|
+
struct xml_attribute_list attributes;
|
|
1438
|
+
struct xml_attribute *attribute;
|
|
1439
|
+
double left = self->margin_left;
|
|
1440
|
+
double right = self->margin_right;
|
|
1441
|
+
double top = self->margin_top;
|
|
1442
|
+
double bottom = self->margin_bottom;
|
|
1443
|
+
double header = self->margin_header;
|
|
1444
|
+
double footer = self->margin_footer;
|
|
1445
|
+
|
|
1446
|
+
LXW_INIT_ATTRIBUTES();
|
|
1447
|
+
LXW_PUSH_ATTRIBUTES_DBL("left", left);
|
|
1448
|
+
LXW_PUSH_ATTRIBUTES_DBL("right", right);
|
|
1449
|
+
LXW_PUSH_ATTRIBUTES_DBL("top", top);
|
|
1450
|
+
LXW_PUSH_ATTRIBUTES_DBL("bottom", bottom);
|
|
1451
|
+
LXW_PUSH_ATTRIBUTES_DBL("header", header);
|
|
1452
|
+
LXW_PUSH_ATTRIBUTES_DBL("footer", footer);
|
|
1453
|
+
|
|
1454
|
+
lxw_xml_empty_tag(self->file, "pageMargins", &attributes);
|
|
1455
|
+
|
|
1456
|
+
LXW_FREE_ATTRIBUTES();
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/*
|
|
1460
|
+
* Write the <pageSetup> element.
|
|
1461
|
+
* The following is an example taken from Excel.
|
|
1462
|
+
* <pageSetup
|
|
1463
|
+
* paperSize="9"
|
|
1464
|
+
* scale="110"
|
|
1465
|
+
* fitToWidth="2"
|
|
1466
|
+
* fitToHeight="2"
|
|
1467
|
+
* pageOrder="overThenDown"
|
|
1468
|
+
* orientation="portrait"
|
|
1469
|
+
* blackAndWhite="1"
|
|
1470
|
+
* draft="1"
|
|
1471
|
+
* horizontalDpi="200"
|
|
1472
|
+
* verticalDpi="200"
|
|
1473
|
+
* r:id="rId1"
|
|
1474
|
+
* />
|
|
1475
|
+
*/
|
|
1476
|
+
STATIC void
|
|
1477
|
+
_worksheet_write_page_setup(lxw_worksheet *self)
|
|
1478
|
+
{
|
|
1479
|
+
struct xml_attribute_list attributes;
|
|
1480
|
+
struct xml_attribute *attribute;
|
|
1481
|
+
|
|
1482
|
+
LXW_INIT_ATTRIBUTES();
|
|
1483
|
+
|
|
1484
|
+
if (!self->page_setup_changed)
|
|
1485
|
+
return;
|
|
1486
|
+
|
|
1487
|
+
/* Set paper size. */
|
|
1488
|
+
if (self->paper_size)
|
|
1489
|
+
LXW_PUSH_ATTRIBUTES_INT("paperSize", self->paper_size);
|
|
1490
|
+
|
|
1491
|
+
/* Set the print_scale. */
|
|
1492
|
+
if (self->print_scale != 100)
|
|
1493
|
+
LXW_PUSH_ATTRIBUTES_INT("scale", self->print_scale);
|
|
1494
|
+
|
|
1495
|
+
/* Set the "Fit to page" properties. */
|
|
1496
|
+
if (self->fit_page && self->fit_width != 1)
|
|
1497
|
+
LXW_PUSH_ATTRIBUTES_INT("fitToWidth", self->fit_width);
|
|
1498
|
+
|
|
1499
|
+
if (self->fit_page && self->fit_height != 1)
|
|
1500
|
+
LXW_PUSH_ATTRIBUTES_INT("fitToHeight", self->fit_height);
|
|
1501
|
+
|
|
1502
|
+
/* Set the page print direction. */
|
|
1503
|
+
if (self->page_order)
|
|
1504
|
+
LXW_PUSH_ATTRIBUTES_STR("pageOrder", "overThenDown");
|
|
1505
|
+
|
|
1506
|
+
/* Set start page. */
|
|
1507
|
+
if (self->page_start > 1)
|
|
1508
|
+
LXW_PUSH_ATTRIBUTES_INT("firstPageNumber", self->page_start);
|
|
1509
|
+
|
|
1510
|
+
/* Set page orientation. */
|
|
1511
|
+
if (self->orientation)
|
|
1512
|
+
LXW_PUSH_ATTRIBUTES_STR("orientation", "portrait");
|
|
1513
|
+
else
|
|
1514
|
+
LXW_PUSH_ATTRIBUTES_STR("orientation", "landscape");
|
|
1515
|
+
|
|
1516
|
+
/* Set start page active flag. */
|
|
1517
|
+
if (self->page_start)
|
|
1518
|
+
LXW_PUSH_ATTRIBUTES_INT("useFirstPageNumber", 1);
|
|
1519
|
+
|
|
1520
|
+
/* Set the DPI. Mainly only for testing. */
|
|
1521
|
+
if (self->horizontal_dpi)
|
|
1522
|
+
LXW_PUSH_ATTRIBUTES_INT("horizontalDpi", self->horizontal_dpi);
|
|
1523
|
+
|
|
1524
|
+
if (self->vertical_dpi)
|
|
1525
|
+
LXW_PUSH_ATTRIBUTES_INT("verticalDpi", self->vertical_dpi);
|
|
1526
|
+
|
|
1527
|
+
lxw_xml_empty_tag(self->file, "pageSetup", &attributes);
|
|
1528
|
+
|
|
1529
|
+
LXW_FREE_ATTRIBUTES();
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/*
|
|
1533
|
+
* Write the <printOptions> element.
|
|
1534
|
+
*/
|
|
1535
|
+
STATIC void
|
|
1536
|
+
_worksheet_write_print_options(lxw_worksheet *self)
|
|
1537
|
+
{
|
|
1538
|
+
struct xml_attribute_list attributes;
|
|
1539
|
+
struct xml_attribute *attribute;
|
|
1540
|
+
if (!self->print_options_changed)
|
|
1541
|
+
return;
|
|
1542
|
+
|
|
1543
|
+
LXW_INIT_ATTRIBUTES();
|
|
1544
|
+
|
|
1545
|
+
/* Set horizontal centering. */
|
|
1546
|
+
if (self->hcenter) {
|
|
1547
|
+
LXW_PUSH_ATTRIBUTES_STR("horizontalCentered", "1");
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/* Set vertical centering. */
|
|
1551
|
+
if (self->vcenter) {
|
|
1552
|
+
LXW_PUSH_ATTRIBUTES_STR("verticalCentered", "1");
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/* Enable row and column headers. */
|
|
1556
|
+
if (self->print_headers) {
|
|
1557
|
+
LXW_PUSH_ATTRIBUTES_STR("headings", "1");
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/* Set printed gridlines. */
|
|
1561
|
+
if (self->print_gridlines) {
|
|
1562
|
+
LXW_PUSH_ATTRIBUTES_STR("gridLines", "1");
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
lxw_xml_empty_tag(self->file, "printOptions", &attributes);
|
|
1566
|
+
|
|
1567
|
+
LXW_FREE_ATTRIBUTES();
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/*
|
|
1571
|
+
* Write the <row> element.
|
|
1572
|
+
*/
|
|
1573
|
+
STATIC void
|
|
1574
|
+
_write_row(lxw_worksheet *self, lxw_row *row, char *spans)
|
|
1575
|
+
{
|
|
1576
|
+
struct xml_attribute_list attributes;
|
|
1577
|
+
struct xml_attribute *attribute;
|
|
1578
|
+
int32_t xf_index = 0;
|
|
1579
|
+
double height;
|
|
1580
|
+
|
|
1581
|
+
if (row->format) {
|
|
1582
|
+
xf_index = lxw_format_get_xf_index(row->format);
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
if (row->height_changed)
|
|
1586
|
+
height = row->height;
|
|
1587
|
+
else
|
|
1588
|
+
height = self->default_row_height;
|
|
1589
|
+
|
|
1590
|
+
LXW_INIT_ATTRIBUTES();
|
|
1591
|
+
LXW_PUSH_ATTRIBUTES_INT("r", row->row_num + 1);
|
|
1592
|
+
|
|
1593
|
+
if (spans)
|
|
1594
|
+
LXW_PUSH_ATTRIBUTES_STR("spans", spans);
|
|
1595
|
+
|
|
1596
|
+
if (xf_index)
|
|
1597
|
+
LXW_PUSH_ATTRIBUTES_INT("s", xf_index);
|
|
1598
|
+
|
|
1599
|
+
if (row->format)
|
|
1600
|
+
LXW_PUSH_ATTRIBUTES_STR("customFormat", "1");
|
|
1601
|
+
|
|
1602
|
+
if (height != LXW_DEF_ROW_HEIGHT)
|
|
1603
|
+
LXW_PUSH_ATTRIBUTES_DBL("ht", height);
|
|
1604
|
+
|
|
1605
|
+
if (row->hidden)
|
|
1606
|
+
LXW_PUSH_ATTRIBUTES_STR("hidden", "1");
|
|
1607
|
+
|
|
1608
|
+
if (height != LXW_DEF_ROW_HEIGHT)
|
|
1609
|
+
LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
|
|
1610
|
+
|
|
1611
|
+
if (row->collapsed)
|
|
1612
|
+
LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
|
|
1613
|
+
|
|
1614
|
+
if (!row->data_changed)
|
|
1615
|
+
lxw_xml_empty_tag(self->file, "row", &attributes);
|
|
1616
|
+
else
|
|
1617
|
+
lxw_xml_start_tag(self->file, "row", &attributes);
|
|
1618
|
+
|
|
1619
|
+
LXW_FREE_ATTRIBUTES();
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/*
|
|
1623
|
+
* Convert the width of a cell from user's units to pixels. Excel rounds the
|
|
1624
|
+
* column width to the nearest pixel. If the width hasn't been set by the user
|
|
1625
|
+
* we use the default value. If the column is hidden it has a value of zero.
|
|
1626
|
+
*/
|
|
1627
|
+
STATIC int32_t
|
|
1628
|
+
_worksheet_size_col(lxw_worksheet *self, lxw_col_t col_num)
|
|
1629
|
+
{
|
|
1630
|
+
lxw_col_options *col_opt = NULL;
|
|
1631
|
+
uint32_t pixels;
|
|
1632
|
+
double width;
|
|
1633
|
+
double max_digit_width = 7.0; /* For Calabri 11. */
|
|
1634
|
+
double padding = 5.0;
|
|
1635
|
+
lxw_col_t col_index;
|
|
1636
|
+
|
|
1637
|
+
/* Search for the col number in the array of col_options. Each col_option
|
|
1638
|
+
* entry contains the start and end column for a range.
|
|
1639
|
+
*/
|
|
1640
|
+
for (col_index = 0; col_index < self->col_options_max; col_index++) {
|
|
1641
|
+
col_opt = self->col_options[col_index];
|
|
1642
|
+
|
|
1643
|
+
if (col_opt) {
|
|
1644
|
+
if (col_num >= col_opt->firstcol && col_num <= col_opt->lastcol)
|
|
1645
|
+
break;
|
|
1646
|
+
else
|
|
1647
|
+
col_opt = NULL;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
if (col_opt) {
|
|
1652
|
+
width = col_opt->width;
|
|
1653
|
+
|
|
1654
|
+
/* Convert to pixels. */
|
|
1655
|
+
if (width == 0) {
|
|
1656
|
+
pixels = 0;
|
|
1657
|
+
}
|
|
1658
|
+
else if (width < 1.0) {
|
|
1659
|
+
pixels = (uint32_t) (width * (max_digit_width + padding) + 0.5);
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
pixels = (uint32_t) (width * max_digit_width + 0.5) + 5;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
else {
|
|
1666
|
+
pixels = self->default_col_pixels;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
return pixels;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/*
|
|
1673
|
+
* Convert the height of a cell from user's units to pixels. If the height
|
|
1674
|
+
* hasn't been set by the user we use the default value. If the row is hidden
|
|
1675
|
+
* it has a value of zero.
|
|
1676
|
+
*/
|
|
1677
|
+
STATIC int32_t
|
|
1678
|
+
_worksheet_size_row(lxw_worksheet *self, lxw_row_t row_num)
|
|
1679
|
+
{
|
|
1680
|
+
lxw_row *row;
|
|
1681
|
+
uint32_t pixels;
|
|
1682
|
+
double height;
|
|
1683
|
+
|
|
1684
|
+
row = lxw_worksheet_find_row(self, row_num);
|
|
1685
|
+
|
|
1686
|
+
if (row) {
|
|
1687
|
+
height = row->height;
|
|
1688
|
+
|
|
1689
|
+
if (height == 0)
|
|
1690
|
+
pixels = 0;
|
|
1691
|
+
else
|
|
1692
|
+
pixels = (uint32_t) (4.0 / 3.0 * height);
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
pixels = (uint32_t) (4.0 / 3.0 * self->default_row_height);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
return pixels;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
/*
|
|
1702
|
+
* Calculate the vertices that define the position of a graphical object
|
|
1703
|
+
* within the worksheet in pixels.
|
|
1704
|
+
* +------------+------------+
|
|
1705
|
+
* | A | B |
|
|
1706
|
+
* +-----+------------+------------+
|
|
1707
|
+
* | |(x1,y1) | |
|
|
1708
|
+
* | 1 |(A1)._______|______ |
|
|
1709
|
+
* | | | | |
|
|
1710
|
+
* | | | | |
|
|
1711
|
+
* +-----+----| BITMAP |-----+
|
|
1712
|
+
* | | | | |
|
|
1713
|
+
* | 2 | |______________. |
|
|
1714
|
+
* | | | (B2)|
|
|
1715
|
+
* | | | (x2,y2)|
|
|
1716
|
+
* +---- +------------+------------+
|
|
1717
|
+
*
|
|
1718
|
+
* Example of an object that covers some of the area from cell A1 to cell B2.
|
|
1719
|
+
* Based on the width and height of the object we need to calculate 8 vars:
|
|
1720
|
+
*
|
|
1721
|
+
* col_start, row_start, col_end, row_end, x1, y1, x2, y2.
|
|
1722
|
+
*
|
|
1723
|
+
* We also calculate the absolute x and y position of the top left vertex of
|
|
1724
|
+
* the object. This is required for images:
|
|
1725
|
+
*
|
|
1726
|
+
* x_abs, y_abs
|
|
1727
|
+
*
|
|
1728
|
+
* The width and height of the cells that the object occupies can be variable
|
|
1729
|
+
* and have to be taken into account.
|
|
1730
|
+
*
|
|
1731
|
+
* The values of col_start and row_start are passed in from the calling
|
|
1732
|
+
* function. The values of col_end and row_end are calculated by subtracting
|
|
1733
|
+
* the width and height of the object from the width and height of the
|
|
1734
|
+
* underlying cells.
|
|
1735
|
+
*/
|
|
1736
|
+
STATIC void
|
|
1737
|
+
_worksheet_position_object_pixels(lxw_worksheet *self,
|
|
1738
|
+
lxw_image_options *image,
|
|
1739
|
+
lxw_drawing_object *drawing_object)
|
|
1740
|
+
{
|
|
1741
|
+
lxw_col_t col_start; /* Column containing upper left corner. */
|
|
1742
|
+
int32_t x1; /* Distance to left side of object. */
|
|
1743
|
+
|
|
1744
|
+
lxw_row_t row_start; /* Row containing top left corner. */
|
|
1745
|
+
int32_t y1; /* Distance to top of object. */
|
|
1746
|
+
|
|
1747
|
+
lxw_col_t col_end; /* Column containing lower right corner. */
|
|
1748
|
+
double x2; /* Distance to right side of object. */
|
|
1749
|
+
|
|
1750
|
+
lxw_row_t row_end; /* Row containing bottom right corner. */
|
|
1751
|
+
double y2; /* Distance to bottom of object. */
|
|
1752
|
+
|
|
1753
|
+
double width; /* Width of object frame. */
|
|
1754
|
+
double height; /* Height of object frame. */
|
|
1755
|
+
|
|
1756
|
+
uint32_t x_abs = 0; /* Abs. distance to left side of object. */
|
|
1757
|
+
uint32_t y_abs = 0; /* Abs. distance to top side of object. */
|
|
1758
|
+
|
|
1759
|
+
uint32_t i;
|
|
1760
|
+
|
|
1761
|
+
col_start = image->col;
|
|
1762
|
+
row_start = image->row;
|
|
1763
|
+
x1 = image->x_offset;
|
|
1764
|
+
y1 = image->y_offset;
|
|
1765
|
+
width = image->width;
|
|
1766
|
+
height = image->height;
|
|
1767
|
+
|
|
1768
|
+
/* Adjust start column for negative offsets. */
|
|
1769
|
+
while (x1 < 0 && col_start > 0) {
|
|
1770
|
+
x1 += _worksheet_size_col(self, col_start - 1);
|
|
1771
|
+
col_start--;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/* Adjust start row for negative offsets. */
|
|
1775
|
+
while (y1 < 0 && row_start > 0) {
|
|
1776
|
+
y1 += _worksheet_size_row(self, row_start - 1);
|
|
1777
|
+
row_start--;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/* Ensure that the image isn't shifted off the page at top left. */
|
|
1781
|
+
if (x1 < 0)
|
|
1782
|
+
x1 = 0;
|
|
1783
|
+
|
|
1784
|
+
if (y1 < 0)
|
|
1785
|
+
y1 = 0;
|
|
1786
|
+
|
|
1787
|
+
/* Calculate the absolute x offset of the top-left vertex. */
|
|
1788
|
+
if (self->col_size_changed) {
|
|
1789
|
+
for (i = 0; i < col_start; i++)
|
|
1790
|
+
x_abs += _worksheet_size_col(self, i);
|
|
1791
|
+
}
|
|
1792
|
+
else {
|
|
1793
|
+
/* Optimization for when the column widths haven't changed. */
|
|
1794
|
+
x_abs += self->default_col_pixels * col_start;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
x_abs += x1;
|
|
1798
|
+
|
|
1799
|
+
/* Calculate the absolute y offset of the top-left vertex. */
|
|
1800
|
+
/* Store the column change to allow optimizations. */
|
|
1801
|
+
if (self->row_size_changed) {
|
|
1802
|
+
for (i = 0; i < row_start; i++)
|
|
1803
|
+
y_abs += _worksheet_size_row(self, i);
|
|
1804
|
+
}
|
|
1805
|
+
else {
|
|
1806
|
+
/* Optimization for when the row heights haven"t changed. */
|
|
1807
|
+
y_abs += self->default_row_pixels * row_start;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
y_abs += y1;
|
|
1811
|
+
|
|
1812
|
+
/* Adjust start col for offsets that are greater than the col width. */
|
|
1813
|
+
while (x1 >= _worksheet_size_col(self, col_start)) {
|
|
1814
|
+
x1 -= _worksheet_size_col(self, col_start);
|
|
1815
|
+
col_start++;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/* Adjust start row for offsets that are greater than the row height. */
|
|
1819
|
+
while (y1 >= _worksheet_size_row(self, row_start)) {
|
|
1820
|
+
y1 -= _worksheet_size_row(self, row_start);
|
|
1821
|
+
row_start++;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/* Initialize end cell to the same as the start cell. */
|
|
1825
|
+
col_end = col_start;
|
|
1826
|
+
row_end = row_start;
|
|
1827
|
+
|
|
1828
|
+
width = width + x1;
|
|
1829
|
+
height = height + y1;
|
|
1830
|
+
|
|
1831
|
+
/* Subtract the underlying cell widths to find the end cell. */
|
|
1832
|
+
while (width >= _worksheet_size_col(self, col_end)) {
|
|
1833
|
+
width -= _worksheet_size_col(self, col_end);
|
|
1834
|
+
col_end++;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/* Subtract the underlying cell heights to find the end cell. */
|
|
1838
|
+
while (height >= _worksheet_size_row(self, row_end)) {
|
|
1839
|
+
height -= _worksheet_size_row(self, row_end);
|
|
1840
|
+
row_end++;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/* The end vertices are whatever is left from the width and height. */
|
|
1844
|
+
x2 = width;
|
|
1845
|
+
y2 = height;
|
|
1846
|
+
|
|
1847
|
+
/* Add the dimensions to the drawing object. */
|
|
1848
|
+
drawing_object->from.col = col_start;
|
|
1849
|
+
drawing_object->from.row = row_start;
|
|
1850
|
+
drawing_object->from.col_offset = x1;
|
|
1851
|
+
drawing_object->from.row_offset = y1;
|
|
1852
|
+
drawing_object->to.col = col_end;
|
|
1853
|
+
drawing_object->to.row = row_end;
|
|
1854
|
+
drawing_object->to.col_offset = x2;
|
|
1855
|
+
drawing_object->to.row_offset = y2;
|
|
1856
|
+
drawing_object->col_absolute = x_abs;
|
|
1857
|
+
drawing_object->row_absolute = y_abs;
|
|
1858
|
+
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
/*
|
|
1862
|
+
* Calculate the vertices that define the position of a graphical object
|
|
1863
|
+
* within the worksheet in EMUs. The vertices are expressed as English
|
|
1864
|
+
* Metric Units (EMUs). There are 12,700 EMUs per point.
|
|
1865
|
+
* Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
|
|
1866
|
+
*/
|
|
1867
|
+
STATIC void
|
|
1868
|
+
_worksheet_position_object_emus(lxw_worksheet *self,
|
|
1869
|
+
lxw_image_options *image,
|
|
1870
|
+
lxw_drawing_object *drawing_object)
|
|
1871
|
+
{
|
|
1872
|
+
|
|
1873
|
+
_worksheet_position_object_pixels(self, image, drawing_object);
|
|
1874
|
+
|
|
1875
|
+
/* Convert the pixel values to EMUs. See above. */
|
|
1876
|
+
drawing_object->from.col_offset *= 9525;
|
|
1877
|
+
drawing_object->from.row_offset *= 9525;
|
|
1878
|
+
drawing_object->to.col_offset *= 9525;
|
|
1879
|
+
drawing_object->to.row_offset *= 9525;
|
|
1880
|
+
drawing_object->to.col_offset += 0.5;
|
|
1881
|
+
drawing_object->to.row_offset += 0.5;
|
|
1882
|
+
drawing_object->col_absolute *= 9525;
|
|
1883
|
+
drawing_object->row_absolute *= 9525;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/*
|
|
1887
|
+
* Set up image/drawings.
|
|
1888
|
+
*/
|
|
1889
|
+
void
|
|
1890
|
+
lxw_worksheet_prepare_image(lxw_worksheet *self,
|
|
1891
|
+
uint16_t image_ref_id, uint16_t drawing_id,
|
|
1892
|
+
lxw_image_options *image_data)
|
|
1893
|
+
{
|
|
1894
|
+
lxw_drawing_object *drawing_object;
|
|
1895
|
+
lxw_rel_tuple *relationship;
|
|
1896
|
+
double width;
|
|
1897
|
+
double height;
|
|
1898
|
+
char filename[LXW_FILENAME_LENGTH];
|
|
1899
|
+
|
|
1900
|
+
if (!self->drawing) {
|
|
1901
|
+
self->drawing = lxw_drawing_new();
|
|
1902
|
+
self->drawing->embedded = LXW_TRUE;
|
|
1903
|
+
RETURN_VOID_ON_MEM_ERROR(self->drawing);
|
|
1904
|
+
|
|
1905
|
+
relationship = calloc(1, sizeof(lxw_rel_tuple));
|
|
1906
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
|
|
1907
|
+
|
|
1908
|
+
relationship->type = lxw_strdup("/drawing");
|
|
1909
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
|
|
1910
|
+
|
|
1911
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
1912
|
+
"../drawings/drawing%d.xml", drawing_id);
|
|
1913
|
+
|
|
1914
|
+
relationship->target = lxw_strdup(filename);
|
|
1915
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
|
|
1916
|
+
|
|
1917
|
+
STAILQ_INSERT_TAIL(self->external_drawing_links, relationship,
|
|
1918
|
+
list_pointers);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
drawing_object = calloc(1, sizeof(lxw_drawing_object));
|
|
1922
|
+
RETURN_VOID_ON_MEM_ERROR(drawing_object);
|
|
1923
|
+
|
|
1924
|
+
drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE;
|
|
1925
|
+
drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
|
|
1926
|
+
drawing_object->description = lxw_strdup(image_data->short_name);
|
|
1927
|
+
|
|
1928
|
+
/* Scale to user scale. */
|
|
1929
|
+
width = image_data->width * image_data->x_scale;
|
|
1930
|
+
height = image_data->height * image_data->y_scale;
|
|
1931
|
+
|
|
1932
|
+
/* Scale by non 96dpi resolutions. */
|
|
1933
|
+
width *= 96.0 / image_data->x_dpi;
|
|
1934
|
+
height *= 96.0 / image_data->y_dpi;
|
|
1935
|
+
|
|
1936
|
+
/* Convert to the nearest pixel. */
|
|
1937
|
+
image_data->width = width;
|
|
1938
|
+
image_data->height = height;
|
|
1939
|
+
|
|
1940
|
+
_worksheet_position_object_emus(self, image_data, drawing_object);
|
|
1941
|
+
|
|
1942
|
+
/* Convert from pixels to emus. */
|
|
1943
|
+
drawing_object->width = (uint32_t) (0.5 + width * 9525);
|
|
1944
|
+
drawing_object->height = (uint32_t) (0.5 + height * 9525);
|
|
1945
|
+
|
|
1946
|
+
lxw_add_drawing_object(self->drawing, drawing_object);
|
|
1947
|
+
|
|
1948
|
+
relationship = calloc(1, sizeof(lxw_rel_tuple));
|
|
1949
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
|
|
1950
|
+
|
|
1951
|
+
relationship->type = lxw_strdup("/image");
|
|
1952
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
|
|
1953
|
+
|
|
1954
|
+
lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id,
|
|
1955
|
+
image_data->extension);
|
|
1956
|
+
|
|
1957
|
+
relationship->target = lxw_strdup(filename);
|
|
1958
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
|
|
1959
|
+
|
|
1960
|
+
STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
|
|
1961
|
+
|
|
1962
|
+
return;
|
|
1963
|
+
|
|
1964
|
+
mem_error:
|
|
1965
|
+
if (relationship) {
|
|
1966
|
+
free(relationship->type);
|
|
1967
|
+
free(relationship->target);
|
|
1968
|
+
free(relationship->target_mode);
|
|
1969
|
+
free(relationship);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/*
|
|
1974
|
+
* Set up chart/drawings.
|
|
1975
|
+
*/
|
|
1976
|
+
void
|
|
1977
|
+
lxw_worksheet_prepare_chart(lxw_worksheet *self,
|
|
1978
|
+
uint16_t chart_ref_id, uint16_t drawing_id,
|
|
1979
|
+
lxw_image_options *image_data)
|
|
1980
|
+
{
|
|
1981
|
+
lxw_drawing_object *drawing_object;
|
|
1982
|
+
lxw_rel_tuple *relationship;
|
|
1983
|
+
double width;
|
|
1984
|
+
double height;
|
|
1985
|
+
char filename[LXW_FILENAME_LENGTH];
|
|
1986
|
+
|
|
1987
|
+
if (!self->drawing) {
|
|
1988
|
+
self->drawing = lxw_drawing_new();
|
|
1989
|
+
self->drawing->embedded = LXW_TRUE;
|
|
1990
|
+
RETURN_VOID_ON_MEM_ERROR(self->drawing);
|
|
1991
|
+
|
|
1992
|
+
relationship = calloc(1, sizeof(lxw_rel_tuple));
|
|
1993
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
|
|
1994
|
+
|
|
1995
|
+
relationship->type = lxw_strdup("/drawing");
|
|
1996
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
|
|
1997
|
+
|
|
1998
|
+
lxw_snprintf(filename, LXW_FILENAME_LENGTH,
|
|
1999
|
+
"../drawings/drawing%d.xml", drawing_id);
|
|
2000
|
+
|
|
2001
|
+
relationship->target = lxw_strdup(filename);
|
|
2002
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
|
|
2003
|
+
|
|
2004
|
+
STAILQ_INSERT_TAIL(self->external_drawing_links, relationship,
|
|
2005
|
+
list_pointers);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
drawing_object = calloc(1, sizeof(lxw_drawing_object));
|
|
2009
|
+
RETURN_VOID_ON_MEM_ERROR(drawing_object);
|
|
2010
|
+
|
|
2011
|
+
drawing_object->anchor_type = LXW_ANCHOR_TYPE_CHART;
|
|
2012
|
+
drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
|
|
2013
|
+
drawing_object->description = lxw_strdup("TODO_DESC");
|
|
2014
|
+
|
|
2015
|
+
/* Scale to user scale. */
|
|
2016
|
+
width = image_data->width * image_data->x_scale;
|
|
2017
|
+
height = image_data->height * image_data->y_scale;
|
|
2018
|
+
|
|
2019
|
+
/* Convert to the nearest pixel. */
|
|
2020
|
+
image_data->width = width;
|
|
2021
|
+
image_data->height = height;
|
|
2022
|
+
|
|
2023
|
+
_worksheet_position_object_emus(self, image_data, drawing_object);
|
|
2024
|
+
|
|
2025
|
+
/* Convert from pixels to emus. */
|
|
2026
|
+
drawing_object->width = (uint32_t) (0.5 + width * 9525);
|
|
2027
|
+
drawing_object->height = (uint32_t) (0.5 + height * 9525);
|
|
2028
|
+
|
|
2029
|
+
lxw_add_drawing_object(self->drawing, drawing_object);
|
|
2030
|
+
|
|
2031
|
+
relationship = calloc(1, sizeof(lxw_rel_tuple));
|
|
2032
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
|
|
2033
|
+
|
|
2034
|
+
relationship->type = lxw_strdup("/chart");
|
|
2035
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
|
|
2036
|
+
|
|
2037
|
+
lxw_snprintf(filename, 32, "../charts/chart%d.xml", chart_ref_id);
|
|
2038
|
+
|
|
2039
|
+
relationship->target = lxw_strdup(filename);
|
|
2040
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
|
|
2041
|
+
|
|
2042
|
+
STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
|
|
2043
|
+
|
|
2044
|
+
return;
|
|
2045
|
+
|
|
2046
|
+
mem_error:
|
|
2047
|
+
if (relationship) {
|
|
2048
|
+
free(relationship->type);
|
|
2049
|
+
free(relationship->target);
|
|
2050
|
+
free(relationship->target_mode);
|
|
2051
|
+
free(relationship);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/*
|
|
2056
|
+
* Extract width and height information from a PNG file.
|
|
2057
|
+
*/
|
|
2058
|
+
STATIC lxw_error
|
|
2059
|
+
_process_png(lxw_image_options *image_options)
|
|
2060
|
+
{
|
|
2061
|
+
uint32_t length;
|
|
2062
|
+
uint32_t offset;
|
|
2063
|
+
char type[4];
|
|
2064
|
+
uint32_t width = 0;
|
|
2065
|
+
uint32_t height = 0;
|
|
2066
|
+
double x_dpi = 96;
|
|
2067
|
+
double y_dpi = 96;
|
|
2068
|
+
int fseek_err;
|
|
2069
|
+
|
|
2070
|
+
FILE *stream = image_options->stream;
|
|
2071
|
+
|
|
2072
|
+
/* Skip another 4 bytes to the end of the PNG header. */
|
|
2073
|
+
fseek_err = fseek(stream, 4, SEEK_CUR);
|
|
2074
|
+
if (fseek_err)
|
|
2075
|
+
goto file_error;
|
|
2076
|
+
|
|
2077
|
+
while (!feof(stream)) {
|
|
2078
|
+
|
|
2079
|
+
/* Read the PNG length and type fields for the sub-section. */
|
|
2080
|
+
if (fread(&length, sizeof(length), 1, stream) < 1)
|
|
2081
|
+
break;
|
|
2082
|
+
|
|
2083
|
+
if (fread(&type, 1, 4, stream) < 4)
|
|
2084
|
+
break;
|
|
2085
|
+
|
|
2086
|
+
/* Convert the length to network order. */
|
|
2087
|
+
length = LXW_UINT32_NETWORK(length);
|
|
2088
|
+
|
|
2089
|
+
/* The offset for next fseek() is the field length + type length. */
|
|
2090
|
+
offset = length + 4;
|
|
2091
|
+
|
|
2092
|
+
if (memcmp(type, "IHDR", 4) == 0) {
|
|
2093
|
+
if (fread(&width, sizeof(width), 1, stream) < 1)
|
|
2094
|
+
break;
|
|
2095
|
+
|
|
2096
|
+
if (fread(&height, sizeof(height), 1, stream) < 1)
|
|
2097
|
+
break;
|
|
2098
|
+
|
|
2099
|
+
width = LXW_UINT32_NETWORK(width);
|
|
2100
|
+
height = LXW_UINT32_NETWORK(height);
|
|
2101
|
+
|
|
2102
|
+
/* Reduce the offset by the length of previous freads(). */
|
|
2103
|
+
offset -= 8;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
if (memcmp(type, "pHYs", 4) == 0) {
|
|
2107
|
+
uint32_t x_ppu = 0;
|
|
2108
|
+
uint32_t y_ppu = 0;
|
|
2109
|
+
uint8_t units = 1;
|
|
2110
|
+
|
|
2111
|
+
if (fread(&x_ppu, sizeof(x_ppu), 1, stream) < 1)
|
|
2112
|
+
break;
|
|
2113
|
+
|
|
2114
|
+
if (fread(&y_ppu, sizeof(y_ppu), 1, stream) < 1)
|
|
2115
|
+
break;
|
|
2116
|
+
|
|
2117
|
+
if (fread(&units, sizeof(units), 1, stream) < 1)
|
|
2118
|
+
break;
|
|
2119
|
+
|
|
2120
|
+
if (units == 1) {
|
|
2121
|
+
x_ppu = LXW_UINT32_NETWORK(x_ppu);
|
|
2122
|
+
y_ppu = LXW_UINT32_NETWORK(y_ppu);
|
|
2123
|
+
|
|
2124
|
+
x_dpi = (double) x_ppu *0.0254;
|
|
2125
|
+
y_dpi = (double) y_ppu *0.0254;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
/* Reduce the offset by the length of previous freads(). */
|
|
2129
|
+
offset -= 9;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
if (memcmp(type, "IEND", 4) == 0)
|
|
2133
|
+
break;
|
|
2134
|
+
|
|
2135
|
+
if (!feof(stream)) {
|
|
2136
|
+
fseek_err = fseek(stream, offset, SEEK_CUR);
|
|
2137
|
+
if (fseek_err)
|
|
2138
|
+
goto file_error;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
/* Ensure that we read some valid data from the file. */
|
|
2143
|
+
if (width == 0)
|
|
2144
|
+
goto file_error;
|
|
2145
|
+
|
|
2146
|
+
/* Set the image metadata. */
|
|
2147
|
+
image_options->image_type = LXW_IMAGE_PNG;
|
|
2148
|
+
image_options->width = width;
|
|
2149
|
+
image_options->height = height;
|
|
2150
|
+
image_options->x_dpi = x_dpi ? x_dpi : 96;
|
|
2151
|
+
image_options->y_dpi = y_dpi ? x_dpi : 96;
|
|
2152
|
+
image_options->extension = lxw_strdup("png");
|
|
2153
|
+
|
|
2154
|
+
return LXW_NO_ERROR;
|
|
2155
|
+
|
|
2156
|
+
file_error:
|
|
2157
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
2158
|
+
"no size data found in file: %s.",
|
|
2159
|
+
image_options->filename);
|
|
2160
|
+
|
|
2161
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
/*
|
|
2165
|
+
* Extract width and height information from a JPEG file.
|
|
2166
|
+
*/
|
|
2167
|
+
STATIC lxw_error
|
|
2168
|
+
_process_jpeg(lxw_image_options *image_options)
|
|
2169
|
+
{
|
|
2170
|
+
uint16_t length;
|
|
2171
|
+
uint16_t marker;
|
|
2172
|
+
uint32_t offset;
|
|
2173
|
+
uint16_t width = 0;
|
|
2174
|
+
uint16_t height = 0;
|
|
2175
|
+
double x_dpi = 96;
|
|
2176
|
+
double y_dpi = 96;
|
|
2177
|
+
int fseek_err;
|
|
2178
|
+
|
|
2179
|
+
FILE *stream = image_options->stream;
|
|
2180
|
+
|
|
2181
|
+
/* Read back 2 bytes to the end of the initial 0xFFD8 marker. */
|
|
2182
|
+
fseek_err = fseek(stream, -2, SEEK_CUR);
|
|
2183
|
+
if (fseek_err)
|
|
2184
|
+
goto file_error;
|
|
2185
|
+
|
|
2186
|
+
/* Search through the image data to read the height and width in the */
|
|
2187
|
+
/* 0xFFC0/C2 element. Also read the DPI in the 0xFFE0 element. */
|
|
2188
|
+
while (!feof(stream)) {
|
|
2189
|
+
|
|
2190
|
+
/* Read the JPEG marker and length fields for the sub-section. */
|
|
2191
|
+
if (fread(&marker, sizeof(marker), 1, stream) < 1)
|
|
2192
|
+
break;
|
|
2193
|
+
|
|
2194
|
+
if (fread(&length, sizeof(length), 1, stream) < 1)
|
|
2195
|
+
break;
|
|
2196
|
+
|
|
2197
|
+
/* Convert the marker and length to network order. */
|
|
2198
|
+
marker = LXW_UINT16_NETWORK(marker);
|
|
2199
|
+
length = LXW_UINT16_NETWORK(length);
|
|
2200
|
+
|
|
2201
|
+
/* The offset for next fseek() is the field length + type length. */
|
|
2202
|
+
offset = length - 2;
|
|
2203
|
+
|
|
2204
|
+
if (marker == 0xFFC0 || marker == 0xFFC2) {
|
|
2205
|
+
/* Skip 1 byte to height and width. */
|
|
2206
|
+
fseek_err = fseek(stream, 1, SEEK_CUR);
|
|
2207
|
+
if (fseek_err)
|
|
2208
|
+
goto file_error;
|
|
2209
|
+
|
|
2210
|
+
if (fread(&height, sizeof(height), 1, stream) < 1)
|
|
2211
|
+
break;
|
|
2212
|
+
|
|
2213
|
+
if (fread(&width, sizeof(width), 1, stream) < 1)
|
|
2214
|
+
break;
|
|
2215
|
+
|
|
2216
|
+
height = LXW_UINT16_NETWORK(height);
|
|
2217
|
+
width = LXW_UINT16_NETWORK(width);
|
|
2218
|
+
|
|
2219
|
+
offset -= 9;
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
if (marker == 0xFFE0) {
|
|
2223
|
+
uint16_t x_density = 0;
|
|
2224
|
+
uint16_t y_density = 0;
|
|
2225
|
+
uint8_t units = 1;
|
|
2226
|
+
|
|
2227
|
+
fseek_err = fseek(stream, 7, SEEK_CUR);
|
|
2228
|
+
if (fseek_err)
|
|
2229
|
+
goto file_error;
|
|
2230
|
+
|
|
2231
|
+
if (fread(&units, sizeof(units), 1, stream) < 1)
|
|
2232
|
+
break;
|
|
2233
|
+
|
|
2234
|
+
if (fread(&x_density, sizeof(x_density), 1, stream) < 1)
|
|
2235
|
+
break;
|
|
2236
|
+
|
|
2237
|
+
if (fread(&y_density, sizeof(y_density), 1, stream) < 1)
|
|
2238
|
+
break;
|
|
2239
|
+
|
|
2240
|
+
x_density = LXW_UINT16_NETWORK(x_density);
|
|
2241
|
+
y_density = LXW_UINT16_NETWORK(y_density);
|
|
2242
|
+
|
|
2243
|
+
if (units == 1) {
|
|
2244
|
+
x_dpi = x_density;
|
|
2245
|
+
y_dpi = y_density;
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
if (units == 2) {
|
|
2249
|
+
x_dpi = x_density * 2.54;
|
|
2250
|
+
y_dpi = y_density * 2.54;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
offset -= 12;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
if (marker == 0xFFDA)
|
|
2257
|
+
break;
|
|
2258
|
+
|
|
2259
|
+
if (!feof(stream)) {
|
|
2260
|
+
fseek_err = fseek(stream, offset, SEEK_CUR);
|
|
2261
|
+
if (fseek_err)
|
|
2262
|
+
goto file_error;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
/* Ensure that we read some valid data from the file. */
|
|
2267
|
+
if (width == 0)
|
|
2268
|
+
goto file_error;
|
|
2269
|
+
|
|
2270
|
+
/* Set the image metadata. */
|
|
2271
|
+
image_options->image_type = LXW_IMAGE_JPEG;
|
|
2272
|
+
image_options->width = width;
|
|
2273
|
+
image_options->height = height;
|
|
2274
|
+
image_options->x_dpi = x_dpi ? x_dpi : 96;
|
|
2275
|
+
image_options->y_dpi = y_dpi ? x_dpi : 96;
|
|
2276
|
+
image_options->extension = lxw_strdup("jpeg");
|
|
2277
|
+
|
|
2278
|
+
return LXW_NO_ERROR;
|
|
2279
|
+
|
|
2280
|
+
file_error:
|
|
2281
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
2282
|
+
"no size data found in file: %s.",
|
|
2283
|
+
image_options->filename);
|
|
2284
|
+
|
|
2285
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
/*
|
|
2289
|
+
* Extract width and height information from a BMP file.
|
|
2290
|
+
*/
|
|
2291
|
+
STATIC lxw_error
|
|
2292
|
+
_process_bmp(lxw_image_options *image_options)
|
|
2293
|
+
{
|
|
2294
|
+
uint32_t width = 0;
|
|
2295
|
+
uint32_t height = 0;
|
|
2296
|
+
double x_dpi = 96;
|
|
2297
|
+
double y_dpi = 96;
|
|
2298
|
+
int fseek_err;
|
|
2299
|
+
|
|
2300
|
+
FILE *stream = image_options->stream;
|
|
2301
|
+
|
|
2302
|
+
/* Skip another 14 bytes to the start of the BMP height/width. */
|
|
2303
|
+
fseek_err = fseek(stream, 14, SEEK_CUR);
|
|
2304
|
+
if (fseek_err)
|
|
2305
|
+
goto file_error;
|
|
2306
|
+
|
|
2307
|
+
if (fread(&width, sizeof(width), 1, stream) < 1)
|
|
2308
|
+
width = 0;
|
|
2309
|
+
|
|
2310
|
+
if (fread(&height, sizeof(height), 1, stream) < 1)
|
|
2311
|
+
height = 0;
|
|
2312
|
+
|
|
2313
|
+
/* Ensure that we read some valid data from the file. */
|
|
2314
|
+
if (width == 0)
|
|
2315
|
+
goto file_error;
|
|
2316
|
+
|
|
2317
|
+
/* Set the image metadata. */
|
|
2318
|
+
image_options->image_type = LXW_IMAGE_BMP;
|
|
2319
|
+
image_options->width = width;
|
|
2320
|
+
image_options->height = height;
|
|
2321
|
+
image_options->x_dpi = x_dpi;
|
|
2322
|
+
image_options->y_dpi = y_dpi;
|
|
2323
|
+
image_options->extension = lxw_strdup("bmp");
|
|
2324
|
+
|
|
2325
|
+
return LXW_NO_ERROR;
|
|
2326
|
+
|
|
2327
|
+
file_error:
|
|
2328
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
2329
|
+
"no size data found in file: %s.",
|
|
2330
|
+
image_options->filename);
|
|
2331
|
+
|
|
2332
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
/*
|
|
2336
|
+
* Extract information from the image file such as dimension, type, filename,
|
|
2337
|
+
* and extension.
|
|
2338
|
+
*/
|
|
2339
|
+
STATIC lxw_error
|
|
2340
|
+
_get_image_properties(lxw_image_options *image_options)
|
|
2341
|
+
{
|
|
2342
|
+
unsigned char signature[4];
|
|
2343
|
+
|
|
2344
|
+
/* Read 4 bytes to look for the file header/signature. */
|
|
2345
|
+
if (fread(signature, 1, 4, image_options->stream) < 4) {
|
|
2346
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
2347
|
+
"couldn't read file type for file: %s.",
|
|
2348
|
+
image_options->filename);
|
|
2349
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
if (memcmp(&signature[1], "PNG", 3) == 0) {
|
|
2353
|
+
if (_process_png(image_options) != LXW_NO_ERROR)
|
|
2354
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2355
|
+
}
|
|
2356
|
+
else if (signature[0] == 0xFF && signature[1] == 0xD8) {
|
|
2357
|
+
if (_process_jpeg(image_options) != LXW_NO_ERROR)
|
|
2358
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2359
|
+
}
|
|
2360
|
+
else if (memcmp(signature, "BM", 2) == 0) {
|
|
2361
|
+
if (_process_bmp(image_options) != LXW_NO_ERROR)
|
|
2362
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2363
|
+
}
|
|
2364
|
+
else {
|
|
2365
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
2366
|
+
"unsupported image format for file: %s.",
|
|
2367
|
+
image_options->filename);
|
|
2368
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
return LXW_NO_ERROR;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
/*****************************************************************************
|
|
2375
|
+
*
|
|
2376
|
+
* XML file assembly functions.
|
|
2377
|
+
*
|
|
2378
|
+
****************************************************************************/
|
|
2379
|
+
|
|
2380
|
+
/*
|
|
2381
|
+
* Write out a number worksheet cell. Doesn't use the xml functions as an
|
|
2382
|
+
* optimization in the inner cell writing loop.
|
|
2383
|
+
*/
|
|
2384
|
+
STATIC void
|
|
2385
|
+
_write_number_cell(lxw_worksheet *self, char *range,
|
|
2386
|
+
int32_t style_index, lxw_cell *cell)
|
|
2387
|
+
{
|
|
2388
|
+
if (style_index)
|
|
2389
|
+
fprintf(self->file,
|
|
2390
|
+
"<c r=\"%s\" s=\"%d\"><v>%.16g</v></c>",
|
|
2391
|
+
range, style_index, cell->u.number);
|
|
2392
|
+
else
|
|
2393
|
+
fprintf(self->file,
|
|
2394
|
+
"<c r=\"%s\"><v>%.16g</v></c>", range, cell->u.number);
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/*
|
|
2398
|
+
* Write out a string worksheet cell. Doesn't use the xml functions as an
|
|
2399
|
+
* optimization in the inner cell writing loop.
|
|
2400
|
+
*/
|
|
2401
|
+
STATIC void
|
|
2402
|
+
_write_string_cell(lxw_worksheet *self, char *range,
|
|
2403
|
+
int32_t style_index, lxw_cell *cell)
|
|
2404
|
+
{
|
|
2405
|
+
if (style_index)
|
|
2406
|
+
fprintf(self->file,
|
|
2407
|
+
"<c r=\"%s\" s=\"%d\" t=\"s\"><v>%d</v></c>",
|
|
2408
|
+
range, style_index, cell->u.string_id);
|
|
2409
|
+
else
|
|
2410
|
+
fprintf(self->file,
|
|
2411
|
+
"<c r=\"%s\" t=\"s\"><v>%d</v></c>",
|
|
2412
|
+
range, cell->u.string_id);
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
/*
|
|
2416
|
+
* Write out an inline string. Doesn't use the xml functions as an
|
|
2417
|
+
* optimization in the inner cell writing loop.
|
|
2418
|
+
*/
|
|
2419
|
+
STATIC void
|
|
2420
|
+
_write_inline_string_cell(lxw_worksheet *self, char *range,
|
|
2421
|
+
int32_t style_index, lxw_cell *cell)
|
|
2422
|
+
{
|
|
2423
|
+
char *string = lxw_escape_data(cell->u.string);
|
|
2424
|
+
|
|
2425
|
+
/* Add attribute to preserve leading or trailing whitespace. */
|
|
2426
|
+
if (isspace((unsigned char) string[0])
|
|
2427
|
+
|| isspace((unsigned char) string[strlen(string) - 1])) {
|
|
2428
|
+
|
|
2429
|
+
if (style_index)
|
|
2430
|
+
fprintf(self->file,
|
|
2431
|
+
"<c r=\"%s\" s=\"%d\" t=\"inlineStr\"><is>"
|
|
2432
|
+
"<t xml:space=\"preserve\">%s</t></is></c>",
|
|
2433
|
+
range, style_index, string);
|
|
2434
|
+
else
|
|
2435
|
+
fprintf(self->file,
|
|
2436
|
+
"<c r=\"%s\" t=\"inlineStr\"><is>"
|
|
2437
|
+
"<t xml:space=\"preserve\">%s</t></is></c>",
|
|
2438
|
+
range, string);
|
|
2439
|
+
}
|
|
2440
|
+
else {
|
|
2441
|
+
if (style_index)
|
|
2442
|
+
fprintf(self->file,
|
|
2443
|
+
"<c r=\"%s\" s=\"%d\" t=\"inlineStr\">"
|
|
2444
|
+
"<is><t>%s</t></is></c>", range, style_index, string);
|
|
2445
|
+
else
|
|
2446
|
+
fprintf(self->file,
|
|
2447
|
+
"<c r=\"%s\" t=\"inlineStr\">"
|
|
2448
|
+
"<is><t>%s</t></is></c>", range, string);
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
free(string);
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
/*
|
|
2455
|
+
* Write out a formula worksheet cell with a numeric result.
|
|
2456
|
+
*/
|
|
2457
|
+
STATIC void
|
|
2458
|
+
_write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
|
|
2459
|
+
{
|
|
2460
|
+
char data[LXW_ATTR_32];
|
|
2461
|
+
|
|
2462
|
+
lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
|
|
2463
|
+
|
|
2464
|
+
lxw_xml_data_element(self->file, "f", cell->u.string, NULL);
|
|
2465
|
+
lxw_xml_data_element(self->file, "v", data, NULL);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
/*
|
|
2469
|
+
* Write out an array formula worksheet cell with a numeric result.
|
|
2470
|
+
*/
|
|
2471
|
+
STATIC void
|
|
2472
|
+
_write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
|
|
2473
|
+
{
|
|
2474
|
+
struct xml_attribute_list attributes;
|
|
2475
|
+
struct xml_attribute *attribute;
|
|
2476
|
+
char data[LXW_ATTR_32];
|
|
2477
|
+
|
|
2478
|
+
LXW_INIT_ATTRIBUTES();
|
|
2479
|
+
LXW_PUSH_ATTRIBUTES_STR("t", "array");
|
|
2480
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1);
|
|
2481
|
+
|
|
2482
|
+
lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
|
|
2483
|
+
|
|
2484
|
+
lxw_xml_data_element(self->file, "f", cell->u.string, &attributes);
|
|
2485
|
+
lxw_xml_data_element(self->file, "v", data, NULL);
|
|
2486
|
+
|
|
2487
|
+
LXW_FREE_ATTRIBUTES();
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
/*
|
|
2491
|
+
* Write out a boolean worksheet cell.
|
|
2492
|
+
*/
|
|
2493
|
+
STATIC void
|
|
2494
|
+
_write_boolean_cell(lxw_worksheet *self, lxw_cell *cell)
|
|
2495
|
+
{
|
|
2496
|
+
char data[LXW_ATTR_32];
|
|
2497
|
+
|
|
2498
|
+
if (cell->u.number)
|
|
2499
|
+
data[0] = '1';
|
|
2500
|
+
else
|
|
2501
|
+
data[0] = '0';
|
|
2502
|
+
|
|
2503
|
+
data[1] = '\0';
|
|
2504
|
+
|
|
2505
|
+
lxw_xml_data_element(self->file, "v", data, NULL);
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
/*
|
|
2509
|
+
* Calculate the "spans" attribute of the <row> tag. This is an XLSX
|
|
2510
|
+
* optimization and isn't strictly required. However, it makes comparing
|
|
2511
|
+
* files easier.
|
|
2512
|
+
*
|
|
2513
|
+
* The span is the same for each block of 16 rows.
|
|
2514
|
+
*/
|
|
2515
|
+
STATIC void
|
|
2516
|
+
_calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
|
|
2517
|
+
{
|
|
2518
|
+
lxw_col_t span_col_min = RB_MIN(lxw_table_cells, row->cells)->col_num;
|
|
2519
|
+
lxw_col_t span_col_max = RB_MAX(lxw_table_cells, row->cells)->col_num;
|
|
2520
|
+
lxw_col_t col_min;
|
|
2521
|
+
lxw_col_t col_max;
|
|
2522
|
+
*block_num = row->row_num / 16;
|
|
2523
|
+
|
|
2524
|
+
row = RB_NEXT(lxw_table_rows, root, row);
|
|
2525
|
+
|
|
2526
|
+
while (row && (int32_t) (row->row_num / 16) == *block_num) {
|
|
2527
|
+
|
|
2528
|
+
if (!RB_EMPTY(row->cells)) {
|
|
2529
|
+
col_min = RB_MIN(lxw_table_cells, row->cells)->col_num;
|
|
2530
|
+
col_max = RB_MAX(lxw_table_cells, row->cells)->col_num;
|
|
2531
|
+
|
|
2532
|
+
if (col_min < span_col_min)
|
|
2533
|
+
span_col_min = col_min;
|
|
2534
|
+
|
|
2535
|
+
if (col_max > span_col_max)
|
|
2536
|
+
span_col_max = col_max;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
row = RB_NEXT(lxw_table_rows, root, row);
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
lxw_snprintf(span, LXW_MAX_CELL_RANGE_LENGTH,
|
|
2543
|
+
"%d:%d", span_col_min + 1, span_col_max + 1);
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
/*
|
|
2547
|
+
* Write out a generic worksheet cell.
|
|
2548
|
+
*/
|
|
2549
|
+
STATIC void
|
|
2550
|
+
_write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format)
|
|
2551
|
+
{
|
|
2552
|
+
struct xml_attribute_list attributes;
|
|
2553
|
+
struct xml_attribute *attribute;
|
|
2554
|
+
char range[LXW_MAX_CELL_NAME_LENGTH] = { 0 };
|
|
2555
|
+
lxw_row_t row_num = cell->row_num;
|
|
2556
|
+
lxw_col_t col_num = cell->col_num;
|
|
2557
|
+
int32_t style_index = 0;
|
|
2558
|
+
|
|
2559
|
+
lxw_rowcol_to_cell(range, row_num, col_num);
|
|
2560
|
+
|
|
2561
|
+
if (cell->format) {
|
|
2562
|
+
style_index = lxw_format_get_xf_index(cell->format);
|
|
2563
|
+
}
|
|
2564
|
+
else if (row_format) {
|
|
2565
|
+
style_index = lxw_format_get_xf_index(row_format);
|
|
2566
|
+
}
|
|
2567
|
+
else if (col_num < self->col_formats_max && self->col_formats[col_num]) {
|
|
2568
|
+
style_index = lxw_format_get_xf_index(self->col_formats[col_num]);
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
/* Unrolled optimization for most commonly written cell types. */
|
|
2572
|
+
if (cell->type == NUMBER_CELL) {
|
|
2573
|
+
_write_number_cell(self, range, style_index, cell);
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
if (cell->type == STRING_CELL) {
|
|
2578
|
+
_write_string_cell(self, range, style_index, cell);
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
if (cell->type == INLINE_STRING_CELL) {
|
|
2583
|
+
_write_inline_string_cell(self, range, style_index, cell);
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
/* For other cell types use the general functions. */
|
|
2588
|
+
LXW_INIT_ATTRIBUTES();
|
|
2589
|
+
LXW_PUSH_ATTRIBUTES_STR("r", range);
|
|
2590
|
+
|
|
2591
|
+
if (style_index)
|
|
2592
|
+
LXW_PUSH_ATTRIBUTES_INT("s", style_index);
|
|
2593
|
+
|
|
2594
|
+
if (cell->type == FORMULA_CELL) {
|
|
2595
|
+
lxw_xml_start_tag(self->file, "c", &attributes);
|
|
2596
|
+
_write_formula_num_cell(self, cell);
|
|
2597
|
+
lxw_xml_end_tag(self->file, "c");
|
|
2598
|
+
}
|
|
2599
|
+
else if (cell->type == BLANK_CELL) {
|
|
2600
|
+
lxw_xml_empty_tag(self->file, "c", &attributes);
|
|
2601
|
+
}
|
|
2602
|
+
else if (cell->type == BOOLEAN_CELL) {
|
|
2603
|
+
LXW_PUSH_ATTRIBUTES_STR("t", "b");
|
|
2604
|
+
lxw_xml_start_tag(self->file, "c", &attributes);
|
|
2605
|
+
_write_boolean_cell(self, cell);
|
|
2606
|
+
lxw_xml_end_tag(self->file, "c");
|
|
2607
|
+
}
|
|
2608
|
+
else if (cell->type == ARRAY_FORMULA_CELL) {
|
|
2609
|
+
lxw_xml_start_tag(self->file, "c", &attributes);
|
|
2610
|
+
_write_array_formula_num_cell(self, cell);
|
|
2611
|
+
lxw_xml_end_tag(self->file, "c");
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
LXW_FREE_ATTRIBUTES();
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
/*
|
|
2618
|
+
* Write out the worksheet data as a series of rows and cells.
|
|
2619
|
+
*/
|
|
2620
|
+
STATIC void
|
|
2621
|
+
_worksheet_write_rows(lxw_worksheet *self)
|
|
2622
|
+
{
|
|
2623
|
+
lxw_row *row;
|
|
2624
|
+
lxw_cell *cell;
|
|
2625
|
+
int32_t block_num = -1;
|
|
2626
|
+
char spans[LXW_MAX_CELL_RANGE_LENGTH] = { 0 };
|
|
2627
|
+
|
|
2628
|
+
RB_FOREACH(row, lxw_table_rows, self->table) {
|
|
2629
|
+
|
|
2630
|
+
if (RB_EMPTY(row->cells)) {
|
|
2631
|
+
/* Row contains no cells but has height, format or other data. */
|
|
2632
|
+
|
|
2633
|
+
/* Write a default span for default rows. */
|
|
2634
|
+
if (self->default_row_set)
|
|
2635
|
+
_write_row(self, row, "1:1");
|
|
2636
|
+
else
|
|
2637
|
+
_write_row(self, row, NULL);
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
/* Row and cell data. */
|
|
2641
|
+
if ((int32_t) row->row_num / 16 > block_num)
|
|
2642
|
+
_calculate_spans(row, spans, &block_num);
|
|
2643
|
+
|
|
2644
|
+
_write_row(self, row, spans);
|
|
2645
|
+
|
|
2646
|
+
RB_FOREACH(cell, lxw_table_cells, row->cells) {
|
|
2647
|
+
_write_cell(self, cell, row->format);
|
|
2648
|
+
}
|
|
2649
|
+
lxw_xml_end_tag(self->file, "row");
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
/*
|
|
2655
|
+
* Write out the worksheet data as a single row with cells. This method is
|
|
2656
|
+
* used when memory optimization is on. A single row is written and the data
|
|
2657
|
+
* array is reset. That way only one row of data is kept in memory at any one
|
|
2658
|
+
* time. We don't write span data in the optimized case since it is optional.
|
|
2659
|
+
*/
|
|
2660
|
+
void
|
|
2661
|
+
lxw_worksheet_write_single_row(lxw_worksheet *self)
|
|
2662
|
+
{
|
|
2663
|
+
lxw_row *row = self->optimize_row;
|
|
2664
|
+
lxw_col_t col;
|
|
2665
|
+
|
|
2666
|
+
/* skip row if it doesn't contain row formatting, cell data or a comment. */
|
|
2667
|
+
if (!(row->row_changed || row->data_changed))
|
|
2668
|
+
return;
|
|
2669
|
+
|
|
2670
|
+
/* Write the cells if the row contains data. */
|
|
2671
|
+
if (!row->data_changed) {
|
|
2672
|
+
/* Row data only. No cells. */
|
|
2673
|
+
_write_row(self, row, NULL);
|
|
2674
|
+
}
|
|
2675
|
+
else {
|
|
2676
|
+
/* Row and cell data. */
|
|
2677
|
+
_write_row(self, row, NULL);
|
|
2678
|
+
|
|
2679
|
+
for (col = self->dim_colmin; col <= self->dim_colmax; col++) {
|
|
2680
|
+
if (self->array[col]) {
|
|
2681
|
+
_write_cell(self, self->array[col], row->format);
|
|
2682
|
+
_free_cell(self->array[col]);
|
|
2683
|
+
self->array[col] = NULL;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
lxw_xml_end_tag(self->file, "row");
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
/* Reset the row. */
|
|
2691
|
+
row->height = LXW_DEF_ROW_HEIGHT;
|
|
2692
|
+
row->format = NULL;
|
|
2693
|
+
row->hidden = LXW_FALSE;
|
|
2694
|
+
row->level = 0;
|
|
2695
|
+
row->collapsed = LXW_FALSE;
|
|
2696
|
+
row->data_changed = LXW_FALSE;
|
|
2697
|
+
row->row_changed = LXW_FALSE;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
/*
|
|
2701
|
+
* Write the <col> element.
|
|
2702
|
+
*/
|
|
2703
|
+
STATIC void
|
|
2704
|
+
_worksheet_write_col_info(lxw_worksheet *self, lxw_col_options *options)
|
|
2705
|
+
{
|
|
2706
|
+
struct xml_attribute_list attributes;
|
|
2707
|
+
struct xml_attribute *attribute;
|
|
2708
|
+
|
|
2709
|
+
double width = options->width;
|
|
2710
|
+
uint8_t has_custom_width = LXW_TRUE;
|
|
2711
|
+
int32_t xf_index = 0;
|
|
2712
|
+
double max_digit_width = 7.0; /* For Calabri 11. */
|
|
2713
|
+
double padding = 5.0;
|
|
2714
|
+
|
|
2715
|
+
/* Get the format index. */
|
|
2716
|
+
if (options->format) {
|
|
2717
|
+
xf_index = lxw_format_get_xf_index(options->format);
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
/* Check if width is the Excel default. */
|
|
2721
|
+
if (width == LXW_DEF_COL_WIDTH) {
|
|
2722
|
+
|
|
2723
|
+
/* The default col width changes to 0 for hidden columns. */
|
|
2724
|
+
if (options->hidden)
|
|
2725
|
+
width = 0;
|
|
2726
|
+
else
|
|
2727
|
+
has_custom_width = LXW_FALSE;
|
|
2728
|
+
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
/* Convert column width from user units to character width. */
|
|
2732
|
+
if (width > 0) {
|
|
2733
|
+
if (width < 1) {
|
|
2734
|
+
width = (uint16_t) (((uint16_t)
|
|
2735
|
+
(width * (max_digit_width + padding) + 0.5))
|
|
2736
|
+
/ max_digit_width * 256.0) / 256.0;
|
|
2737
|
+
}
|
|
2738
|
+
else {
|
|
2739
|
+
width = (uint16_t) (((uint16_t)
|
|
2740
|
+
(width * max_digit_width + 0.5) + padding)
|
|
2741
|
+
/ max_digit_width * 256.0) / 256.0;
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
LXW_INIT_ATTRIBUTES();
|
|
2746
|
+
LXW_PUSH_ATTRIBUTES_INT("min", 1 + options->firstcol);
|
|
2747
|
+
LXW_PUSH_ATTRIBUTES_INT("max", 1 + options->lastcol);
|
|
2748
|
+
LXW_PUSH_ATTRIBUTES_DBL("width", width);
|
|
2749
|
+
|
|
2750
|
+
if (xf_index)
|
|
2751
|
+
LXW_PUSH_ATTRIBUTES_INT("style", xf_index);
|
|
2752
|
+
|
|
2753
|
+
if (options->hidden)
|
|
2754
|
+
LXW_PUSH_ATTRIBUTES_STR("hidden", "1");
|
|
2755
|
+
|
|
2756
|
+
if (has_custom_width)
|
|
2757
|
+
LXW_PUSH_ATTRIBUTES_STR("customWidth", "1");
|
|
2758
|
+
|
|
2759
|
+
if (options->level)
|
|
2760
|
+
LXW_PUSH_ATTRIBUTES_INT("outlineLevel", options->level);
|
|
2761
|
+
|
|
2762
|
+
if (options->collapsed)
|
|
2763
|
+
LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
|
|
2764
|
+
|
|
2765
|
+
lxw_xml_empty_tag(self->file, "col", &attributes);
|
|
2766
|
+
|
|
2767
|
+
LXW_FREE_ATTRIBUTES();
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
/*
|
|
2771
|
+
* Write the <cols> element and <col> sub elements.
|
|
2772
|
+
*/
|
|
2773
|
+
STATIC void
|
|
2774
|
+
_worksheet_write_cols(lxw_worksheet *self)
|
|
2775
|
+
{
|
|
2776
|
+
lxw_col_t col;
|
|
2777
|
+
|
|
2778
|
+
if (!self->col_size_changed)
|
|
2779
|
+
return;
|
|
2780
|
+
|
|
2781
|
+
lxw_xml_start_tag(self->file, "cols", NULL);
|
|
2782
|
+
|
|
2783
|
+
for (col = 0; col < self->col_options_max; col++) {
|
|
2784
|
+
if (self->col_options[col])
|
|
2785
|
+
_worksheet_write_col_info(self, self->col_options[col]);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
lxw_xml_end_tag(self->file, "cols");
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
/*
|
|
2792
|
+
* Write the <mergeCell> element.
|
|
2793
|
+
*/
|
|
2794
|
+
STATIC void
|
|
2795
|
+
_worksheet_write_merge_cell(lxw_worksheet *self,
|
|
2796
|
+
lxw_merged_range *merged_range)
|
|
2797
|
+
{
|
|
2798
|
+
|
|
2799
|
+
struct xml_attribute_list attributes;
|
|
2800
|
+
struct xml_attribute *attribute;
|
|
2801
|
+
char ref[LXW_MAX_CELL_RANGE_LENGTH];
|
|
2802
|
+
|
|
2803
|
+
LXW_INIT_ATTRIBUTES();
|
|
2804
|
+
|
|
2805
|
+
/* Convert the merge dimensions to a cell range. */
|
|
2806
|
+
lxw_rowcol_to_range(ref, merged_range->first_row, merged_range->first_col,
|
|
2807
|
+
merged_range->last_row, merged_range->last_col);
|
|
2808
|
+
|
|
2809
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", ref);
|
|
2810
|
+
|
|
2811
|
+
lxw_xml_empty_tag(self->file, "mergeCell", &attributes);
|
|
2812
|
+
|
|
2813
|
+
LXW_FREE_ATTRIBUTES();
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
/*
|
|
2817
|
+
* Write the <mergeCells> element.
|
|
2818
|
+
*/
|
|
2819
|
+
STATIC void
|
|
2820
|
+
_worksheet_write_merge_cells(lxw_worksheet *self)
|
|
2821
|
+
{
|
|
2822
|
+
struct xml_attribute_list attributes;
|
|
2823
|
+
struct xml_attribute *attribute;
|
|
2824
|
+
lxw_merged_range *merged_range;
|
|
2825
|
+
|
|
2826
|
+
if (self->merged_range_count) {
|
|
2827
|
+
LXW_INIT_ATTRIBUTES();
|
|
2828
|
+
|
|
2829
|
+
LXW_PUSH_ATTRIBUTES_INT("count", self->merged_range_count);
|
|
2830
|
+
|
|
2831
|
+
lxw_xml_start_tag(self->file, "mergeCells", &attributes);
|
|
2832
|
+
|
|
2833
|
+
STAILQ_FOREACH(merged_range, self->merged_ranges, list_pointers) {
|
|
2834
|
+
_worksheet_write_merge_cell(self, merged_range);
|
|
2835
|
+
}
|
|
2836
|
+
lxw_xml_end_tag(self->file, "mergeCells");
|
|
2837
|
+
|
|
2838
|
+
LXW_FREE_ATTRIBUTES();
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
/*
|
|
2843
|
+
* Write the <oddHeader> element.
|
|
2844
|
+
*/
|
|
2845
|
+
STATIC void
|
|
2846
|
+
_worksheet_write_odd_header(lxw_worksheet *self)
|
|
2847
|
+
{
|
|
2848
|
+
lxw_xml_data_element(self->file, "oddHeader", self->header, NULL);
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
/*
|
|
2852
|
+
* Write the <oddFooter> element.
|
|
2853
|
+
*/
|
|
2854
|
+
STATIC void
|
|
2855
|
+
_worksheet_write_odd_footer(lxw_worksheet *self)
|
|
2856
|
+
{
|
|
2857
|
+
lxw_xml_data_element(self->file, "oddFooter", self->footer, NULL);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
/*
|
|
2861
|
+
* Write the <headerFooter> element.
|
|
2862
|
+
*/
|
|
2863
|
+
STATIC void
|
|
2864
|
+
_worksheet_write_header_footer(lxw_worksheet *self)
|
|
2865
|
+
{
|
|
2866
|
+
if (!self->header_footer_changed)
|
|
2867
|
+
return;
|
|
2868
|
+
|
|
2869
|
+
lxw_xml_start_tag(self->file, "headerFooter", NULL);
|
|
2870
|
+
|
|
2871
|
+
if (self->header[0] != '\0')
|
|
2872
|
+
_worksheet_write_odd_header(self);
|
|
2873
|
+
|
|
2874
|
+
if (self->footer[0] != '\0')
|
|
2875
|
+
_worksheet_write_odd_footer(self);
|
|
2876
|
+
|
|
2877
|
+
lxw_xml_end_tag(self->file, "headerFooter");
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
/*
|
|
2881
|
+
* Write the <pageSetUpPr> element.
|
|
2882
|
+
*/
|
|
2883
|
+
STATIC void
|
|
2884
|
+
_worksheet_write_page_set_up_pr(lxw_worksheet *self)
|
|
2885
|
+
{
|
|
2886
|
+
struct xml_attribute_list attributes;
|
|
2887
|
+
struct xml_attribute *attribute;
|
|
2888
|
+
|
|
2889
|
+
if (!self->fit_page)
|
|
2890
|
+
return;
|
|
2891
|
+
|
|
2892
|
+
LXW_INIT_ATTRIBUTES();
|
|
2893
|
+
LXW_PUSH_ATTRIBUTES_STR("fitToPage", "1");
|
|
2894
|
+
|
|
2895
|
+
lxw_xml_empty_tag(self->file, "pageSetUpPr", &attributes);
|
|
2896
|
+
|
|
2897
|
+
LXW_FREE_ATTRIBUTES();
|
|
2898
|
+
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
/*
|
|
2902
|
+
* Write the <tabColor> element.
|
|
2903
|
+
*/
|
|
2904
|
+
STATIC void
|
|
2905
|
+
_worksheet_write_tab_color(lxw_worksheet *self)
|
|
2906
|
+
{
|
|
2907
|
+
struct xml_attribute_list attributes;
|
|
2908
|
+
struct xml_attribute *attribute;
|
|
2909
|
+
char rgb_str[LXW_ATTR_32];
|
|
2910
|
+
|
|
2911
|
+
if (self->tab_color == LXW_COLOR_UNSET)
|
|
2912
|
+
return;
|
|
2913
|
+
|
|
2914
|
+
lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X",
|
|
2915
|
+
self->tab_color & LXW_COLOR_MASK);
|
|
2916
|
+
|
|
2917
|
+
LXW_INIT_ATTRIBUTES();
|
|
2918
|
+
LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
|
|
2919
|
+
|
|
2920
|
+
lxw_xml_empty_tag(self->file, "tabColor", &attributes);
|
|
2921
|
+
|
|
2922
|
+
LXW_FREE_ATTRIBUTES();
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
/*
|
|
2926
|
+
* Write the <sheetPr> element for Sheet level properties.
|
|
2927
|
+
*/
|
|
2928
|
+
STATIC void
|
|
2929
|
+
_worksheet_write_sheet_pr(lxw_worksheet *self)
|
|
2930
|
+
{
|
|
2931
|
+
struct xml_attribute_list attributes;
|
|
2932
|
+
struct xml_attribute *attribute;
|
|
2933
|
+
|
|
2934
|
+
if (!self->fit_page
|
|
2935
|
+
&& !self->filter_on
|
|
2936
|
+
&& self->tab_color == LXW_COLOR_UNSET
|
|
2937
|
+
&& !self->outline_changed && !self->vba_codename) {
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
LXW_INIT_ATTRIBUTES();
|
|
2942
|
+
|
|
2943
|
+
if (self->vba_codename)
|
|
2944
|
+
LXW_PUSH_ATTRIBUTES_INT("codeName", self->vba_codename);
|
|
2945
|
+
|
|
2946
|
+
if (self->filter_on)
|
|
2947
|
+
LXW_PUSH_ATTRIBUTES_STR("filterMode", "1");
|
|
2948
|
+
|
|
2949
|
+
if (self->fit_page || self->tab_color != LXW_COLOR_UNSET
|
|
2950
|
+
|| self->outline_changed) {
|
|
2951
|
+
lxw_xml_start_tag(self->file, "sheetPr", &attributes);
|
|
2952
|
+
_worksheet_write_tab_color(self);
|
|
2953
|
+
/* _worksheet_write_outline_pr(self); */
|
|
2954
|
+
_worksheet_write_page_set_up_pr(self);
|
|
2955
|
+
lxw_xml_end_tag(self->file, "sheetPr");
|
|
2956
|
+
}
|
|
2957
|
+
else {
|
|
2958
|
+
lxw_xml_empty_tag(self->file, "sheetPr", &attributes);
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
LXW_FREE_ATTRIBUTES();
|
|
2962
|
+
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
/*
|
|
2966
|
+
* Write the <brk> element.
|
|
2967
|
+
*/
|
|
2968
|
+
STATIC void
|
|
2969
|
+
_worksheet_write_brk(lxw_worksheet *self, uint32_t id, uint32_t max)
|
|
2970
|
+
{
|
|
2971
|
+
struct xml_attribute_list attributes;
|
|
2972
|
+
struct xml_attribute *attribute;
|
|
2973
|
+
|
|
2974
|
+
LXW_INIT_ATTRIBUTES();
|
|
2975
|
+
LXW_PUSH_ATTRIBUTES_INT("id", id);
|
|
2976
|
+
LXW_PUSH_ATTRIBUTES_INT("max", max);
|
|
2977
|
+
LXW_PUSH_ATTRIBUTES_STR("man", "1");
|
|
2978
|
+
|
|
2979
|
+
lxw_xml_empty_tag(self->file, "brk", &attributes);
|
|
2980
|
+
|
|
2981
|
+
LXW_FREE_ATTRIBUTES();
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
/*
|
|
2985
|
+
* Write the <rowBreaks> element.
|
|
2986
|
+
*/
|
|
2987
|
+
STATIC void
|
|
2988
|
+
_worksheet_write_row_breaks(lxw_worksheet *self)
|
|
2989
|
+
{
|
|
2990
|
+
struct xml_attribute_list attributes;
|
|
2991
|
+
struct xml_attribute *attribute;
|
|
2992
|
+
uint16_t count = self->hbreaks_count;
|
|
2993
|
+
uint16_t i;
|
|
2994
|
+
|
|
2995
|
+
if (!count)
|
|
2996
|
+
return;
|
|
2997
|
+
|
|
2998
|
+
LXW_INIT_ATTRIBUTES();
|
|
2999
|
+
LXW_PUSH_ATTRIBUTES_INT("count", count);
|
|
3000
|
+
LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count);
|
|
3001
|
+
|
|
3002
|
+
lxw_xml_start_tag(self->file, "rowBreaks", &attributes);
|
|
3003
|
+
|
|
3004
|
+
for (i = 0; i < count; i++)
|
|
3005
|
+
_worksheet_write_brk(self, self->hbreaks[i], LXW_COL_MAX - 1);
|
|
3006
|
+
|
|
3007
|
+
lxw_xml_end_tag(self->file, "rowBreaks");
|
|
3008
|
+
|
|
3009
|
+
LXW_FREE_ATTRIBUTES();
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
/*
|
|
3013
|
+
* Write the <colBreaks> element.
|
|
3014
|
+
*/
|
|
3015
|
+
STATIC void
|
|
3016
|
+
_worksheet_write_col_breaks(lxw_worksheet *self)
|
|
3017
|
+
{
|
|
3018
|
+
struct xml_attribute_list attributes;
|
|
3019
|
+
struct xml_attribute *attribute;
|
|
3020
|
+
uint16_t count = self->vbreaks_count;
|
|
3021
|
+
uint16_t i;
|
|
3022
|
+
|
|
3023
|
+
if (!count)
|
|
3024
|
+
return;
|
|
3025
|
+
|
|
3026
|
+
LXW_INIT_ATTRIBUTES();
|
|
3027
|
+
LXW_PUSH_ATTRIBUTES_INT("count", count);
|
|
3028
|
+
LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count);
|
|
3029
|
+
|
|
3030
|
+
lxw_xml_start_tag(self->file, "colBreaks", &attributes);
|
|
3031
|
+
|
|
3032
|
+
for (i = 0; i < count; i++)
|
|
3033
|
+
_worksheet_write_brk(self, self->vbreaks[i], LXW_ROW_MAX - 1);
|
|
3034
|
+
|
|
3035
|
+
lxw_xml_end_tag(self->file, "colBreaks");
|
|
3036
|
+
|
|
3037
|
+
LXW_FREE_ATTRIBUTES();
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
/*
|
|
3041
|
+
* Write the <autoFilter> element.
|
|
3042
|
+
*/
|
|
3043
|
+
STATIC void
|
|
3044
|
+
_worksheet_write_auto_filter(lxw_worksheet *self)
|
|
3045
|
+
{
|
|
3046
|
+
struct xml_attribute_list attributes;
|
|
3047
|
+
struct xml_attribute *attribute;
|
|
3048
|
+
char range[LXW_MAX_CELL_RANGE_LENGTH];
|
|
3049
|
+
|
|
3050
|
+
if (!self->autofilter.in_use)
|
|
3051
|
+
return;
|
|
3052
|
+
|
|
3053
|
+
lxw_rowcol_to_range(range,
|
|
3054
|
+
self->autofilter.first_row,
|
|
3055
|
+
self->autofilter.first_col,
|
|
3056
|
+
self->autofilter.last_row, self->autofilter.last_col);
|
|
3057
|
+
|
|
3058
|
+
LXW_INIT_ATTRIBUTES();
|
|
3059
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", range);
|
|
3060
|
+
|
|
3061
|
+
lxw_xml_empty_tag(self->file, "autoFilter", &attributes);
|
|
3062
|
+
|
|
3063
|
+
LXW_FREE_ATTRIBUTES();
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
/*
|
|
3067
|
+
* Write the <hyperlink> element for external links.
|
|
3068
|
+
*/
|
|
3069
|
+
STATIC void
|
|
3070
|
+
_worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num,
|
|
3071
|
+
lxw_col_t col_num, const char *location,
|
|
3072
|
+
const char *tooltip, uint16_t id)
|
|
3073
|
+
{
|
|
3074
|
+
struct xml_attribute_list attributes;
|
|
3075
|
+
struct xml_attribute *attribute;
|
|
3076
|
+
char ref[LXW_MAX_CELL_NAME_LENGTH];
|
|
3077
|
+
char r_id[LXW_MAX_ATTRIBUTE_LENGTH];
|
|
3078
|
+
|
|
3079
|
+
lxw_rowcol_to_cell(ref, row_num, col_num);
|
|
3080
|
+
|
|
3081
|
+
lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id);
|
|
3082
|
+
|
|
3083
|
+
LXW_INIT_ATTRIBUTES();
|
|
3084
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", ref);
|
|
3085
|
+
LXW_PUSH_ATTRIBUTES_STR("r:id", r_id);
|
|
3086
|
+
|
|
3087
|
+
if (location)
|
|
3088
|
+
LXW_PUSH_ATTRIBUTES_STR("location", location);
|
|
3089
|
+
|
|
3090
|
+
if (tooltip)
|
|
3091
|
+
LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip);
|
|
3092
|
+
|
|
3093
|
+
lxw_xml_empty_tag(self->file, "hyperlink", &attributes);
|
|
3094
|
+
|
|
3095
|
+
LXW_FREE_ATTRIBUTES();
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
/*
|
|
3099
|
+
* Write the <hyperlink> element for internal links.
|
|
3100
|
+
*/
|
|
3101
|
+
STATIC void
|
|
3102
|
+
_worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num,
|
|
3103
|
+
lxw_col_t col_num, const char *location,
|
|
3104
|
+
const char *display, const char *tooltip)
|
|
3105
|
+
{
|
|
3106
|
+
struct xml_attribute_list attributes;
|
|
3107
|
+
struct xml_attribute *attribute;
|
|
3108
|
+
char ref[LXW_MAX_CELL_NAME_LENGTH];
|
|
3109
|
+
|
|
3110
|
+
lxw_rowcol_to_cell(ref, row_num, col_num);
|
|
3111
|
+
|
|
3112
|
+
LXW_INIT_ATTRIBUTES();
|
|
3113
|
+
LXW_PUSH_ATTRIBUTES_STR("ref", ref);
|
|
3114
|
+
|
|
3115
|
+
if (location)
|
|
3116
|
+
LXW_PUSH_ATTRIBUTES_STR("location", location);
|
|
3117
|
+
|
|
3118
|
+
if (tooltip)
|
|
3119
|
+
LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip);
|
|
3120
|
+
|
|
3121
|
+
if (display)
|
|
3122
|
+
LXW_PUSH_ATTRIBUTES_STR("display", display);
|
|
3123
|
+
|
|
3124
|
+
lxw_xml_empty_tag(self->file, "hyperlink", &attributes);
|
|
3125
|
+
|
|
3126
|
+
LXW_FREE_ATTRIBUTES();
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
/*
|
|
3130
|
+
* Process any stored hyperlinks in row/col order and write the <hyperlinks>
|
|
3131
|
+
* element. The attributes are different for internal and external links.
|
|
3132
|
+
*/
|
|
3133
|
+
STATIC void
|
|
3134
|
+
_worksheet_write_hyperlinks(lxw_worksheet *self)
|
|
3135
|
+
{
|
|
3136
|
+
|
|
3137
|
+
lxw_row *row;
|
|
3138
|
+
lxw_cell *link;
|
|
3139
|
+
lxw_rel_tuple *relationship;
|
|
3140
|
+
|
|
3141
|
+
if (RB_EMPTY(self->hyperlinks))
|
|
3142
|
+
return;
|
|
3143
|
+
|
|
3144
|
+
/* Write the hyperlink elements. */
|
|
3145
|
+
lxw_xml_start_tag(self->file, "hyperlinks", NULL);
|
|
3146
|
+
|
|
3147
|
+
RB_FOREACH(row, lxw_table_rows, self->hyperlinks) {
|
|
3148
|
+
|
|
3149
|
+
RB_FOREACH(link, lxw_table_cells, row->cells) {
|
|
3150
|
+
|
|
3151
|
+
if (link->type == HYPERLINK_URL
|
|
3152
|
+
|| link->type == HYPERLINK_EXTERNAL) {
|
|
3153
|
+
|
|
3154
|
+
self->rel_count++;
|
|
3155
|
+
|
|
3156
|
+
relationship = calloc(1, sizeof(lxw_rel_tuple));
|
|
3157
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
|
|
3158
|
+
|
|
3159
|
+
relationship->type = lxw_strdup("/hyperlink");
|
|
3160
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
|
|
3161
|
+
|
|
3162
|
+
relationship->target = lxw_strdup(link->u.string);
|
|
3163
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
|
|
3164
|
+
|
|
3165
|
+
relationship->target_mode = lxw_strdup("External");
|
|
3166
|
+
GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error);
|
|
3167
|
+
|
|
3168
|
+
STAILQ_INSERT_TAIL(self->external_hyperlinks, relationship,
|
|
3169
|
+
list_pointers);
|
|
3170
|
+
|
|
3171
|
+
_worksheet_write_hyperlink_external(self, link->row_num,
|
|
3172
|
+
link->col_num,
|
|
3173
|
+
link->user_data1,
|
|
3174
|
+
link->user_data2,
|
|
3175
|
+
self->rel_count);
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
if (link->type == HYPERLINK_INTERNAL) {
|
|
3179
|
+
|
|
3180
|
+
_worksheet_write_hyperlink_internal(self, link->row_num,
|
|
3181
|
+
link->col_num,
|
|
3182
|
+
link->u.string,
|
|
3183
|
+
link->user_data1,
|
|
3184
|
+
link->user_data2);
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
lxw_xml_end_tag(self->file, "hyperlinks");
|
|
3192
|
+
return;
|
|
3193
|
+
|
|
3194
|
+
mem_error:
|
|
3195
|
+
if (relationship) {
|
|
3196
|
+
free(relationship->type);
|
|
3197
|
+
free(relationship->target);
|
|
3198
|
+
free(relationship->target_mode);
|
|
3199
|
+
free(relationship);
|
|
3200
|
+
}
|
|
3201
|
+
lxw_xml_end_tag(self->file, "hyperlinks");
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
/*
|
|
3205
|
+
* Write the <sheetProtection> element.
|
|
3206
|
+
*/
|
|
3207
|
+
STATIC void
|
|
3208
|
+
_worksheet_write_sheet_protection(lxw_worksheet *self)
|
|
3209
|
+
{
|
|
3210
|
+
struct xml_attribute_list attributes;
|
|
3211
|
+
struct xml_attribute *attribute;
|
|
3212
|
+
|
|
3213
|
+
struct lxw_protection *protect = &self->protection;
|
|
3214
|
+
|
|
3215
|
+
if (!protect->is_configured)
|
|
3216
|
+
return;
|
|
3217
|
+
|
|
3218
|
+
LXW_INIT_ATTRIBUTES();
|
|
3219
|
+
|
|
3220
|
+
if (*protect->hash)
|
|
3221
|
+
LXW_PUSH_ATTRIBUTES_STR("password", protect->hash);
|
|
3222
|
+
|
|
3223
|
+
if (!protect->no_sheet)
|
|
3224
|
+
LXW_PUSH_ATTRIBUTES_INT("sheet", 1);
|
|
3225
|
+
|
|
3226
|
+
if (protect->content)
|
|
3227
|
+
LXW_PUSH_ATTRIBUTES_INT("content", 1);
|
|
3228
|
+
|
|
3229
|
+
if (!protect->objects)
|
|
3230
|
+
LXW_PUSH_ATTRIBUTES_INT("objects", 1);
|
|
3231
|
+
|
|
3232
|
+
if (!protect->scenarios)
|
|
3233
|
+
LXW_PUSH_ATTRIBUTES_INT("scenarios", 1);
|
|
3234
|
+
|
|
3235
|
+
if (protect->format_cells)
|
|
3236
|
+
LXW_PUSH_ATTRIBUTES_INT("formatCells", 0);
|
|
3237
|
+
|
|
3238
|
+
if (protect->format_columns)
|
|
3239
|
+
LXW_PUSH_ATTRIBUTES_INT("formatColumns", 0);
|
|
3240
|
+
|
|
3241
|
+
if (protect->format_rows)
|
|
3242
|
+
LXW_PUSH_ATTRIBUTES_INT("formatRows", 0);
|
|
3243
|
+
|
|
3244
|
+
if (protect->insert_columns)
|
|
3245
|
+
LXW_PUSH_ATTRIBUTES_INT("insertColumns", 0);
|
|
3246
|
+
|
|
3247
|
+
if (protect->insert_rows)
|
|
3248
|
+
LXW_PUSH_ATTRIBUTES_INT("insertRows", 0);
|
|
3249
|
+
|
|
3250
|
+
if (protect->insert_hyperlinks)
|
|
3251
|
+
LXW_PUSH_ATTRIBUTES_INT("insertHyperlinks", 0);
|
|
3252
|
+
|
|
3253
|
+
if (protect->delete_columns)
|
|
3254
|
+
LXW_PUSH_ATTRIBUTES_INT("deleteColumns", 0);
|
|
3255
|
+
|
|
3256
|
+
if (protect->delete_rows)
|
|
3257
|
+
LXW_PUSH_ATTRIBUTES_INT("deleteRows", 0);
|
|
3258
|
+
|
|
3259
|
+
if (protect->no_select_locked_cells)
|
|
3260
|
+
LXW_PUSH_ATTRIBUTES_INT("selectLockedCells", 1);
|
|
3261
|
+
|
|
3262
|
+
if (protect->sort)
|
|
3263
|
+
LXW_PUSH_ATTRIBUTES_INT("sort", 0);
|
|
3264
|
+
|
|
3265
|
+
if (protect->autofilter)
|
|
3266
|
+
LXW_PUSH_ATTRIBUTES_INT("autoFilter", 0);
|
|
3267
|
+
|
|
3268
|
+
if (protect->pivot_tables)
|
|
3269
|
+
LXW_PUSH_ATTRIBUTES_INT("pivotTables", 0);
|
|
3270
|
+
|
|
3271
|
+
if (protect->no_select_unlocked_cells)
|
|
3272
|
+
LXW_PUSH_ATTRIBUTES_INT("selectUnlockedCells", 1);
|
|
3273
|
+
|
|
3274
|
+
lxw_xml_empty_tag(self->file, "sheetProtection", &attributes);
|
|
3275
|
+
|
|
3276
|
+
LXW_FREE_ATTRIBUTES();
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
/*
|
|
3280
|
+
* Write the <drawing> element.
|
|
3281
|
+
*/
|
|
3282
|
+
STATIC void
|
|
3283
|
+
_write_drawing(lxw_worksheet *self, uint16_t id)
|
|
3284
|
+
{
|
|
3285
|
+
struct xml_attribute_list attributes;
|
|
3286
|
+
struct xml_attribute *attribute;
|
|
3287
|
+
char r_id[LXW_MAX_ATTRIBUTE_LENGTH];
|
|
3288
|
+
|
|
3289
|
+
lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id);
|
|
3290
|
+
|
|
3291
|
+
LXW_INIT_ATTRIBUTES();
|
|
3292
|
+
|
|
3293
|
+
LXW_PUSH_ATTRIBUTES_STR("r:id", r_id);
|
|
3294
|
+
|
|
3295
|
+
lxw_xml_empty_tag(self->file, "drawing", &attributes);
|
|
3296
|
+
|
|
3297
|
+
LXW_FREE_ATTRIBUTES();
|
|
3298
|
+
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
/*
|
|
3302
|
+
* Write the <drawing> elements.
|
|
3303
|
+
*/
|
|
3304
|
+
STATIC void
|
|
3305
|
+
_write_drawings(lxw_worksheet *self)
|
|
3306
|
+
{
|
|
3307
|
+
if (!self->drawing)
|
|
3308
|
+
return;
|
|
3309
|
+
|
|
3310
|
+
self->rel_count++;
|
|
3311
|
+
|
|
3312
|
+
_write_drawing(self, self->rel_count);
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
/*
|
|
3316
|
+
* Assemble and write the XML file.
|
|
3317
|
+
*/
|
|
3318
|
+
void
|
|
3319
|
+
lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
|
|
3320
|
+
{
|
|
3321
|
+
/* Write the XML declaration. */
|
|
3322
|
+
_worksheet_xml_declaration(self);
|
|
3323
|
+
|
|
3324
|
+
/* Write the worksheet element. */
|
|
3325
|
+
_worksheet_write_worksheet(self);
|
|
3326
|
+
|
|
3327
|
+
/* Write the worksheet properties. */
|
|
3328
|
+
_worksheet_write_sheet_pr(self);
|
|
3329
|
+
|
|
3330
|
+
/* Write the worksheet dimensions. */
|
|
3331
|
+
_worksheet_write_dimension(self);
|
|
3332
|
+
|
|
3333
|
+
/* Write the sheet view properties. */
|
|
3334
|
+
_worksheet_write_sheet_views(self);
|
|
3335
|
+
|
|
3336
|
+
/* Write the sheet format properties. */
|
|
3337
|
+
_worksheet_write_sheet_format_pr(self);
|
|
3338
|
+
|
|
3339
|
+
/* Write the sheet column info. */
|
|
3340
|
+
_worksheet_write_cols(self);
|
|
3341
|
+
|
|
3342
|
+
/* Write the sheetData element. */
|
|
3343
|
+
if (!self->optimize)
|
|
3344
|
+
_worksheet_write_sheet_data(self);
|
|
3345
|
+
else
|
|
3346
|
+
_worksheet_write_optimized_sheet_data(self);
|
|
3347
|
+
|
|
3348
|
+
/* Write the sheetProtection element. */
|
|
3349
|
+
_worksheet_write_sheet_protection(self);
|
|
3350
|
+
|
|
3351
|
+
/* Write the autoFilter element. */
|
|
3352
|
+
_worksheet_write_auto_filter(self);
|
|
3353
|
+
|
|
3354
|
+
/* Write the mergeCells element. */
|
|
3355
|
+
_worksheet_write_merge_cells(self);
|
|
3356
|
+
|
|
3357
|
+
/* Write the hyperlink element. */
|
|
3358
|
+
_worksheet_write_hyperlinks(self);
|
|
3359
|
+
|
|
3360
|
+
/* Write the printOptions element. */
|
|
3361
|
+
_worksheet_write_print_options(self);
|
|
3362
|
+
|
|
3363
|
+
/* Write the worksheet page_margins. */
|
|
3364
|
+
_worksheet_write_page_margins(self);
|
|
3365
|
+
|
|
3366
|
+
/* Write the worksheet page setup. */
|
|
3367
|
+
_worksheet_write_page_setup(self);
|
|
3368
|
+
|
|
3369
|
+
/* Write the headerFooter element. */
|
|
3370
|
+
_worksheet_write_header_footer(self);
|
|
3371
|
+
|
|
3372
|
+
/* Write the rowBreaks element. */
|
|
3373
|
+
_worksheet_write_row_breaks(self);
|
|
3374
|
+
|
|
3375
|
+
/* Write the colBreaks element. */
|
|
3376
|
+
_worksheet_write_col_breaks(self);
|
|
3377
|
+
|
|
3378
|
+
/* Write the drawing element. */
|
|
3379
|
+
_write_drawings(self);
|
|
3380
|
+
|
|
3381
|
+
/* Close the worksheet tag. */
|
|
3382
|
+
lxw_xml_end_tag(self->file, "worksheet");
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
/*****************************************************************************
|
|
3386
|
+
*
|
|
3387
|
+
* Public functions.
|
|
3388
|
+
*
|
|
3389
|
+
****************************************************************************/
|
|
3390
|
+
|
|
3391
|
+
/*
|
|
3392
|
+
* Write a number to a cell in Excel.
|
|
3393
|
+
*/
|
|
3394
|
+
lxw_error
|
|
3395
|
+
worksheet_write_number(lxw_worksheet *self,
|
|
3396
|
+
lxw_row_t row_num,
|
|
3397
|
+
lxw_col_t col_num, double value, lxw_format *format)
|
|
3398
|
+
{
|
|
3399
|
+
lxw_cell *cell;
|
|
3400
|
+
lxw_error err;
|
|
3401
|
+
|
|
3402
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3403
|
+
if (err)
|
|
3404
|
+
return err;
|
|
3405
|
+
|
|
3406
|
+
cell = _new_number_cell(row_num, col_num, value, format);
|
|
3407
|
+
|
|
3408
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3409
|
+
|
|
3410
|
+
return LXW_NO_ERROR;
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
/*
|
|
3414
|
+
* Write a string to an Excel file.
|
|
3415
|
+
*/
|
|
3416
|
+
lxw_error
|
|
3417
|
+
worksheet_write_string(lxw_worksheet *self,
|
|
3418
|
+
lxw_row_t row_num,
|
|
3419
|
+
lxw_col_t col_num, const char *string,
|
|
3420
|
+
lxw_format *format)
|
|
3421
|
+
{
|
|
3422
|
+
lxw_cell *cell;
|
|
3423
|
+
int32_t string_id;
|
|
3424
|
+
char *string_copy;
|
|
3425
|
+
struct sst_element *sst_element;
|
|
3426
|
+
lxw_error err;
|
|
3427
|
+
|
|
3428
|
+
if (!string || !*string) {
|
|
3429
|
+
/* Treat a NULL or empty string with formatting as a blank cell. */
|
|
3430
|
+
/* Null strings without formats should be ignored. */
|
|
3431
|
+
if (format)
|
|
3432
|
+
return worksheet_write_blank(self, row_num, col_num, format);
|
|
3433
|
+
else
|
|
3434
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3438
|
+
if (err)
|
|
3439
|
+
return err;
|
|
3440
|
+
|
|
3441
|
+
if (lxw_utf8_strlen(string) > LXW_STR_MAX)
|
|
3442
|
+
return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED;
|
|
3443
|
+
|
|
3444
|
+
if (!self->optimize) {
|
|
3445
|
+
/* Get the SST element and string id. */
|
|
3446
|
+
sst_element = lxw_get_sst_index(self->sst, string);
|
|
3447
|
+
|
|
3448
|
+
if (!sst_element)
|
|
3449
|
+
return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND;
|
|
3450
|
+
|
|
3451
|
+
string_id = sst_element->index;
|
|
3452
|
+
cell = _new_string_cell(row_num, col_num, string_id,
|
|
3453
|
+
sst_element->string, format);
|
|
3454
|
+
}
|
|
3455
|
+
else {
|
|
3456
|
+
/* Look for and escape control chars in the string. */
|
|
3457
|
+
if (strpbrk(string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C"
|
|
3458
|
+
"\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"
|
|
3459
|
+
"\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) {
|
|
3460
|
+
string_copy = lxw_escape_control_characters(string);
|
|
3461
|
+
}
|
|
3462
|
+
else {
|
|
3463
|
+
string_copy = lxw_strdup(string);
|
|
3464
|
+
}
|
|
3465
|
+
cell = _new_inline_string_cell(row_num, col_num, string_copy, format);
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3469
|
+
|
|
3470
|
+
return LXW_NO_ERROR;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
/*
|
|
3474
|
+
* Write a formula with a numerical result to a cell in Excel.
|
|
3475
|
+
*/
|
|
3476
|
+
lxw_error
|
|
3477
|
+
worksheet_write_formula_num(lxw_worksheet *self,
|
|
3478
|
+
lxw_row_t row_num,
|
|
3479
|
+
lxw_col_t col_num,
|
|
3480
|
+
const char *formula,
|
|
3481
|
+
lxw_format *format, double result)
|
|
3482
|
+
{
|
|
3483
|
+
lxw_cell *cell;
|
|
3484
|
+
char *formula_copy;
|
|
3485
|
+
lxw_error err;
|
|
3486
|
+
|
|
3487
|
+
if (!formula)
|
|
3488
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
3489
|
+
|
|
3490
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3491
|
+
if (err)
|
|
3492
|
+
return err;
|
|
3493
|
+
|
|
3494
|
+
/* Strip leading "=" from formula. */
|
|
3495
|
+
if (formula[0] == '=')
|
|
3496
|
+
formula_copy = lxw_strdup(formula + 1);
|
|
3497
|
+
else
|
|
3498
|
+
formula_copy = lxw_strdup(formula);
|
|
3499
|
+
|
|
3500
|
+
cell = _new_formula_cell(row_num, col_num, formula_copy, format);
|
|
3501
|
+
cell->formula_result = result;
|
|
3502
|
+
|
|
3503
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3504
|
+
|
|
3505
|
+
return LXW_NO_ERROR;
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
/*
|
|
3509
|
+
* Write a formula with a default result to a cell in Excel .
|
|
3510
|
+
*/
|
|
3511
|
+
lxw_error
|
|
3512
|
+
worksheet_write_formula(lxw_worksheet *self,
|
|
3513
|
+
lxw_row_t row_num,
|
|
3514
|
+
lxw_col_t col_num, const char *formula,
|
|
3515
|
+
lxw_format *format)
|
|
3516
|
+
{
|
|
3517
|
+
return worksheet_write_formula_num(self, row_num, col_num, formula,
|
|
3518
|
+
format, 0);
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
/*
|
|
3522
|
+
* Write a formula with a numerical result to a cell in Excel.
|
|
3523
|
+
*/
|
|
3524
|
+
lxw_error
|
|
3525
|
+
worksheet_write_array_formula_num(lxw_worksheet *self,
|
|
3526
|
+
lxw_row_t first_row,
|
|
3527
|
+
lxw_col_t first_col,
|
|
3528
|
+
lxw_row_t last_row,
|
|
3529
|
+
lxw_col_t last_col,
|
|
3530
|
+
const char *formula,
|
|
3531
|
+
lxw_format *format, double result)
|
|
3532
|
+
{
|
|
3533
|
+
lxw_cell *cell;
|
|
3534
|
+
lxw_row_t tmp_row;
|
|
3535
|
+
lxw_col_t tmp_col;
|
|
3536
|
+
char *formula_copy;
|
|
3537
|
+
char *range;
|
|
3538
|
+
lxw_error err;
|
|
3539
|
+
|
|
3540
|
+
/* Swap last row/col with first row/col as necessary */
|
|
3541
|
+
if (first_row > last_row) {
|
|
3542
|
+
tmp_row = last_row;
|
|
3543
|
+
last_row = first_row;
|
|
3544
|
+
first_row = tmp_row;
|
|
3545
|
+
}
|
|
3546
|
+
if (first_col > last_col) {
|
|
3547
|
+
tmp_col = last_col;
|
|
3548
|
+
last_col = first_col;
|
|
3549
|
+
first_col = tmp_col;
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
if (!formula)
|
|
3553
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
3554
|
+
|
|
3555
|
+
/* Check that column number is valid and store the max value */
|
|
3556
|
+
err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
|
|
3557
|
+
if (err)
|
|
3558
|
+
return err;
|
|
3559
|
+
|
|
3560
|
+
/* Define the array range. */
|
|
3561
|
+
range = calloc(1, LXW_MAX_CELL_RANGE_LENGTH);
|
|
3562
|
+
RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
3563
|
+
|
|
3564
|
+
if (first_row == last_row && first_col == last_col)
|
|
3565
|
+
lxw_rowcol_to_cell(range, first_row, last_col);
|
|
3566
|
+
else
|
|
3567
|
+
lxw_rowcol_to_range(range, first_row, first_col, last_row, last_col);
|
|
3568
|
+
|
|
3569
|
+
/* Copy and trip leading "{=" from formula. */
|
|
3570
|
+
if (formula[0] == '{')
|
|
3571
|
+
if (formula[1] == '=')
|
|
3572
|
+
formula_copy = lxw_strdup(formula + 2);
|
|
3573
|
+
else
|
|
3574
|
+
formula_copy = lxw_strdup(formula + 1);
|
|
3575
|
+
else
|
|
3576
|
+
formula_copy = lxw_strdup(formula);
|
|
3577
|
+
|
|
3578
|
+
/* Strip trailing "}" from formula. */
|
|
3579
|
+
if (formula_copy[strlen(formula_copy) - 1] == '}')
|
|
3580
|
+
formula_copy[strlen(formula_copy) - 1] = '\0';
|
|
3581
|
+
|
|
3582
|
+
/* Create a new array formula cell object. */
|
|
3583
|
+
cell = _new_array_formula_cell(first_row, first_col,
|
|
3584
|
+
formula_copy, range, format);
|
|
3585
|
+
|
|
3586
|
+
cell->formula_result = result;
|
|
3587
|
+
|
|
3588
|
+
_insert_cell(self, first_row, first_col, cell);
|
|
3589
|
+
|
|
3590
|
+
/* Pad out the rest of the area with formatted zeroes. */
|
|
3591
|
+
if (!self->optimize) {
|
|
3592
|
+
for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) {
|
|
3593
|
+
for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) {
|
|
3594
|
+
if (tmp_row == first_row && tmp_col == first_col)
|
|
3595
|
+
continue;
|
|
3596
|
+
|
|
3597
|
+
worksheet_write_number(self, tmp_row, tmp_col, 0, format);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
return LXW_NO_ERROR;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
/*
|
|
3606
|
+
* Write an array formula with a default result to a cell in Excel .
|
|
3607
|
+
*/
|
|
3608
|
+
lxw_error
|
|
3609
|
+
worksheet_write_array_formula(lxw_worksheet *self,
|
|
3610
|
+
lxw_row_t first_row,
|
|
3611
|
+
lxw_col_t first_col,
|
|
3612
|
+
lxw_row_t last_row,
|
|
3613
|
+
lxw_col_t last_col,
|
|
3614
|
+
const char *formula, lxw_format *format)
|
|
3615
|
+
{
|
|
3616
|
+
return worksheet_write_array_formula_num(self, first_row, first_col,
|
|
3617
|
+
last_row, last_col, formula,
|
|
3618
|
+
format, 0);
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
/*
|
|
3622
|
+
* Write a blank cell with a format to a cell in Excel.
|
|
3623
|
+
*/
|
|
3624
|
+
lxw_error
|
|
3625
|
+
worksheet_write_blank(lxw_worksheet *self,
|
|
3626
|
+
lxw_row_t row_num, lxw_col_t col_num,
|
|
3627
|
+
lxw_format *format)
|
|
3628
|
+
{
|
|
3629
|
+
lxw_cell *cell;
|
|
3630
|
+
lxw_error err;
|
|
3631
|
+
|
|
3632
|
+
/* Blank cells without formatting are ignored by Excel. */
|
|
3633
|
+
if (!format)
|
|
3634
|
+
return LXW_NO_ERROR;
|
|
3635
|
+
|
|
3636
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3637
|
+
if (err)
|
|
3638
|
+
return err;
|
|
3639
|
+
|
|
3640
|
+
cell = _new_blank_cell(row_num, col_num, format);
|
|
3641
|
+
|
|
3642
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3643
|
+
|
|
3644
|
+
return LXW_NO_ERROR;
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3647
|
+
/*
|
|
3648
|
+
* Write a boolean cell with a format to a cell in Excel.
|
|
3649
|
+
*/
|
|
3650
|
+
lxw_error
|
|
3651
|
+
worksheet_write_boolean(lxw_worksheet *self,
|
|
3652
|
+
lxw_row_t row_num, lxw_col_t col_num,
|
|
3653
|
+
int value, lxw_format *format)
|
|
3654
|
+
{
|
|
3655
|
+
lxw_cell *cell;
|
|
3656
|
+
lxw_error err;
|
|
3657
|
+
|
|
3658
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3659
|
+
|
|
3660
|
+
if (err)
|
|
3661
|
+
return err;
|
|
3662
|
+
|
|
3663
|
+
cell = _new_boolean_cell(row_num, col_num, value, format);
|
|
3664
|
+
|
|
3665
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3666
|
+
|
|
3667
|
+
return LXW_NO_ERROR;
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
/*
|
|
3671
|
+
* Write a date and or time to a cell in Excel.
|
|
3672
|
+
*/
|
|
3673
|
+
lxw_error
|
|
3674
|
+
worksheet_write_datetime(lxw_worksheet *self,
|
|
3675
|
+
lxw_row_t row_num,
|
|
3676
|
+
lxw_col_t col_num, lxw_datetime *datetime,
|
|
3677
|
+
lxw_format *format)
|
|
3678
|
+
{
|
|
3679
|
+
lxw_cell *cell;
|
|
3680
|
+
double excel_date;
|
|
3681
|
+
lxw_error err;
|
|
3682
|
+
|
|
3683
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3684
|
+
if (err)
|
|
3685
|
+
return err;
|
|
3686
|
+
|
|
3687
|
+
excel_date = lxw_datetime_to_excel_date(datetime, LXW_EPOCH_1900);
|
|
3688
|
+
|
|
3689
|
+
cell = _new_number_cell(row_num, col_num, excel_date, format);
|
|
3690
|
+
|
|
3691
|
+
_insert_cell(self, row_num, col_num, cell);
|
|
3692
|
+
|
|
3693
|
+
return LXW_NO_ERROR;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
/*
|
|
3697
|
+
* Write a hyperlink/url to an Excel file.
|
|
3698
|
+
*/
|
|
3699
|
+
lxw_error
|
|
3700
|
+
worksheet_write_url_opt(lxw_worksheet *self,
|
|
3701
|
+
lxw_row_t row_num,
|
|
3702
|
+
lxw_col_t col_num, const char *url,
|
|
3703
|
+
lxw_format *format, const char *string,
|
|
3704
|
+
const char *tooltip)
|
|
3705
|
+
{
|
|
3706
|
+
lxw_cell *link;
|
|
3707
|
+
char *string_copy = NULL;
|
|
3708
|
+
char *url_copy = NULL;
|
|
3709
|
+
char *url_external = NULL;
|
|
3710
|
+
char *url_string = NULL;
|
|
3711
|
+
char *tooltip_copy = NULL;
|
|
3712
|
+
char *found_string;
|
|
3713
|
+
lxw_error err;
|
|
3714
|
+
size_t string_size;
|
|
3715
|
+
size_t i;
|
|
3716
|
+
enum cell_types link_type = HYPERLINK_URL;
|
|
3717
|
+
|
|
3718
|
+
if (!url || !*url)
|
|
3719
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
3720
|
+
|
|
3721
|
+
/* Check the Excel limit of URLS per worksheet. */
|
|
3722
|
+
if (self->hlink_count > LXW_MAX_NUMBER_URLS)
|
|
3723
|
+
return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED;
|
|
3724
|
+
|
|
3725
|
+
err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
|
|
3726
|
+
if (err)
|
|
3727
|
+
return err;
|
|
3728
|
+
|
|
3729
|
+
/* Set the URI scheme from internal links. */
|
|
3730
|
+
found_string = strstr(url, "internal:");
|
|
3731
|
+
if (found_string)
|
|
3732
|
+
link_type = HYPERLINK_INTERNAL;
|
|
3733
|
+
|
|
3734
|
+
/* Set the URI scheme from external links. */
|
|
3735
|
+
found_string = strstr(url, "external:");
|
|
3736
|
+
if (found_string)
|
|
3737
|
+
link_type = HYPERLINK_EXTERNAL;
|
|
3738
|
+
|
|
3739
|
+
if (string) {
|
|
3740
|
+
string_copy = lxw_strdup(string);
|
|
3741
|
+
GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error);
|
|
3742
|
+
}
|
|
3743
|
+
else {
|
|
3744
|
+
if (link_type == HYPERLINK_URL) {
|
|
3745
|
+
/* Strip the mailto header. */
|
|
3746
|
+
found_string = strstr(url, "mailto:");
|
|
3747
|
+
if (found_string)
|
|
3748
|
+
string_copy = lxw_strdup(url + sizeof("mailto"));
|
|
3749
|
+
else
|
|
3750
|
+
string_copy = lxw_strdup(url);
|
|
3751
|
+
}
|
|
3752
|
+
else {
|
|
3753
|
+
string_copy = lxw_strdup(url + sizeof("__ternal"));
|
|
3754
|
+
}
|
|
3755
|
+
GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error);
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
if (url) {
|
|
3759
|
+
if (link_type == HYPERLINK_URL)
|
|
3760
|
+
url_copy = lxw_strdup(url);
|
|
3761
|
+
else
|
|
3762
|
+
url_copy = lxw_strdup(url + sizeof("__ternal"));
|
|
3763
|
+
|
|
3764
|
+
GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
if (tooltip) {
|
|
3768
|
+
tooltip_copy = lxw_strdup(tooltip);
|
|
3769
|
+
GOTO_LABEL_ON_MEM_ERROR(tooltip_copy, mem_error);
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
if (link_type == HYPERLINK_INTERNAL) {
|
|
3773
|
+
url_string = lxw_strdup(string_copy);
|
|
3774
|
+
GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error);
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
/* Escape the URL. */
|
|
3778
|
+
if (link_type == HYPERLINK_URL && strlen(url_copy) >= 3) {
|
|
3779
|
+
uint8_t not_escaped = 1;
|
|
3780
|
+
|
|
3781
|
+
/* First check if the URL is already escaped by the user. */
|
|
3782
|
+
for (i = 0; i <= strlen(url_copy) - 3; i++) {
|
|
3783
|
+
if (url_copy[i] == '%' && isxdigit(url_copy[i + 1])
|
|
3784
|
+
&& isxdigit(url_copy[i + 2])) {
|
|
3785
|
+
|
|
3786
|
+
not_escaped = 0;
|
|
3787
|
+
break;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
if (not_escaped) {
|
|
3792
|
+
url_external = calloc(1, strlen(url_copy) * 3 + 1);
|
|
3793
|
+
GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error);
|
|
3794
|
+
|
|
3795
|
+
for (i = 0; i <= strlen(url_copy); i++) {
|
|
3796
|
+
switch (url_copy[i]) {
|
|
3797
|
+
case (' '):
|
|
3798
|
+
case ('"'):
|
|
3799
|
+
case ('%'):
|
|
3800
|
+
case ('<'):
|
|
3801
|
+
case ('>'):
|
|
3802
|
+
case ('['):
|
|
3803
|
+
case (']'):
|
|
3804
|
+
case ('`'):
|
|
3805
|
+
case ('^'):
|
|
3806
|
+
case ('{'):
|
|
3807
|
+
case ('}'):
|
|
3808
|
+
lxw_snprintf(url_external + strlen(url_external),
|
|
3809
|
+
sizeof("%xx"), "%%%2x", url_copy[i]);
|
|
3810
|
+
break;
|
|
3811
|
+
default:
|
|
3812
|
+
url_external[strlen(url_external)] = url_copy[i];
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3817
|
+
free(url_copy);
|
|
3818
|
+
url_copy = lxw_strdup(url_external);
|
|
3819
|
+
GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
|
|
3820
|
+
|
|
3821
|
+
free(url_external);
|
|
3822
|
+
url_external = NULL;
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
if (link_type == HYPERLINK_EXTERNAL) {
|
|
3827
|
+
/* External Workbook links need to be modified into the right format.
|
|
3828
|
+
* The URL will look something like "c:\temp\file.xlsx#Sheet!A1".
|
|
3829
|
+
* We need the part to the left of the # as the URL and the part to
|
|
3830
|
+
* the right as the "location" string (if it exists).
|
|
3831
|
+
*/
|
|
3832
|
+
|
|
3833
|
+
/* For external links change the dir separator from Unix to DOS. */
|
|
3834
|
+
for (i = 0; i <= strlen(url_copy); i++)
|
|
3835
|
+
if (url_copy[i] == '/')
|
|
3836
|
+
url_copy[i] = '\\';
|
|
3837
|
+
|
|
3838
|
+
for (i = 0; i <= strlen(string_copy); i++)
|
|
3839
|
+
if (string_copy[i] == '/')
|
|
3840
|
+
string_copy[i] = '\\';
|
|
3841
|
+
|
|
3842
|
+
found_string = strchr(url_copy, '#');
|
|
3843
|
+
|
|
3844
|
+
if (found_string) {
|
|
3845
|
+
url_string = lxw_strdup(found_string + 1);
|
|
3846
|
+
GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error);
|
|
3847
|
+
|
|
3848
|
+
*found_string = '\0';
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
/* Look for Windows style "C:/" link or Windows share "\\" link. */
|
|
3852
|
+
found_string = strchr(url_copy, ':');
|
|
3853
|
+
if (!found_string)
|
|
3854
|
+
found_string = strstr(url_copy, "\\\\");
|
|
3855
|
+
|
|
3856
|
+
if (found_string) {
|
|
3857
|
+
/* Add the file:/// URI to the url if non-local. */
|
|
3858
|
+
string_size = sizeof("file:///") + strlen(url_copy);
|
|
3859
|
+
url_external = calloc(1, string_size);
|
|
3860
|
+
GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error);
|
|
3861
|
+
|
|
3862
|
+
lxw_snprintf(url_external, string_size, "file:///%s", url_copy);
|
|
3863
|
+
|
|
3864
|
+
}
|
|
3865
|
+
|
|
3866
|
+
/* Convert a ./dir/file.xlsx link to dir/file.xlsx. */
|
|
3867
|
+
found_string = strstr(url_copy, ".\\");
|
|
3868
|
+
if (found_string == url_copy)
|
|
3869
|
+
memmove(url_copy, url_copy + 2, strlen(url_copy) - 1);
|
|
3870
|
+
|
|
3871
|
+
if (url_external) {
|
|
3872
|
+
free(url_copy);
|
|
3873
|
+
url_copy = lxw_strdup(url_external);
|
|
3874
|
+
GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
|
|
3875
|
+
|
|
3876
|
+
free(url_external);
|
|
3877
|
+
url_external = NULL;
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
/* Excel limits escaped URL to 255 characters. */
|
|
3883
|
+
if (lxw_utf8_strlen(url_copy) > 255)
|
|
3884
|
+
goto mem_error;
|
|
3885
|
+
|
|
3886
|
+
err = worksheet_write_string(self, row_num, col_num, string_copy, format);
|
|
3887
|
+
if (err)
|
|
3888
|
+
goto mem_error;
|
|
3889
|
+
|
|
3890
|
+
link = _new_hyperlink_cell(row_num, col_num, link_type, url_copy,
|
|
3891
|
+
url_string, tooltip_copy);
|
|
3892
|
+
GOTO_LABEL_ON_MEM_ERROR(link, mem_error);
|
|
3893
|
+
|
|
3894
|
+
_insert_hyperlink(self, row_num, col_num, link);
|
|
3895
|
+
|
|
3896
|
+
free(string_copy);
|
|
3897
|
+
self->hlink_count++;
|
|
3898
|
+
return LXW_NO_ERROR;
|
|
3899
|
+
|
|
3900
|
+
mem_error:
|
|
3901
|
+
free(string_copy);
|
|
3902
|
+
free(url_copy);
|
|
3903
|
+
free(url_external);
|
|
3904
|
+
free(url_string);
|
|
3905
|
+
free(tooltip_copy);
|
|
3906
|
+
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
/*
|
|
3910
|
+
* Write a hyperlink/url to an Excel file.
|
|
3911
|
+
*/
|
|
3912
|
+
lxw_error
|
|
3913
|
+
worksheet_write_url(lxw_worksheet *self,
|
|
3914
|
+
lxw_row_t row_num,
|
|
3915
|
+
lxw_col_t col_num, const char *url, lxw_format *format)
|
|
3916
|
+
{
|
|
3917
|
+
return worksheet_write_url_opt(self, row_num, col_num, url, format, NULL,
|
|
3918
|
+
NULL);
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
/*
|
|
3922
|
+
* Set the properties of a single column or a range of columns with options.
|
|
3923
|
+
*/
|
|
3924
|
+
lxw_error
|
|
3925
|
+
worksheet_set_column_opt(lxw_worksheet *self,
|
|
3926
|
+
lxw_col_t firstcol,
|
|
3927
|
+
lxw_col_t lastcol,
|
|
3928
|
+
double width,
|
|
3929
|
+
lxw_format *format,
|
|
3930
|
+
lxw_row_col_options *user_options)
|
|
3931
|
+
{
|
|
3932
|
+
lxw_col_options *copied_options;
|
|
3933
|
+
uint8_t ignore_row = LXW_TRUE;
|
|
3934
|
+
uint8_t ignore_col = LXW_TRUE;
|
|
3935
|
+
uint8_t hidden = LXW_FALSE;
|
|
3936
|
+
uint8_t level = 0;
|
|
3937
|
+
uint8_t collapsed = LXW_FALSE;
|
|
3938
|
+
lxw_col_t col;
|
|
3939
|
+
lxw_error err;
|
|
3940
|
+
|
|
3941
|
+
if (user_options) {
|
|
3942
|
+
hidden = user_options->hidden;
|
|
3943
|
+
level = user_options->level;
|
|
3944
|
+
collapsed = user_options->collapsed;
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
/* Ensure second col is larger than first. */
|
|
3948
|
+
if (firstcol > lastcol) {
|
|
3949
|
+
lxw_col_t tmp = firstcol;
|
|
3950
|
+
firstcol = lastcol;
|
|
3951
|
+
lastcol = tmp;
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
/* Ensure that the cols are valid and store max and min values.
|
|
3955
|
+
* NOTE: The check shouldn't modify the row dimensions and should only
|
|
3956
|
+
* modify the column dimensions in certain cases. */
|
|
3957
|
+
if (format != NULL || (width != LXW_DEF_COL_WIDTH && hidden))
|
|
3958
|
+
ignore_col = LXW_FALSE;
|
|
3959
|
+
|
|
3960
|
+
err = _check_dimensions(self, 0, firstcol, ignore_row, ignore_col);
|
|
3961
|
+
|
|
3962
|
+
if (!err)
|
|
3963
|
+
err = _check_dimensions(self, 0, lastcol, ignore_row, ignore_col);
|
|
3964
|
+
|
|
3965
|
+
if (err)
|
|
3966
|
+
return err;
|
|
3967
|
+
|
|
3968
|
+
/* Resize the col_options array if required. */
|
|
3969
|
+
if (firstcol >= self->col_options_max) {
|
|
3970
|
+
lxw_col_t col;
|
|
3971
|
+
lxw_col_t old_size = self->col_options_max;
|
|
3972
|
+
lxw_col_t new_size = _next_power_of_two(firstcol + 1);
|
|
3973
|
+
lxw_col_options **new_ptr = realloc(self->col_options,
|
|
3974
|
+
new_size *
|
|
3975
|
+
sizeof(lxw_col_options *));
|
|
3976
|
+
|
|
3977
|
+
if (new_ptr) {
|
|
3978
|
+
for (col = old_size; col < new_size; col++)
|
|
3979
|
+
new_ptr[col] = NULL;
|
|
3980
|
+
|
|
3981
|
+
self->col_options = new_ptr;
|
|
3982
|
+
self->col_options_max = new_size;
|
|
3983
|
+
}
|
|
3984
|
+
else {
|
|
3985
|
+
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
/* Resize the col_formats array if required. */
|
|
3990
|
+
if (lastcol >= self->col_formats_max) {
|
|
3991
|
+
lxw_col_t col;
|
|
3992
|
+
lxw_col_t old_size = self->col_formats_max;
|
|
3993
|
+
lxw_col_t new_size = _next_power_of_two(lastcol + 1);
|
|
3994
|
+
lxw_format **new_ptr = realloc(self->col_formats,
|
|
3995
|
+
new_size * sizeof(lxw_format *));
|
|
3996
|
+
|
|
3997
|
+
if (new_ptr) {
|
|
3998
|
+
for (col = old_size; col < new_size; col++)
|
|
3999
|
+
new_ptr[col] = NULL;
|
|
4000
|
+
|
|
4001
|
+
self->col_formats = new_ptr;
|
|
4002
|
+
self->col_formats_max = new_size;
|
|
4003
|
+
}
|
|
4004
|
+
else {
|
|
4005
|
+
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
/* Store the column options. */
|
|
4010
|
+
copied_options = calloc(1, sizeof(lxw_col_options));
|
|
4011
|
+
RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
4012
|
+
|
|
4013
|
+
copied_options->firstcol = firstcol;
|
|
4014
|
+
copied_options->lastcol = lastcol;
|
|
4015
|
+
copied_options->width = width;
|
|
4016
|
+
copied_options->format = format;
|
|
4017
|
+
copied_options->hidden = hidden;
|
|
4018
|
+
copied_options->level = level;
|
|
4019
|
+
copied_options->collapsed = collapsed;
|
|
4020
|
+
|
|
4021
|
+
self->col_options[firstcol] = copied_options;
|
|
4022
|
+
|
|
4023
|
+
/* Store the column formats for use when writing cell data. */
|
|
4024
|
+
for (col = firstcol; col <= lastcol; col++) {
|
|
4025
|
+
self->col_formats[col] = format;
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
/* Store the column change to allow optimizations. */
|
|
4029
|
+
self->col_size_changed = LXW_TRUE;
|
|
4030
|
+
|
|
4031
|
+
return LXW_NO_ERROR;
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
/*
|
|
4035
|
+
* Set the properties of a single column or a range of columns.
|
|
4036
|
+
*/
|
|
4037
|
+
lxw_error
|
|
4038
|
+
worksheet_set_column(lxw_worksheet *self,
|
|
4039
|
+
lxw_col_t firstcol,
|
|
4040
|
+
lxw_col_t lastcol, double width, lxw_format *format)
|
|
4041
|
+
{
|
|
4042
|
+
return worksheet_set_column_opt(self, firstcol, lastcol, width, format,
|
|
4043
|
+
NULL);
|
|
4044
|
+
}
|
|
4045
|
+
|
|
4046
|
+
/*
|
|
4047
|
+
* Set the properties of a row with options.
|
|
4048
|
+
*/
|
|
4049
|
+
lxw_error
|
|
4050
|
+
worksheet_set_row_opt(lxw_worksheet *self,
|
|
4051
|
+
lxw_row_t row_num,
|
|
4052
|
+
double height,
|
|
4053
|
+
lxw_format *format, lxw_row_col_options *user_options)
|
|
4054
|
+
{
|
|
4055
|
+
|
|
4056
|
+
lxw_col_t min_col;
|
|
4057
|
+
uint8_t hidden = LXW_FALSE;
|
|
4058
|
+
uint8_t level = 0;
|
|
4059
|
+
uint8_t collapsed = LXW_FALSE;
|
|
4060
|
+
lxw_row *row;
|
|
4061
|
+
lxw_error err;
|
|
4062
|
+
|
|
4063
|
+
if (user_options) {
|
|
4064
|
+
hidden = user_options->hidden;
|
|
4065
|
+
level = user_options->level;
|
|
4066
|
+
collapsed = user_options->collapsed;
|
|
4067
|
+
}
|
|
4068
|
+
|
|
4069
|
+
/* Use minimum col in _check_dimensions(). */
|
|
4070
|
+
if (self->dim_colmin != LXW_COL_MAX)
|
|
4071
|
+
min_col = self->dim_colmin;
|
|
4072
|
+
else
|
|
4073
|
+
min_col = 0;
|
|
4074
|
+
|
|
4075
|
+
err = _check_dimensions(self, row_num, min_col, LXW_FALSE, LXW_FALSE);
|
|
4076
|
+
if (err)
|
|
4077
|
+
return err;
|
|
4078
|
+
|
|
4079
|
+
/* If the height is 0 the row is hidden and the height is the default. */
|
|
4080
|
+
if (height == 0) {
|
|
4081
|
+
hidden = LXW_TRUE;
|
|
4082
|
+
height = self->default_row_height;
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
row = _get_row(self, row_num);
|
|
4086
|
+
|
|
4087
|
+
row->height = height;
|
|
4088
|
+
row->format = format;
|
|
4089
|
+
row->hidden = hidden;
|
|
4090
|
+
row->level = level;
|
|
4091
|
+
row->collapsed = collapsed;
|
|
4092
|
+
row->row_changed = LXW_TRUE;
|
|
4093
|
+
|
|
4094
|
+
if (height != self->default_row_height)
|
|
4095
|
+
row->height_changed = LXW_TRUE;
|
|
4096
|
+
|
|
4097
|
+
return LXW_NO_ERROR;
|
|
4098
|
+
}
|
|
4099
|
+
|
|
4100
|
+
/*
|
|
4101
|
+
* Set the properties of a row.
|
|
4102
|
+
*/
|
|
4103
|
+
lxw_error
|
|
4104
|
+
worksheet_set_row(lxw_worksheet *self,
|
|
4105
|
+
lxw_row_t row_num, double height, lxw_format *format)
|
|
4106
|
+
{
|
|
4107
|
+
return worksheet_set_row_opt(self, row_num, height, format, NULL);
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
/*
|
|
4111
|
+
* Merge a range of cells. The first cell should contain the data and the others
|
|
4112
|
+
* should be blank. All cells should contain the same format.
|
|
4113
|
+
*/
|
|
4114
|
+
lxw_error
|
|
4115
|
+
worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row,
|
|
4116
|
+
lxw_col_t first_col, lxw_row_t last_row,
|
|
4117
|
+
lxw_col_t last_col, const char *string,
|
|
4118
|
+
lxw_format *format)
|
|
4119
|
+
{
|
|
4120
|
+
lxw_merged_range *merged_range;
|
|
4121
|
+
lxw_row_t tmp_row;
|
|
4122
|
+
lxw_col_t tmp_col;
|
|
4123
|
+
lxw_error err;
|
|
4124
|
+
|
|
4125
|
+
/* Excel doesn't allow a single cell to be merged */
|
|
4126
|
+
if (first_row == last_row && first_col == last_col)
|
|
4127
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4128
|
+
|
|
4129
|
+
/* Swap last row/col with first row/col as necessary */
|
|
4130
|
+
if (first_row > last_row) {
|
|
4131
|
+
tmp_row = last_row;
|
|
4132
|
+
last_row = first_row;
|
|
4133
|
+
first_row = tmp_row;
|
|
4134
|
+
}
|
|
4135
|
+
if (first_col > last_col) {
|
|
4136
|
+
tmp_col = last_col;
|
|
4137
|
+
last_col = first_col;
|
|
4138
|
+
first_col = tmp_col;
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
/* Check that column number is valid and store the max value */
|
|
4142
|
+
err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
|
|
4143
|
+
if (err)
|
|
4144
|
+
return err;
|
|
4145
|
+
|
|
4146
|
+
/* Store the merge range. */
|
|
4147
|
+
merged_range = calloc(1, sizeof(lxw_merged_range));
|
|
4148
|
+
RETURN_ON_MEM_ERROR(merged_range, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
4149
|
+
|
|
4150
|
+
merged_range->first_row = first_row;
|
|
4151
|
+
merged_range->first_col = first_col;
|
|
4152
|
+
merged_range->last_row = last_row;
|
|
4153
|
+
merged_range->last_col = last_col;
|
|
4154
|
+
|
|
4155
|
+
STAILQ_INSERT_TAIL(self->merged_ranges, merged_range, list_pointers);
|
|
4156
|
+
self->merged_range_count++;
|
|
4157
|
+
|
|
4158
|
+
/* Write the first cell */
|
|
4159
|
+
worksheet_write_string(self, first_row, first_col, string, format);
|
|
4160
|
+
|
|
4161
|
+
/* Pad out the rest of the area with formatted blank cells. */
|
|
4162
|
+
for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) {
|
|
4163
|
+
for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) {
|
|
4164
|
+
if (tmp_row == first_row && tmp_col == first_col)
|
|
4165
|
+
continue;
|
|
4166
|
+
|
|
4167
|
+
worksheet_write_blank(self, tmp_row, tmp_col, format);
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
|
|
4171
|
+
return LXW_NO_ERROR;
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
/*
|
|
4175
|
+
* Set the autofilter area in the worksheet.
|
|
4176
|
+
*/
|
|
4177
|
+
lxw_error
|
|
4178
|
+
worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row,
|
|
4179
|
+
lxw_col_t first_col, lxw_row_t last_row,
|
|
4180
|
+
lxw_col_t last_col)
|
|
4181
|
+
{
|
|
4182
|
+
lxw_row_t tmp_row;
|
|
4183
|
+
lxw_col_t tmp_col;
|
|
4184
|
+
lxw_error err;
|
|
4185
|
+
|
|
4186
|
+
/* Excel doesn't allow a single cell to be merged */
|
|
4187
|
+
if (first_row == last_row && first_col == last_col)
|
|
4188
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4189
|
+
|
|
4190
|
+
/* Swap last row/col with first row/col as necessary */
|
|
4191
|
+
if (first_row > last_row) {
|
|
4192
|
+
tmp_row = last_row;
|
|
4193
|
+
last_row = first_row;
|
|
4194
|
+
first_row = tmp_row;
|
|
4195
|
+
}
|
|
4196
|
+
if (first_col > last_col) {
|
|
4197
|
+
tmp_col = last_col;
|
|
4198
|
+
last_col = first_col;
|
|
4199
|
+
first_col = tmp_col;
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
/* Check that column number is valid and store the max value */
|
|
4203
|
+
err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
|
|
4204
|
+
if (err)
|
|
4205
|
+
return err;
|
|
4206
|
+
|
|
4207
|
+
self->autofilter.in_use = LXW_TRUE;
|
|
4208
|
+
self->autofilter.first_row = first_row;
|
|
4209
|
+
self->autofilter.first_col = first_col;
|
|
4210
|
+
self->autofilter.last_row = last_row;
|
|
4211
|
+
self->autofilter.last_col = last_col;
|
|
4212
|
+
|
|
4213
|
+
return LXW_NO_ERROR;
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
/*
|
|
4217
|
+
* Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
|
|
4218
|
+
* highlighted.
|
|
4219
|
+
*/
|
|
4220
|
+
void
|
|
4221
|
+
worksheet_select(lxw_worksheet *self)
|
|
4222
|
+
{
|
|
4223
|
+
self->selected = LXW_TRUE;
|
|
4224
|
+
|
|
4225
|
+
/* Selected worksheet can't be hidden. */
|
|
4226
|
+
self->hidden = LXW_FALSE;
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
/*
|
|
4230
|
+
* Set this worksheet as the active worksheet, i.e. the worksheet that is
|
|
4231
|
+
* displayed when the workbook is opened. Also set it as selected.
|
|
4232
|
+
*/
|
|
4233
|
+
void
|
|
4234
|
+
worksheet_activate(lxw_worksheet *self)
|
|
4235
|
+
{
|
|
4236
|
+
self->selected = LXW_TRUE;
|
|
4237
|
+
self->active = LXW_TRUE;
|
|
4238
|
+
|
|
4239
|
+
/* Active worksheet can't be hidden. */
|
|
4240
|
+
self->hidden = LXW_FALSE;
|
|
4241
|
+
|
|
4242
|
+
*self->active_sheet = self->index;
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
/*
|
|
4246
|
+
* Set this worksheet as the first visible sheet. This is necessary
|
|
4247
|
+
* when there are a large number of worksheets and the activated
|
|
4248
|
+
* worksheet is not visible on the screen.
|
|
4249
|
+
*/
|
|
4250
|
+
void
|
|
4251
|
+
worksheet_set_first_sheet(lxw_worksheet *self)
|
|
4252
|
+
{
|
|
4253
|
+
/* Active worksheet can't be hidden. */
|
|
4254
|
+
self->hidden = LXW_FALSE;
|
|
4255
|
+
|
|
4256
|
+
*self->first_sheet = self->index;
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
/*
|
|
4260
|
+
* Hide this worksheet.
|
|
4261
|
+
*/
|
|
4262
|
+
void
|
|
4263
|
+
worksheet_hide(lxw_worksheet *self)
|
|
4264
|
+
{
|
|
4265
|
+
self->hidden = LXW_TRUE;
|
|
4266
|
+
|
|
4267
|
+
/* A hidden worksheet shouldn't be active or selected. */
|
|
4268
|
+
self->selected = LXW_FALSE;
|
|
4269
|
+
|
|
4270
|
+
/* If this is active_sheet or first_sheet reset the workbook value. */
|
|
4271
|
+
if (*self->first_sheet == self->index)
|
|
4272
|
+
*self->first_sheet = 0;
|
|
4273
|
+
|
|
4274
|
+
if (*self->active_sheet == self->index)
|
|
4275
|
+
*self->active_sheet = 0;
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
/*
|
|
4279
|
+
* Set which cell or cells are selected in a worksheet.
|
|
4280
|
+
*/
|
|
4281
|
+
void
|
|
4282
|
+
worksheet_set_selection(lxw_worksheet *self,
|
|
4283
|
+
lxw_row_t first_row, lxw_col_t first_col,
|
|
4284
|
+
lxw_row_t last_row, lxw_col_t last_col)
|
|
4285
|
+
{
|
|
4286
|
+
lxw_selection *selection;
|
|
4287
|
+
lxw_row_t tmp_row;
|
|
4288
|
+
lxw_col_t tmp_col;
|
|
4289
|
+
char active_cell[LXW_MAX_CELL_RANGE_LENGTH];
|
|
4290
|
+
char sqref[LXW_MAX_CELL_RANGE_LENGTH];
|
|
4291
|
+
|
|
4292
|
+
/* Only allow selection to be set once to avoid freeing/re-creating it. */
|
|
4293
|
+
if (!STAILQ_EMPTY(self->selections))
|
|
4294
|
+
return;
|
|
4295
|
+
|
|
4296
|
+
/* Excel doesn't set a selection for cell A1 since it is the default. */
|
|
4297
|
+
if (first_row == 0 && first_col == 0 && last_row == 0 && last_col == 0)
|
|
4298
|
+
return;
|
|
4299
|
+
|
|
4300
|
+
selection = calloc(1, sizeof(lxw_selection));
|
|
4301
|
+
RETURN_VOID_ON_MEM_ERROR(selection);
|
|
4302
|
+
|
|
4303
|
+
/* Set the cell range selection. Do this before swapping max/min to */
|
|
4304
|
+
/* allow the selection direction to be reversed. */
|
|
4305
|
+
lxw_rowcol_to_cell(active_cell, first_row, first_col);
|
|
4306
|
+
|
|
4307
|
+
/* Swap last row/col for first row/col if necessary. */
|
|
4308
|
+
if (first_row > last_row) {
|
|
4309
|
+
tmp_row = first_row;
|
|
4310
|
+
first_row = last_row;
|
|
4311
|
+
last_row = tmp_row;
|
|
4312
|
+
}
|
|
4313
|
+
|
|
4314
|
+
if (first_col > last_col) {
|
|
4315
|
+
tmp_col = first_col;
|
|
4316
|
+
first_col = last_col;
|
|
4317
|
+
last_col = tmp_col;
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
/* If the first and last cell are the same write a single cell. */
|
|
4321
|
+
if ((first_row == last_row) && (first_col == last_col))
|
|
4322
|
+
lxw_rowcol_to_cell(sqref, first_row, first_col);
|
|
4323
|
+
else
|
|
4324
|
+
lxw_rowcol_to_range(sqref, first_row, first_col, last_row, last_col);
|
|
4325
|
+
|
|
4326
|
+
lxw_strcpy(selection->pane, "");
|
|
4327
|
+
lxw_strcpy(selection->active_cell, active_cell);
|
|
4328
|
+
lxw_strcpy(selection->sqref, sqref);
|
|
4329
|
+
|
|
4330
|
+
STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
/*
|
|
4334
|
+
* Set panes and mark them as frozen. With extra options.
|
|
4335
|
+
*/
|
|
4336
|
+
void
|
|
4337
|
+
worksheet_freeze_panes_opt(lxw_worksheet *self,
|
|
4338
|
+
lxw_row_t first_row, lxw_col_t first_col,
|
|
4339
|
+
lxw_row_t top_row, lxw_col_t left_col,
|
|
4340
|
+
uint8_t type)
|
|
4341
|
+
{
|
|
4342
|
+
self->panes.first_row = first_row;
|
|
4343
|
+
self->panes.first_col = first_col;
|
|
4344
|
+
self->panes.top_row = top_row;
|
|
4345
|
+
self->panes.left_col = left_col;
|
|
4346
|
+
self->panes.x_split = 0.0;
|
|
4347
|
+
self->panes.y_split = 0.0;
|
|
4348
|
+
|
|
4349
|
+
if (type)
|
|
4350
|
+
self->panes.type = FREEZE_SPLIT_PANES;
|
|
4351
|
+
else
|
|
4352
|
+
self->panes.type = FREEZE_PANES;
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
/*
|
|
4356
|
+
* Set panes and mark them as frozen.
|
|
4357
|
+
*/
|
|
4358
|
+
void
|
|
4359
|
+
worksheet_freeze_panes(lxw_worksheet *self,
|
|
4360
|
+
lxw_row_t first_row, lxw_col_t first_col)
|
|
4361
|
+
{
|
|
4362
|
+
worksheet_freeze_panes_opt(self, first_row, first_col,
|
|
4363
|
+
first_row, first_col, 0);
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
/*
|
|
4367
|
+
* Set panes and mark them as split.With extra options.
|
|
4368
|
+
*/
|
|
4369
|
+
void
|
|
4370
|
+
worksheet_split_panes_opt(lxw_worksheet *self,
|
|
4371
|
+
double y_split, double x_split,
|
|
4372
|
+
lxw_row_t top_row, lxw_col_t left_col)
|
|
4373
|
+
{
|
|
4374
|
+
self->panes.first_row = 0;
|
|
4375
|
+
self->panes.first_col = 0;
|
|
4376
|
+
self->panes.top_row = top_row;
|
|
4377
|
+
self->panes.left_col = left_col;
|
|
4378
|
+
self->panes.x_split = x_split;
|
|
4379
|
+
self->panes.y_split = y_split;
|
|
4380
|
+
self->panes.type = SPLIT_PANES;
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4383
|
+
/*
|
|
4384
|
+
* Set panes and mark them as split.
|
|
4385
|
+
*/
|
|
4386
|
+
void
|
|
4387
|
+
worksheet_split_panes(lxw_worksheet *self, double y_split, double x_split)
|
|
4388
|
+
{
|
|
4389
|
+
worksheet_split_panes_opt(self, y_split, x_split, 0, 0);
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
/*
|
|
4393
|
+
* Set the page orientation as portrait.
|
|
4394
|
+
*/
|
|
4395
|
+
void
|
|
4396
|
+
worksheet_set_portrait(lxw_worksheet *self)
|
|
4397
|
+
{
|
|
4398
|
+
self->orientation = LXW_PORTRAIT;
|
|
4399
|
+
self->page_setup_changed = LXW_TRUE;
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
/*
|
|
4403
|
+
* Set the page orientation as landscape.
|
|
4404
|
+
*/
|
|
4405
|
+
void
|
|
4406
|
+
worksheet_set_landscape(lxw_worksheet *self)
|
|
4407
|
+
{
|
|
4408
|
+
self->orientation = LXW_LANDSCAPE;
|
|
4409
|
+
self->page_setup_changed = LXW_TRUE;
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
/*
|
|
4413
|
+
* Set the page view mode for Mac Excel.
|
|
4414
|
+
*/
|
|
4415
|
+
void
|
|
4416
|
+
worksheet_set_page_view(lxw_worksheet *self)
|
|
4417
|
+
{
|
|
4418
|
+
self->page_view = LXW_TRUE;
|
|
4419
|
+
}
|
|
4420
|
+
|
|
4421
|
+
/*
|
|
4422
|
+
* Set the paper type. Example. 1 = US Letter, 9 = A4
|
|
4423
|
+
*/
|
|
4424
|
+
void
|
|
4425
|
+
worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size)
|
|
4426
|
+
{
|
|
4427
|
+
self->paper_size = paper_size;
|
|
4428
|
+
self->page_setup_changed = LXW_TRUE;
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
/*
|
|
4432
|
+
* Set the order in which pages are printed.
|
|
4433
|
+
*/
|
|
4434
|
+
void
|
|
4435
|
+
worksheet_print_across(lxw_worksheet *self)
|
|
4436
|
+
{
|
|
4437
|
+
self->page_order = LXW_PRINT_ACROSS;
|
|
4438
|
+
self->page_setup_changed = LXW_TRUE;
|
|
4439
|
+
}
|
|
4440
|
+
|
|
4441
|
+
/*
|
|
4442
|
+
* Set all the page margins in inches.
|
|
4443
|
+
*/
|
|
4444
|
+
void
|
|
4445
|
+
worksheet_set_margins(lxw_worksheet *self, double left, double right,
|
|
4446
|
+
double top, double bottom)
|
|
4447
|
+
{
|
|
4448
|
+
|
|
4449
|
+
if (left >= 0)
|
|
4450
|
+
self->margin_left = left;
|
|
4451
|
+
|
|
4452
|
+
if (right >= 0)
|
|
4453
|
+
self->margin_right = right;
|
|
4454
|
+
|
|
4455
|
+
if (top >= 0)
|
|
4456
|
+
self->margin_top = top;
|
|
4457
|
+
|
|
4458
|
+
if (bottom >= 0)
|
|
4459
|
+
self->margin_bottom = bottom;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4462
|
+
/*
|
|
4463
|
+
* Set the page header caption and options.
|
|
4464
|
+
*/
|
|
4465
|
+
lxw_error
|
|
4466
|
+
worksheet_set_header_opt(lxw_worksheet *self, const char *string,
|
|
4467
|
+
lxw_header_footer_options *options)
|
|
4468
|
+
{
|
|
4469
|
+
if (options) {
|
|
4470
|
+
if (options->margin > 0)
|
|
4471
|
+
self->margin_header = options->margin;
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
if (!string)
|
|
4475
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4476
|
+
|
|
4477
|
+
if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX)
|
|
4478
|
+
return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
|
|
4479
|
+
|
|
4480
|
+
lxw_strcpy(self->header, string);
|
|
4481
|
+
self->header_footer_changed = 1;
|
|
4482
|
+
|
|
4483
|
+
return LXW_NO_ERROR;
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
/*
|
|
4487
|
+
* Set the page footer caption and options.
|
|
4488
|
+
*/
|
|
4489
|
+
lxw_error
|
|
4490
|
+
worksheet_set_footer_opt(lxw_worksheet *self, const char *string,
|
|
4491
|
+
lxw_header_footer_options *options)
|
|
4492
|
+
{
|
|
4493
|
+
if (options) {
|
|
4494
|
+
if (options->margin > 0)
|
|
4495
|
+
self->margin_footer = options->margin;
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4498
|
+
if (!string)
|
|
4499
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4500
|
+
|
|
4501
|
+
if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX)
|
|
4502
|
+
return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
|
|
4503
|
+
|
|
4504
|
+
lxw_strcpy(self->footer, string);
|
|
4505
|
+
self->header_footer_changed = 1;
|
|
4506
|
+
|
|
4507
|
+
return LXW_NO_ERROR;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
/*
|
|
4511
|
+
* Set the page header caption.
|
|
4512
|
+
*/
|
|
4513
|
+
lxw_error
|
|
4514
|
+
worksheet_set_header(lxw_worksheet *self, const char *string)
|
|
4515
|
+
{
|
|
4516
|
+
return worksheet_set_header_opt(self, string, NULL);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
/*
|
|
4520
|
+
* Set the page footer caption.
|
|
4521
|
+
*/
|
|
4522
|
+
lxw_error
|
|
4523
|
+
worksheet_set_footer(lxw_worksheet *self, const char *string)
|
|
4524
|
+
{
|
|
4525
|
+
return worksheet_set_footer_opt(self, string, NULL);
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
/*
|
|
4529
|
+
* Set the option to show/hide gridlines on the screen and the printed page.
|
|
4530
|
+
*/
|
|
4531
|
+
void
|
|
4532
|
+
worksheet_gridlines(lxw_worksheet *self, uint8_t option)
|
|
4533
|
+
{
|
|
4534
|
+
if (option == LXW_HIDE_ALL_GRIDLINES) {
|
|
4535
|
+
self->print_gridlines = 0;
|
|
4536
|
+
self->screen_gridlines = 0;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
if (option & LXW_SHOW_SCREEN_GRIDLINES) {
|
|
4540
|
+
self->screen_gridlines = 1;
|
|
4541
|
+
}
|
|
4542
|
+
|
|
4543
|
+
if (option & LXW_SHOW_PRINT_GRIDLINES) {
|
|
4544
|
+
self->print_gridlines = 1;
|
|
4545
|
+
self->print_options_changed = 1;
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
/*
|
|
4550
|
+
* Center the page horizontally.
|
|
4551
|
+
*/
|
|
4552
|
+
void
|
|
4553
|
+
worksheet_center_horizontally(lxw_worksheet *self)
|
|
4554
|
+
{
|
|
4555
|
+
self->print_options_changed = 1;
|
|
4556
|
+
self->hcenter = 1;
|
|
4557
|
+
}
|
|
4558
|
+
|
|
4559
|
+
/*
|
|
4560
|
+
* Center the page horizontally.
|
|
4561
|
+
*/
|
|
4562
|
+
void
|
|
4563
|
+
worksheet_center_vertically(lxw_worksheet *self)
|
|
4564
|
+
{
|
|
4565
|
+
self->print_options_changed = 1;
|
|
4566
|
+
self->vcenter = 1;
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
/*
|
|
4570
|
+
* Set the option to print the row and column headers on the printed page.
|
|
4571
|
+
*/
|
|
4572
|
+
void
|
|
4573
|
+
worksheet_print_row_col_headers(lxw_worksheet *self)
|
|
4574
|
+
{
|
|
4575
|
+
self->print_headers = 1;
|
|
4576
|
+
self->print_options_changed = 1;
|
|
4577
|
+
}
|
|
4578
|
+
|
|
4579
|
+
/*
|
|
4580
|
+
* Set the rows to repeat at the top of each printed page.
|
|
4581
|
+
*/
|
|
4582
|
+
lxw_error
|
|
4583
|
+
worksheet_repeat_rows(lxw_worksheet *self, lxw_row_t first_row,
|
|
4584
|
+
lxw_row_t last_row)
|
|
4585
|
+
{
|
|
4586
|
+
lxw_row_t tmp_row;
|
|
4587
|
+
lxw_error err;
|
|
4588
|
+
|
|
4589
|
+
if (first_row > last_row) {
|
|
4590
|
+
tmp_row = last_row;
|
|
4591
|
+
last_row = first_row;
|
|
4592
|
+
first_row = tmp_row;
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
err = _check_dimensions(self, last_row, 0, LXW_IGNORE, LXW_IGNORE);
|
|
4596
|
+
if (err)
|
|
4597
|
+
return err;
|
|
4598
|
+
|
|
4599
|
+
self->repeat_rows.in_use = LXW_TRUE;
|
|
4600
|
+
self->repeat_rows.first_row = first_row;
|
|
4601
|
+
self->repeat_rows.last_row = last_row;
|
|
4602
|
+
|
|
4603
|
+
return LXW_NO_ERROR;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
/*
|
|
4607
|
+
* Set the columns to repeat at the left hand side of each printed page.
|
|
4608
|
+
*/
|
|
4609
|
+
lxw_error
|
|
4610
|
+
worksheet_repeat_columns(lxw_worksheet *self, lxw_col_t first_col,
|
|
4611
|
+
lxw_col_t last_col)
|
|
4612
|
+
{
|
|
4613
|
+
lxw_col_t tmp_col;
|
|
4614
|
+
lxw_error err;
|
|
4615
|
+
|
|
4616
|
+
if (first_col > last_col) {
|
|
4617
|
+
tmp_col = last_col;
|
|
4618
|
+
last_col = first_col;
|
|
4619
|
+
first_col = tmp_col;
|
|
4620
|
+
}
|
|
4621
|
+
|
|
4622
|
+
err = _check_dimensions(self, last_col, 0, LXW_IGNORE, LXW_IGNORE);
|
|
4623
|
+
if (err)
|
|
4624
|
+
return err;
|
|
4625
|
+
|
|
4626
|
+
self->repeat_cols.in_use = LXW_TRUE;
|
|
4627
|
+
self->repeat_cols.first_col = first_col;
|
|
4628
|
+
self->repeat_cols.last_col = last_col;
|
|
4629
|
+
|
|
4630
|
+
return LXW_NO_ERROR;
|
|
4631
|
+
}
|
|
4632
|
+
|
|
4633
|
+
/*
|
|
4634
|
+
* Set the print area in the current worksheet.
|
|
4635
|
+
*/
|
|
4636
|
+
lxw_error
|
|
4637
|
+
worksheet_print_area(lxw_worksheet *self, lxw_row_t first_row,
|
|
4638
|
+
lxw_col_t first_col, lxw_row_t last_row,
|
|
4639
|
+
lxw_col_t last_col)
|
|
4640
|
+
{
|
|
4641
|
+
lxw_row_t tmp_row;
|
|
4642
|
+
lxw_col_t tmp_col;
|
|
4643
|
+
lxw_error err;
|
|
4644
|
+
|
|
4645
|
+
if (first_row > last_row) {
|
|
4646
|
+
tmp_row = last_row;
|
|
4647
|
+
last_row = first_row;
|
|
4648
|
+
first_row = tmp_row;
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
if (first_col > last_col) {
|
|
4652
|
+
tmp_col = last_col;
|
|
4653
|
+
last_col = first_col;
|
|
4654
|
+
first_col = tmp_col;
|
|
4655
|
+
}
|
|
4656
|
+
|
|
4657
|
+
err = _check_dimensions(self, last_row, last_col, LXW_IGNORE, LXW_IGNORE);
|
|
4658
|
+
if (err)
|
|
4659
|
+
return err;
|
|
4660
|
+
|
|
4661
|
+
/* Ignore max area since it is the same as no print area in Excel. */
|
|
4662
|
+
if (first_row == 0 && first_col == 0 && last_row == LXW_ROW_MAX - 1
|
|
4663
|
+
&& last_col == LXW_COL_MAX - 1) {
|
|
4664
|
+
return LXW_NO_ERROR;
|
|
4665
|
+
}
|
|
4666
|
+
|
|
4667
|
+
self->print_area.in_use = LXW_TRUE;
|
|
4668
|
+
self->print_area.first_row = first_row;
|
|
4669
|
+
self->print_area.last_row = last_row;
|
|
4670
|
+
self->print_area.first_col = first_col;
|
|
4671
|
+
self->print_area.last_col = last_col;
|
|
4672
|
+
|
|
4673
|
+
return LXW_NO_ERROR;
|
|
4674
|
+
}
|
|
4675
|
+
|
|
4676
|
+
/* Store the vertical and horizontal number of pages that will define the
|
|
4677
|
+
* maximum area printed.
|
|
4678
|
+
*/
|
|
4679
|
+
void
|
|
4680
|
+
worksheet_fit_to_pages(lxw_worksheet *self, uint16_t width, uint16_t height)
|
|
4681
|
+
{
|
|
4682
|
+
self->fit_page = 1;
|
|
4683
|
+
self->fit_width = width;
|
|
4684
|
+
self->fit_height = height;
|
|
4685
|
+
self->page_setup_changed = 1;
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4688
|
+
/*
|
|
4689
|
+
* Set the start page number.
|
|
4690
|
+
*/
|
|
4691
|
+
void
|
|
4692
|
+
worksheet_set_start_page(lxw_worksheet *self, uint16_t start_page)
|
|
4693
|
+
{
|
|
4694
|
+
self->page_start = start_page;
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
/*
|
|
4698
|
+
* Set the scale factor for the printed page.
|
|
4699
|
+
*/
|
|
4700
|
+
void
|
|
4701
|
+
worksheet_set_print_scale(lxw_worksheet *self, uint16_t scale)
|
|
4702
|
+
{
|
|
4703
|
+
/* Confine the scale to Excel"s range */
|
|
4704
|
+
if (scale < 10 || scale > 400)
|
|
4705
|
+
return;
|
|
4706
|
+
|
|
4707
|
+
/* Turn off "fit to page" option. */
|
|
4708
|
+
self->fit_page = LXW_FALSE;
|
|
4709
|
+
|
|
4710
|
+
self->print_scale = scale;
|
|
4711
|
+
self->page_setup_changed = LXW_TRUE;
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4714
|
+
/*
|
|
4715
|
+
* Store the horizontal page breaks on a worksheet.
|
|
4716
|
+
*/
|
|
4717
|
+
lxw_error
|
|
4718
|
+
worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[])
|
|
4719
|
+
{
|
|
4720
|
+
uint16_t count = 0;
|
|
4721
|
+
|
|
4722
|
+
if (hbreaks == NULL)
|
|
4723
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4724
|
+
|
|
4725
|
+
while (hbreaks[count])
|
|
4726
|
+
count++;
|
|
4727
|
+
|
|
4728
|
+
/* The Excel 2007 specification says that the maximum number of page
|
|
4729
|
+
* breaks is 1026. However, in practice it is actually 1023. */
|
|
4730
|
+
if (count > LXW_BREAKS_MAX)
|
|
4731
|
+
count = LXW_BREAKS_MAX;
|
|
4732
|
+
|
|
4733
|
+
self->hbreaks = calloc(count, sizeof(lxw_row_t));
|
|
4734
|
+
RETURN_ON_MEM_ERROR(self->hbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
4735
|
+
memcpy(self->hbreaks, hbreaks, count * sizeof(lxw_row_t));
|
|
4736
|
+
self->hbreaks_count = count;
|
|
4737
|
+
|
|
4738
|
+
return LXW_NO_ERROR;
|
|
4739
|
+
}
|
|
4740
|
+
|
|
4741
|
+
/*
|
|
4742
|
+
* Store the vertical page breaks on a worksheet.
|
|
4743
|
+
*/
|
|
4744
|
+
lxw_error
|
|
4745
|
+
worksheet_set_v_pagebreaks(lxw_worksheet *self, lxw_col_t vbreaks[])
|
|
4746
|
+
{
|
|
4747
|
+
uint16_t count = 0;
|
|
4748
|
+
|
|
4749
|
+
if (vbreaks == NULL)
|
|
4750
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4751
|
+
|
|
4752
|
+
while (vbreaks[count])
|
|
4753
|
+
count++;
|
|
4754
|
+
|
|
4755
|
+
/* The Excel 2007 specification says that the maximum number of page
|
|
4756
|
+
* breaks is 1026. However, in practice it is actually 1023. */
|
|
4757
|
+
if (count > LXW_BREAKS_MAX)
|
|
4758
|
+
count = LXW_BREAKS_MAX;
|
|
4759
|
+
|
|
4760
|
+
self->vbreaks = calloc(count, sizeof(lxw_col_t));
|
|
4761
|
+
RETURN_ON_MEM_ERROR(self->vbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
4762
|
+
memcpy(self->vbreaks, vbreaks, count * sizeof(lxw_col_t));
|
|
4763
|
+
self->vbreaks_count = count;
|
|
4764
|
+
|
|
4765
|
+
return LXW_NO_ERROR;
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4768
|
+
/*
|
|
4769
|
+
* Set the worksheet zoom factor.
|
|
4770
|
+
*/
|
|
4771
|
+
void
|
|
4772
|
+
worksheet_set_zoom(lxw_worksheet *self, uint16_t scale)
|
|
4773
|
+
{
|
|
4774
|
+
/* Confine the scale to Excel"s range */
|
|
4775
|
+
if (scale < 10 || scale > 400) {
|
|
4776
|
+
LXW_WARN("worksheet_set_zoom(): "
|
|
4777
|
+
"Zoom factor scale outside range: 10 <= zoom <= 400.");
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
self->zoom = scale;
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4784
|
+
/*
|
|
4785
|
+
* Hide cell zero values.
|
|
4786
|
+
*/
|
|
4787
|
+
void
|
|
4788
|
+
worksheet_hide_zero(lxw_worksheet *self)
|
|
4789
|
+
{
|
|
4790
|
+
self->show_zeros = LXW_FALSE;
|
|
4791
|
+
}
|
|
4792
|
+
|
|
4793
|
+
/*
|
|
4794
|
+
* Display the worksheet right to left for some eastern versions of Excel.
|
|
4795
|
+
*/
|
|
4796
|
+
void
|
|
4797
|
+
worksheet_right_to_left(lxw_worksheet *self)
|
|
4798
|
+
{
|
|
4799
|
+
self->right_to_left = LXW_TRUE;
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
/*
|
|
4803
|
+
* Set the color of the worksheet tab.
|
|
4804
|
+
*/
|
|
4805
|
+
void
|
|
4806
|
+
worksheet_set_tab_color(lxw_worksheet *self, lxw_color_t color)
|
|
4807
|
+
{
|
|
4808
|
+
self->tab_color = color;
|
|
4809
|
+
}
|
|
4810
|
+
|
|
4811
|
+
/*
|
|
4812
|
+
* Set the worksheet protection flags to prevent modification of worksheet
|
|
4813
|
+
* objects.
|
|
4814
|
+
*/
|
|
4815
|
+
void
|
|
4816
|
+
worksheet_protect(lxw_worksheet *self, const char *password,
|
|
4817
|
+
lxw_protection *options)
|
|
4818
|
+
{
|
|
4819
|
+
struct lxw_protection *protect = &self->protection;
|
|
4820
|
+
|
|
4821
|
+
/* Copy any user parameters to the internal structure. */
|
|
4822
|
+
if (options)
|
|
4823
|
+
memcpy(protect, options, sizeof(lxw_protection));
|
|
4824
|
+
|
|
4825
|
+
/* Zero the hash storage in case of copied initialization data. */
|
|
4826
|
+
protect->hash[0] = '\0';
|
|
4827
|
+
|
|
4828
|
+
if (password) {
|
|
4829
|
+
uint16_t hash = _hash_password(password);
|
|
4830
|
+
lxw_snprintf(protect->hash, 5, "%X", hash);
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4833
|
+
protect->is_configured = LXW_TRUE;
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
/*
|
|
4837
|
+
* Set the default row properties
|
|
4838
|
+
*/
|
|
4839
|
+
void
|
|
4840
|
+
worksheet_set_default_row(lxw_worksheet *self, double height,
|
|
4841
|
+
uint8_t hide_unused_rows)
|
|
4842
|
+
{
|
|
4843
|
+
if (height < 0)
|
|
4844
|
+
height = self->default_row_height;
|
|
4845
|
+
|
|
4846
|
+
if (height != self->default_row_height) {
|
|
4847
|
+
self->default_row_height = height;
|
|
4848
|
+
self->row_size_changed = LXW_TRUE;
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
if (hide_unused_rows)
|
|
4852
|
+
self->default_row_zeroed = LXW_TRUE;
|
|
4853
|
+
|
|
4854
|
+
self->default_row_set = LXW_TRUE;
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
/*
|
|
4858
|
+
* Insert an image into the worksheet.
|
|
4859
|
+
*/
|
|
4860
|
+
lxw_error
|
|
4861
|
+
worksheet_insert_image_opt(lxw_worksheet *self,
|
|
4862
|
+
lxw_row_t row_num, lxw_col_t col_num,
|
|
4863
|
+
const char *filename,
|
|
4864
|
+
lxw_image_options *user_options)
|
|
4865
|
+
{
|
|
4866
|
+
FILE *image_stream;
|
|
4867
|
+
char *short_name;
|
|
4868
|
+
lxw_image_options *options;
|
|
4869
|
+
|
|
4870
|
+
if (!filename) {
|
|
4871
|
+
LXW_WARN("worksheet_insert_image()/_opt(): "
|
|
4872
|
+
"filename must be specified.");
|
|
4873
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4876
|
+
/* Check that the image file exists and can be opened. */
|
|
4877
|
+
image_stream = fopen(filename, "rb");
|
|
4878
|
+
if (!image_stream) {
|
|
4879
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
4880
|
+
"file doesn't exist or can't be opened: %s.",
|
|
4881
|
+
filename);
|
|
4882
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
/* Get the filename from the full path to add to the Drawing object. */
|
|
4886
|
+
short_name = lxw_basename(filename);
|
|
4887
|
+
if (!short_name) {
|
|
4888
|
+
LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
|
|
4889
|
+
"couldn't get basename for file: %s.", filename);
|
|
4890
|
+
fclose(image_stream);
|
|
4891
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4892
|
+
}
|
|
4893
|
+
|
|
4894
|
+
/* Create a new object to hold the image options. */
|
|
4895
|
+
options = calloc(1, sizeof(lxw_image_options));
|
|
4896
|
+
if (!options) {
|
|
4897
|
+
fclose(image_stream);
|
|
4898
|
+
return LXW_ERROR_MEMORY_MALLOC_FAILED;
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
if (user_options) {
|
|
4902
|
+
memcpy(options, user_options, sizeof(lxw_image_options));
|
|
4903
|
+
options->url = lxw_strdup(user_options->url);
|
|
4904
|
+
options->tip = lxw_strdup(user_options->tip);
|
|
4905
|
+
}
|
|
4906
|
+
|
|
4907
|
+
/* Copy other options or set defaults. */
|
|
4908
|
+
options->filename = lxw_strdup(filename);
|
|
4909
|
+
options->short_name = lxw_strdup(short_name);
|
|
4910
|
+
options->stream = image_stream;
|
|
4911
|
+
options->row = row_num;
|
|
4912
|
+
options->col = col_num;
|
|
4913
|
+
|
|
4914
|
+
if (!options->x_scale)
|
|
4915
|
+
options->x_scale = 1;
|
|
4916
|
+
|
|
4917
|
+
if (!options->y_scale)
|
|
4918
|
+
options->y_scale = 1;
|
|
4919
|
+
|
|
4920
|
+
if (_get_image_properties(options) == LXW_NO_ERROR) {
|
|
4921
|
+
STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
|
|
4922
|
+
return LXW_NO_ERROR;
|
|
4923
|
+
}
|
|
4924
|
+
else {
|
|
4925
|
+
free(options);
|
|
4926
|
+
return LXW_ERROR_IMAGE_DIMENSIONS;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
/*
|
|
4931
|
+
* Insert an image into the worksheet.
|
|
4932
|
+
*/
|
|
4933
|
+
lxw_error
|
|
4934
|
+
worksheet_insert_image(lxw_worksheet *self,
|
|
4935
|
+
lxw_row_t row_num, lxw_col_t col_num,
|
|
4936
|
+
const char *filename)
|
|
4937
|
+
{
|
|
4938
|
+
return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL);
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4941
|
+
/*
|
|
4942
|
+
* Insert an chart into the worksheet.
|
|
4943
|
+
*/
|
|
4944
|
+
lxw_error
|
|
4945
|
+
worksheet_insert_chart_opt(lxw_worksheet *self,
|
|
4946
|
+
lxw_row_t row_num, lxw_col_t col_num,
|
|
4947
|
+
lxw_chart *chart, lxw_image_options *user_options)
|
|
4948
|
+
{
|
|
4949
|
+
lxw_image_options *options;
|
|
4950
|
+
lxw_chart_series *series;
|
|
4951
|
+
|
|
4952
|
+
if (!chart) {
|
|
4953
|
+
LXW_WARN("worksheet_insert_chart()/_opt(): chart must be non-NULL.");
|
|
4954
|
+
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
|
4955
|
+
}
|
|
4956
|
+
|
|
4957
|
+
/* Check that the chart isn't being used more than once. */
|
|
4958
|
+
if (chart->in_use) {
|
|
4959
|
+
LXW_WARN("worksheet_insert_chart()/_opt(): the same chart object "
|
|
4960
|
+
"cannot be inserted in a worksheet more than once.");
|
|
4961
|
+
|
|
4962
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
/* Check that the chart has a data series. */
|
|
4966
|
+
if (STAILQ_EMPTY(chart->series_list)) {
|
|
4967
|
+
LXW_WARN
|
|
4968
|
+
("worksheet_insert_chart()/_opt(): chart must have a series.");
|
|
4969
|
+
|
|
4970
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4973
|
+
/* Check that the chart has a 'values' series. */
|
|
4974
|
+
STAILQ_FOREACH(series, chart->series_list, list_pointers) {
|
|
4975
|
+
if (!series->values->formula && !series->values->sheetname) {
|
|
4976
|
+
LXW_WARN("worksheet_insert_chart()/_opt(): chart must have a "
|
|
4977
|
+
"'values' series.");
|
|
4978
|
+
|
|
4979
|
+
return LXW_ERROR_PARAMETER_VALIDATION;
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4983
|
+
/* Create a new object to hold the chart image options. */
|
|
4984
|
+
options = calloc(1, sizeof(lxw_image_options));
|
|
4985
|
+
RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
|
4986
|
+
|
|
4987
|
+
if (user_options)
|
|
4988
|
+
memcpy(options, user_options, sizeof(lxw_image_options));
|
|
4989
|
+
|
|
4990
|
+
/* Copy other options or set defaults. */
|
|
4991
|
+
options->row = row_num;
|
|
4992
|
+
options->col = col_num;
|
|
4993
|
+
|
|
4994
|
+
/* TODO. Read defaults from chart. */
|
|
4995
|
+
options->width = 480;
|
|
4996
|
+
options->height = 288;
|
|
4997
|
+
|
|
4998
|
+
if (!options->x_scale)
|
|
4999
|
+
options->x_scale = 1;
|
|
5000
|
+
|
|
5001
|
+
if (!options->y_scale)
|
|
5002
|
+
options->y_scale = 1;
|
|
5003
|
+
|
|
5004
|
+
/* Store chart references so they can be ordered in the workbook. */
|
|
5005
|
+
options->chart = chart;
|
|
5006
|
+
|
|
5007
|
+
STAILQ_INSERT_TAIL(self->chart_data, options, list_pointers);
|
|
5008
|
+
|
|
5009
|
+
chart->in_use = LXW_TRUE;
|
|
5010
|
+
|
|
5011
|
+
return LXW_NO_ERROR;
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
/*
|
|
5015
|
+
* Insert an image into the worksheet.
|
|
5016
|
+
*/
|
|
5017
|
+
lxw_error
|
|
5018
|
+
worksheet_insert_chart(lxw_worksheet *self,
|
|
5019
|
+
lxw_row_t row_num, lxw_col_t col_num, lxw_chart *chart)
|
|
5020
|
+
{
|
|
5021
|
+
return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
|
|
5022
|
+
}
|