/src/libxlsxwriter/src/worksheet.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * worksheet - A library for creating Excel XLSX worksheet files. |
3 | | * |
4 | | * Used in conjunction with the libxlsxwriter library. |
5 | | * |
6 | | * SPDX-License-Identifier: BSD-2-Clause |
7 | | * Copyright 2014-2026, John McNamara, jmcnamara@cpan.org. |
8 | | * |
9 | | */ |
10 | | |
11 | | #ifdef USE_FMEMOPEN |
12 | | #define _POSIX_C_SOURCE 200809L |
13 | | #endif |
14 | | |
15 | | #include "xlsxwriter/xmlwriter.h" |
16 | | #include "xlsxwriter/worksheet.h" |
17 | | #include "xlsxwriter/format.h" |
18 | | #include "xlsxwriter/utility.h" |
19 | | |
20 | | #ifdef USE_OPENSSL_MD5 |
21 | | #include <openssl/md5.h> |
22 | | #else |
23 | | #ifndef USE_NO_MD5 |
24 | | #include "xlsxwriter/third_party/md5.h" |
25 | | #endif |
26 | | #endif |
27 | | |
28 | 66.9k | #define LXW_STR_MAX 32767 |
29 | 0 | #define LXW_BUFFER_SIZE 4096 |
30 | 0 | #define LXW_PRINT_ACROSS 1 |
31 | 0 | #define LXW_VALIDATION_MAX_TITLE_LENGTH 32 |
32 | 0 | #define LXW_VALIDATION_MAX_STRING_LENGTH 255 |
33 | 0 | #define LXW_THIS_ROW "[#This Row]," |
34 | | /* |
35 | | * Forward declarations. |
36 | | */ |
37 | | STATIC void _worksheet_write_rows(lxw_worksheet *self); |
38 | | STATIC int _row_cmp(lxw_row *row1, lxw_row *row2); |
39 | | STATIC int _cell_cmp(lxw_cell *cell1, lxw_cell *cell2); |
40 | | STATIC int _drawing_rel_id_cmp(lxw_drawing_rel_id *tuple1, |
41 | | lxw_drawing_rel_id *tuple2); |
42 | | STATIC int _cond_format_hash_cmp(lxw_cond_format_hash_element *elem_1, |
43 | | lxw_cond_format_hash_element *elem_2); |
44 | | |
45 | | #ifndef __clang_analyzer__ |
46 | 95.7k | LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); Unexecuted instantiation: worksheet.c:lxw_table_rows_RB_FIND worksheet.c:lxw_table_rows_RB_MINMAX Line | Count | Source | 46 | | LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); |
worksheet.c:lxw_table_rows_RB_REMOVE Line | Count | Source | 46 | | LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); |
worksheet.c:lxw_table_rows_RB_REMOVE_COLOR Line | Count | Source | 46 | | LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); |
worksheet.c:lxw_table_rows_RB_INSERT Line | Count | Source | 46 | | LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp); |
Unexecuted instantiation: worksheet.c:lxw_table_rows_RB_INSERT_COLOR |
47 | 1.18M | LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); Unexecuted instantiation: worksheet.c:lxw_table_cells_RB_FIND worksheet.c:lxw_table_cells_RB_MINMAX Line | Count | Source | 47 | | LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); |
worksheet.c:lxw_table_cells_RB_REMOVE Line | Count | Source | 47 | | LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); |
worksheet.c:lxw_table_cells_RB_REMOVE_COLOR Line | Count | Source | 47 | | LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); |
worksheet.c:lxw_table_cells_RB_INSERT Line | Count | Source | 47 | | LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp); |
Unexecuted instantiation: worksheet.c:lxw_table_cells_RB_INSERT_COLOR |
48 | 1.18M | LXW_RB_GENERATE_DRAWING_REL_IDS(lxw_drawing_rel_ids, lxw_drawing_rel_id, worksheet.c:lxw_drawing_rel_ids_RB_MINMAX Line | Count | Source | 48 | | LXW_RB_GENERATE_DRAWING_REL_IDS(lxw_drawing_rel_ids, lxw_drawing_rel_id, |
Unexecuted instantiation: worksheet.c:lxw_drawing_rel_ids_RB_REMOVE Unexecuted instantiation: worksheet.c:lxw_drawing_rel_ids_RB_REMOVE_COLOR Unexecuted instantiation: worksheet.c:lxw_drawing_rel_ids_RB_FIND Unexecuted instantiation: worksheet.c:lxw_drawing_rel_ids_RB_INSERT Unexecuted instantiation: worksheet.c:lxw_drawing_rel_ids_RB_INSERT_COLOR |
49 | 1.30k | tree_pointers, _drawing_rel_id_cmp); |
50 | 1.30k | LXW_RB_GENERATE_VML_DRAWING_REL_IDS(lxw_vml_drawing_rel_ids, worksheet.c:lxw_vml_drawing_rel_ids_RB_MINMAX Line | Count | Source | 50 | | LXW_RB_GENERATE_VML_DRAWING_REL_IDS(lxw_vml_drawing_rel_ids, |
Unexecuted instantiation: worksheet.c:lxw_vml_drawing_rel_ids_RB_REMOVE Unexecuted instantiation: worksheet.c:lxw_vml_drawing_rel_ids_RB_REMOVE_COLOR Unexecuted instantiation: worksheet.c:lxw_vml_drawing_rel_ids_RB_FIND Unexecuted instantiation: worksheet.c:lxw_vml_drawing_rel_ids_RB_INSERT Unexecuted instantiation: worksheet.c:lxw_vml_drawing_rel_ids_RB_INSERT_COLOR |
51 | 1.30k | lxw_drawing_rel_id, tree_pointers, |
52 | 1.30k | _drawing_rel_id_cmp); |
53 | 2.61k | LXW_RB_GENERATE_COND_FORMAT_HASH(lxw_cond_format_hash, worksheet.c:lxw_cond_format_hash_RB_MINMAX Line | Count | Source | 53 | | LXW_RB_GENERATE_COND_FORMAT_HASH(lxw_cond_format_hash, |
Unexecuted instantiation: worksheet.c:lxw_cond_format_hash_RB_REMOVE Unexecuted instantiation: worksheet.c:lxw_cond_format_hash_RB_REMOVE_COLOR Unexecuted instantiation: worksheet.c:lxw_cond_format_hash_RB_FIND Unexecuted instantiation: worksheet.c:lxw_cond_format_hash_RB_INSERT Unexecuted instantiation: worksheet.c:lxw_cond_format_hash_RB_INSERT_COLOR |
54 | 2.61k | lxw_cond_format_hash_element, tree_pointers, |
55 | 2.61k | _cond_format_hash_cmp); |
56 | 2.61k | #endif |
57 | 2.61k | |
58 | 2.61k | /***************************************************************************** |
59 | 2.61k | * |
60 | 2.61k | * Private functions. |
61 | 2.61k | * |
62 | 2.61k | ****************************************************************************/ |
63 | 2.61k | |
64 | 2.61k | /* |
65 | 2.61k | * Find but don't create a row object for a given row number. |
66 | 2.61k | */ |
67 | 2.61k | lxw_row * |
68 | 2.61k | lxw_worksheet_find_row(lxw_worksheet *self, lxw_row_t row_num) |
69 | 2.61k | { |
70 | 0 | lxw_row tmp_row; |
71 | |
|
72 | 0 | tmp_row.row_num = row_num; |
73 | |
|
74 | 0 | return RB_FIND(lxw_table_rows, self->table, &tmp_row); |
75 | 0 | } |
76 | | |
77 | | /* |
78 | | * Find but don't create a cell object for a given row object and col number. |
79 | | */ |
80 | | lxw_cell * |
81 | | lxw_worksheet_find_cell_in_row(lxw_row *row, lxw_col_t col_num) |
82 | 0 | { |
83 | 0 | lxw_cell tmp_cell; |
84 | |
|
85 | 0 | if (!row) |
86 | 0 | return NULL; |
87 | | |
88 | 0 | tmp_cell.col_num = col_num; |
89 | |
|
90 | 0 | return RB_FIND(lxw_table_cells, row->cells, &tmp_cell); |
91 | 0 | } |
92 | | |
93 | | /* |
94 | | * Create a new worksheet object. |
95 | | */ |
96 | | lxw_worksheet * |
97 | | lxw_worksheet_new(lxw_worksheet_init_data *init_data) |
98 | 1.30k | { |
99 | 1.30k | lxw_worksheet *worksheet = calloc(1, sizeof(lxw_worksheet)); |
100 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error); |
101 | | |
102 | 1.30k | worksheet->table = calloc(1, sizeof(struct lxw_table_rows)); |
103 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->table, mem_error); |
104 | 1.30k | RB_INIT(worksheet->table); |
105 | | |
106 | 1.30k | worksheet->hyperlinks = calloc(1, sizeof(struct lxw_table_rows)); |
107 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->hyperlinks, mem_error); |
108 | 1.30k | RB_INIT(worksheet->hyperlinks); |
109 | | |
110 | 1.30k | worksheet->comments = calloc(1, sizeof(struct lxw_table_rows)); |
111 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->comments, mem_error); |
112 | 1.30k | RB_INIT(worksheet->comments); |
113 | | |
114 | | /* Initialize the cached rows. */ |
115 | 1.30k | worksheet->table->cached_row_num = LXW_ROW_MAX + 1; |
116 | 1.30k | worksheet->hyperlinks->cached_row_num = LXW_ROW_MAX + 1; |
117 | 1.30k | worksheet->comments->cached_row_num = LXW_ROW_MAX + 1; |
118 | | |
119 | 1.30k | if (init_data && init_data->optimize) { |
120 | 0 | worksheet->array = calloc(LXW_COL_MAX, sizeof(struct lxw_cell *)); |
121 | 0 | GOTO_LABEL_ON_MEM_ERROR(worksheet->array, mem_error); |
122 | 0 | } |
123 | | |
124 | 1.30k | worksheet->col_options = |
125 | 1.30k | calloc(LXW_COL_META_MAX, sizeof(lxw_col_options *)); |
126 | 1.30k | worksheet->col_options_max = LXW_COL_META_MAX; |
127 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->col_options, mem_error); |
128 | | |
129 | 1.30k | worksheet->col_formats = calloc(LXW_COL_META_MAX, sizeof(lxw_format *)); |
130 | 1.30k | worksheet->col_formats_max = LXW_COL_META_MAX; |
131 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->col_formats, mem_error); |
132 | | |
133 | 1.30k | worksheet->optimize_row = calloc(1, sizeof(struct lxw_row)); |
134 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_row, mem_error); |
135 | 1.30k | worksheet->optimize_row->height = LXW_DEF_ROW_HEIGHT; |
136 | | |
137 | 1.30k | worksheet->merged_ranges = calloc(1, sizeof(struct lxw_merged_ranges)); |
138 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->merged_ranges, mem_error); |
139 | 1.30k | STAILQ_INIT(worksheet->merged_ranges); |
140 | | |
141 | 1.30k | worksheet->image_props = calloc(1, sizeof(struct lxw_image_props)); |
142 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->image_props, mem_error); |
143 | 1.30k | STAILQ_INIT(worksheet->image_props); |
144 | | |
145 | 1.30k | worksheet->embedded_image_props = |
146 | 1.30k | calloc(1, sizeof(struct lxw_embedded_image_props)); |
147 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->embedded_image_props, mem_error); |
148 | 1.30k | STAILQ_INIT(worksheet->embedded_image_props); |
149 | | |
150 | 1.30k | worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_props)); |
151 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error); |
152 | 1.30k | STAILQ_INIT(worksheet->chart_data); |
153 | | |
154 | 1.30k | worksheet->comment_objs = calloc(1, sizeof(struct lxw_comment_objs)); |
155 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->comment_objs, mem_error); |
156 | 1.30k | STAILQ_INIT(worksheet->comment_objs); |
157 | | |
158 | 1.30k | worksheet->header_image_objs = calloc(1, sizeof(struct lxw_comment_objs)); |
159 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->header_image_objs, mem_error); |
160 | 1.30k | STAILQ_INIT(worksheet->header_image_objs); |
161 | | |
162 | 1.30k | worksheet->button_objs = calloc(1, sizeof(struct lxw_comment_objs)); |
163 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->button_objs, mem_error); |
164 | 1.30k | STAILQ_INIT(worksheet->button_objs); |
165 | | |
166 | 1.30k | worksheet->selections = calloc(1, sizeof(struct lxw_selections)); |
167 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error); |
168 | 1.30k | STAILQ_INIT(worksheet->selections); |
169 | | |
170 | 1.30k | worksheet->data_validations = |
171 | 1.30k | calloc(1, sizeof(struct lxw_data_validations)); |
172 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error); |
173 | 1.30k | STAILQ_INIT(worksheet->data_validations); |
174 | | |
175 | 1.30k | worksheet->table_objs = calloc(1, sizeof(struct lxw_table_objs)); |
176 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->table_objs, mem_error); |
177 | 1.30k | STAILQ_INIT(worksheet->table_objs); |
178 | | |
179 | 1.30k | worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples)); |
180 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error); |
181 | 1.30k | STAILQ_INIT(worksheet->external_hyperlinks); |
182 | | |
183 | 1.30k | worksheet->external_drawing_links = |
184 | 1.30k | calloc(1, sizeof(struct lxw_rel_tuples)); |
185 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->external_drawing_links, mem_error); |
186 | 1.30k | STAILQ_INIT(worksheet->external_drawing_links); |
187 | | |
188 | 1.30k | worksheet->drawing_links = calloc(1, sizeof(struct lxw_rel_tuples)); |
189 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_links, mem_error); |
190 | 1.30k | STAILQ_INIT(worksheet->drawing_links); |
191 | | |
192 | 1.30k | worksheet->vml_drawing_links = calloc(1, sizeof(struct lxw_rel_tuples)); |
193 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_links, mem_error); |
194 | 1.30k | STAILQ_INIT(worksheet->vml_drawing_links); |
195 | | |
196 | 1.30k | worksheet->external_table_links = |
197 | 1.30k | calloc(1, sizeof(struct lxw_rel_tuples)); |
198 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->external_table_links, mem_error); |
199 | 1.30k | STAILQ_INIT(worksheet->external_table_links); |
200 | | |
201 | 1.30k | if (init_data && init_data->optimize) { |
202 | 0 | FILE *tmpfile; |
203 | |
|
204 | 0 | worksheet->optimize_buffer = NULL; |
205 | 0 | worksheet->optimize_buffer_size = 0; |
206 | 0 | tmpfile = lxw_get_filehandle(&worksheet->optimize_buffer, |
207 | 0 | &worksheet->optimize_buffer_size, |
208 | 0 | init_data->tmpdir); |
209 | 0 | if (!tmpfile) { |
210 | 0 | LXW_ERROR("Error creating tmpfile() for worksheet in " |
211 | 0 | "'constant_memory' mode."); |
212 | 0 | goto mem_error; |
213 | 0 | } |
214 | | |
215 | 0 | worksheet->optimize_tmpfile = tmpfile; |
216 | 0 | GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_tmpfile, mem_error); |
217 | 0 | worksheet->file = worksheet->optimize_tmpfile; |
218 | 0 | } |
219 | | |
220 | 1.30k | worksheet->drawing_rel_ids = |
221 | 1.30k | calloc(1, sizeof(struct lxw_drawing_rel_ids)); |
222 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_rel_ids, mem_error); |
223 | 1.30k | RB_INIT(worksheet->drawing_rel_ids); |
224 | | |
225 | 1.30k | worksheet->vml_drawing_rel_ids = |
226 | 1.30k | calloc(1, sizeof(struct lxw_vml_drawing_rel_ids)); |
227 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->vml_drawing_rel_ids, mem_error); |
228 | 1.30k | RB_INIT(worksheet->vml_drawing_rel_ids); |
229 | | |
230 | 1.30k | worksheet->conditional_formats = |
231 | 1.30k | calloc(1, sizeof(struct lxw_cond_format_hash)); |
232 | 1.30k | GOTO_LABEL_ON_MEM_ERROR(worksheet->conditional_formats, mem_error); |
233 | 1.30k | RB_INIT(worksheet->conditional_formats); |
234 | | |
235 | | /* Initialize the worksheet dimensions. */ |
236 | 1.30k | worksheet->dim_rowmax = 0; |
237 | 1.30k | worksheet->dim_colmax = 0; |
238 | 1.30k | worksheet->dim_rowmin = LXW_ROW_MAX; |
239 | 1.30k | worksheet->dim_colmin = LXW_COL_MAX; |
240 | | |
241 | 1.30k | worksheet->default_row_height = LXW_DEF_ROW_HEIGHT; |
242 | 1.30k | worksheet->default_row_pixels = 20; |
243 | 1.30k | worksheet->default_col_pixels = 64; |
244 | | |
245 | | /* Initialize the page setup properties. */ |
246 | 1.30k | worksheet->fit_height = 0; |
247 | 1.30k | worksheet->fit_width = 0; |
248 | 1.30k | worksheet->page_start = 0; |
249 | 1.30k | worksheet->print_scale = 100; |
250 | 1.30k | worksheet->fit_page = 0; |
251 | 1.30k | worksheet->orientation = LXW_TRUE; |
252 | 1.30k | worksheet->page_order = 0; |
253 | 1.30k | worksheet->page_setup_changed = LXW_FALSE; |
254 | 1.30k | worksheet->page_view = LXW_FALSE; |
255 | 1.30k | worksheet->paper_size = 0; |
256 | 1.30k | worksheet->vertical_dpi = 0; |
257 | 1.30k | worksheet->horizontal_dpi = 0; |
258 | 1.30k | worksheet->margin_left = 0.7; |
259 | 1.30k | worksheet->margin_right = 0.7; |
260 | 1.30k | worksheet->margin_top = 0.75; |
261 | 1.30k | worksheet->margin_bottom = 0.75; |
262 | 1.30k | worksheet->margin_header = 0.3; |
263 | 1.30k | worksheet->margin_footer = 0.3; |
264 | 1.30k | worksheet->print_gridlines = 0; |
265 | 1.30k | worksheet->screen_gridlines = 1; |
266 | 1.30k | worksheet->print_options_changed = 0; |
267 | 1.30k | worksheet->zoom = 100; |
268 | 1.30k | worksheet->zoom_scale_normal = LXW_TRUE; |
269 | 1.30k | worksheet->show_zeros = LXW_TRUE; |
270 | 1.30k | worksheet->outline_on = LXW_TRUE; |
271 | 1.30k | worksheet->outline_style = LXW_TRUE; |
272 | 1.30k | worksheet->outline_below = LXW_TRUE; |
273 | 1.30k | worksheet->outline_right = LXW_FALSE; |
274 | 1.30k | worksheet->tab_color = LXW_COLOR_UNSET; |
275 | 1.30k | worksheet->max_url_length = 2079; |
276 | 1.30k | worksheet->comment_display_default = LXW_COMMENT_DISPLAY_HIDDEN; |
277 | | |
278 | 1.30k | worksheet->header_footer_objs[0] = &worksheet->header_left_object_props; |
279 | 1.30k | worksheet->header_footer_objs[1] = &worksheet->header_center_object_props; |
280 | 1.30k | worksheet->header_footer_objs[2] = &worksheet->header_right_object_props; |
281 | 1.30k | worksheet->header_footer_objs[3] = &worksheet->footer_left_object_props; |
282 | 1.30k | worksheet->header_footer_objs[4] = &worksheet->footer_center_object_props; |
283 | 1.30k | worksheet->header_footer_objs[5] = &worksheet->footer_right_object_props; |
284 | | |
285 | 1.30k | if (init_data) { |
286 | 1.30k | worksheet->name = init_data->name; |
287 | 1.30k | worksheet->quoted_name = init_data->quoted_name; |
288 | 1.30k | worksheet->tmpdir = init_data->tmpdir; |
289 | 1.30k | worksheet->index = init_data->index; |
290 | 1.30k | worksheet->hidden = init_data->hidden; |
291 | 1.30k | worksheet->sst = init_data->sst; |
292 | 1.30k | worksheet->optimize = init_data->optimize; |
293 | 1.30k | worksheet->active_sheet = init_data->active_sheet; |
294 | 1.30k | worksheet->first_sheet = init_data->first_sheet; |
295 | 1.30k | worksheet->default_url_format = init_data->default_url_format; |
296 | 1.30k | worksheet->max_url_length = init_data->max_url_length; |
297 | 1.30k | worksheet->use_1904_epoch = init_data->use_1904_epoch; |
298 | 1.30k | } |
299 | | |
300 | 1.30k | return worksheet; |
301 | | |
302 | 0 | mem_error: |
303 | 0 | lxw_worksheet_free(worksheet); |
304 | 0 | return NULL; |
305 | 1.30k | } |
306 | | |
307 | | /* |
308 | | * Free vml object. |
309 | | */ |
310 | | STATIC void |
311 | | _free_vml_object(lxw_vml_obj *vml_obj) |
312 | 66.9k | { |
313 | 66.9k | if (!vml_obj) |
314 | 66.9k | return; |
315 | | |
316 | 0 | free(vml_obj->author); |
317 | 0 | free(vml_obj->font_name); |
318 | 0 | free(vml_obj->text); |
319 | 0 | free(vml_obj->image_position); |
320 | 0 | free(vml_obj->name); |
321 | 0 | free(vml_obj->macro); |
322 | |
|
323 | 0 | free(vml_obj); |
324 | 0 | } |
325 | | |
326 | | /* |
327 | | * Free autofilter rule object. |
328 | | */ |
329 | | STATIC void |
330 | | _free_filter_rule(lxw_filter_rule_obj *rule_obj) |
331 | 0 | { |
332 | 0 | uint16_t i; |
333 | |
|
334 | 0 | if (!rule_obj) |
335 | 0 | return; |
336 | | |
337 | 0 | free(rule_obj->value1_string); |
338 | 0 | free(rule_obj->value2_string); |
339 | |
|
340 | 0 | if (rule_obj->list) { |
341 | 0 | for (i = 0; i < rule_obj->num_list_filters; i++) |
342 | 0 | free(rule_obj->list[i]); |
343 | |
|
344 | 0 | free(rule_obj->list); |
345 | 0 | } |
346 | |
|
347 | 0 | free(rule_obj); |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * Free autofilter rules. |
352 | | */ |
353 | | STATIC void |
354 | | _free_filter_rules(lxw_worksheet *worksheet) |
355 | 1.30k | { |
356 | 1.30k | uint16_t i; |
357 | | |
358 | 1.30k | if (!worksheet->filter_rules) |
359 | 1.30k | return; |
360 | | |
361 | 0 | for (i = 0; i < worksheet->num_filter_rules; i++) |
362 | 0 | _free_filter_rule(worksheet->filter_rules[i]); |
363 | |
|
364 | 0 | free(worksheet->filter_rules); |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Free a worksheet cell. |
369 | | */ |
370 | | STATIC void |
371 | | _free_cell(lxw_cell *cell) |
372 | 66.9k | { |
373 | 66.9k | if (!cell) |
374 | 0 | return; |
375 | | |
376 | 66.9k | if (cell->type != NUMBER_CELL && cell->type != STRING_CELL |
377 | 0 | && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL |
378 | 0 | && cell->type != ERROR_CELL) { |
379 | |
|
380 | 0 | free((void *) cell->u.string); |
381 | 0 | } |
382 | | |
383 | 66.9k | free(cell->user_data1); |
384 | 66.9k | free(cell->user_data2); |
385 | | |
386 | 66.9k | _free_vml_object(cell->comment); |
387 | | |
388 | 66.9k | free(cell); |
389 | 66.9k | } |
390 | | |
391 | | /* |
392 | | * Free a worksheet row. |
393 | | */ |
394 | | STATIC void |
395 | | _free_row(lxw_row *row) |
396 | 5.73k | { |
397 | 5.73k | lxw_cell *cell; |
398 | 5.73k | lxw_cell *next_cell; |
399 | | |
400 | 5.73k | if (!row) |
401 | 0 | return; |
402 | | |
403 | 72.6k | for (cell = RB_MIN(lxw_table_cells, row->cells); cell; cell = next_cell) { |
404 | 66.9k | next_cell = RB_NEXT(lxw_table_cells, row->cells, cell); |
405 | 66.9k | RB_REMOVE(lxw_table_cells, row->cells, cell); |
406 | 66.9k | _free_cell(cell); |
407 | 66.9k | } |
408 | | |
409 | 5.73k | free(row->cells); |
410 | 5.73k | free(row); |
411 | 5.73k | } |
412 | | |
413 | | /* |
414 | | * Free a worksheet image_options. |
415 | | */ |
416 | | STATIC void |
417 | | _free_object_properties(lxw_object_properties *object_property) |
418 | 0 | { |
419 | 0 | if (!object_property) |
420 | 0 | return; |
421 | | |
422 | 0 | free(object_property->filename); |
423 | 0 | free(object_property->description); |
424 | 0 | free(object_property->extension); |
425 | 0 | free(object_property->url); |
426 | 0 | free(object_property->tip); |
427 | 0 | free(object_property->image_buffer); |
428 | 0 | free(object_property->md5); |
429 | 0 | free(object_property->image_position); |
430 | 0 | free(object_property); |
431 | 0 | object_property = NULL; |
432 | 0 | } |
433 | | |
434 | | /* |
435 | | * Free a worksheet data_validation. |
436 | | */ |
437 | | STATIC void |
438 | | _free_data_validation(lxw_data_val_obj *data_validation) |
439 | 0 | { |
440 | 0 | if (!data_validation) |
441 | 0 | return; |
442 | | |
443 | 0 | free(data_validation->value_formula); |
444 | 0 | free(data_validation->maximum_formula); |
445 | 0 | free(data_validation->input_title); |
446 | 0 | free(data_validation->input_message); |
447 | 0 | free(data_validation->error_title); |
448 | 0 | free(data_validation->error_message); |
449 | 0 | free(data_validation->minimum_formula); |
450 | |
|
451 | 0 | free(data_validation); |
452 | 0 | } |
453 | | |
454 | | /* |
455 | | * Free a worksheet conditional format obj. |
456 | | */ |
457 | | STATIC void |
458 | | _free_cond_format(lxw_cond_format_obj *cond_format) |
459 | 0 | { |
460 | 0 | if (!cond_format) |
461 | 0 | return; |
462 | | |
463 | 0 | free(cond_format->min_value_string); |
464 | 0 | free(cond_format->mid_value_string); |
465 | 0 | free(cond_format->max_value_string); |
466 | 0 | free(cond_format->type_string); |
467 | 0 | free(cond_format->guid); |
468 | |
|
469 | 0 | free(cond_format); |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | * Free a relationship structure. |
474 | | */ |
475 | | STATIC void |
476 | | _free_relationship(lxw_rel_tuple *relationship) |
477 | 5.22k | { |
478 | 5.22k | if (!relationship) |
479 | 5.22k | return; |
480 | | |
481 | 0 | free(relationship->type); |
482 | 0 | free(relationship->target); |
483 | 0 | free(relationship->target_mode); |
484 | |
|
485 | 0 | free(relationship); |
486 | 0 | } |
487 | | |
488 | | /* |
489 | | * Free a worksheet table column object. |
490 | | */ |
491 | | STATIC void |
492 | | _free_worksheet_table_column(lxw_table_column *column) |
493 | 0 | { |
494 | 0 | if (!column) |
495 | 0 | return; |
496 | | |
497 | 0 | free((void *) column->header); |
498 | 0 | free((void *) column->formula); |
499 | 0 | free((void *) column->total_string); |
500 | |
|
501 | 0 | free(column); |
502 | 0 | } |
503 | | |
504 | | /* |
505 | | * Free a worksheet table object. |
506 | | */ |
507 | | STATIC void |
508 | | _free_worksheet_table(lxw_table_obj *table) |
509 | 0 | { |
510 | 0 | uint16_t i; |
511 | |
|
512 | 0 | if (!table) |
513 | 0 | return; |
514 | | |
515 | 0 | for (i = 0; i < table->num_cols; i++) |
516 | 0 | _free_worksheet_table_column(table->columns[i]); |
517 | |
|
518 | 0 | free(table->name); |
519 | 0 | free(table->total_string); |
520 | 0 | free(table->columns); |
521 | |
|
522 | 0 | free(table); |
523 | 0 | } |
524 | | |
525 | | /* |
526 | | * Free a worksheet object. |
527 | | */ |
528 | | void |
529 | | lxw_worksheet_free(lxw_worksheet *worksheet) |
530 | 1.30k | { |
531 | 1.30k | lxw_row *row; |
532 | 1.30k | lxw_row *next_row; |
533 | 1.30k | lxw_col_t col; |
534 | 1.30k | lxw_merged_range *merged_range; |
535 | 1.30k | lxw_object_properties *object_props; |
536 | 1.30k | lxw_vml_obj *vml_obj; |
537 | 1.30k | lxw_selection *selection; |
538 | 1.30k | lxw_data_val_obj *data_validation; |
539 | 1.30k | lxw_rel_tuple *relationship; |
540 | 1.30k | lxw_cond_format_obj *cond_format; |
541 | 1.30k | lxw_table_obj *table_obj; |
542 | 1.30k | struct lxw_drawing_rel_id *drawing_rel_id; |
543 | 1.30k | struct lxw_drawing_rel_id *next_drawing_rel_id; |
544 | 1.30k | struct lxw_cond_format_hash_element *cond_format_elem; |
545 | 1.30k | struct lxw_cond_format_hash_element *next_cond_format_elem; |
546 | | |
547 | 1.30k | if (!worksheet) |
548 | 0 | return; |
549 | | |
550 | 1.30k | if (worksheet->col_options) { |
551 | 168k | for (col = 0; col < worksheet->col_options_max; col++) { |
552 | 167k | if (worksheet->col_options[col]) |
553 | 0 | free(worksheet->col_options[col]); |
554 | 167k | } |
555 | 1.30k | } |
556 | | |
557 | 1.30k | free(worksheet->col_options); |
558 | 1.30k | free(worksheet->col_sizes); |
559 | 1.30k | free(worksheet->col_formats); |
560 | | |
561 | 1.30k | if (worksheet->table) { |
562 | 7.03k | for (row = RB_MIN(lxw_table_rows, worksheet->table); row; |
563 | 5.73k | row = next_row) { |
564 | | |
565 | 5.73k | next_row = RB_NEXT(lxw_table_rows, worksheet->table, row); |
566 | 5.73k | RB_REMOVE(lxw_table_rows, worksheet->table, row); |
567 | 5.73k | _free_row(row); |
568 | 5.73k | } |
569 | | |
570 | 1.30k | free(worksheet->table); |
571 | 1.30k | } |
572 | | |
573 | 1.30k | if (worksheet->hyperlinks) { |
574 | 1.30k | for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row; |
575 | 1.30k | row = next_row) { |
576 | |
|
577 | 0 | next_row = RB_NEXT(lxw_table_rows, worksheet->hyperlinks, row); |
578 | 0 | RB_REMOVE(lxw_table_rows, worksheet->hyperlinks, row); |
579 | 0 | _free_row(row); |
580 | 0 | } |
581 | | |
582 | 1.30k | free(worksheet->hyperlinks); |
583 | 1.30k | } |
584 | | |
585 | 1.30k | if (worksheet->comments) { |
586 | 1.30k | for (row = RB_MIN(lxw_table_rows, worksheet->comments); row; |
587 | 1.30k | row = next_row) { |
588 | |
|
589 | 0 | next_row = RB_NEXT(lxw_table_rows, worksheet->comments, row); |
590 | 0 | RB_REMOVE(lxw_table_rows, worksheet->comments, row); |
591 | 0 | _free_row(row); |
592 | 0 | } |
593 | | |
594 | 1.30k | free(worksheet->comments); |
595 | 1.30k | } |
596 | | |
597 | 1.30k | if (worksheet->merged_ranges) { |
598 | 1.30k | while (!STAILQ_EMPTY(worksheet->merged_ranges)) { |
599 | 0 | merged_range = STAILQ_FIRST(worksheet->merged_ranges); |
600 | 0 | STAILQ_REMOVE_HEAD(worksheet->merged_ranges, list_pointers); |
601 | 0 | free(merged_range); |
602 | 0 | } |
603 | | |
604 | 1.30k | free(worksheet->merged_ranges); |
605 | 1.30k | } |
606 | | |
607 | 1.30k | if (worksheet->image_props) { |
608 | 1.30k | while (!STAILQ_EMPTY(worksheet->image_props)) { |
609 | 0 | object_props = STAILQ_FIRST(worksheet->image_props); |
610 | 0 | STAILQ_REMOVE_HEAD(worksheet->image_props, list_pointers); |
611 | 0 | _free_object_properties(object_props); |
612 | 0 | } |
613 | | |
614 | 1.30k | free(worksheet->image_props); |
615 | 1.30k | } |
616 | | |
617 | 1.30k | if (worksheet->embedded_image_props) { |
618 | 1.30k | while (!STAILQ_EMPTY(worksheet->embedded_image_props)) { |
619 | 0 | object_props = STAILQ_FIRST(worksheet->embedded_image_props); |
620 | 0 | STAILQ_REMOVE_HEAD(worksheet->embedded_image_props, |
621 | 0 | list_pointers); |
622 | 0 | _free_object_properties(object_props); |
623 | 0 | } |
624 | | |
625 | 1.30k | free(worksheet->embedded_image_props); |
626 | 1.30k | } |
627 | | |
628 | 1.30k | if (worksheet->chart_data) { |
629 | 1.30k | while (!STAILQ_EMPTY(worksheet->chart_data)) { |
630 | 0 | object_props = STAILQ_FIRST(worksheet->chart_data); |
631 | 0 | STAILQ_REMOVE_HEAD(worksheet->chart_data, list_pointers); |
632 | 0 | _free_object_properties(object_props); |
633 | 0 | } |
634 | | |
635 | 1.30k | free(worksheet->chart_data); |
636 | 1.30k | } |
637 | | |
638 | | /* Just free the list. The list objects are freed from the RB tree. */ |
639 | 1.30k | free(worksheet->comment_objs); |
640 | | |
641 | 1.30k | if (worksheet->header_image_objs) { |
642 | 1.30k | while (!STAILQ_EMPTY(worksheet->header_image_objs)) { |
643 | 0 | vml_obj = STAILQ_FIRST(worksheet->header_image_objs); |
644 | 0 | STAILQ_REMOVE_HEAD(worksheet->header_image_objs, list_pointers); |
645 | 0 | _free_vml_object(vml_obj); |
646 | 0 | } |
647 | | |
648 | 1.30k | free(worksheet->header_image_objs); |
649 | 1.30k | } |
650 | | |
651 | 1.30k | if (worksheet->button_objs) { |
652 | 1.30k | while (!STAILQ_EMPTY(worksheet->button_objs)) { |
653 | 0 | vml_obj = STAILQ_FIRST(worksheet->button_objs); |
654 | 0 | STAILQ_REMOVE_HEAD(worksheet->button_objs, list_pointers); |
655 | 0 | _free_vml_object(vml_obj); |
656 | 0 | } |
657 | | |
658 | 1.30k | free(worksheet->button_objs); |
659 | 1.30k | } |
660 | | |
661 | 1.30k | if (worksheet->selections) { |
662 | 1.30k | while (!STAILQ_EMPTY(worksheet->selections)) { |
663 | 0 | selection = STAILQ_FIRST(worksheet->selections); |
664 | 0 | STAILQ_REMOVE_HEAD(worksheet->selections, list_pointers); |
665 | 0 | free(selection); |
666 | 0 | } |
667 | | |
668 | 1.30k | free(worksheet->selections); |
669 | 1.30k | } |
670 | | |
671 | 1.30k | if (worksheet->table_objs) { |
672 | 1.30k | while (!STAILQ_EMPTY(worksheet->table_objs)) { |
673 | 0 | table_obj = STAILQ_FIRST(worksheet->table_objs); |
674 | 0 | STAILQ_REMOVE_HEAD(worksheet->table_objs, list_pointers); |
675 | 0 | _free_worksheet_table(table_obj); |
676 | 0 | } |
677 | | |
678 | 1.30k | free(worksheet->table_objs); |
679 | 1.30k | } |
680 | | |
681 | 1.30k | if (worksheet->data_validations) { |
682 | 1.30k | while (!STAILQ_EMPTY(worksheet->data_validations)) { |
683 | 0 | data_validation = STAILQ_FIRST(worksheet->data_validations); |
684 | 0 | STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers); |
685 | 0 | _free_data_validation(data_validation); |
686 | 0 | } |
687 | | |
688 | 1.30k | free(worksheet->data_validations); |
689 | 1.30k | } |
690 | | |
691 | 1.30k | while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) { |
692 | 0 | relationship = STAILQ_FIRST(worksheet->external_hyperlinks); |
693 | 0 | STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers); |
694 | 0 | _free_relationship(relationship); |
695 | 0 | } |
696 | 1.30k | free(worksheet->external_hyperlinks); |
697 | | |
698 | 1.30k | while (!STAILQ_EMPTY(worksheet->external_drawing_links)) { |
699 | 0 | relationship = STAILQ_FIRST(worksheet->external_drawing_links); |
700 | 0 | STAILQ_REMOVE_HEAD(worksheet->external_drawing_links, list_pointers); |
701 | 0 | _free_relationship(relationship); |
702 | 0 | } |
703 | 1.30k | free(worksheet->external_drawing_links); |
704 | | |
705 | 1.30k | while (!STAILQ_EMPTY(worksheet->drawing_links)) { |
706 | 0 | relationship = STAILQ_FIRST(worksheet->drawing_links); |
707 | 0 | STAILQ_REMOVE_HEAD(worksheet->drawing_links, list_pointers); |
708 | 0 | _free_relationship(relationship); |
709 | 0 | } |
710 | 1.30k | free(worksheet->drawing_links); |
711 | | |
712 | 1.30k | while (!STAILQ_EMPTY(worksheet->vml_drawing_links)) { |
713 | 0 | relationship = STAILQ_FIRST(worksheet->vml_drawing_links); |
714 | 0 | STAILQ_REMOVE_HEAD(worksheet->vml_drawing_links, list_pointers); |
715 | 0 | _free_relationship(relationship); |
716 | 0 | } |
717 | 1.30k | free(worksheet->vml_drawing_links); |
718 | | |
719 | 1.30k | while (!STAILQ_EMPTY(worksheet->external_table_links)) { |
720 | 0 | relationship = STAILQ_FIRST(worksheet->external_table_links); |
721 | 0 | STAILQ_REMOVE_HEAD(worksheet->external_table_links, list_pointers); |
722 | 0 | _free_relationship(relationship); |
723 | 0 | } |
724 | 1.30k | free(worksheet->external_table_links); |
725 | | |
726 | 1.30k | if (worksheet->drawing_rel_ids) { |
727 | 1.30k | for (drawing_rel_id = |
728 | 1.30k | RB_MIN(lxw_drawing_rel_ids, worksheet->drawing_rel_ids); |
729 | 1.30k | drawing_rel_id; drawing_rel_id = next_drawing_rel_id) { |
730 | |
|
731 | 0 | next_drawing_rel_id = |
732 | 0 | RB_NEXT(lxw_drawing_rel_ids, worksheet->drawing_rel_id, |
733 | 0 | drawing_rel_id); |
734 | 0 | RB_REMOVE(lxw_drawing_rel_ids, worksheet->drawing_rel_ids, |
735 | 0 | drawing_rel_id); |
736 | 0 | free(drawing_rel_id->target); |
737 | 0 | free(drawing_rel_id); |
738 | 0 | } |
739 | | |
740 | 1.30k | free(worksheet->drawing_rel_ids); |
741 | 1.30k | } |
742 | | |
743 | 1.30k | if (worksheet->vml_drawing_rel_ids) { |
744 | 1.30k | for (drawing_rel_id = |
745 | 1.30k | RB_MIN(lxw_vml_drawing_rel_ids, worksheet->vml_drawing_rel_ids); |
746 | 1.30k | drawing_rel_id; drawing_rel_id = next_drawing_rel_id) { |
747 | |
|
748 | 0 | next_drawing_rel_id = |
749 | 0 | RB_NEXT(lxw_vml_drawing_rel_ids, worksheet->drawing_rel_id, |
750 | 0 | drawing_rel_id); |
751 | 0 | RB_REMOVE(lxw_vml_drawing_rel_ids, worksheet->vml_drawing_rel_ids, |
752 | 0 | drawing_rel_id); |
753 | 0 | free(drawing_rel_id->target); |
754 | 0 | free(drawing_rel_id); |
755 | 0 | } |
756 | | |
757 | 1.30k | free(worksheet->vml_drawing_rel_ids); |
758 | 1.30k | } |
759 | | |
760 | 1.30k | if (worksheet->conditional_formats) { |
761 | 1.30k | for (cond_format_elem = |
762 | 1.30k | RB_MIN(lxw_cond_format_hash, worksheet->conditional_formats); |
763 | 1.30k | cond_format_elem; cond_format_elem = next_cond_format_elem) { |
764 | |
|
765 | 0 | next_cond_format_elem = RB_NEXT(lxw_cond_format_hash, |
766 | 0 | worksheet->conditional_formats, |
767 | 0 | cond_format_elem); |
768 | 0 | RB_REMOVE(lxw_cond_format_hash, |
769 | 0 | worksheet->conditional_formats, cond_format_elem); |
770 | |
|
771 | 0 | while (!STAILQ_EMPTY(cond_format_elem->cond_formats)) { |
772 | 0 | cond_format = STAILQ_FIRST(cond_format_elem->cond_formats); |
773 | 0 | STAILQ_REMOVE_HEAD(cond_format_elem->cond_formats, |
774 | 0 | list_pointers); |
775 | 0 | _free_cond_format(cond_format); |
776 | 0 | } |
777 | |
|
778 | 0 | free(cond_format_elem->cond_formats); |
779 | 0 | free(cond_format_elem); |
780 | 0 | } |
781 | | |
782 | 1.30k | free(worksheet->conditional_formats); |
783 | 1.30k | } |
784 | | |
785 | 1.30k | _free_relationship(worksheet->external_vml_comment_link); |
786 | 1.30k | _free_relationship(worksheet->external_comment_link); |
787 | 1.30k | _free_relationship(worksheet->external_vml_header_link); |
788 | 1.30k | _free_relationship(worksheet->external_background_link); |
789 | | |
790 | 1.30k | _free_filter_rules(worksheet); |
791 | | |
792 | 1.30k | if (worksheet->array) { |
793 | 0 | for (col = 0; col < LXW_COL_MAX; col++) { |
794 | 0 | _free_cell(worksheet->array[col]); |
795 | 0 | } |
796 | 0 | free(worksheet->array); |
797 | 0 | } |
798 | | |
799 | 1.30k | if (worksheet->optimize_row) |
800 | 1.30k | free(worksheet->optimize_row); |
801 | | |
802 | 1.30k | if (worksheet->drawing) |
803 | 0 | lxw_drawing_free(worksheet->drawing); |
804 | | |
805 | 1.30k | free(worksheet->hbreaks); |
806 | 1.30k | free(worksheet->vbreaks); |
807 | 1.30k | free((void *) worksheet->name); |
808 | 1.30k | free((void *) worksheet->quoted_name); |
809 | 1.30k | free(worksheet->vba_codename); |
810 | 1.30k | free(worksheet->vml_data_id_str); |
811 | 1.30k | free(worksheet->vml_header_id_str); |
812 | 1.30k | free(worksheet->comment_author); |
813 | 1.30k | free(worksheet->ignore_number_stored_as_text); |
814 | 1.30k | free(worksheet->ignore_eval_error); |
815 | 1.30k | free(worksheet->ignore_formula_differs); |
816 | 1.30k | free(worksheet->ignore_formula_range); |
817 | 1.30k | free(worksheet->ignore_formula_unlocked); |
818 | 1.30k | free(worksheet->ignore_empty_cell_reference); |
819 | 1.30k | free(worksheet->ignore_list_data_validation); |
820 | 1.30k | free(worksheet->ignore_calculated_column); |
821 | 1.30k | free(worksheet->ignore_two_digit_text_year); |
822 | 1.30k | free(worksheet->header); |
823 | 1.30k | free(worksheet->footer); |
824 | | |
825 | 1.30k | free(worksheet); |
826 | 1.30k | worksheet = NULL; |
827 | 1.30k | } |
828 | | |
829 | | /* |
830 | | * Create a new worksheet row object. |
831 | | */ |
832 | | STATIC lxw_row * |
833 | | _new_row(lxw_row_t row_num) |
834 | 5.73k | { |
835 | 5.73k | lxw_row *row = calloc(1, sizeof(lxw_row)); |
836 | | |
837 | 5.73k | if (row) { |
838 | 5.73k | row->row_num = row_num; |
839 | 5.73k | row->cells = calloc(1, sizeof(struct lxw_table_cells)); |
840 | 5.73k | row->height = LXW_DEF_ROW_HEIGHT; |
841 | | |
842 | 5.73k | if (row->cells) |
843 | 5.73k | RB_INIT(row->cells); |
844 | 0 | else |
845 | 5.73k | LXW_MEM_ERROR(); |
846 | 5.73k | } |
847 | 0 | else { |
848 | 0 | LXW_MEM_ERROR(); |
849 | 0 | } |
850 | | |
851 | 5.73k | return row; |
852 | 5.73k | } |
853 | | |
854 | | /* |
855 | | * Create a new worksheet number cell object. |
856 | | */ |
857 | | STATIC lxw_cell * |
858 | | _new_number_cell(lxw_row_t row_num, |
859 | | lxw_col_t col_num, double value, lxw_format *format) |
860 | 0 | { |
861 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
862 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
863 | | |
864 | 0 | cell->row_num = row_num; |
865 | 0 | cell->col_num = col_num; |
866 | 0 | cell->type = NUMBER_CELL; |
867 | 0 | cell->format = format; |
868 | 0 | cell->u.number = value; |
869 | |
|
870 | 0 | return cell; |
871 | 0 | } |
872 | | |
873 | | /* |
874 | | * Create a new worksheet string cell object. |
875 | | */ |
876 | | STATIC lxw_cell * |
877 | | _new_string_cell(lxw_row_t row_num, |
878 | | lxw_col_t col_num, int32_t string_id, char *sst_string, |
879 | | lxw_format *format) |
880 | 66.9k | { |
881 | 66.9k | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
882 | 66.9k | RETURN_ON_MEM_ERROR(cell, cell); |
883 | | |
884 | 66.9k | cell->row_num = row_num; |
885 | 66.9k | cell->col_num = col_num; |
886 | 66.9k | cell->type = STRING_CELL; |
887 | 66.9k | cell->format = format; |
888 | 66.9k | cell->u.string_id = string_id; |
889 | 66.9k | cell->sst_string = sst_string; |
890 | | |
891 | 66.9k | return cell; |
892 | 66.9k | } |
893 | | |
894 | | /* |
895 | | * Create a new worksheet inline_string cell object. |
896 | | */ |
897 | | STATIC lxw_cell * |
898 | | _new_inline_string_cell(lxw_row_t row_num, |
899 | | lxw_col_t col_num, char *string, lxw_format *format) |
900 | 0 | { |
901 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
902 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
903 | | |
904 | 0 | cell->row_num = row_num; |
905 | 0 | cell->col_num = col_num; |
906 | 0 | cell->type = INLINE_STRING_CELL; |
907 | 0 | cell->format = format; |
908 | 0 | cell->u.string = string; |
909 | |
|
910 | 0 | return cell; |
911 | 0 | } |
912 | | |
913 | | /* |
914 | | * Create a new worksheet inline_string cell object for rich strings. |
915 | | */ |
916 | | STATIC lxw_cell * |
917 | | _new_inline_rich_string_cell(lxw_row_t row_num, |
918 | | lxw_col_t col_num, const char *string, |
919 | | lxw_format *format) |
920 | 0 | { |
921 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
922 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
923 | | |
924 | 0 | cell->row_num = row_num; |
925 | 0 | cell->col_num = col_num; |
926 | 0 | cell->type = INLINE_RICH_STRING_CELL; |
927 | 0 | cell->format = format; |
928 | 0 | cell->u.string = string; |
929 | |
|
930 | 0 | return cell; |
931 | 0 | } |
932 | | |
933 | | /* |
934 | | * Create a new worksheet formula cell object. |
935 | | */ |
936 | | STATIC lxw_cell * |
937 | | _new_formula_cell(lxw_row_t row_num, |
938 | | lxw_col_t col_num, char *formula, lxw_format *format) |
939 | 0 | { |
940 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
941 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
942 | | |
943 | 0 | cell->row_num = row_num; |
944 | 0 | cell->col_num = col_num; |
945 | 0 | cell->type = FORMULA_CELL; |
946 | 0 | cell->format = format; |
947 | 0 | cell->u.string = formula; |
948 | |
|
949 | 0 | return cell; |
950 | 0 | } |
951 | | |
952 | | /* |
953 | | * Create a new worksheet array formula cell object. |
954 | | */ |
955 | | STATIC lxw_cell * |
956 | | _new_array_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula, |
957 | | char *range, lxw_format *format, uint8_t is_dynamic) |
958 | 0 | { |
959 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
960 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
961 | | |
962 | 0 | cell->row_num = row_num; |
963 | 0 | cell->col_num = col_num; |
964 | 0 | cell->format = format; |
965 | 0 | cell->u.string = formula; |
966 | 0 | cell->user_data1 = range; |
967 | |
|
968 | 0 | if (is_dynamic) |
969 | 0 | cell->type = DYNAMIC_ARRAY_FORMULA_CELL; |
970 | 0 | else |
971 | 0 | cell->type = ARRAY_FORMULA_CELL; |
972 | |
|
973 | 0 | return cell; |
974 | 0 | } |
975 | | |
976 | | /* |
977 | | * Create a new worksheet blank cell object. |
978 | | */ |
979 | | STATIC lxw_cell * |
980 | | _new_blank_cell(lxw_row_t row_num, lxw_col_t col_num, lxw_format *format) |
981 | 0 | { |
982 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
983 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
984 | | |
985 | 0 | cell->row_num = row_num; |
986 | 0 | cell->col_num = col_num; |
987 | 0 | cell->type = BLANK_CELL; |
988 | 0 | cell->format = format; |
989 | |
|
990 | 0 | return cell; |
991 | 0 | } |
992 | | |
993 | | /* |
994 | | * Create a new worksheet boolean cell object. |
995 | | */ |
996 | | STATIC lxw_cell * |
997 | | _new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value, |
998 | | lxw_format *format) |
999 | 0 | { |
1000 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
1001 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
1002 | | |
1003 | 0 | cell->row_num = row_num; |
1004 | 0 | cell->col_num = col_num; |
1005 | 0 | cell->type = BOOLEAN_CELL; |
1006 | 0 | cell->format = format; |
1007 | 0 | cell->u.number = value; |
1008 | |
|
1009 | 0 | return cell; |
1010 | 0 | } |
1011 | | |
1012 | | /* |
1013 | | * Create a new worksheet error cell object. |
1014 | | */ |
1015 | | STATIC lxw_cell * |
1016 | | _new_error_cell(lxw_row_t row_num, lxw_col_t col_num, uint32_t value, |
1017 | | lxw_format *format) |
1018 | 0 | { |
1019 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
1020 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
1021 | | |
1022 | 0 | cell->row_num = row_num; |
1023 | 0 | cell->col_num = col_num; |
1024 | 0 | cell->type = ERROR_CELL; |
1025 | 0 | cell->format = format; |
1026 | 0 | cell->u.number = value; |
1027 | |
|
1028 | 0 | return cell; |
1029 | 0 | } |
1030 | | |
1031 | | /* |
1032 | | * Create a new comment cell object. |
1033 | | */ |
1034 | | STATIC lxw_cell * |
1035 | | _new_comment_cell(lxw_row_t row_num, lxw_col_t col_num, |
1036 | | lxw_vml_obj *comment_obj) |
1037 | 0 | { |
1038 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
1039 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
1040 | | |
1041 | 0 | cell->row_num = row_num; |
1042 | 0 | cell->col_num = col_num; |
1043 | 0 | cell->type = COMMENT; |
1044 | 0 | cell->comment = comment_obj; |
1045 | |
|
1046 | 0 | return cell; |
1047 | 0 | } |
1048 | | |
1049 | | /* |
1050 | | * Create a new worksheet hyperlink cell object. |
1051 | | */ |
1052 | | STATIC lxw_cell * |
1053 | | _new_hyperlink_cell(lxw_row_t row_num, lxw_col_t col_num, |
1054 | | enum cell_types link_type, char *url, char *string, |
1055 | | char *tooltip) |
1056 | 0 | { |
1057 | 0 | lxw_cell *cell = calloc(1, sizeof(lxw_cell)); |
1058 | 0 | RETURN_ON_MEM_ERROR(cell, cell); |
1059 | | |
1060 | 0 | cell->row_num = row_num; |
1061 | 0 | cell->col_num = col_num; |
1062 | 0 | cell->type = link_type; |
1063 | 0 | cell->u.string = url; |
1064 | 0 | cell->user_data1 = string; |
1065 | 0 | cell->user_data2 = tooltip; |
1066 | |
|
1067 | 0 | return cell; |
1068 | 0 | } |
1069 | | |
1070 | | /* |
1071 | | * Get or create the row object for a given row number. |
1072 | | */ |
1073 | | STATIC lxw_row * |
1074 | | _get_row_list(struct lxw_table_rows *table, lxw_row_t row_num) |
1075 | 66.9k | { |
1076 | 66.9k | lxw_row *row; |
1077 | 66.9k | lxw_row *existing_row; |
1078 | | |
1079 | 66.9k | if (table->cached_row_num == row_num) |
1080 | 61.2k | return table->cached_row; |
1081 | | |
1082 | | /* Create a new row and try and insert it. */ |
1083 | 5.73k | row = _new_row(row_num); |
1084 | 5.73k | existing_row = RB_INSERT(lxw_table_rows, table, row); |
1085 | | |
1086 | | /* If existing_row is not NULL, then it already existed. Free new row */ |
1087 | | /* and return existing_row. */ |
1088 | 5.73k | if (existing_row) { |
1089 | 0 | _free_row(row); |
1090 | 0 | row = existing_row; |
1091 | 0 | } |
1092 | | |
1093 | 5.73k | table->cached_row = row; |
1094 | 5.73k | table->cached_row_num = row_num; |
1095 | | |
1096 | 5.73k | return row; |
1097 | 66.9k | } |
1098 | | |
1099 | | /* |
1100 | | * Get or create the row object for a given row number. |
1101 | | */ |
1102 | | STATIC lxw_row * |
1103 | | _get_row(lxw_worksheet *self, lxw_row_t row_num) |
1104 | 66.9k | { |
1105 | 66.9k | lxw_row *row; |
1106 | | |
1107 | 66.9k | if (!self->optimize) { |
1108 | 66.9k | row = _get_row_list(self->table, row_num); |
1109 | 66.9k | return row; |
1110 | 66.9k | } |
1111 | 0 | else { |
1112 | 0 | if (row_num < self->optimize_row->row_num) { |
1113 | 0 | return NULL; |
1114 | 0 | } |
1115 | 0 | else if (row_num == self->optimize_row->row_num) { |
1116 | 0 | return self->optimize_row; |
1117 | 0 | } |
1118 | 0 | else { |
1119 | | /* Flush row. */ |
1120 | 0 | lxw_worksheet_write_single_row(self); |
1121 | 0 | row = self->optimize_row; |
1122 | 0 | row->row_num = row_num; |
1123 | 0 | return row; |
1124 | 0 | } |
1125 | 0 | } |
1126 | 66.9k | } |
1127 | | |
1128 | | /* |
1129 | | * Insert a cell object in the cell list of a row object. |
1130 | | */ |
1131 | | STATIC void |
1132 | | _insert_cell_list(struct lxw_table_cells *cell_list, |
1133 | | lxw_cell *cell, lxw_col_t col_num) |
1134 | 66.9k | { |
1135 | 66.9k | lxw_cell *existing_cell; |
1136 | | |
1137 | 66.9k | cell->col_num = col_num; |
1138 | | |
1139 | 66.9k | existing_cell = RB_INSERT(lxw_table_cells, cell_list, cell); |
1140 | | |
1141 | | /* If existing_cell is not NULL, then that cell already existed. */ |
1142 | | /* Remove existing_cell and add new one in again. */ |
1143 | 66.9k | if (existing_cell) { |
1144 | 0 | RB_REMOVE(lxw_table_cells, cell_list, existing_cell); |
1145 | | |
1146 | | /* Add it in again. */ |
1147 | 0 | RB_INSERT(lxw_table_cells, cell_list, cell); |
1148 | 0 | _free_cell(existing_cell); |
1149 | 0 | } |
1150 | | |
1151 | 66.9k | return; |
1152 | 66.9k | } |
1153 | | |
1154 | | /* |
1155 | | * Insert a cell object into the cell list or array. |
1156 | | */ |
1157 | | STATIC void |
1158 | | _insert_cell(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, |
1159 | | lxw_cell *cell) |
1160 | 66.9k | { |
1161 | 66.9k | lxw_row *row = _get_row(self, row_num); |
1162 | | |
1163 | 66.9k | if (!self->optimize) { |
1164 | 66.9k | row->data_changed = LXW_TRUE; |
1165 | 66.9k | _insert_cell_list(row->cells, cell, col_num); |
1166 | 66.9k | } |
1167 | 0 | else { |
1168 | 0 | if (row) { |
1169 | 0 | row->data_changed = LXW_TRUE; |
1170 | | |
1171 | | /* Overwrite an existing cell if necessary. */ |
1172 | 0 | if (self->array[col_num]) |
1173 | 0 | _free_cell(self->array[col_num]); |
1174 | |
|
1175 | 0 | self->array[col_num] = cell; |
1176 | 0 | } |
1177 | 0 | } |
1178 | 66.9k | } |
1179 | | |
1180 | | /* |
1181 | | * Insert a blank placeholder cell in the cells RB tree in the same position |
1182 | | * as a comment so that the rows "spans" calculation is correct. Since the |
1183 | | * blank cell doesn't have a format it is ignored when writing. If there is |
1184 | | * already a cell in the required position we don't have add a new cell. |
1185 | | */ |
1186 | | STATIC void |
1187 | | _insert_cell_placeholder(lxw_worksheet *self, lxw_row_t row_num, |
1188 | | lxw_col_t col_num) |
1189 | 0 | { |
1190 | 0 | lxw_row *row; |
1191 | 0 | lxw_cell *cell; |
1192 | | |
1193 | | /* The spans calculation isn't required in constant_memory mode. */ |
1194 | 0 | if (self->optimize) |
1195 | 0 | return; |
1196 | | |
1197 | 0 | cell = _new_blank_cell(row_num, col_num, NULL); |
1198 | 0 | if (!cell) |
1199 | 0 | return; |
1200 | | |
1201 | | /* Only add a cell if one doesn't already exist. */ |
1202 | 0 | row = _get_row(self, row_num); |
1203 | 0 | if (!RB_FIND(lxw_table_cells, row->cells, cell)) { |
1204 | 0 | _insert_cell_list(row->cells, cell, col_num); |
1205 | 0 | } |
1206 | 0 | else { |
1207 | 0 | _free_cell(cell); |
1208 | 0 | } |
1209 | 0 | } |
1210 | | |
1211 | | /* |
1212 | | * Insert a hyperlink object into the hyperlink RB tree. |
1213 | | */ |
1214 | | STATIC void |
1215 | | _insert_hyperlink(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, |
1216 | | lxw_cell *link) |
1217 | 0 | { |
1218 | 0 | lxw_row *row = _get_row_list(self->hyperlinks, row_num); |
1219 | |
|
1220 | 0 | _insert_cell_list(row->cells, link, col_num); |
1221 | 0 | } |
1222 | | |
1223 | | /* |
1224 | | * Insert a comment into the comment RB tree. |
1225 | | */ |
1226 | | STATIC void |
1227 | | _insert_comment(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num, |
1228 | | lxw_cell *link) |
1229 | 0 | { |
1230 | 0 | lxw_row *row = _get_row_list(self->comments, row_num); |
1231 | |
|
1232 | 0 | _insert_cell_list(row->cells, link, col_num); |
1233 | 0 | } |
1234 | | |
1235 | | /* |
1236 | | * Next power of two for column reallocs. Taken from bithacks in the public |
1237 | | * domain. |
1238 | | */ |
1239 | | STATIC lxw_col_t |
1240 | | _next_power_of_two(uint16_t col) |
1241 | 0 | { |
1242 | 0 | col--; |
1243 | 0 | col |= col >> 1; |
1244 | 0 | col |= col >> 2; |
1245 | 0 | col |= col >> 4; |
1246 | 0 | col |= col >> 8; |
1247 | 0 | col++; |
1248 | |
|
1249 | 0 | return col; |
1250 | 0 | } |
1251 | | |
1252 | | /* |
1253 | | * Check that row and col are within the allowed Excel range and store max |
1254 | | * and min values for use in other methods/elements. |
1255 | | * |
1256 | | * The ignore_row/ignore_col flags are used to indicate that we wish to |
1257 | | * perform the dimension check without storing the value. |
1258 | | */ |
1259 | | STATIC lxw_error |
1260 | | _check_dimensions(lxw_worksheet *self, |
1261 | | lxw_row_t row_num, |
1262 | | lxw_col_t col_num, int8_t ignore_row, int8_t ignore_col) |
1263 | 66.9k | { |
1264 | 66.9k | if (row_num >= LXW_ROW_MAX) |
1265 | 0 | return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; |
1266 | | |
1267 | 66.9k | if (col_num >= LXW_COL_MAX) |
1268 | 0 | return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; |
1269 | | |
1270 | | /* In optimization mode we don't change dimensions for rows that are */ |
1271 | | /* already written. */ |
1272 | 66.9k | if (!ignore_row && !ignore_col && self->optimize) { |
1273 | 0 | if (row_num < self->optimize_row->row_num) |
1274 | 0 | return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE; |
1275 | 0 | } |
1276 | | |
1277 | 66.9k | if (!ignore_row) { |
1278 | 66.9k | if (row_num < self->dim_rowmin) |
1279 | 1.22k | self->dim_rowmin = row_num; |
1280 | 66.9k | if (row_num > self->dim_rowmax) |
1281 | 4.56k | self->dim_rowmax = row_num; |
1282 | 66.9k | } |
1283 | | |
1284 | 66.9k | if (!ignore_col) { |
1285 | 66.9k | if (col_num < self->dim_colmin) |
1286 | 1.30k | self->dim_colmin = col_num; |
1287 | 66.9k | if (col_num > self->dim_colmax) |
1288 | 7.95k | self->dim_colmax = col_num; |
1289 | 66.9k | } |
1290 | | |
1291 | 66.9k | return LXW_NO_ERROR; |
1292 | 66.9k | } |
1293 | | |
1294 | | /* |
1295 | | * Comparator for the row structure red/black tree. |
1296 | | */ |
1297 | | STATIC int |
1298 | | _row_cmp(lxw_row *row1, lxw_row *row2) |
1299 | 17.6k | { |
1300 | 17.6k | if (row1->row_num > row2->row_num) |
1301 | 17.6k | return 1; |
1302 | 0 | if (row1->row_num < row2->row_num) |
1303 | 0 | return -1; |
1304 | 0 | return 0; |
1305 | 0 | } |
1306 | | |
1307 | | /* |
1308 | | * Comparator for the cell structure red/black tree. |
1309 | | */ |
1310 | | STATIC int |
1311 | | _cell_cmp(lxw_cell *cell1, lxw_cell *cell2) |
1312 | 261k | { |
1313 | 261k | if (cell1->col_num > cell2->col_num) |
1314 | 261k | return 1; |
1315 | 0 | if (cell1->col_num < cell2->col_num) |
1316 | 0 | return -1; |
1317 | 0 | return 0; |
1318 | 0 | } |
1319 | | |
1320 | | /* |
1321 | | * Comparator for the image/hyperlink relationship ids. |
1322 | | */ |
1323 | | STATIC int |
1324 | | _drawing_rel_id_cmp(lxw_drawing_rel_id *rel_id1, lxw_drawing_rel_id *rel_id2) |
1325 | 0 | { |
1326 | 0 | return strcmp(rel_id1->target, rel_id2->target); |
1327 | 0 | } |
1328 | | |
1329 | | /* |
1330 | | * Comparator for the conditional format RB hash elements. |
1331 | | */ |
1332 | | STATIC int |
1333 | | _cond_format_hash_cmp(lxw_cond_format_hash_element *elem_1, |
1334 | | lxw_cond_format_hash_element *elem_2) |
1335 | 0 | { |
1336 | 0 | return strcmp(elem_1->sqref, elem_2->sqref); |
1337 | 0 | } |
1338 | | |
1339 | | /* |
1340 | | * Get the index used to address a drawing rel link. |
1341 | | */ |
1342 | | STATIC uint32_t |
1343 | | _get_drawing_rel_index(lxw_worksheet *self, char *target) |
1344 | 0 | { |
1345 | 0 | lxw_drawing_rel_id tmp_drawing_rel_id; |
1346 | 0 | lxw_drawing_rel_id *found_duplicate_target = NULL; |
1347 | 0 | lxw_drawing_rel_id *new_drawing_rel_id = NULL; |
1348 | |
|
1349 | 0 | if (target) { |
1350 | 0 | tmp_drawing_rel_id.target = target; |
1351 | 0 | found_duplicate_target = RB_FIND(lxw_drawing_rel_ids, |
1352 | 0 | self->drawing_rel_ids, |
1353 | 0 | &tmp_drawing_rel_id); |
1354 | 0 | } |
1355 | |
|
1356 | 0 | if (found_duplicate_target) { |
1357 | 0 | return found_duplicate_target->id; |
1358 | 0 | } |
1359 | 0 | else { |
1360 | 0 | self->drawing_rel_id++; |
1361 | |
|
1362 | 0 | if (target) { |
1363 | 0 | new_drawing_rel_id = calloc(1, sizeof(lxw_drawing_rel_id)); |
1364 | |
|
1365 | 0 | if (new_drawing_rel_id) { |
1366 | 0 | new_drawing_rel_id->id = self->drawing_rel_id; |
1367 | 0 | new_drawing_rel_id->target = lxw_strdup(target); |
1368 | |
|
1369 | 0 | RB_INSERT(lxw_drawing_rel_ids, self->drawing_rel_ids, |
1370 | 0 | new_drawing_rel_id); |
1371 | 0 | } |
1372 | 0 | } |
1373 | |
|
1374 | 0 | return self->drawing_rel_id; |
1375 | 0 | } |
1376 | 0 | } |
1377 | | |
1378 | | /* |
1379 | | * find the index used to address a drawing rel link. |
1380 | | */ |
1381 | | STATIC uint32_t |
1382 | | _find_drawing_rel_index(lxw_worksheet *self, char *target) |
1383 | 0 | { |
1384 | 0 | lxw_drawing_rel_id tmp_drawing_rel_id; |
1385 | 0 | lxw_drawing_rel_id *found_duplicate_target = NULL; |
1386 | |
|
1387 | 0 | if (!target) |
1388 | 0 | return 0; |
1389 | | |
1390 | 0 | tmp_drawing_rel_id.target = target; |
1391 | 0 | found_duplicate_target = RB_FIND(lxw_drawing_rel_ids, |
1392 | 0 | self->drawing_rel_ids, |
1393 | 0 | &tmp_drawing_rel_id); |
1394 | |
|
1395 | 0 | if (found_duplicate_target) |
1396 | 0 | return found_duplicate_target->id; |
1397 | 0 | else |
1398 | 0 | return 0; |
1399 | 0 | } |
1400 | | |
1401 | | /* |
1402 | | * Get the index used to address a VMLdrawing rel link. |
1403 | | */ |
1404 | | STATIC uint32_t |
1405 | | _get_vml_drawing_rel_index(lxw_worksheet *self, char *target) |
1406 | 0 | { |
1407 | 0 | lxw_drawing_rel_id tmp_drawing_rel_id; |
1408 | 0 | lxw_drawing_rel_id *found_duplicate_target = NULL; |
1409 | 0 | lxw_drawing_rel_id *new_drawing_rel_id = NULL; |
1410 | |
|
1411 | 0 | if (target) { |
1412 | 0 | tmp_drawing_rel_id.target = target; |
1413 | 0 | found_duplicate_target = RB_FIND(lxw_vml_drawing_rel_ids, |
1414 | 0 | self->vml_drawing_rel_ids, |
1415 | 0 | &tmp_drawing_rel_id); |
1416 | 0 | } |
1417 | |
|
1418 | 0 | if (found_duplicate_target) { |
1419 | 0 | return found_duplicate_target->id; |
1420 | 0 | } |
1421 | 0 | else { |
1422 | 0 | self->vml_drawing_rel_id++; |
1423 | |
|
1424 | 0 | if (target) { |
1425 | 0 | new_drawing_rel_id = calloc(1, sizeof(lxw_drawing_rel_id)); |
1426 | |
|
1427 | 0 | if (new_drawing_rel_id) { |
1428 | 0 | new_drawing_rel_id->id = self->vml_drawing_rel_id; |
1429 | 0 | new_drawing_rel_id->target = lxw_strdup(target); |
1430 | |
|
1431 | 0 | RB_INSERT(lxw_vml_drawing_rel_ids, self->vml_drawing_rel_ids, |
1432 | 0 | new_drawing_rel_id); |
1433 | 0 | } |
1434 | 0 | } |
1435 | |
|
1436 | 0 | return self->vml_drawing_rel_id; |
1437 | 0 | } |
1438 | 0 | } |
1439 | | |
1440 | | /* |
1441 | | * find the index used to address a VML drawing rel link. |
1442 | | */ |
1443 | | STATIC uint32_t |
1444 | | _find_vml_drawing_rel_index(lxw_worksheet *self, char *target) |
1445 | 0 | { |
1446 | 0 | lxw_drawing_rel_id tmp_drawing_rel_id; |
1447 | 0 | lxw_drawing_rel_id *found_duplicate_target = NULL; |
1448 | |
|
1449 | 0 | if (!target) |
1450 | 0 | return 0; |
1451 | | |
1452 | 0 | tmp_drawing_rel_id.target = target; |
1453 | 0 | found_duplicate_target = RB_FIND(lxw_vml_drawing_rel_ids, |
1454 | 0 | self->vml_drawing_rel_ids, |
1455 | 0 | &tmp_drawing_rel_id); |
1456 | |
|
1457 | 0 | if (found_duplicate_target) |
1458 | 0 | return found_duplicate_target->id; |
1459 | 0 | else |
1460 | 0 | return 0; |
1461 | 0 | } |
1462 | | |
1463 | | /* |
1464 | | * Simple replacement for libgen.h basename() for compatibility with MSVC. It |
1465 | | * handles forward and back slashes. It doesn't copy exactly the return |
1466 | | * format of basename(). |
1467 | | */ |
1468 | | const char * |
1469 | | lxw_basename(const char *path) |
1470 | 0 | { |
1471 | |
|
1472 | 0 | const char *forward_slash; |
1473 | 0 | const char *back_slash; |
1474 | |
|
1475 | 0 | if (!path) |
1476 | 0 | return NULL; |
1477 | | |
1478 | 0 | forward_slash = strrchr(path, '/'); |
1479 | 0 | back_slash = strrchr(path, '\\'); |
1480 | |
|
1481 | 0 | if (!forward_slash && !back_slash) |
1482 | 0 | return path; |
1483 | | |
1484 | 0 | if (forward_slash > back_slash) |
1485 | 0 | return forward_slash + 1; |
1486 | 0 | else |
1487 | 0 | return back_slash + 1; |
1488 | 0 | } |
1489 | | |
1490 | | /* Function to count the total concatenated length of the strings in a |
1491 | | * validation list array, including commas. */ |
1492 | | size_t |
1493 | | _validation_list_length(const char **list) |
1494 | 0 | { |
1495 | 0 | uint8_t i = 0; |
1496 | 0 | size_t length = 0; |
1497 | |
|
1498 | 0 | if (!list || !list[0]) |
1499 | 0 | return 0; |
1500 | | |
1501 | 0 | while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) { |
1502 | | /* Include commas in the length. */ |
1503 | 0 | length += 1 + lxw_utf8_strlen(list[i]); |
1504 | 0 | i++; |
1505 | 0 | } |
1506 | | |
1507 | | /* Adjust the count for extraneous comma at end. */ |
1508 | 0 | length--; |
1509 | |
|
1510 | 0 | return length; |
1511 | 0 | } |
1512 | | |
1513 | | /* Function to convert an array of strings into a CSV string for data |
1514 | | * validation lists. */ |
1515 | | char * |
1516 | | _validation_list_to_csv(const char **list) |
1517 | 0 | { |
1518 | 0 | uint8_t i = 0; |
1519 | 0 | char *str; |
1520 | | |
1521 | | /* Create a buffer for the concatenated, and quoted, string. */ |
1522 | | /* Allow for 4 byte UTF-8 chars and add 3 bytes for quotes and EOL. */ |
1523 | 0 | str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH * 4 + 3); |
1524 | 0 | if (!str) |
1525 | 0 | return NULL; |
1526 | | |
1527 | | /* Add the start quote and first element. */ |
1528 | 0 | strcat(str, "\""); |
1529 | 0 | strcat(str, list[0]); |
1530 | | |
1531 | | /* Add the other elements preceded by a comma. */ |
1532 | 0 | i = 1; |
1533 | 0 | while (list[i]) { |
1534 | 0 | strcat(str, ","); |
1535 | 0 | strcat(str, list[i]); |
1536 | 0 | i++; |
1537 | 0 | } |
1538 | | |
1539 | | /* Add the end quote. */ |
1540 | 0 | strcat(str, "\""); |
1541 | |
|
1542 | 0 | return str; |
1543 | 0 | } |
1544 | | |
1545 | | STATIC double |
1546 | | _pixels_to_width(double pixels) |
1547 | 0 | { |
1548 | 0 | double max_digit_width = 7.0; |
1549 | 0 | double padding = 5.0; |
1550 | 0 | double width; |
1551 | |
|
1552 | 0 | if (pixels == LXW_DEF_COL_WIDTH_PIXELS) |
1553 | 0 | width = LXW_DEF_COL_WIDTH; |
1554 | 0 | else if (pixels <= 12.0) |
1555 | 0 | width = pixels / (max_digit_width + padding); |
1556 | 0 | else |
1557 | 0 | width = (pixels - padding) / max_digit_width; |
1558 | |
|
1559 | 0 | return width; |
1560 | 0 | } |
1561 | | |
1562 | | STATIC double |
1563 | | _pixels_to_height(double pixels) |
1564 | 0 | { |
1565 | 0 | if (pixels == LXW_DEF_ROW_HEIGHT_PIXELS) |
1566 | 0 | return LXW_DEF_ROW_HEIGHT; |
1567 | 0 | else |
1568 | 0 | return pixels * 0.75; |
1569 | 0 | } |
1570 | | |
1571 | | /* Check and set if an autofilter is a standard or custom filter. */ |
1572 | | void |
1573 | | _set_custom_filter(lxw_filter_rule_obj *rule_obj) |
1574 | 0 | { |
1575 | 0 | rule_obj->is_custom = LXW_TRUE; |
1576 | |
|
1577 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) |
1578 | 0 | rule_obj->is_custom = LXW_FALSE; |
1579 | |
|
1580 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) |
1581 | 0 | rule_obj->is_custom = LXW_FALSE; |
1582 | |
|
1583 | 0 | if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NONE) { |
1584 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_EQUAL_TO) |
1585 | 0 | rule_obj->is_custom = LXW_FALSE; |
1586 | |
|
1587 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) |
1588 | 0 | rule_obj->is_custom = LXW_FALSE; |
1589 | |
|
1590 | 0 | if (rule_obj->type == LXW_FILTER_TYPE_AND) |
1591 | 0 | rule_obj->is_custom = LXW_TRUE; |
1592 | 0 | } |
1593 | |
|
1594 | 0 | if (rule_obj->value1_string && strpbrk(rule_obj->value1_string, "*?")) |
1595 | 0 | rule_obj->is_custom = LXW_TRUE; |
1596 | |
|
1597 | 0 | if (rule_obj->value2_string && strpbrk(rule_obj->value2_string, "*?")) |
1598 | 0 | rule_obj->is_custom = LXW_TRUE; |
1599 | 0 | } |
1600 | | |
1601 | | /* Check and copy user input for table styles in worksheet_add_table(). */ |
1602 | | void |
1603 | | _check_and_copy_table_style(lxw_table_obj *table_obj, |
1604 | | lxw_table_options *user_options) |
1605 | 0 | { |
1606 | 0 | if (!user_options) |
1607 | 0 | return; |
1608 | | |
1609 | | /* Set the defaults. */ |
1610 | 0 | table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; |
1611 | 0 | table_obj->style_type_number = 9; |
1612 | |
|
1613 | 0 | if (user_options->style_type > LXW_TABLE_STYLE_TYPE_DARK) { |
1614 | 0 | LXW_WARN_FORMAT1 |
1615 | 0 | ("worksheet_add_table(): invalid style_type = %d. " |
1616 | 0 | "Using default TableStyleMedium9", user_options->style_type); |
1617 | |
|
1618 | 0 | table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; |
1619 | 0 | table_obj->style_type_number = 9; |
1620 | 0 | } |
1621 | 0 | else { |
1622 | 0 | table_obj->style_type = user_options->style_type; |
1623 | 0 | } |
1624 | | |
1625 | | /* Each type (light, medium and dark) has a different number of styles. */ |
1626 | 0 | if (user_options->style_type == LXW_TABLE_STYLE_TYPE_LIGHT) { |
1627 | 0 | if (user_options->style_type_number > 21) { |
1628 | 0 | LXW_WARN_FORMAT1("worksheet_add_table(): " |
1629 | 0 | "invalid style_type_number = %d for style type " |
1630 | 0 | "LXW_TABLE_STYLE_TYPE_LIGHT. " |
1631 | 0 | "Using default TableStyleMedium9", |
1632 | 0 | user_options->style_type); |
1633 | |
|
1634 | 0 | table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; |
1635 | 0 | table_obj->style_type_number = 9; |
1636 | 0 | } |
1637 | 0 | else { |
1638 | 0 | table_obj->style_type_number = user_options->style_type_number; |
1639 | 0 | } |
1640 | 0 | } |
1641 | |
|
1642 | 0 | if (user_options->style_type == LXW_TABLE_STYLE_TYPE_MEDIUM) { |
1643 | 0 | if (user_options->style_type_number < 1 |
1644 | 0 | || user_options->style_type_number > 28) { |
1645 | 0 | LXW_WARN_FORMAT1("worksheet_add_table(): " |
1646 | 0 | "invalid style_type_number = %d for style type " |
1647 | 0 | "LXW_TABLE_STYLE_TYPE_MEDIUM. " |
1648 | 0 | "Using default TableStyleMedium9", |
1649 | 0 | user_options->style_type_number); |
1650 | |
|
1651 | 0 | table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; |
1652 | 0 | table_obj->style_type_number = 9; |
1653 | 0 | } |
1654 | 0 | else { |
1655 | 0 | table_obj->style_type_number = user_options->style_type_number; |
1656 | 0 | } |
1657 | 0 | } |
1658 | |
|
1659 | 0 | if (user_options->style_type == LXW_TABLE_STYLE_TYPE_DARK) { |
1660 | 0 | if (user_options->style_type_number < 1 |
1661 | 0 | || user_options->style_type_number > 11) { |
1662 | 0 | LXW_WARN_FORMAT1("worksheet_add_table(): " |
1663 | 0 | "invalid style_type_number = %d for style type " |
1664 | 0 | "LXW_TABLE_STYLE_TYPE_DARK. " |
1665 | 0 | "Using default TableStyleMedium9", |
1666 | 0 | user_options->style_type_number); |
1667 | |
|
1668 | 0 | table_obj->style_type = LXW_TABLE_STYLE_TYPE_MEDIUM; |
1669 | 0 | table_obj->style_type_number = 9; |
1670 | 0 | } |
1671 | 0 | else { |
1672 | 0 | table_obj->style_type_number = user_options->style_type_number; |
1673 | 0 | } |
1674 | 0 | } |
1675 | 0 | } |
1676 | | |
1677 | | /* Set the defaults for table columns in worksheet_add_table(). */ |
1678 | | lxw_error |
1679 | | _set_default_table_columns(lxw_table_obj *table_obj) |
1680 | 0 | { |
1681 | |
|
1682 | 0 | char col_name[LXW_ATTR_32]; |
1683 | 0 | char *header; |
1684 | 0 | uint16_t i; |
1685 | 0 | lxw_table_column *column; |
1686 | 0 | uint16_t num_cols = table_obj->num_cols; |
1687 | 0 | lxw_table_column **columns = table_obj->columns; |
1688 | |
|
1689 | 0 | for (i = 0; i < num_cols; i++) { |
1690 | 0 | lxw_snprintf(col_name, LXW_ATTR_32, "Column%d", i + 1); |
1691 | |
|
1692 | 0 | column = calloc(num_cols, sizeof(lxw_table_column)); |
1693 | 0 | RETURN_ON_MEM_ERROR(column, LXW_ERROR_MEMORY_MALLOC_FAILED); |
1694 | | |
1695 | 0 | header = lxw_strdup(col_name); |
1696 | 0 | if (!header) { |
1697 | 0 | free(column); |
1698 | 0 | RETURN_ON_MEM_ERROR(header, LXW_ERROR_MEMORY_MALLOC_FAILED); |
1699 | 0 | } |
1700 | 0 | columns[i] = column; |
1701 | 0 | columns[i]->header = header; |
1702 | 0 | } |
1703 | | |
1704 | 0 | return LXW_NO_ERROR; |
1705 | 0 | } |
1706 | | |
1707 | | /* Convert Excel 2010 style "@" structural references to the Excel 2007 style |
1708 | | * "[#This Row]" in table formulas. This is the format that Excel uses to |
1709 | | * store the references. */ |
1710 | | char * |
1711 | | _expand_table_formula(const char *formula) |
1712 | 0 | { |
1713 | 0 | char *expanded; |
1714 | 0 | const char *ptr; |
1715 | 0 | size_t i; |
1716 | 0 | size_t ref_count = 0; |
1717 | 0 | size_t expanded_len; |
1718 | |
|
1719 | 0 | ptr = formula; |
1720 | |
|
1721 | 0 | while (*ptr) { |
1722 | 0 | if (*ptr == '@') |
1723 | 0 | ref_count++; |
1724 | |
|
1725 | 0 | ptr++; |
1726 | 0 | } |
1727 | |
|
1728 | 0 | if (ref_count == 0) { |
1729 | | /* String doesn't need to be expanded. Just copy it. */ |
1730 | 0 | expanded = lxw_strdup_formula(formula); |
1731 | 0 | } |
1732 | 0 | else { |
1733 | | /* Convert "@" in the formula string to "[#This Row],". */ |
1734 | 0 | expanded_len = strlen(formula) + (sizeof(LXW_THIS_ROW) * ref_count); |
1735 | 0 | expanded = calloc(1, expanded_len); |
1736 | |
|
1737 | 0 | if (!expanded) |
1738 | 0 | return NULL; |
1739 | | |
1740 | 0 | i = 0; |
1741 | 0 | ptr = formula; |
1742 | | /* Ignore the = in the formula. */ |
1743 | 0 | if (*ptr == '=') |
1744 | 0 | ptr++; |
1745 | | |
1746 | | /* Do the "@" expansion. */ |
1747 | 0 | while (*ptr) { |
1748 | 0 | if (*ptr == '@') { |
1749 | 0 | strcat(&expanded[i], LXW_THIS_ROW); |
1750 | 0 | i += sizeof(LXW_THIS_ROW) - 1; |
1751 | 0 | } |
1752 | 0 | else { |
1753 | 0 | expanded[i] = *ptr; |
1754 | 0 | i++; |
1755 | 0 | } |
1756 | |
|
1757 | 0 | ptr++; |
1758 | 0 | } |
1759 | 0 | } |
1760 | | |
1761 | 0 | return expanded; |
1762 | 0 | } |
1763 | | |
1764 | | /* Set user values for table columns in worksheet_add_table(). */ |
1765 | | lxw_error |
1766 | | _set_custom_table_columns(lxw_table_obj *table_obj, |
1767 | | lxw_table_options *user_options) |
1768 | 0 | { |
1769 | 0 | char *str; |
1770 | 0 | uint16_t i; |
1771 | 0 | lxw_table_column *table_column; |
1772 | 0 | lxw_table_column *user_column; |
1773 | 0 | uint16_t num_cols = table_obj->num_cols; |
1774 | 0 | lxw_table_column **user_columns = user_options->columns; |
1775 | |
|
1776 | 0 | for (i = 0; i < num_cols; i++) { |
1777 | |
|
1778 | 0 | user_column = user_columns[i]; |
1779 | 0 | table_column = table_obj->columns[i]; |
1780 | | |
1781 | | /* NULL indicates end of user input array. */ |
1782 | 0 | if (user_column == NULL) |
1783 | 0 | return LXW_NO_ERROR; |
1784 | | |
1785 | 0 | if (user_column->header) { |
1786 | 0 | if (lxw_utf8_strlen(user_column->header) > 255) { |
1787 | 0 | LXW_WARN_FORMAT("worksheet_add_table(): column parameter " |
1788 | 0 | "'header' exceeds Excel length limit of 255."); |
1789 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
1790 | 0 | } |
1791 | | |
1792 | 0 | str = lxw_strdup(user_column->header); |
1793 | 0 | RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); |
1794 | | |
1795 | | /* Free the default column header. */ |
1796 | 0 | free((void *) table_column->header); |
1797 | 0 | table_column->header = str; |
1798 | 0 | } |
1799 | | |
1800 | 0 | if (user_column->total_string) { |
1801 | 0 | str = lxw_strdup(user_column->total_string); |
1802 | 0 | RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); |
1803 | | |
1804 | 0 | table_column->total_string = str; |
1805 | 0 | } |
1806 | | |
1807 | 0 | if (user_column->formula) { |
1808 | 0 | str = _expand_table_formula(user_column->formula); |
1809 | 0 | RETURN_ON_MEM_ERROR(str, LXW_ERROR_MEMORY_MALLOC_FAILED); |
1810 | | |
1811 | 0 | table_column->formula = str; |
1812 | 0 | } |
1813 | | |
1814 | 0 | table_column->format = user_column->format; |
1815 | 0 | table_column->total_value = user_column->total_value; |
1816 | 0 | table_column->header_format = user_column->header_format; |
1817 | 0 | table_column->total_function = user_column->total_function; |
1818 | 0 | } |
1819 | | |
1820 | 0 | return LXW_NO_ERROR; |
1821 | 0 | } |
1822 | | |
1823 | | /* Write a worksheet table column formula like SUBTOTAL(109,[Column1]). */ |
1824 | | void |
1825 | | _write_column_function(lxw_worksheet *self, lxw_row_t row, lxw_col_t col, |
1826 | | lxw_table_column *column) |
1827 | 0 | { |
1828 | 0 | size_t offset; |
1829 | 0 | char formula[LXW_MAX_ATTRIBUTE_LENGTH]; |
1830 | 0 | lxw_format *format = column->format; |
1831 | 0 | uint8_t total_function = column->total_function; |
1832 | 0 | double value = column->total_value; |
1833 | 0 | const char *header = column->header; |
1834 | | |
1835 | | /* Write the subtotal formula number. */ |
1836 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, "SUBTOTAL(%d,[", |
1837 | 0 | total_function); |
1838 | | |
1839 | | /* Copy the header string but escape any special characters. Note, this is |
1840 | | * guaranteed to fit in the 2k buffer since the header is max 255 |
1841 | | * characters, checked in _set_custom_table_columns(). */ |
1842 | 0 | offset = strlen(formula); |
1843 | 0 | while (*header) { |
1844 | 0 | switch (*header) { |
1845 | 0 | case '\'': |
1846 | 0 | case '#': |
1847 | 0 | case '[': |
1848 | 0 | case ']': |
1849 | 0 | formula[offset++] = '\''; |
1850 | 0 | formula[offset] = *header; |
1851 | 0 | break; |
1852 | 0 | default: |
1853 | 0 | formula[offset] = *header; |
1854 | 0 | break; |
1855 | 0 | } |
1856 | 0 | offset++; |
1857 | 0 | header++; |
1858 | 0 | } |
1859 | | |
1860 | | /* Write the end of the string. */ |
1861 | 0 | memcpy(&formula[offset], "])\0", sizeof("])\0")); |
1862 | |
|
1863 | 0 | worksheet_write_formula_num(self, row, col, formula, format, value); |
1864 | 0 | } |
1865 | | |
1866 | | /* Write a worksheet table column formula. */ |
1867 | | void |
1868 | | _write_column_formula(lxw_worksheet *self, lxw_row_t first_row, |
1869 | | lxw_row_t last_row, lxw_col_t col, |
1870 | | lxw_table_column *column) |
1871 | 0 | { |
1872 | 0 | lxw_row_t row; |
1873 | 0 | const char *formula = column->formula; |
1874 | 0 | lxw_format *format = column->format; |
1875 | |
|
1876 | 0 | for (row = first_row; row <= last_row; row++) |
1877 | 0 | worksheet_write_formula(self, row, col, formula, format); |
1878 | 0 | } |
1879 | | |
1880 | | /* Set the defaults for table columns in worksheet_add_table(). */ |
1881 | | void |
1882 | | _write_table_column_data(lxw_worksheet *self, lxw_table_obj *table_obj) |
1883 | 0 | { |
1884 | 0 | uint16_t i; |
1885 | 0 | lxw_table_column *column; |
1886 | 0 | lxw_table_column **columns = table_obj->columns; |
1887 | |
|
1888 | 0 | lxw_col_t col; |
1889 | 0 | lxw_row_t first_row = table_obj->first_row; |
1890 | 0 | lxw_col_t first_col = table_obj->first_col; |
1891 | 0 | lxw_row_t last_row = table_obj->last_row; |
1892 | 0 | lxw_row_t first_data_row = first_row; |
1893 | 0 | lxw_row_t last_data_row = last_row; |
1894 | |
|
1895 | 0 | if (!table_obj->no_header_row) |
1896 | 0 | first_data_row++; |
1897 | |
|
1898 | 0 | if (table_obj->total_row) |
1899 | 0 | last_data_row--; |
1900 | |
|
1901 | 0 | for (i = 0; i < table_obj->num_cols; i++) { |
1902 | 0 | col = first_col + i; |
1903 | 0 | column = columns[i]; |
1904 | |
|
1905 | 0 | if (table_obj->no_header_row == LXW_FALSE) |
1906 | 0 | worksheet_write_string(self, first_row, col, column->header, |
1907 | 0 | column->header_format); |
1908 | |
|
1909 | 0 | if (column->total_string) |
1910 | 0 | worksheet_write_string(self, last_row, col, column->total_string, |
1911 | 0 | NULL); |
1912 | |
|
1913 | 0 | if (column->total_function) |
1914 | 0 | _write_column_function(self, last_row, col, column); |
1915 | |
|
1916 | 0 | if (column->formula) |
1917 | 0 | _write_column_formula(self, first_data_row, last_data_row, col, |
1918 | 0 | column); |
1919 | 0 | } |
1920 | 0 | } |
1921 | | |
1922 | | /* |
1923 | | * Check that there are sufficient data rows in a worksheet table. |
1924 | | */ |
1925 | | lxw_error |
1926 | | _check_table_rows(lxw_row_t first_row, lxw_row_t last_row, |
1927 | | lxw_table_options *user_options) |
1928 | 0 | { |
1929 | 0 | lxw_row_t num_non_header_rows = last_row - first_row; |
1930 | |
|
1931 | 0 | if (user_options && user_options->no_header_row == LXW_TRUE) |
1932 | 0 | num_non_header_rows++; |
1933 | |
|
1934 | 0 | if (num_non_header_rows == 0) { |
1935 | 0 | LXW_WARN_FORMAT("worksheet_add_table(): " |
1936 | 0 | "table must have at least 1 non-header row."); |
1937 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
1938 | 0 | } |
1939 | | |
1940 | 0 | return LXW_NO_ERROR; |
1941 | 0 | } |
1942 | | |
1943 | | /* |
1944 | | * Check that the the table name is valid. |
1945 | | */ |
1946 | | lxw_error |
1947 | | _check_table_name(lxw_table_options *user_options) |
1948 | 0 | { |
1949 | 0 | const char *name; |
1950 | 0 | char *ptr; |
1951 | 0 | char first[2] = { 0, 0 }; |
1952 | |
|
1953 | 0 | if (!user_options) |
1954 | 0 | return LXW_NO_ERROR; |
1955 | | |
1956 | 0 | if (!user_options->name) |
1957 | 0 | return LXW_NO_ERROR; |
1958 | | |
1959 | 0 | name = user_options->name; |
1960 | | |
1961 | | /* Check table name length. */ |
1962 | 0 | if (lxw_utf8_strlen(name) > 255) { |
1963 | 0 | LXW_WARN_FORMAT("worksheet_add_table(): " |
1964 | 0 | "Table name exceeds Excel's limit of 255."); |
1965 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
1966 | 0 | } |
1967 | | |
1968 | | /* Check some short invalid names. */ |
1969 | 0 | if (strlen(name) == 1 |
1970 | 0 | && (name[0] == 'C' || name[0] == 'c' || name[0] == 'R' |
1971 | 0 | || name[0] == 'r')) { |
1972 | 0 | LXW_WARN_FORMAT1("worksheet_add_table(): " |
1973 | 0 | "invalid table name \"%s\".", name); |
1974 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
1975 | 0 | } |
1976 | | |
1977 | | /* Check for invalid characters in Table name, while trying to allow |
1978 | | * for utf8 strings. */ |
1979 | 0 | ptr = strpbrk(name, " !\"#$%&'()*+,-/:;<=>?@[\\]^`{|}~"); |
1980 | 0 | if (ptr) { |
1981 | 0 | LXW_WARN_FORMAT2("worksheet_add_table(): " |
1982 | 0 | "invalid character '%c' in table name \"%s\".", |
1983 | 0 | *ptr, name); |
1984 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
1985 | 0 | } |
1986 | | |
1987 | | /* Check for invalid initial character in Table name, while trying to allow |
1988 | | * for utf8 strings. */ |
1989 | 0 | first[0] = name[0]; |
1990 | 0 | ptr = strpbrk(first, " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^`{|}~"); |
1991 | 0 | if (ptr) { |
1992 | 0 | LXW_WARN_FORMAT2("worksheet_add_table(): " |
1993 | 0 | "invalid first character '%c' in table name \"%s\".", |
1994 | 0 | *ptr, name); |
1995 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
1996 | 0 | } |
1997 | | |
1998 | 0 | return LXW_NO_ERROR; |
1999 | 0 | } |
2000 | | |
2001 | | /***************************************************************************** |
2002 | | * |
2003 | | * XML functions. |
2004 | | * |
2005 | | ****************************************************************************/ |
2006 | | /* |
2007 | | * Write the XML declaration. |
2008 | | */ |
2009 | | STATIC void |
2010 | | _worksheet_xml_declaration(lxw_worksheet *self) |
2011 | 1.30k | { |
2012 | 1.30k | lxw_xml_declaration(self->file); |
2013 | 1.30k | } |
2014 | | |
2015 | | /* |
2016 | | * Write the <worksheet> element. |
2017 | | */ |
2018 | | STATIC void |
2019 | | _worksheet_write_worksheet(lxw_worksheet *self) |
2020 | 1.30k | { |
2021 | 1.30k | struct xml_attribute_list attributes; |
2022 | 1.30k | struct xml_attribute *attribute; |
2023 | 1.30k | char xmlns[] = "http://schemas.openxmlformats.org/" |
2024 | 1.30k | "spreadsheetml/2006/main"; |
2025 | 1.30k | char xmlns_r[] = "http://schemas.openxmlformats.org/" |
2026 | 1.30k | "officeDocument/2006/relationships"; |
2027 | 1.30k | char xmlns_mc[] = "http://schemas.openxmlformats.org/" |
2028 | 1.30k | "markup-compatibility/2006"; |
2029 | 1.30k | char xmlns_x14ac[] = "http://schemas.microsoft.com/" |
2030 | 1.30k | "office/spreadsheetml/2009/9/ac"; |
2031 | | |
2032 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2033 | 1.30k | LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns); |
2034 | 1.30k | LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r); |
2035 | | |
2036 | 1.30k | if (self->excel_version == 2010) { |
2037 | 0 | LXW_PUSH_ATTRIBUTES_STR("xmlns:mc", xmlns_mc); |
2038 | 0 | LXW_PUSH_ATTRIBUTES_STR("xmlns:x14ac", xmlns_x14ac); |
2039 | 0 | LXW_PUSH_ATTRIBUTES_STR("mc:Ignorable", "x14ac"); |
2040 | 0 | } |
2041 | | |
2042 | 1.30k | lxw_xml_start_tag(self->file, "worksheet", &attributes); |
2043 | 1.30k | LXW_FREE_ATTRIBUTES(); |
2044 | 1.30k | } |
2045 | | |
2046 | | /* |
2047 | | * Write the <dimension> element. |
2048 | | */ |
2049 | | STATIC void |
2050 | | _worksheet_write_dimension(lxw_worksheet *self) |
2051 | 1.30k | { |
2052 | 1.30k | struct xml_attribute_list attributes; |
2053 | 1.30k | struct xml_attribute *attribute; |
2054 | 1.30k | char ref[LXW_MAX_CELL_RANGE_LENGTH]; |
2055 | 1.30k | lxw_row_t dim_rowmin = self->dim_rowmin; |
2056 | 1.30k | lxw_row_t dim_rowmax = self->dim_rowmax; |
2057 | 1.30k | lxw_col_t dim_colmin = self->dim_colmin; |
2058 | 1.30k | lxw_col_t dim_colmax = self->dim_colmax; |
2059 | | |
2060 | 1.30k | if (dim_rowmin == LXW_ROW_MAX && dim_colmin == LXW_COL_MAX) { |
2061 | | /* If the rows and cols are still the defaults then no dimensions have |
2062 | | * been set and we use the default range "A1". */ |
2063 | 79 | lxw_rowcol_to_range(ref, 0, 0, 0, 0); |
2064 | 79 | } |
2065 | 1.22k | else if (dim_rowmin == LXW_ROW_MAX && dim_colmin != LXW_COL_MAX) { |
2066 | | /* If the rows aren't set but the columns are then the dimensions have |
2067 | | * been changed via set_column(). */ |
2068 | 0 | lxw_rowcol_to_range(ref, 0, dim_colmin, 0, dim_colmax); |
2069 | 0 | } |
2070 | 1.22k | else { |
2071 | 1.22k | lxw_rowcol_to_range(ref, dim_rowmin, dim_colmin, dim_rowmax, |
2072 | 1.22k | dim_colmax); |
2073 | 1.22k | } |
2074 | | |
2075 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2076 | 1.30k | LXW_PUSH_ATTRIBUTES_STR("ref", ref); |
2077 | | |
2078 | 1.30k | lxw_xml_empty_tag(self->file, "dimension", &attributes); |
2079 | | |
2080 | 1.30k | LXW_FREE_ATTRIBUTES(); |
2081 | 1.30k | } |
2082 | | |
2083 | | /* |
2084 | | * Write the <pane> element for freeze panes. |
2085 | | */ |
2086 | | STATIC void |
2087 | | _worksheet_write_freeze_panes(lxw_worksheet *self) |
2088 | 0 | { |
2089 | 0 | struct xml_attribute_list attributes; |
2090 | 0 | struct xml_attribute *attribute; |
2091 | |
|
2092 | 0 | lxw_selection *selection; |
2093 | 0 | lxw_selection *user_selection; |
2094 | 0 | lxw_row_t row = self->panes.first_row; |
2095 | 0 | lxw_col_t col = self->panes.first_col; |
2096 | 0 | lxw_row_t top_row = self->panes.top_row; |
2097 | 0 | lxw_col_t left_col = self->panes.left_col; |
2098 | |
|
2099 | 0 | char row_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2100 | 0 | char col_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2101 | 0 | char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2102 | 0 | char active_pane[LXW_PANE_NAME_LENGTH]; |
2103 | | |
2104 | | /* If there is a user selection we remove it from the list and use it. */ |
2105 | 0 | if (!STAILQ_EMPTY(self->selections)) { |
2106 | 0 | user_selection = STAILQ_FIRST(self->selections); |
2107 | 0 | STAILQ_REMOVE_HEAD(self->selections, list_pointers); |
2108 | 0 | } |
2109 | 0 | else { |
2110 | | /* or else create a new blank selection. */ |
2111 | 0 | user_selection = calloc(1, sizeof(lxw_selection)); |
2112 | 0 | RETURN_VOID_ON_MEM_ERROR(user_selection); |
2113 | 0 | } |
2114 | | |
2115 | 0 | LXW_INIT_ATTRIBUTES(); |
2116 | |
|
2117 | 0 | lxw_rowcol_to_cell(top_left_cell, top_row, left_col); |
2118 | | |
2119 | | /* Set the active pane. */ |
2120 | 0 | if (row && col) { |
2121 | 0 | lxw_strcpy(active_pane, "bottomRight"); |
2122 | |
|
2123 | 0 | lxw_rowcol_to_cell(row_cell, row, 0); |
2124 | 0 | lxw_rowcol_to_cell(col_cell, 0, col); |
2125 | |
|
2126 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2127 | 0 | if (selection) { |
2128 | 0 | lxw_strcpy(selection->pane, "topRight"); |
2129 | 0 | lxw_strcpy(selection->active_cell, col_cell); |
2130 | 0 | lxw_strcpy(selection->sqref, col_cell); |
2131 | |
|
2132 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2133 | 0 | } |
2134 | |
|
2135 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2136 | 0 | if (selection) { |
2137 | 0 | lxw_strcpy(selection->pane, "bottomLeft"); |
2138 | 0 | lxw_strcpy(selection->active_cell, row_cell); |
2139 | 0 | lxw_strcpy(selection->sqref, row_cell); |
2140 | |
|
2141 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2142 | 0 | } |
2143 | |
|
2144 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2145 | 0 | if (selection) { |
2146 | 0 | lxw_strcpy(selection->pane, "bottomRight"); |
2147 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2148 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2149 | |
|
2150 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2151 | 0 | } |
2152 | 0 | } |
2153 | 0 | else if (col) { |
2154 | 0 | lxw_strcpy(active_pane, "topRight"); |
2155 | |
|
2156 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2157 | 0 | if (selection) { |
2158 | 0 | lxw_strcpy(selection->pane, "topRight"); |
2159 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2160 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2161 | |
|
2162 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2163 | 0 | } |
2164 | 0 | } |
2165 | 0 | else { |
2166 | 0 | lxw_strcpy(active_pane, "bottomLeft"); |
2167 | |
|
2168 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2169 | 0 | if (selection) { |
2170 | 0 | lxw_strcpy(selection->pane, "bottomLeft"); |
2171 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2172 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2173 | |
|
2174 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2175 | 0 | } |
2176 | 0 | } |
2177 | |
|
2178 | 0 | if (col) |
2179 | 0 | LXW_PUSH_ATTRIBUTES_INT("xSplit", col); |
2180 | |
|
2181 | 0 | if (row) |
2182 | 0 | LXW_PUSH_ATTRIBUTES_INT("ySplit", row); |
2183 | |
|
2184 | 0 | LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell); |
2185 | 0 | LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane); |
2186 | |
|
2187 | 0 | if (self->panes.type == FREEZE_PANES) |
2188 | 0 | LXW_PUSH_ATTRIBUTES_STR("state", "frozen"); |
2189 | 0 | else if (self->panes.type == FREEZE_SPLIT_PANES) |
2190 | 0 | LXW_PUSH_ATTRIBUTES_STR("state", "frozenSplit"); |
2191 | |
|
2192 | 0 | lxw_xml_empty_tag(self->file, "pane", &attributes); |
2193 | |
|
2194 | 0 | free(user_selection); |
2195 | |
|
2196 | 0 | LXW_FREE_ATTRIBUTES(); |
2197 | 0 | } |
2198 | | |
2199 | | /* |
2200 | | * Convert column width from user units to pane split width. |
2201 | | */ |
2202 | | STATIC uint32_t |
2203 | | _worksheet_calculate_x_split_width(double x_split) |
2204 | 0 | { |
2205 | 0 | uint32_t width; |
2206 | 0 | uint32_t pixels; |
2207 | 0 | uint32_t points; |
2208 | 0 | uint32_t twips; |
2209 | 0 | double max_digit_width = 7.0; /* For Calabri 11. */ |
2210 | 0 | double padding = 5.0; |
2211 | | |
2212 | | /* Convert to pixels. */ |
2213 | 0 | if (x_split < 1.0) { |
2214 | 0 | pixels = (uint32_t) (x_split * (max_digit_width + padding) + 0.5); |
2215 | 0 | } |
2216 | 0 | else { |
2217 | 0 | pixels = (uint32_t) (x_split * max_digit_width + 0.5) + 5; |
2218 | 0 | } |
2219 | | |
2220 | | /* Convert to points. */ |
2221 | 0 | points = (pixels * 3) / 4; |
2222 | | |
2223 | | /* Convert to twips (twentieths of a point). */ |
2224 | 0 | twips = points * 20; |
2225 | | |
2226 | | /* Add offset/padding. */ |
2227 | 0 | width = twips + 390; |
2228 | |
|
2229 | 0 | return width; |
2230 | 0 | } |
2231 | | |
2232 | | /* |
2233 | | * Write the <pane> element for split panes. |
2234 | | */ |
2235 | | STATIC void |
2236 | | _worksheet_write_split_panes(lxw_worksheet *self) |
2237 | 0 | { |
2238 | 0 | struct xml_attribute_list attributes; |
2239 | 0 | struct xml_attribute *attribute; |
2240 | |
|
2241 | 0 | lxw_selection *selection; |
2242 | 0 | lxw_selection *user_selection; |
2243 | 0 | lxw_row_t row = self->panes.first_row; |
2244 | 0 | lxw_col_t col = self->panes.first_col; |
2245 | 0 | lxw_row_t top_row = self->panes.top_row; |
2246 | 0 | lxw_col_t left_col = self->panes.left_col; |
2247 | 0 | double x_split = self->panes.x_split; |
2248 | 0 | double y_split = self->panes.y_split; |
2249 | 0 | uint8_t has_selection = LXW_FALSE; |
2250 | |
|
2251 | 0 | char row_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2252 | 0 | char col_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2253 | 0 | char top_left_cell[LXW_MAX_CELL_NAME_LENGTH]; |
2254 | 0 | char active_pane[LXW_PANE_NAME_LENGTH]; |
2255 | | |
2256 | | /* If there is a user selection we remove it from the list and use it. */ |
2257 | 0 | if (!STAILQ_EMPTY(self->selections)) { |
2258 | 0 | user_selection = STAILQ_FIRST(self->selections); |
2259 | 0 | STAILQ_REMOVE_HEAD(self->selections, list_pointers); |
2260 | 0 | has_selection = LXW_TRUE; |
2261 | 0 | } |
2262 | 0 | else { |
2263 | | /* or else create a new blank selection. */ |
2264 | 0 | user_selection = calloc(1, sizeof(lxw_selection)); |
2265 | 0 | RETURN_VOID_ON_MEM_ERROR(user_selection); |
2266 | 0 | } |
2267 | | |
2268 | 0 | LXW_INIT_ATTRIBUTES(); |
2269 | | |
2270 | | /* Convert the row and col to 1/20 twip units with padding. */ |
2271 | 0 | if (y_split > 0.0) |
2272 | 0 | y_split = (uint32_t) (20 * y_split + 300); |
2273 | |
|
2274 | 0 | if (x_split > 0.0) |
2275 | 0 | x_split = _worksheet_calculate_x_split_width(x_split); |
2276 | | |
2277 | | /* For non-explicit topLeft definitions, estimate the cell offset based on |
2278 | | * the pixels dimensions. This is only a workaround and doesn't take |
2279 | | * adjusted cell dimensions into account. |
2280 | | */ |
2281 | 0 | if (top_row == row && left_col == col) { |
2282 | 0 | top_row = (lxw_row_t) (0.5 + (y_split - 300.0) / 20.0 / 15.0); |
2283 | 0 | left_col = (lxw_col_t) (0.5 + (x_split - 390.0) / 20.0 / 3.0 / 16.0); |
2284 | 0 | } |
2285 | |
|
2286 | 0 | lxw_rowcol_to_cell(top_left_cell, top_row, left_col); |
2287 | | |
2288 | | /* If there is no selection set the active cell to the top left cell. */ |
2289 | 0 | if (!has_selection) { |
2290 | 0 | lxw_strcpy(user_selection->active_cell, top_left_cell); |
2291 | 0 | lxw_strcpy(user_selection->sqref, top_left_cell); |
2292 | 0 | } |
2293 | | |
2294 | | /* Set the active pane. */ |
2295 | 0 | if (y_split > 0.0 && x_split > 0.0) { |
2296 | 0 | lxw_strcpy(active_pane, "bottomRight"); |
2297 | |
|
2298 | 0 | lxw_rowcol_to_cell(row_cell, top_row, 0); |
2299 | 0 | lxw_rowcol_to_cell(col_cell, 0, left_col); |
2300 | |
|
2301 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2302 | 0 | if (selection) { |
2303 | 0 | lxw_strcpy(selection->pane, "topRight"); |
2304 | 0 | lxw_strcpy(selection->active_cell, col_cell); |
2305 | 0 | lxw_strcpy(selection->sqref, col_cell); |
2306 | |
|
2307 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2308 | 0 | } |
2309 | |
|
2310 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2311 | 0 | if (selection) { |
2312 | 0 | lxw_strcpy(selection->pane, "bottomLeft"); |
2313 | 0 | lxw_strcpy(selection->active_cell, row_cell); |
2314 | 0 | lxw_strcpy(selection->sqref, row_cell); |
2315 | |
|
2316 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2317 | 0 | } |
2318 | |
|
2319 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2320 | 0 | if (selection) { |
2321 | 0 | lxw_strcpy(selection->pane, "bottomRight"); |
2322 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2323 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2324 | |
|
2325 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2326 | 0 | } |
2327 | 0 | } |
2328 | 0 | else if (x_split > 0.0) { |
2329 | 0 | lxw_strcpy(active_pane, "topRight"); |
2330 | |
|
2331 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2332 | 0 | if (selection) { |
2333 | 0 | lxw_strcpy(selection->pane, "topRight"); |
2334 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2335 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2336 | |
|
2337 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2338 | 0 | } |
2339 | 0 | } |
2340 | 0 | else { |
2341 | 0 | lxw_strcpy(active_pane, "bottomLeft"); |
2342 | |
|
2343 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
2344 | 0 | if (selection) { |
2345 | 0 | lxw_strcpy(selection->pane, "bottomLeft"); |
2346 | 0 | lxw_strcpy(selection->active_cell, user_selection->active_cell); |
2347 | 0 | lxw_strcpy(selection->sqref, user_selection->sqref); |
2348 | |
|
2349 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
2350 | 0 | } |
2351 | 0 | } |
2352 | |
|
2353 | 0 | if (x_split > 0.0) |
2354 | 0 | LXW_PUSH_ATTRIBUTES_DBL("xSplit", x_split); |
2355 | |
|
2356 | 0 | if (y_split > 0.0) |
2357 | 0 | LXW_PUSH_ATTRIBUTES_DBL("ySplit", y_split); |
2358 | |
|
2359 | 0 | LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell); |
2360 | |
|
2361 | 0 | if (has_selection) |
2362 | 0 | LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane); |
2363 | |
|
2364 | 0 | lxw_xml_empty_tag(self->file, "pane", &attributes); |
2365 | |
|
2366 | 0 | free(user_selection); |
2367 | |
|
2368 | 0 | LXW_FREE_ATTRIBUTES(); |
2369 | 0 | } |
2370 | | |
2371 | | /* |
2372 | | * Write the <selection> element. |
2373 | | */ |
2374 | | STATIC void |
2375 | | _worksheet_write_selection(lxw_worksheet *self, lxw_selection *selection) |
2376 | 0 | { |
2377 | 0 | struct xml_attribute_list attributes; |
2378 | 0 | struct xml_attribute *attribute; |
2379 | |
|
2380 | 0 | LXW_INIT_ATTRIBUTES(); |
2381 | |
|
2382 | 0 | if (*selection->pane) |
2383 | 0 | LXW_PUSH_ATTRIBUTES_STR("pane", selection->pane); |
2384 | |
|
2385 | 0 | if (*selection->active_cell) |
2386 | 0 | LXW_PUSH_ATTRIBUTES_STR("activeCell", selection->active_cell); |
2387 | |
|
2388 | 0 | if (*selection->sqref) |
2389 | 0 | LXW_PUSH_ATTRIBUTES_STR("sqref", selection->sqref); |
2390 | |
|
2391 | 0 | lxw_xml_empty_tag(self->file, "selection", &attributes); |
2392 | |
|
2393 | 0 | LXW_FREE_ATTRIBUTES(); |
2394 | 0 | } |
2395 | | |
2396 | | /* |
2397 | | * Write the <selection> elements. |
2398 | | */ |
2399 | | STATIC void |
2400 | | _worksheet_write_selections(lxw_worksheet *self) |
2401 | 0 | { |
2402 | 0 | lxw_selection *selection; |
2403 | |
|
2404 | 0 | STAILQ_FOREACH(selection, self->selections, list_pointers) { |
2405 | 0 | _worksheet_write_selection(self, selection); |
2406 | 0 | } |
2407 | 0 | } |
2408 | | |
2409 | | /* |
2410 | | * Write the frozen or split <pane> elements. |
2411 | | */ |
2412 | | STATIC void |
2413 | | _worksheet_write_panes(lxw_worksheet *self) |
2414 | 0 | { |
2415 | 0 | if (self->panes.type == NO_PANES) |
2416 | 0 | return; |
2417 | | |
2418 | 0 | else if (self->panes.type == FREEZE_PANES) |
2419 | 0 | _worksheet_write_freeze_panes(self); |
2420 | | |
2421 | 0 | else if (self->panes.type == FREEZE_SPLIT_PANES) |
2422 | 0 | _worksheet_write_freeze_panes(self); |
2423 | | |
2424 | 0 | else if (self->panes.type == SPLIT_PANES) |
2425 | 0 | _worksheet_write_split_panes(self); |
2426 | 0 | } |
2427 | | |
2428 | | /* |
2429 | | * Write the <sheetView> element. |
2430 | | */ |
2431 | | STATIC void |
2432 | | _worksheet_write_sheet_view(lxw_worksheet *self) |
2433 | 1.30k | { |
2434 | 1.30k | struct xml_attribute_list attributes; |
2435 | 1.30k | struct xml_attribute *attribute; |
2436 | | |
2437 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2438 | | |
2439 | | /* Hide screen gridlines if required */ |
2440 | 1.30k | if (!self->screen_gridlines) |
2441 | 0 | LXW_PUSH_ATTRIBUTES_STR("showGridLines", "0"); |
2442 | | |
2443 | | /* Hide zeroes in cells. */ |
2444 | 1.30k | if (!self->show_zeros) |
2445 | 0 | LXW_PUSH_ATTRIBUTES_STR("showZeros", "0"); |
2446 | | |
2447 | | /* Display worksheet right to left for Hebrew, Arabic and others. */ |
2448 | 1.30k | if (self->right_to_left) |
2449 | 0 | LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1"); |
2450 | | |
2451 | | /* Show that the sheet tab is selected. */ |
2452 | 1.30k | if (self->selected) |
2453 | 1.30k | LXW_PUSH_ATTRIBUTES_STR("tabSelected", "1"); |
2454 | | |
2455 | | /* Turn outlines off. Also required in the outlinePr element. */ |
2456 | 1.30k | if (!self->outline_on) |
2457 | 0 | LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0"); |
2458 | | |
2459 | | /* Set the page view/layout mode if required. */ |
2460 | 1.30k | if (self->page_view) |
2461 | 0 | LXW_PUSH_ATTRIBUTES_STR("view", "pageLayout"); |
2462 | | |
2463 | | /* Set the top left cell if required. */ |
2464 | 1.30k | if (self->top_left_cell[0]) |
2465 | 0 | LXW_PUSH_ATTRIBUTES_STR("topLeftCell", self->top_left_cell); |
2466 | | |
2467 | | /* Set the zoom level. */ |
2468 | 1.30k | if (self->zoom != 100 && !self->page_view) { |
2469 | 0 | LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom); |
2470 | |
|
2471 | 0 | if (self->zoom_scale_normal) |
2472 | 0 | LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom); |
2473 | 0 | } |
2474 | | |
2475 | 1.30k | LXW_PUSH_ATTRIBUTES_STR("workbookViewId", "0"); |
2476 | | |
2477 | 1.30k | if (self->panes.type != NO_PANES || !STAILQ_EMPTY(self->selections)) { |
2478 | 0 | lxw_xml_start_tag(self->file, "sheetView", &attributes); |
2479 | 0 | _worksheet_write_panes(self); |
2480 | 0 | _worksheet_write_selections(self); |
2481 | 0 | lxw_xml_end_tag(self->file, "sheetView"); |
2482 | 0 | } |
2483 | 1.30k | else { |
2484 | 1.30k | lxw_xml_empty_tag(self->file, "sheetView", &attributes); |
2485 | 1.30k | } |
2486 | | |
2487 | 1.30k | LXW_FREE_ATTRIBUTES(); |
2488 | 1.30k | } |
2489 | | |
2490 | | /* |
2491 | | * Write the <sheetViews> element. |
2492 | | */ |
2493 | | STATIC void |
2494 | | _worksheet_write_sheet_views(lxw_worksheet *self) |
2495 | 1.30k | { |
2496 | 1.30k | lxw_xml_start_tag(self->file, "sheetViews", NULL); |
2497 | | |
2498 | | /* Write the sheetView element. */ |
2499 | 1.30k | _worksheet_write_sheet_view(self); |
2500 | | |
2501 | 1.30k | lxw_xml_end_tag(self->file, "sheetViews"); |
2502 | 1.30k | } |
2503 | | |
2504 | | /* |
2505 | | * Write the <sheetFormatPr> element. |
2506 | | */ |
2507 | | STATIC void |
2508 | | _worksheet_write_sheet_format_pr(lxw_worksheet *self) |
2509 | 1.30k | { |
2510 | 1.30k | struct xml_attribute_list attributes; |
2511 | 1.30k | struct xml_attribute *attribute; |
2512 | | |
2513 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2514 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("defaultRowHeight", self->default_row_height); |
2515 | | |
2516 | 1.30k | if (self->default_row_height != LXW_DEF_ROW_HEIGHT) |
2517 | 0 | LXW_PUSH_ATTRIBUTES_STR("customHeight", "1"); |
2518 | | |
2519 | 1.30k | if (self->default_row_zeroed) |
2520 | 0 | LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1"); |
2521 | | |
2522 | 1.30k | if (self->outline_row_level) |
2523 | 0 | LXW_PUSH_ATTRIBUTES_INT("outlineLevelRow", self->outline_row_level); |
2524 | | |
2525 | 1.30k | if (self->outline_col_level) |
2526 | 0 | LXW_PUSH_ATTRIBUTES_INT("outlineLevelCol", self->outline_col_level); |
2527 | | |
2528 | 1.30k | if (self->excel_version == 2010) |
2529 | 0 | LXW_PUSH_ATTRIBUTES_STR("x14ac:dyDescent", "0.25"); |
2530 | | |
2531 | 1.30k | lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes); |
2532 | | |
2533 | 1.30k | LXW_FREE_ATTRIBUTES(); |
2534 | 1.30k | } |
2535 | | |
2536 | | /* |
2537 | | * Write the <sheetData> element. |
2538 | | */ |
2539 | | STATIC void |
2540 | | _worksheet_write_sheet_data(lxw_worksheet *self) |
2541 | 1.30k | { |
2542 | 1.30k | if (RB_EMPTY(self->table)) { |
2543 | 88 | lxw_xml_empty_tag(self->file, "sheetData", NULL); |
2544 | 88 | } |
2545 | 1.21k | else { |
2546 | 1.21k | lxw_xml_start_tag(self->file, "sheetData", NULL); |
2547 | 1.21k | _worksheet_write_rows(self); |
2548 | 1.21k | lxw_xml_end_tag(self->file, "sheetData"); |
2549 | 1.21k | } |
2550 | 1.30k | } |
2551 | | |
2552 | | /* |
2553 | | * Write the <sheetData> element when the memory optimization is on. In which |
2554 | | * case we read the data stored in the temp file and rewrite it to the XML |
2555 | | * sheet file. |
2556 | | */ |
2557 | | STATIC void |
2558 | | _worksheet_write_optimized_sheet_data(lxw_worksheet *self) |
2559 | 0 | { |
2560 | 0 | size_t read_size = 1; |
2561 | 0 | char buffer[LXW_BUFFER_SIZE]; |
2562 | |
|
2563 | 0 | if (self->dim_rowmin == LXW_ROW_MAX) { |
2564 | | /* If the dimensions aren't defined then there is no data to write. */ |
2565 | 0 | lxw_xml_empty_tag(self->file, "sheetData", NULL); |
2566 | 0 | } |
2567 | 0 | else { |
2568 | |
|
2569 | 0 | lxw_xml_start_tag(self->file, "sheetData", NULL); |
2570 | | |
2571 | | /* Flush the temp file. */ |
2572 | 0 | fflush(self->optimize_tmpfile); |
2573 | |
|
2574 | 0 | if (self->optimize_buffer) { |
2575 | | /* Ignore return value. There is no easy way to raise error. */ |
2576 | 0 | (void) fwrite(self->optimize_buffer, self->optimize_buffer_size, |
2577 | 0 | 1, self->file); |
2578 | 0 | } |
2579 | 0 | else { |
2580 | | /* Rewind the temp file. */ |
2581 | 0 | rewind(self->optimize_tmpfile); |
2582 | 0 | while (read_size) { |
2583 | 0 | read_size = |
2584 | 0 | fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile); |
2585 | | /* Ignore return value. There is no easy way to raise error. */ |
2586 | 0 | (void) fwrite(buffer, 1, read_size, self->file); |
2587 | 0 | } |
2588 | 0 | } |
2589 | |
|
2590 | 0 | fclose(self->optimize_tmpfile); |
2591 | 0 | free(self->optimize_buffer); |
2592 | |
|
2593 | 0 | lxw_xml_end_tag(self->file, "sheetData"); |
2594 | 0 | } |
2595 | 0 | } |
2596 | | |
2597 | | /* |
2598 | | * Write the <pageMargins> element. |
2599 | | */ |
2600 | | STATIC void |
2601 | | _worksheet_write_page_margins(lxw_worksheet *self) |
2602 | 1.30k | { |
2603 | 1.30k | struct xml_attribute_list attributes; |
2604 | 1.30k | struct xml_attribute *attribute; |
2605 | 1.30k | double left = self->margin_left; |
2606 | 1.30k | double right = self->margin_right; |
2607 | 1.30k | double top = self->margin_top; |
2608 | 1.30k | double bottom = self->margin_bottom; |
2609 | 1.30k | double header = self->margin_header; |
2610 | 1.30k | double footer = self->margin_footer; |
2611 | | |
2612 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2613 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("left", left); |
2614 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("right", right); |
2615 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("top", top); |
2616 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("bottom", bottom); |
2617 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("header", header); |
2618 | 1.30k | LXW_PUSH_ATTRIBUTES_DBL("footer", footer); |
2619 | | |
2620 | 1.30k | lxw_xml_empty_tag(self->file, "pageMargins", &attributes); |
2621 | | |
2622 | 1.30k | LXW_FREE_ATTRIBUTES(); |
2623 | 1.30k | } |
2624 | | |
2625 | | /* |
2626 | | * Write the <pageSetup> element. |
2627 | | * The following is an example taken from Excel. |
2628 | | * <pageSetup |
2629 | | * paperSize="9" |
2630 | | * scale="110" |
2631 | | * fitToWidth="2" |
2632 | | * fitToHeight="2" |
2633 | | * pageOrder="overThenDown" |
2634 | | * orientation="portrait" |
2635 | | * blackAndWhite="1" |
2636 | | * draft="1" |
2637 | | * horizontalDpi="200" |
2638 | | * verticalDpi="200" |
2639 | | * r:id="rId1" |
2640 | | * /> |
2641 | | */ |
2642 | | STATIC void |
2643 | | _worksheet_write_page_setup(lxw_worksheet *self) |
2644 | 1.30k | { |
2645 | 1.30k | struct xml_attribute_list attributes; |
2646 | 1.30k | struct xml_attribute *attribute; |
2647 | | |
2648 | 1.30k | LXW_INIT_ATTRIBUTES(); |
2649 | | |
2650 | 1.30k | if (!self->page_setup_changed) |
2651 | 1.30k | return; |
2652 | | |
2653 | | /* Set paper size. */ |
2654 | 0 | if (self->paper_size) |
2655 | 0 | LXW_PUSH_ATTRIBUTES_INT("paperSize", self->paper_size); |
2656 | | |
2657 | | /* Set the print_scale. */ |
2658 | 0 | if (self->print_scale != 100) |
2659 | 0 | LXW_PUSH_ATTRIBUTES_INT("scale", self->print_scale); |
2660 | | |
2661 | | /* Set the "Fit to page" properties. */ |
2662 | 0 | if (self->fit_page && self->fit_width != 1) |
2663 | 0 | LXW_PUSH_ATTRIBUTES_INT("fitToWidth", self->fit_width); |
2664 | |
|
2665 | 0 | if (self->fit_page && self->fit_height != 1) |
2666 | 0 | LXW_PUSH_ATTRIBUTES_INT("fitToHeight", self->fit_height); |
2667 | | |
2668 | | /* Set the page print direction. */ |
2669 | 0 | if (self->page_order) |
2670 | 0 | LXW_PUSH_ATTRIBUTES_STR("pageOrder", "overThenDown"); |
2671 | | |
2672 | | /* Set start page. */ |
2673 | 0 | if (self->page_start > 1) |
2674 | 0 | LXW_PUSH_ATTRIBUTES_INT("firstPageNumber", self->page_start); |
2675 | | |
2676 | | /* Set page orientation. */ |
2677 | 0 | if (self->orientation) |
2678 | 0 | LXW_PUSH_ATTRIBUTES_STR("orientation", "portrait"); |
2679 | 0 | else |
2680 | 0 | LXW_PUSH_ATTRIBUTES_STR("orientation", "landscape"); |
2681 | |
|
2682 | 0 | if (self->black_white) |
2683 | 0 | LXW_PUSH_ATTRIBUTES_STR("blackAndWhite", "1"); |
2684 | | |
2685 | | /* Set start page active flag. */ |
2686 | 0 | if (self->page_start) |
2687 | 0 | LXW_PUSH_ATTRIBUTES_INT("useFirstPageNumber", 1); |
2688 | | |
2689 | | /* Set the DPI. Mainly only for testing. */ |
2690 | 0 | if (self->horizontal_dpi) |
2691 | 0 | LXW_PUSH_ATTRIBUTES_INT("horizontalDpi", self->horizontal_dpi); |
2692 | |
|
2693 | 0 | if (self->vertical_dpi) |
2694 | 0 | LXW_PUSH_ATTRIBUTES_INT("verticalDpi", self->vertical_dpi); |
2695 | |
|
2696 | 0 | lxw_xml_empty_tag(self->file, "pageSetup", &attributes); |
2697 | |
|
2698 | 0 | LXW_FREE_ATTRIBUTES(); |
2699 | 0 | } |
2700 | | |
2701 | | /* |
2702 | | * Write the <printOptions> element. |
2703 | | */ |
2704 | | STATIC void |
2705 | | _worksheet_write_print_options(lxw_worksheet *self) |
2706 | 1.30k | { |
2707 | 1.30k | struct xml_attribute_list attributes; |
2708 | 1.30k | struct xml_attribute *attribute; |
2709 | 1.30k | if (!self->print_options_changed) |
2710 | 1.30k | return; |
2711 | | |
2712 | 0 | LXW_INIT_ATTRIBUTES(); |
2713 | | |
2714 | | /* Set horizontal centering. */ |
2715 | 0 | if (self->hcenter) { |
2716 | 0 | LXW_PUSH_ATTRIBUTES_STR("horizontalCentered", "1"); |
2717 | 0 | } |
2718 | | |
2719 | | /* Set vertical centering. */ |
2720 | 0 | if (self->vcenter) { |
2721 | 0 | LXW_PUSH_ATTRIBUTES_STR("verticalCentered", "1"); |
2722 | 0 | } |
2723 | | |
2724 | | /* Enable row and column headers. */ |
2725 | 0 | if (self->print_headers) { |
2726 | 0 | LXW_PUSH_ATTRIBUTES_STR("headings", "1"); |
2727 | 0 | } |
2728 | | |
2729 | | /* Set printed gridlines. */ |
2730 | 0 | if (self->print_gridlines) { |
2731 | 0 | LXW_PUSH_ATTRIBUTES_STR("gridLines", "1"); |
2732 | 0 | } |
2733 | |
|
2734 | 0 | lxw_xml_empty_tag(self->file, "printOptions", &attributes); |
2735 | |
|
2736 | 0 | LXW_FREE_ATTRIBUTES(); |
2737 | 0 | } |
2738 | | |
2739 | | /* |
2740 | | * Write the <row> element. |
2741 | | */ |
2742 | | STATIC void |
2743 | | _write_row(lxw_worksheet *self, lxw_row *row, char *spans) |
2744 | 5.73k | { |
2745 | 5.73k | struct xml_attribute_list attributes; |
2746 | 5.73k | struct xml_attribute *attribute; |
2747 | 5.73k | int32_t xf_index = 0; |
2748 | 5.73k | double height; |
2749 | | |
2750 | 5.73k | if (row->format) { |
2751 | 0 | xf_index = lxw_format_get_xf_index(row->format); |
2752 | 0 | } |
2753 | | |
2754 | 5.73k | if (row->height_changed) |
2755 | 0 | height = row->height; |
2756 | 5.73k | else |
2757 | 5.73k | height = self->default_row_height; |
2758 | | |
2759 | 5.73k | LXW_INIT_ATTRIBUTES(); |
2760 | 5.73k | LXW_PUSH_ATTRIBUTES_INT("r", row->row_num + 1); |
2761 | | |
2762 | 5.73k | if (spans) |
2763 | 5.73k | LXW_PUSH_ATTRIBUTES_STR("spans", spans); |
2764 | | |
2765 | 5.73k | if (xf_index) |
2766 | 0 | LXW_PUSH_ATTRIBUTES_INT("s", xf_index); |
2767 | | |
2768 | 5.73k | if (row->format) |
2769 | 0 | LXW_PUSH_ATTRIBUTES_STR("customFormat", "1"); |
2770 | | |
2771 | 5.73k | if (height != LXW_DEF_ROW_HEIGHT) |
2772 | 0 | LXW_PUSH_ATTRIBUTES_DBL("ht", height); |
2773 | | |
2774 | 5.73k | if (row->hidden) |
2775 | 0 | LXW_PUSH_ATTRIBUTES_STR("hidden", "1"); |
2776 | | |
2777 | 5.73k | if (height != LXW_DEF_ROW_HEIGHT) |
2778 | 0 | LXW_PUSH_ATTRIBUTES_STR("customHeight", "1"); |
2779 | | |
2780 | 5.73k | if (row->level) |
2781 | 0 | LXW_PUSH_ATTRIBUTES_INT("outlineLevel", row->level); |
2782 | | |
2783 | 5.73k | if (row->collapsed) |
2784 | 0 | LXW_PUSH_ATTRIBUTES_STR("collapsed", "1"); |
2785 | | |
2786 | 5.73k | if (self->excel_version == 2010) |
2787 | 0 | LXW_PUSH_ATTRIBUTES_STR("x14ac:dyDescent", "0.25"); |
2788 | | |
2789 | 5.73k | if (!row->data_changed) |
2790 | 0 | lxw_xml_empty_tag(self->file, "row", &attributes); |
2791 | 5.73k | else |
2792 | 5.73k | lxw_xml_start_tag(self->file, "row", &attributes); |
2793 | | |
2794 | 5.73k | LXW_FREE_ATTRIBUTES(); |
2795 | 5.73k | } |
2796 | | |
2797 | | /* |
2798 | | * Convert the width of a cell from user's units to pixels. Excel rounds the |
2799 | | * column width to the nearest pixel. If the width hasn't been set by the user |
2800 | | * we use the default value. If the column is hidden it has a value of zero. |
2801 | | */ |
2802 | | STATIC int32_t |
2803 | | _worksheet_size_col(lxw_worksheet *self, lxw_col_t col_num, uint8_t anchor) |
2804 | 0 | { |
2805 | 0 | lxw_col_options *col_opt = NULL; |
2806 | 0 | uint32_t pixels; |
2807 | 0 | double width; |
2808 | 0 | double max_digit_width = 7.0; /* For Calabri 11. */ |
2809 | 0 | double padding = 5.0; |
2810 | 0 | lxw_col_t col_index; |
2811 | | |
2812 | | /* Search for the col number in the array of col_options. Each col_option |
2813 | | * entry contains the start and end column for a range. |
2814 | | */ |
2815 | 0 | for (col_index = 0; col_index < self->col_options_max; col_index++) { |
2816 | 0 | col_opt = self->col_options[col_index]; |
2817 | |
|
2818 | 0 | if (col_opt) { |
2819 | 0 | if (col_num >= col_opt->firstcol && col_num <= col_opt->lastcol) |
2820 | 0 | break; |
2821 | 0 | else |
2822 | 0 | col_opt = NULL; |
2823 | 0 | } |
2824 | 0 | } |
2825 | |
|
2826 | 0 | if (col_opt) { |
2827 | 0 | width = col_opt->width; |
2828 | | |
2829 | | /* Convert to pixels. */ |
2830 | 0 | if (col_opt->hidden && anchor != LXW_OBJECT_MOVE_AND_SIZE_AFTER) { |
2831 | 0 | pixels = 0; |
2832 | 0 | } |
2833 | 0 | else if (width < 1.0) { |
2834 | 0 | pixels = (uint32_t) (width * (max_digit_width + padding) + 0.5); |
2835 | 0 | } |
2836 | 0 | else { |
2837 | 0 | pixels = (uint32_t) (width * max_digit_width + 0.5) + 5; |
2838 | 0 | } |
2839 | 0 | } |
2840 | 0 | else { |
2841 | 0 | pixels = self->default_col_pixels; |
2842 | 0 | } |
2843 | |
|
2844 | 0 | return pixels; |
2845 | 0 | } |
2846 | | |
2847 | | /* |
2848 | | * Convert the height of a cell from user's units to pixels. If the height |
2849 | | * hasn't been set by the user we use the default value. If the row is hidden |
2850 | | * it has a value of zero. |
2851 | | */ |
2852 | | STATIC int32_t |
2853 | | _worksheet_size_row(lxw_worksheet *self, lxw_row_t row_num, uint8_t anchor) |
2854 | 0 | { |
2855 | 0 | lxw_row *row; |
2856 | 0 | uint32_t pixels; |
2857 | |
|
2858 | 0 | row = lxw_worksheet_find_row(self, row_num); |
2859 | | |
2860 | | /* Note, the 0.75 below is due to the difference between 72/96 DPI. */ |
2861 | 0 | if (row) { |
2862 | 0 | if (row->hidden && anchor != LXW_OBJECT_MOVE_AND_SIZE_AFTER) |
2863 | 0 | pixels = 0; |
2864 | 0 | else |
2865 | 0 | pixels = (uint32_t) (row->height / 0.75); |
2866 | 0 | } |
2867 | 0 | else { |
2868 | 0 | pixels = (uint32_t) (self->default_row_height / 0.75); |
2869 | 0 | } |
2870 | |
|
2871 | 0 | return pixels; |
2872 | 0 | } |
2873 | | |
2874 | | /* |
2875 | | * Calculate the vertices that define the position of a graphical object |
2876 | | * within the worksheet in pixels. |
2877 | | * +------------+------------+ |
2878 | | * | A | B | |
2879 | | * +-----+------------+------------+ |
2880 | | * | |(x1,y1) | | |
2881 | | * | 1 |(A1)._______|______ | |
2882 | | * | | | | | |
2883 | | * | | | | | |
2884 | | * +-----+----| BITMAP |-----+ |
2885 | | * | | | | | |
2886 | | * | 2 | |______________. | |
2887 | | * | | | (B2)| |
2888 | | * | | | (x2,y2)| |
2889 | | * +---- +------------+------------+ |
2890 | | * |
2891 | | * Example of an object that covers some of the area from cell A1 to cell B2. |
2892 | | * Based on the width and height of the object we need to calculate 8 vars: |
2893 | | * |
2894 | | * col_start, row_start, col_end, row_end, x1, y1, x2, y2. |
2895 | | * |
2896 | | * We also calculate the absolute x and y position of the top left vertex of |
2897 | | * the object. This is required for images: |
2898 | | * |
2899 | | * x_abs, y_abs |
2900 | | * |
2901 | | * The width and height of the cells that the object occupies can be variable |
2902 | | * and have to be taken into account. |
2903 | | * |
2904 | | * The values of col_start and row_start are passed in from the calling |
2905 | | * function. The values of col_end and row_end are calculated by subtracting |
2906 | | * the width and height of the object from the width and height of the |
2907 | | * underlying cells. |
2908 | | */ |
2909 | | STATIC void |
2910 | | _worksheet_position_object_pixels(lxw_worksheet *self, |
2911 | | lxw_object_properties *object_props, |
2912 | | lxw_drawing_object *drawing_object) |
2913 | 0 | { |
2914 | 0 | lxw_col_t col_start; /* Column containing upper left corner. */ |
2915 | 0 | int32_t x1; /* Distance to left side of object. */ |
2916 | |
|
2917 | 0 | lxw_row_t row_start; /* Row containing top left corner. */ |
2918 | 0 | int32_t y1; /* Distance to top of object. */ |
2919 | |
|
2920 | 0 | lxw_col_t col_end; /* Column containing lower right corner. */ |
2921 | 0 | double x2; /* Distance to right side of object. */ |
2922 | |
|
2923 | 0 | lxw_row_t row_end; /* Row containing bottom right corner. */ |
2924 | 0 | double y2; /* Distance to bottom of object. */ |
2925 | |
|
2926 | 0 | double width; /* Width of object frame. */ |
2927 | 0 | double height; /* Height of object frame. */ |
2928 | |
|
2929 | 0 | uint32_t x_abs = 0; /* Abs. distance to left side of object. */ |
2930 | 0 | uint32_t y_abs = 0; /* Abs. distance to top side of object. */ |
2931 | |
|
2932 | 0 | uint32_t i; |
2933 | 0 | uint8_t anchor = drawing_object->anchor; |
2934 | 0 | uint8_t ignore_anchor = LXW_OBJECT_POSITION_DEFAULT; |
2935 | |
|
2936 | 0 | col_start = object_props->col; |
2937 | 0 | row_start = object_props->row; |
2938 | 0 | x1 = object_props->x_offset; |
2939 | 0 | y1 = object_props->y_offset; |
2940 | 0 | width = object_props->width; |
2941 | 0 | height = object_props->height; |
2942 | | |
2943 | | /* Adjust start column for negative offsets. */ |
2944 | 0 | while (x1 < 0 && col_start > 0) { |
2945 | 0 | x1 += _worksheet_size_col(self, col_start - 1, ignore_anchor); |
2946 | 0 | col_start--; |
2947 | 0 | } |
2948 | | |
2949 | | /* Adjust start row for negative offsets. */ |
2950 | 0 | while (y1 < 0 && row_start > 0) { |
2951 | 0 | y1 += _worksheet_size_row(self, row_start - 1, ignore_anchor); |
2952 | 0 | row_start--; |
2953 | 0 | } |
2954 | | |
2955 | | /* Ensure that the image isn't shifted off the page at top left. */ |
2956 | 0 | if (x1 < 0) |
2957 | 0 | x1 = 0; |
2958 | |
|
2959 | 0 | if (y1 < 0) |
2960 | 0 | y1 = 0; |
2961 | | |
2962 | | /* Calculate the absolute x offset of the top-left vertex. */ |
2963 | 0 | if (self->col_size_changed) { |
2964 | 0 | for (i = 0; i < col_start; i++) |
2965 | 0 | x_abs += _worksheet_size_col(self, i, ignore_anchor); |
2966 | 0 | } |
2967 | 0 | else { |
2968 | | /* Optimization for when the column widths haven't changed. */ |
2969 | 0 | x_abs += self->default_col_pixels * col_start; |
2970 | 0 | } |
2971 | |
|
2972 | 0 | x_abs += x1; |
2973 | | |
2974 | | /* Calculate the absolute y offset of the top-left vertex. */ |
2975 | | /* Store the column change to allow optimizations. */ |
2976 | 0 | if (self->row_size_changed) { |
2977 | 0 | for (i = 0; i < row_start; i++) |
2978 | 0 | y_abs += _worksheet_size_row(self, i, ignore_anchor); |
2979 | 0 | } |
2980 | 0 | else { |
2981 | | /* Optimization for when the row heights haven"t changed. */ |
2982 | 0 | y_abs += self->default_row_pixels * row_start; |
2983 | 0 | } |
2984 | |
|
2985 | 0 | y_abs += y1; |
2986 | | |
2987 | | /* Adjust start col for offsets that are greater than the col width. */ |
2988 | 0 | while (x1 >= _worksheet_size_col(self, col_start, anchor)) { |
2989 | 0 | x1 -= _worksheet_size_col(self, col_start, ignore_anchor); |
2990 | 0 | col_start++; |
2991 | 0 | } |
2992 | | |
2993 | | /* Adjust start row for offsets that are greater than the row height. */ |
2994 | 0 | while (y1 >= _worksheet_size_row(self, row_start, anchor)) { |
2995 | 0 | y1 -= _worksheet_size_row(self, row_start, ignore_anchor); |
2996 | 0 | row_start++; |
2997 | 0 | } |
2998 | | |
2999 | | /* Initialize end cell to the same as the start cell. */ |
3000 | 0 | col_end = col_start; |
3001 | 0 | row_end = row_start; |
3002 | | |
3003 | | /* Only offset the image in the cell if the row/col is hidden. */ |
3004 | 0 | if (_worksheet_size_col(self, col_start, anchor) > 0) |
3005 | 0 | width = width + x1; |
3006 | 0 | if (_worksheet_size_row(self, row_start, anchor) > 0) |
3007 | 0 | height = height + y1; |
3008 | | |
3009 | | /* Subtract the underlying cell widths to find the end cell. */ |
3010 | 0 | while (width >= _worksheet_size_col(self, col_end, anchor) |
3011 | 0 | && col_end < LXW_COL_MAX) { |
3012 | 0 | width -= _worksheet_size_col(self, col_end, anchor); |
3013 | 0 | col_end++; |
3014 | 0 | } |
3015 | | |
3016 | | /* Subtract the underlying cell heights to find the end cell. */ |
3017 | 0 | while (height >= _worksheet_size_row(self, row_end, anchor) |
3018 | 0 | && row_end < LXW_ROW_MAX) { |
3019 | 0 | height -= _worksheet_size_row(self, row_end, anchor); |
3020 | 0 | row_end++; |
3021 | 0 | } |
3022 | | |
3023 | | /* The end vertices are whatever is left from the width and height. */ |
3024 | 0 | x2 = width; |
3025 | 0 | y2 = height; |
3026 | | |
3027 | | /* Add the dimensions to the drawing object. */ |
3028 | 0 | drawing_object->from.col = col_start; |
3029 | 0 | drawing_object->from.row = row_start; |
3030 | 0 | drawing_object->from.col_offset = x1; |
3031 | 0 | drawing_object->from.row_offset = y1; |
3032 | 0 | drawing_object->to.col = col_end; |
3033 | 0 | drawing_object->to.row = row_end; |
3034 | 0 | drawing_object->to.col_offset = x2; |
3035 | 0 | drawing_object->to.row_offset = y2; |
3036 | 0 | drawing_object->col_absolute = x_abs; |
3037 | 0 | drawing_object->row_absolute = y_abs; |
3038 | 0 | } |
3039 | | |
3040 | | /* |
3041 | | * Calculate the vertices that define the position of a graphical object |
3042 | | * within the worksheet in EMUs. The vertices are expressed as English |
3043 | | * Metric Units (EMUs). There are 12,700 EMUs per point. |
3044 | | * Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel. |
3045 | | */ |
3046 | | STATIC void |
3047 | | _worksheet_position_object_emus(lxw_worksheet *self, |
3048 | | lxw_object_properties *image, |
3049 | | lxw_drawing_object *drawing_object) |
3050 | 0 | { |
3051 | |
|
3052 | 0 | _worksheet_position_object_pixels(self, image, drawing_object); |
3053 | | |
3054 | | /* Convert the pixel values to EMUs. See above. */ |
3055 | 0 | drawing_object->from.col_offset *= 9525; |
3056 | 0 | drawing_object->from.row_offset *= 9525; |
3057 | 0 | drawing_object->to.col_offset *= 9525; |
3058 | 0 | drawing_object->to.row_offset *= 9525; |
3059 | 0 | drawing_object->to.col_offset += 0.5; |
3060 | 0 | drawing_object->to.row_offset += 0.5; |
3061 | 0 | drawing_object->col_absolute *= 9525; |
3062 | 0 | drawing_object->row_absolute *= 9525; |
3063 | 0 | } |
3064 | | |
3065 | | /* |
3066 | | * This function handles the additional optional parameters to |
3067 | | * worksheet_write_comment_opt() as well as calculating the comment object |
3068 | | * position and vertices. |
3069 | | */ |
3070 | | void |
3071 | | _get_comment_params(lxw_vml_obj *comment, lxw_comment_options *options) |
3072 | 0 | { |
3073 | |
|
3074 | 0 | lxw_row_t start_row; |
3075 | 0 | lxw_col_t start_col; |
3076 | 0 | int32_t x_offset; |
3077 | 0 | int32_t y_offset; |
3078 | 0 | uint32_t height = 74; |
3079 | 0 | uint32_t width = 128; |
3080 | 0 | double x_scale = 1.0; |
3081 | 0 | double y_scale = 1.0; |
3082 | 0 | lxw_row_t row = comment->row; |
3083 | 0 | lxw_col_t col = comment->col;; |
3084 | | |
3085 | | /* Set the default start cell and offsets for the comment. These are |
3086 | | * generally fixed in relation to the parent cell. However there are some |
3087 | | * edge cases for cells at the, well yes, edges. */ |
3088 | 0 | if (row == 0) |
3089 | 0 | y_offset = 2; |
3090 | 0 | else if (row == LXW_ROW_MAX - 3) |
3091 | 0 | y_offset = 16; |
3092 | 0 | else if (row == LXW_ROW_MAX - 2) |
3093 | 0 | y_offset = 16; |
3094 | 0 | else if (row == LXW_ROW_MAX - 1) |
3095 | 0 | y_offset = 14; |
3096 | 0 | else |
3097 | 0 | y_offset = 10; |
3098 | |
|
3099 | 0 | if (col == LXW_COL_MAX - 3) |
3100 | 0 | x_offset = 49; |
3101 | 0 | else if (col == LXW_COL_MAX - 2) |
3102 | 0 | x_offset = 49; |
3103 | 0 | else if (col == LXW_COL_MAX - 1) |
3104 | 0 | x_offset = 49; |
3105 | 0 | else |
3106 | 0 | x_offset = 15; |
3107 | |
|
3108 | 0 | if (row == 0) |
3109 | 0 | start_row = 0; |
3110 | 0 | else if (row == LXW_ROW_MAX - 3) |
3111 | 0 | start_row = LXW_ROW_MAX - 7; |
3112 | 0 | else if (row == LXW_ROW_MAX - 2) |
3113 | 0 | start_row = LXW_ROW_MAX - 6; |
3114 | 0 | else if (row == LXW_ROW_MAX - 1) |
3115 | 0 | start_row = LXW_ROW_MAX - 5; |
3116 | 0 | else |
3117 | 0 | start_row = row - 1; |
3118 | |
|
3119 | 0 | if (col == LXW_COL_MAX - 3) |
3120 | 0 | start_col = LXW_COL_MAX - 6; |
3121 | 0 | else if (col == LXW_COL_MAX - 2) |
3122 | 0 | start_col = LXW_COL_MAX - 5; |
3123 | 0 | else if (col == LXW_COL_MAX - 1) |
3124 | 0 | start_col = LXW_COL_MAX - 4; |
3125 | 0 | else |
3126 | 0 | start_col = col + 1; |
3127 | | |
3128 | | /* Set the default font properties. */ |
3129 | 0 | comment->font_size = 8; |
3130 | 0 | comment->font_family = 2; |
3131 | | |
3132 | | /* Set any user defined options. */ |
3133 | 0 | if (options) { |
3134 | |
|
3135 | 0 | if (options->width > 0.0) |
3136 | 0 | width = options->width; |
3137 | |
|
3138 | 0 | if (options->height > 0.0) |
3139 | 0 | height = options->height; |
3140 | |
|
3141 | 0 | if (options->x_scale > 0.0) |
3142 | 0 | x_scale = options->x_scale; |
3143 | |
|
3144 | 0 | if (options->y_scale > 0.0) |
3145 | 0 | y_scale = options->y_scale; |
3146 | |
|
3147 | 0 | if (options->x_offset != 0) |
3148 | 0 | x_offset = options->x_offset; |
3149 | |
|
3150 | 0 | if (options->y_offset != 0) |
3151 | 0 | y_offset = options->y_offset; |
3152 | |
|
3153 | 0 | if (options->start_row > 0 || options->start_col > 0) { |
3154 | 0 | start_row = options->start_row; |
3155 | 0 | start_col = options->start_col; |
3156 | 0 | } |
3157 | |
|
3158 | 0 | if (options->font_size > 0.0) |
3159 | 0 | comment->font_size = options->font_size; |
3160 | |
|
3161 | 0 | if (options->font_family > 0) |
3162 | 0 | comment->font_family = options->font_family; |
3163 | |
|
3164 | 0 | comment->visible = options->visible; |
3165 | 0 | comment->color = options->color; |
3166 | 0 | comment->author = lxw_strdup(options->author); |
3167 | 0 | comment->font_name = lxw_strdup(options->font_name); |
3168 | 0 | } |
3169 | | |
3170 | | /* Scale the width/height to the default/user scale and round to the |
3171 | | * nearest pixel. */ |
3172 | 0 | width = (uint32_t) (0.5 + x_scale * width); |
3173 | 0 | height = (uint32_t) (0.5 + y_scale * height); |
3174 | |
|
3175 | 0 | comment->width = width; |
3176 | 0 | comment->height = height; |
3177 | 0 | comment->start_col = start_col; |
3178 | 0 | comment->start_row = start_row; |
3179 | 0 | comment->x_offset = x_offset; |
3180 | 0 | comment->y_offset = y_offset; |
3181 | 0 | } |
3182 | | |
3183 | | /* |
3184 | | * This function handles the additional optional parameters to |
3185 | | * worksheet_insert_button() as well as calculating the button object |
3186 | | * position and vertices. |
3187 | | */ |
3188 | | lxw_error |
3189 | | _get_button_params(lxw_vml_obj *button, uint16_t button_number, |
3190 | | lxw_button_options *options) |
3191 | 0 | { |
3192 | |
|
3193 | 0 | int32_t x_offset = 0; |
3194 | 0 | int32_t y_offset = 0; |
3195 | 0 | uint32_t height = LXW_DEF_ROW_HEIGHT_PIXELS; |
3196 | 0 | uint32_t width = LXW_DEF_COL_WIDTH_PIXELS; |
3197 | 0 | double x_scale = 1.0; |
3198 | 0 | double y_scale = 1.0; |
3199 | 0 | lxw_row_t row = button->row; |
3200 | 0 | lxw_col_t col = button->col; |
3201 | 0 | char buffer[LXW_ATTR_32]; |
3202 | 0 | uint8_t has_caption = LXW_FALSE; |
3203 | 0 | uint8_t has_macro = LXW_FALSE; |
3204 | 0 | size_t len; |
3205 | | |
3206 | | /* Set any user defined options. */ |
3207 | 0 | if (options) { |
3208 | |
|
3209 | 0 | if (options->width > 0.0) |
3210 | 0 | width = options->width; |
3211 | |
|
3212 | 0 | if (options->height > 0.0) |
3213 | 0 | height = options->height; |
3214 | |
|
3215 | 0 | if (options->x_scale > 0.0) |
3216 | 0 | x_scale = options->x_scale; |
3217 | |
|
3218 | 0 | if (options->y_scale > 0.0) |
3219 | 0 | y_scale = options->y_scale; |
3220 | |
|
3221 | 0 | if (options->x_offset != 0) |
3222 | 0 | x_offset = options->x_offset; |
3223 | |
|
3224 | 0 | if (options->y_offset != 0) |
3225 | 0 | y_offset = options->y_offset; |
3226 | |
|
3227 | 0 | if (options->caption) { |
3228 | 0 | button->name = lxw_strdup(options->caption); |
3229 | 0 | RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); |
3230 | 0 | has_caption = LXW_TRUE; |
3231 | 0 | } |
3232 | | |
3233 | 0 | if (options->macro) { |
3234 | 0 | len = sizeof("[0]!") + strlen(options->macro); |
3235 | 0 | button->macro = calloc(1, len); |
3236 | 0 | RETURN_ON_MEM_ERROR(button->macro, |
3237 | 0 | LXW_ERROR_MEMORY_MALLOC_FAILED); |
3238 | | |
3239 | 0 | if (button->macro) |
3240 | 0 | lxw_snprintf(button->macro, len, "[0]!%s", options->macro); |
3241 | |
|
3242 | 0 | has_macro = LXW_TRUE; |
3243 | 0 | } |
3244 | | |
3245 | 0 | if (options->description) { |
3246 | 0 | button->text = lxw_strdup(options->description); |
3247 | 0 | RETURN_ON_MEM_ERROR(button->text, LXW_ERROR_MEMORY_MALLOC_FAILED); |
3248 | 0 | } |
3249 | 0 | } |
3250 | | |
3251 | 0 | if (!has_caption) { |
3252 | 0 | lxw_snprintf(buffer, LXW_ATTR_32, "Button %d", button_number); |
3253 | 0 | button->name = lxw_strdup(buffer); |
3254 | 0 | RETURN_ON_MEM_ERROR(button->name, LXW_ERROR_MEMORY_MALLOC_FAILED); |
3255 | 0 | } |
3256 | | |
3257 | 0 | if (!has_macro) { |
3258 | 0 | lxw_snprintf(buffer, LXW_ATTR_32, "[0]!Button%d_Click", |
3259 | 0 | button_number); |
3260 | 0 | button->macro = lxw_strdup(buffer); |
3261 | 0 | RETURN_ON_MEM_ERROR(button->macro, LXW_ERROR_MEMORY_MALLOC_FAILED); |
3262 | 0 | } |
3263 | | |
3264 | | /* Scale the width/height to the default/user scale and round to the |
3265 | | * nearest pixel. */ |
3266 | 0 | width = (uint32_t) (0.5 + x_scale * width); |
3267 | 0 | height = (uint32_t) (0.5 + y_scale * height); |
3268 | |
|
3269 | 0 | button->width = width; |
3270 | 0 | button->height = height; |
3271 | 0 | button->start_col = col; |
3272 | 0 | button->start_row = row; |
3273 | 0 | button->x_offset = x_offset; |
3274 | 0 | button->y_offset = y_offset; |
3275 | |
|
3276 | 0 | return LXW_NO_ERROR; |
3277 | 0 | } |
3278 | | |
3279 | | /* |
3280 | | * Calculate the vml_obj object position and vertices. |
3281 | | */ |
3282 | | void |
3283 | | _worksheet_position_vml_object(lxw_worksheet *self, lxw_vml_obj *vml_obj) |
3284 | 0 | { |
3285 | 0 | lxw_object_properties object_props; |
3286 | 0 | lxw_drawing_object drawing_object; |
3287 | |
|
3288 | 0 | object_props.col = vml_obj->start_col; |
3289 | 0 | object_props.row = vml_obj->start_row; |
3290 | 0 | object_props.x_offset = vml_obj->x_offset; |
3291 | 0 | object_props.y_offset = vml_obj->y_offset; |
3292 | 0 | object_props.width = vml_obj->width; |
3293 | 0 | object_props.height = vml_obj->height; |
3294 | |
|
3295 | 0 | drawing_object.anchor = LXW_OBJECT_DONT_MOVE_DONT_SIZE; |
3296 | |
|
3297 | 0 | _worksheet_position_object_pixels(self, &object_props, &drawing_object); |
3298 | |
|
3299 | 0 | vml_obj->from.col = drawing_object.from.col; |
3300 | 0 | vml_obj->from.row = drawing_object.from.row; |
3301 | 0 | vml_obj->from.col_offset = drawing_object.from.col_offset; |
3302 | 0 | vml_obj->from.row_offset = drawing_object.from.row_offset; |
3303 | 0 | vml_obj->to.col = drawing_object.to.col; |
3304 | 0 | vml_obj->to.row = drawing_object.to.row; |
3305 | 0 | vml_obj->to.col_offset = drawing_object.to.col_offset; |
3306 | 0 | vml_obj->to.row_offset = drawing_object.to.row_offset; |
3307 | 0 | vml_obj->col_absolute = drawing_object.col_absolute; |
3308 | 0 | vml_obj->row_absolute = drawing_object.row_absolute; |
3309 | 0 | } |
3310 | | |
3311 | | /* |
3312 | | * Set up image/drawings. |
3313 | | */ |
3314 | | void |
3315 | | lxw_worksheet_prepare_image(lxw_worksheet *self, |
3316 | | uint32_t image_ref_id, uint32_t drawing_id, |
3317 | | lxw_object_properties *object_props) |
3318 | 0 | { |
3319 | 0 | lxw_drawing_object *drawing_object; |
3320 | 0 | lxw_rel_tuple *relationship; |
3321 | 0 | double width; |
3322 | 0 | double height; |
3323 | 0 | char *url; |
3324 | 0 | char *found_string; |
3325 | 0 | size_t i; |
3326 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3327 | 0 | enum cell_types link_type = HYPERLINK_URL; |
3328 | |
|
3329 | 0 | if (!self->drawing) { |
3330 | 0 | self->drawing = lxw_drawing_new(); |
3331 | 0 | self->drawing->embedded = LXW_TRUE; |
3332 | 0 | RETURN_VOID_ON_MEM_ERROR(self->drawing); |
3333 | | |
3334 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3335 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3336 | | |
3337 | 0 | relationship->type = lxw_strdup("/drawing"); |
3338 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3339 | | |
3340 | 0 | lxw_snprintf(filename, LXW_FILENAME_LENGTH, |
3341 | 0 | "../drawings/drawing%d.xml", drawing_id); |
3342 | |
|
3343 | 0 | relationship->target = lxw_strdup(filename); |
3344 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3345 | | |
3346 | 0 | STAILQ_INSERT_TAIL(self->external_drawing_links, relationship, |
3347 | 0 | list_pointers); |
3348 | 0 | } |
3349 | | |
3350 | 0 | drawing_object = calloc(1, sizeof(lxw_drawing_object)); |
3351 | 0 | RETURN_VOID_ON_MEM_ERROR(drawing_object); |
3352 | | |
3353 | 0 | drawing_object->anchor = LXW_OBJECT_MOVE_DONT_SIZE; |
3354 | 0 | if (object_props->object_position) |
3355 | 0 | drawing_object->anchor = object_props->object_position; |
3356 | |
|
3357 | 0 | drawing_object->type = LXW_DRAWING_IMAGE; |
3358 | 0 | drawing_object->description = lxw_strdup(object_props->description); |
3359 | 0 | drawing_object->tip = lxw_strdup(object_props->tip); |
3360 | 0 | drawing_object->rel_index = 0; |
3361 | 0 | drawing_object->url_rel_index = 0; |
3362 | 0 | drawing_object->decorative = object_props->decorative; |
3363 | | |
3364 | | /* Scale to user scale. */ |
3365 | 0 | width = object_props->width * object_props->x_scale; |
3366 | 0 | height = object_props->height * object_props->y_scale; |
3367 | | |
3368 | | /* Scale by non 96dpi resolutions. */ |
3369 | 0 | width *= 96.0 / object_props->x_dpi; |
3370 | 0 | height *= 96.0 / object_props->y_dpi; |
3371 | |
|
3372 | 0 | object_props->width = width; |
3373 | 0 | object_props->height = height; |
3374 | |
|
3375 | 0 | _worksheet_position_object_emus(self, object_props, drawing_object); |
3376 | | |
3377 | | /* Convert from pixels to emus. */ |
3378 | 0 | drawing_object->width = (uint32_t) (0.5 + width * 9525); |
3379 | 0 | drawing_object->height = (uint32_t) (0.5 + height * 9525); |
3380 | |
|
3381 | 0 | lxw_add_drawing_object(self->drawing, drawing_object); |
3382 | |
|
3383 | 0 | if (object_props->url) { |
3384 | 0 | url = object_props->url; |
3385 | |
|
3386 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3387 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3388 | | |
3389 | 0 | relationship->type = lxw_strdup("/hyperlink"); |
3390 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3391 | | |
3392 | | /* Check the link type. Default to external hyperlinks. */ |
3393 | 0 | if (strstr(url, "internal:")) |
3394 | 0 | link_type = HYPERLINK_INTERNAL; |
3395 | 0 | else if (strstr(url, "external:")) |
3396 | 0 | link_type = HYPERLINK_EXTERNAL; |
3397 | 0 | else |
3398 | 0 | link_type = HYPERLINK_URL; |
3399 | | |
3400 | | /* Set the relationship object for each type of link. */ |
3401 | 0 | if (link_type == HYPERLINK_INTERNAL) { |
3402 | 0 | relationship->target_mode = NULL; |
3403 | 0 | relationship->target = lxw_strdup(url + sizeof("internal") - 1); |
3404 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3405 | | |
3406 | | /* We need to prefix the internal link/range with #. */ |
3407 | 0 | relationship->target[0] = '#'; |
3408 | 0 | } |
3409 | 0 | else if (link_type == HYPERLINK_EXTERNAL) { |
3410 | 0 | relationship->target_mode = lxw_strdup("External"); |
3411 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); |
3412 | | |
3413 | | /* Look for Windows style "C:/" link or Windows share "\\" link. */ |
3414 | 0 | found_string = strchr(url + sizeof("external:") - 1, ':'); |
3415 | 0 | if (!found_string) |
3416 | 0 | found_string = strstr(url, "\\\\"); |
3417 | |
|
3418 | 0 | if (found_string) { |
3419 | | /* Copy the url with some space at the start to overwrite |
3420 | | * "external:" with "file:///". */ |
3421 | 0 | relationship->target = lxw_escape_url_characters(url + 1, |
3422 | 0 | LXW_TRUE); |
3423 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3424 | | |
3425 | | /* Add the file:/// URI to the url if absolute path. */ |
3426 | 0 | memcpy(relationship->target, "file:///", |
3427 | 0 | sizeof("file:///") - 1); |
3428 | 0 | } |
3429 | 0 | else { |
3430 | | /* Copy the relative url without "external:". */ |
3431 | 0 | relationship->target = |
3432 | 0 | lxw_escape_url_characters(url + sizeof("external:") - 1, |
3433 | 0 | LXW_TRUE); |
3434 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3435 | | |
3436 | | /* Switch backslash to forward slash. */ |
3437 | 0 | for (i = 0; i <= strlen(relationship->target); i++) |
3438 | 0 | if (relationship->target[i] == '\\') |
3439 | 0 | relationship->target[i] = '/'; |
3440 | 0 | } |
3441 | |
|
3442 | 0 | } |
3443 | 0 | else { |
3444 | 0 | relationship->target_mode = lxw_strdup("External"); |
3445 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); |
3446 | | |
3447 | 0 | relationship->target = |
3448 | 0 | lxw_escape_url_characters(object_props->url, LXW_FALSE); |
3449 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3450 | 0 | } |
3451 | | |
3452 | | /* Check if URL exceeds Excel's length limit. */ |
3453 | 0 | if (lxw_utf8_strlen(url) > self->max_url_length) { |
3454 | 0 | LXW_WARN_FORMAT2("worksheet_insert_image()/_opt(): URL exceeds " |
3455 | 0 | "Excel's allowable length of %d characters: %s", |
3456 | 0 | self->max_url_length, url); |
3457 | 0 | goto mem_error; |
3458 | 0 | } |
3459 | | |
3460 | 0 | if (!_find_drawing_rel_index(self, url)) { |
3461 | 0 | STAILQ_INSERT_TAIL(self->drawing_links, relationship, |
3462 | 0 | list_pointers); |
3463 | 0 | } |
3464 | 0 | else { |
3465 | 0 | free(relationship->type); |
3466 | 0 | free(relationship->target); |
3467 | 0 | free(relationship->target_mode); |
3468 | 0 | free(relationship); |
3469 | 0 | } |
3470 | |
|
3471 | 0 | drawing_object->url_rel_index = _get_drawing_rel_index(self, url); |
3472 | |
|
3473 | 0 | } |
3474 | | |
3475 | 0 | if (!_find_drawing_rel_index(self, object_props->md5)) { |
3476 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3477 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3478 | | |
3479 | 0 | relationship->type = lxw_strdup("/image"); |
3480 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3481 | | |
3482 | 0 | lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id, |
3483 | 0 | object_props->extension); |
3484 | |
|
3485 | 0 | relationship->target = lxw_strdup(filename); |
3486 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3487 | | |
3488 | 0 | STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); |
3489 | 0 | } |
3490 | | |
3491 | 0 | drawing_object->rel_index = |
3492 | 0 | _get_drawing_rel_index(self, object_props->md5); |
3493 | |
|
3494 | 0 | return; |
3495 | | |
3496 | 0 | mem_error: |
3497 | 0 | if (relationship) { |
3498 | 0 | free(relationship->type); |
3499 | 0 | free(relationship->target); |
3500 | 0 | free(relationship->target_mode); |
3501 | 0 | free(relationship); |
3502 | 0 | } |
3503 | 0 | } |
3504 | | |
3505 | | /* |
3506 | | * Set up image/drawings for header/footer images. |
3507 | | */ |
3508 | | void |
3509 | | lxw_worksheet_prepare_header_image(lxw_worksheet *self, |
3510 | | uint32_t image_ref_id, |
3511 | | lxw_object_properties *object_props) |
3512 | 0 | { |
3513 | 0 | lxw_rel_tuple *relationship = NULL; |
3514 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3515 | 0 | lxw_vml_obj *header_image_vml; |
3516 | 0 | char *extension; |
3517 | |
|
3518 | 0 | STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); |
3519 | |
|
3520 | 0 | if (!_find_vml_drawing_rel_index(self, object_props->md5)) { |
3521 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3522 | 0 | RETURN_VOID_ON_MEM_ERROR(relationship); |
3523 | | |
3524 | 0 | relationship->type = lxw_strdup("/image"); |
3525 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3526 | | |
3527 | 0 | lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id, |
3528 | 0 | object_props->extension); |
3529 | |
|
3530 | 0 | relationship->target = lxw_strdup(filename); |
3531 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3532 | | |
3533 | 0 | STAILQ_INSERT_TAIL(self->vml_drawing_links, relationship, |
3534 | 0 | list_pointers); |
3535 | 0 | } |
3536 | | |
3537 | 0 | header_image_vml = calloc(1, sizeof(lxw_vml_obj)); |
3538 | 0 | GOTO_LABEL_ON_MEM_ERROR(header_image_vml, mem_error); |
3539 | | |
3540 | 0 | header_image_vml->width = (uint32_t) object_props->width; |
3541 | 0 | header_image_vml->height = (uint32_t) object_props->height; |
3542 | 0 | header_image_vml->x_dpi = object_props->x_dpi; |
3543 | 0 | header_image_vml->y_dpi = object_props->y_dpi; |
3544 | 0 | header_image_vml->rel_index = 1; |
3545 | |
|
3546 | 0 | header_image_vml->image_position = |
3547 | 0 | lxw_strdup(object_props->image_position); |
3548 | 0 | header_image_vml->name = lxw_strdup(object_props->description); |
3549 | | |
3550 | | /* Strip the extension from the filename. */ |
3551 | 0 | extension = strchr(header_image_vml->name, '.'); |
3552 | 0 | if (extension) |
3553 | 0 | *extension = '\0'; |
3554 | |
|
3555 | 0 | header_image_vml->rel_index = |
3556 | 0 | _get_vml_drawing_rel_index(self, object_props->md5); |
3557 | |
|
3558 | 0 | STAILQ_INSERT_TAIL(self->header_image_objs, header_image_vml, |
3559 | 0 | list_pointers); |
3560 | |
|
3561 | 0 | return; |
3562 | | |
3563 | 0 | mem_error: |
3564 | 0 | if (relationship) { |
3565 | 0 | free(relationship->type); |
3566 | 0 | free(relationship->target); |
3567 | 0 | free(relationship->target_mode); |
3568 | 0 | free(relationship); |
3569 | 0 | } |
3570 | 0 | } |
3571 | | |
3572 | | /* |
3573 | | * Set up background image. |
3574 | | */ |
3575 | | void |
3576 | | lxw_worksheet_prepare_background(lxw_worksheet *self, |
3577 | | uint32_t image_ref_id, |
3578 | | lxw_object_properties *object_props) |
3579 | 0 | { |
3580 | 0 | lxw_rel_tuple *relationship = NULL; |
3581 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3582 | |
|
3583 | 0 | STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); |
3584 | |
|
3585 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3586 | 0 | RETURN_VOID_ON_MEM_ERROR(relationship); |
3587 | | |
3588 | 0 | relationship->type = lxw_strdup("/image"); |
3589 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3590 | | |
3591 | 0 | lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id, |
3592 | 0 | object_props->extension); |
3593 | |
|
3594 | 0 | relationship->target = lxw_strdup(filename); |
3595 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3596 | | |
3597 | 0 | self->external_background_link = relationship; |
3598 | |
|
3599 | 0 | return; |
3600 | | |
3601 | 0 | mem_error: |
3602 | 0 | if (relationship) { |
3603 | 0 | free(relationship->type); |
3604 | 0 | free(relationship->target); |
3605 | 0 | free(relationship->target_mode); |
3606 | 0 | free(relationship); |
3607 | 0 | } |
3608 | 0 | } |
3609 | | |
3610 | | /* |
3611 | | * Set up chart/drawings. |
3612 | | */ |
3613 | | void |
3614 | | lxw_worksheet_prepare_chart(lxw_worksheet *self, |
3615 | | uint32_t chart_ref_id, |
3616 | | uint32_t drawing_id, |
3617 | | lxw_object_properties *object_props, |
3618 | | uint8_t is_chartsheet) |
3619 | 0 | { |
3620 | 0 | lxw_drawing_object *drawing_object; |
3621 | 0 | lxw_rel_tuple *relationship; |
3622 | 0 | double width; |
3623 | 0 | double height; |
3624 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3625 | |
|
3626 | 0 | if (!self->drawing) { |
3627 | 0 | self->drawing = lxw_drawing_new(); |
3628 | 0 | RETURN_VOID_ON_MEM_ERROR(self->drawing); |
3629 | | |
3630 | 0 | if (is_chartsheet) { |
3631 | 0 | self->drawing->embedded = LXW_FALSE; |
3632 | 0 | self->drawing->orientation = self->orientation; |
3633 | 0 | } |
3634 | 0 | else { |
3635 | 0 | self->drawing->embedded = LXW_TRUE; |
3636 | 0 | } |
3637 | |
|
3638 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3639 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3640 | | |
3641 | 0 | relationship->type = lxw_strdup("/drawing"); |
3642 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3643 | | |
3644 | 0 | lxw_snprintf(filename, LXW_FILENAME_LENGTH, |
3645 | 0 | "../drawings/drawing%d.xml", drawing_id); |
3646 | |
|
3647 | 0 | relationship->target = lxw_strdup(filename); |
3648 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3649 | | |
3650 | 0 | STAILQ_INSERT_TAIL(self->external_drawing_links, relationship, |
3651 | 0 | list_pointers); |
3652 | 0 | } |
3653 | | |
3654 | 0 | drawing_object = calloc(1, sizeof(lxw_drawing_object)); |
3655 | 0 | RETURN_VOID_ON_MEM_ERROR(drawing_object); |
3656 | | |
3657 | 0 | drawing_object->anchor = LXW_OBJECT_MOVE_AND_SIZE; |
3658 | 0 | if (object_props->object_position) |
3659 | 0 | drawing_object->anchor = object_props->object_position; |
3660 | |
|
3661 | 0 | drawing_object->type = LXW_DRAWING_CHART; |
3662 | 0 | drawing_object->description = lxw_strdup(object_props->description); |
3663 | 0 | drawing_object->tip = NULL; |
3664 | 0 | drawing_object->rel_index = _get_drawing_rel_index(self, NULL); |
3665 | 0 | drawing_object->url_rel_index = 0; |
3666 | 0 | drawing_object->decorative = object_props->decorative; |
3667 | | |
3668 | | /* Scale to user scale. */ |
3669 | 0 | width = object_props->width * object_props->x_scale; |
3670 | 0 | height = object_props->height * object_props->y_scale; |
3671 | | |
3672 | | /* Convert to the nearest pixel. */ |
3673 | 0 | object_props->width = width; |
3674 | 0 | object_props->height = height; |
3675 | |
|
3676 | 0 | _worksheet_position_object_emus(self, object_props, drawing_object); |
3677 | | |
3678 | | /* Convert from pixels to emus. */ |
3679 | 0 | drawing_object->width = (uint32_t) (0.5 + width * 9525); |
3680 | 0 | drawing_object->height = (uint32_t) (0.5 + height * 9525); |
3681 | |
|
3682 | 0 | lxw_add_drawing_object(self->drawing, drawing_object); |
3683 | |
|
3684 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3685 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3686 | | |
3687 | 0 | relationship->type = lxw_strdup("/chart"); |
3688 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3689 | | |
3690 | 0 | lxw_snprintf(filename, 32, "../charts/chart%d.xml", chart_ref_id); |
3691 | |
|
3692 | 0 | relationship->target = lxw_strdup(filename); |
3693 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3694 | | |
3695 | 0 | STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers); |
3696 | |
|
3697 | 0 | return; |
3698 | | |
3699 | 0 | mem_error: |
3700 | 0 | if (relationship) { |
3701 | 0 | free(relationship->type); |
3702 | 0 | free(relationship->target); |
3703 | 0 | free(relationship->target_mode); |
3704 | 0 | free(relationship); |
3705 | 0 | } |
3706 | 0 | } |
3707 | | |
3708 | | /* |
3709 | | * Set up VML objects, such as comments, in the worksheet. |
3710 | | */ |
3711 | | uint32_t |
3712 | | lxw_worksheet_prepare_vml_objects(lxw_worksheet *self, |
3713 | | uint32_t vml_data_id, |
3714 | | uint32_t vml_shape_id, |
3715 | | uint32_t vml_drawing_id, |
3716 | | uint32_t comment_id) |
3717 | 0 | { |
3718 | 0 | lxw_row *row; |
3719 | 0 | lxw_cell *cell; |
3720 | 0 | lxw_rel_tuple *relationship; |
3721 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3722 | 0 | uint32_t comment_count = 0; |
3723 | 0 | uint32_t i; |
3724 | 0 | uint32_t tmp_data_id; |
3725 | 0 | size_t data_str_len = 0; |
3726 | 0 | size_t used = 0; |
3727 | 0 | char *vml_data_id_str; |
3728 | |
|
3729 | 0 | RB_FOREACH(row, lxw_table_rows, self->comments) { |
3730 | |
|
3731 | 0 | RB_FOREACH(cell, lxw_table_cells, row->cells) { |
3732 | | /* Calculate the worksheet position of the comment. */ |
3733 | 0 | _worksheet_position_vml_object(self, cell->comment); |
3734 | | |
3735 | | /* Store comment in a simple list for use by packager. */ |
3736 | 0 | STAILQ_INSERT_TAIL(self->comment_objs, cell->comment, |
3737 | 0 | list_pointers); |
3738 | 0 | comment_count++; |
3739 | 0 | } |
3740 | 0 | } |
3741 | | |
3742 | | /* Set up the VML relationship for comments/buttons/header images. */ |
3743 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3744 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3745 | | |
3746 | 0 | relationship->type = lxw_strdup("/vmlDrawing"); |
3747 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3748 | | |
3749 | 0 | lxw_snprintf(filename, 32, "../drawings/vmlDrawing%d.vml", |
3750 | 0 | vml_drawing_id); |
3751 | |
|
3752 | 0 | relationship->target = lxw_strdup(filename); |
3753 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3754 | | |
3755 | 0 | self->external_vml_comment_link = relationship; |
3756 | |
|
3757 | 0 | if (self->has_comments) { |
3758 | | /* Only need this relationship object for comment VMLs. */ |
3759 | |
|
3760 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3761 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3762 | | |
3763 | 0 | relationship->type = lxw_strdup("/comments"); |
3764 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3765 | | |
3766 | 0 | lxw_snprintf(filename, 32, "../comments%d.xml", comment_id); |
3767 | |
|
3768 | 0 | relationship->target = lxw_strdup(filename); |
3769 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3770 | | |
3771 | 0 | self->external_comment_link = relationship; |
3772 | 0 | } |
3773 | | |
3774 | | /* The vml.c <o:idmap> element data id contains a comma separated range |
3775 | | * when there is more than one 1024 block of comments, like this: |
3776 | | * data="1,2,3". Since this could potentially (but unlikely) exceed |
3777 | | * LXW_MAX_ATTRIBUTE_LENGTH we need to allocate space dynamically. */ |
3778 | | |
3779 | | /* Calculate the total space required for the ID for each 1024 block. */ |
3780 | 0 | for (i = 0; i <= comment_count / 1024; i++) { |
3781 | 0 | tmp_data_id = vml_data_id + i; |
3782 | | |
3783 | | /* Calculate the space required for the digits in the id. */ |
3784 | 0 | while (tmp_data_id) { |
3785 | 0 | data_str_len++; |
3786 | 0 | tmp_data_id /= 10; |
3787 | 0 | } |
3788 | | |
3789 | | /* Add an extra char for comma separator or '\O'. */ |
3790 | 0 | data_str_len++; |
3791 | 0 | }; |
3792 | | |
3793 | | /* If this allocation fails it will be dealt with in packager.c. */ |
3794 | 0 | vml_data_id_str = calloc(1, data_str_len + 2); |
3795 | 0 | GOTO_LABEL_ON_MEM_ERROR(vml_data_id_str, mem_error); |
3796 | | |
3797 | | /* Create the CSV list in the allocated space. */ |
3798 | 0 | for (i = 0; i <= comment_count / 1024; i++) { |
3799 | 0 | tmp_data_id = vml_data_id + i; |
3800 | 0 | lxw_snprintf(vml_data_id_str + used, data_str_len - used, "%d,", |
3801 | 0 | tmp_data_id); |
3802 | |
|
3803 | 0 | used = strlen(vml_data_id_str); |
3804 | 0 | }; |
3805 | |
|
3806 | 0 | self->vml_shape_id = vml_shape_id; |
3807 | 0 | self->vml_data_id_str = vml_data_id_str; |
3808 | |
|
3809 | 0 | return comment_count; |
3810 | | |
3811 | 0 | mem_error: |
3812 | 0 | if (relationship) { |
3813 | 0 | free(relationship->type); |
3814 | 0 | free(relationship->target); |
3815 | 0 | free(relationship->target_mode); |
3816 | 0 | free(relationship); |
3817 | 0 | } |
3818 | |
|
3819 | 0 | return 0; |
3820 | 0 | } |
3821 | | |
3822 | | /* |
3823 | | * Set up external linkage for VML header/footer images. |
3824 | | */ |
3825 | | void |
3826 | | lxw_worksheet_prepare_header_vml_objects(lxw_worksheet *self, |
3827 | | uint32_t vml_header_id, |
3828 | | uint32_t vml_drawing_id) |
3829 | 0 | { |
3830 | |
|
3831 | 0 | lxw_rel_tuple *relationship; |
3832 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3833 | 0 | char *vml_data_id_str; |
3834 | |
|
3835 | 0 | self->vml_header_id = vml_header_id; |
3836 | | |
3837 | | /* Set up the VML relationship for header images. */ |
3838 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3839 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3840 | | |
3841 | 0 | relationship->type = lxw_strdup("/vmlDrawing"); |
3842 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3843 | | |
3844 | 0 | lxw_snprintf(filename, 32, "../drawings/vmlDrawing%d.vml", |
3845 | 0 | vml_drawing_id); |
3846 | |
|
3847 | 0 | relationship->target = lxw_strdup(filename); |
3848 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3849 | | |
3850 | 0 | self->external_vml_header_link = relationship; |
3851 | | |
3852 | | /* If this allocation fails it will be dealt with in packager.c. */ |
3853 | 0 | vml_data_id_str = calloc(1, sizeof("4294967295")); |
3854 | 0 | GOTO_LABEL_ON_MEM_ERROR(vml_data_id_str, mem_error); |
3855 | | |
3856 | 0 | lxw_snprintf(vml_data_id_str, sizeof("4294967295"), "%d", vml_header_id); |
3857 | |
|
3858 | 0 | self->vml_header_id_str = vml_data_id_str; |
3859 | |
|
3860 | 0 | return; |
3861 | | |
3862 | 0 | mem_error: |
3863 | 0 | if (relationship) { |
3864 | 0 | free(relationship->type); |
3865 | 0 | free(relationship->target); |
3866 | 0 | free(relationship->target_mode); |
3867 | 0 | free(relationship); |
3868 | 0 | } |
3869 | |
|
3870 | 0 | return; |
3871 | 0 | } |
3872 | | |
3873 | | /* |
3874 | | * Set up external linkage for VML header/footer images. |
3875 | | */ |
3876 | | void |
3877 | | lxw_worksheet_prepare_tables(lxw_worksheet *self, uint32_t table_id) |
3878 | 0 | { |
3879 | 0 | lxw_table_obj *table_obj; |
3880 | 0 | lxw_rel_tuple *relationship; |
3881 | 0 | char name[LXW_ATTR_32]; |
3882 | 0 | char filename[LXW_FILENAME_LENGTH]; |
3883 | |
|
3884 | 0 | STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { |
3885 | |
|
3886 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
3887 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
3888 | | |
3889 | 0 | relationship->type = lxw_strdup("/table"); |
3890 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
3891 | | |
3892 | 0 | lxw_snprintf(filename, LXW_FILENAME_LENGTH, |
3893 | 0 | "../tables/table%d.xml", table_id); |
3894 | |
|
3895 | 0 | relationship->target = lxw_strdup(filename); |
3896 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
3897 | | |
3898 | 0 | STAILQ_INSERT_TAIL(self->external_table_links, relationship, |
3899 | 0 | list_pointers); |
3900 | |
|
3901 | 0 | if (!table_obj->name) { |
3902 | 0 | lxw_snprintf(name, LXW_ATTR_32, "Table%d", table_id); |
3903 | 0 | table_obj->name = lxw_strdup(name); |
3904 | 0 | GOTO_LABEL_ON_MEM_ERROR(table_obj->name, mem_error); |
3905 | 0 | } |
3906 | 0 | table_obj->id = table_id; |
3907 | 0 | table_id++; |
3908 | 0 | } |
3909 | | |
3910 | 0 | return; |
3911 | | |
3912 | 0 | mem_error: |
3913 | 0 | if (relationship) { |
3914 | 0 | free(relationship->type); |
3915 | 0 | free(relationship->target); |
3916 | 0 | free(relationship->target_mode); |
3917 | 0 | free(relationship); |
3918 | 0 | } |
3919 | |
|
3920 | 0 | return; |
3921 | 0 | } |
3922 | | |
3923 | | /* |
3924 | | * Extract width and height information from a PNG file. |
3925 | | */ |
3926 | | STATIC lxw_error |
3927 | | _process_png(lxw_object_properties *object_props) |
3928 | 0 | { |
3929 | 0 | uint32_t length; |
3930 | 0 | uint32_t offset; |
3931 | 0 | char type[4]; |
3932 | 0 | uint32_t width = 0; |
3933 | 0 | uint32_t height = 0; |
3934 | 0 | double x_dpi = 96; |
3935 | 0 | double y_dpi = 96; |
3936 | 0 | int fseek_err; |
3937 | |
|
3938 | 0 | FILE *stream = object_props->stream; |
3939 | | |
3940 | | /* Skip another 4 bytes to the end of the PNG header. */ |
3941 | 0 | fseek_err = fseek(stream, 4, SEEK_CUR); |
3942 | 0 | if (fseek_err) |
3943 | 0 | goto file_error; |
3944 | | |
3945 | 0 | while (!feof(stream)) { |
3946 | | |
3947 | | /* Read the PNG length and type fields for the sub-section. */ |
3948 | 0 | if (fread(&length, sizeof(length), 1, stream) < 1) |
3949 | 0 | break; |
3950 | | |
3951 | 0 | if (fread(&type, 1, 4, stream) < 4) |
3952 | 0 | break; |
3953 | | |
3954 | | /* Convert the length to network order. */ |
3955 | 0 | length = LXW_UINT32_NETWORK(length); |
3956 | | |
3957 | | /* The offset for next fseek() is the field length + type length. */ |
3958 | 0 | offset = length + 4; |
3959 | |
|
3960 | 0 | if (memcmp(type, "IHDR", 4) == 0) { |
3961 | 0 | if (fread(&width, sizeof(width), 1, stream) < 1) |
3962 | 0 | break; |
3963 | | |
3964 | 0 | if (fread(&height, sizeof(height), 1, stream) < 1) |
3965 | 0 | break; |
3966 | | |
3967 | 0 | width = LXW_UINT32_NETWORK(width); |
3968 | 0 | height = LXW_UINT32_NETWORK(height); |
3969 | | |
3970 | | /* Reduce the offset by the length of previous freads(). */ |
3971 | 0 | offset -= 8; |
3972 | 0 | } |
3973 | | |
3974 | 0 | if (memcmp(type, "pHYs", 4) == 0) { |
3975 | 0 | uint32_t x_ppu = 0; |
3976 | 0 | uint32_t y_ppu = 0; |
3977 | 0 | uint8_t units = 1; |
3978 | |
|
3979 | 0 | if (fread(&x_ppu, sizeof(x_ppu), 1, stream) < 1) |
3980 | 0 | break; |
3981 | | |
3982 | 0 | if (fread(&y_ppu, sizeof(y_ppu), 1, stream) < 1) |
3983 | 0 | break; |
3984 | | |
3985 | 0 | if (fread(&units, sizeof(units), 1, stream) < 1) |
3986 | 0 | break; |
3987 | | |
3988 | 0 | if (units == 1) { |
3989 | 0 | x_ppu = LXW_UINT32_NETWORK(x_ppu); |
3990 | 0 | y_ppu = LXW_UINT32_NETWORK(y_ppu); |
3991 | |
|
3992 | 0 | x_dpi = (double) x_ppu *0.0254; |
3993 | 0 | y_dpi = (double) y_ppu *0.0254; |
3994 | 0 | } |
3995 | | |
3996 | | /* Reduce the offset by the length of previous freads(). */ |
3997 | 0 | offset -= 9; |
3998 | 0 | } |
3999 | | |
4000 | 0 | if (memcmp(type, "IEND", 4) == 0) |
4001 | 0 | break; |
4002 | | |
4003 | 0 | if (!feof(stream)) { |
4004 | 0 | fseek_err = fseek(stream, offset, SEEK_CUR); |
4005 | 0 | if (fseek_err) |
4006 | 0 | goto file_error; |
4007 | 0 | } |
4008 | 0 | } |
4009 | | |
4010 | | /* Ensure that we read some valid data from the file. */ |
4011 | 0 | if (width == 0) |
4012 | 0 | goto file_error; |
4013 | | |
4014 | | /* Set the image metadata. */ |
4015 | 0 | object_props->image_type = LXW_IMAGE_PNG; |
4016 | 0 | object_props->width = width; |
4017 | 0 | object_props->height = height; |
4018 | 0 | object_props->x_dpi = x_dpi ? x_dpi : 96; |
4019 | 0 | object_props->y_dpi = y_dpi ? y_dpi : 96; |
4020 | 0 | object_props->extension = lxw_strdup("png"); |
4021 | |
|
4022 | 0 | return LXW_NO_ERROR; |
4023 | | |
4024 | 0 | file_error: |
4025 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4026 | 0 | "no size data found in: %s.", object_props->filename); |
4027 | |
|
4028 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4029 | 0 | } |
4030 | | |
4031 | | /* |
4032 | | * Extract width and height information from a JPEG file. |
4033 | | */ |
4034 | | STATIC lxw_error |
4035 | | _process_jpeg(lxw_object_properties *image_props) |
4036 | 0 | { |
4037 | 0 | uint16_t length; |
4038 | 0 | uint16_t marker; |
4039 | 0 | uint32_t offset; |
4040 | 0 | uint16_t width = 0; |
4041 | 0 | uint16_t height = 0; |
4042 | 0 | double x_dpi = 96; |
4043 | 0 | double y_dpi = 96; |
4044 | 0 | int fseek_err; |
4045 | |
|
4046 | 0 | FILE *stream = image_props->stream; |
4047 | | |
4048 | | /* Read back 2 bytes to the end of the initial 0xFFD8 marker. */ |
4049 | 0 | fseek_err = fseek(stream, -2, SEEK_CUR); |
4050 | 0 | if (fseek_err) |
4051 | 0 | goto file_error; |
4052 | | |
4053 | | /* Search through the image data and read the JPEG markers. */ |
4054 | 0 | while (!feof(stream)) { |
4055 | | |
4056 | | /* Read the JPEG marker and length fields for the sub-section. */ |
4057 | 0 | if (fread(&marker, sizeof(marker), 1, stream) < 1) |
4058 | 0 | break; |
4059 | | |
4060 | 0 | if (fread(&length, sizeof(length), 1, stream) < 1) |
4061 | 0 | break; |
4062 | | |
4063 | | /* Convert the marker and length to network order. */ |
4064 | 0 | marker = LXW_UINT16_NETWORK(marker); |
4065 | 0 | length = LXW_UINT16_NETWORK(length); |
4066 | | |
4067 | | /* The offset for next fseek() is the field length + type length. */ |
4068 | 0 | offset = length - 2; |
4069 | | |
4070 | | /* Read the height and width in the 0xFFCn elements (except C4, C8 */ |
4071 | | /* and CC which aren't SOF markers). */ |
4072 | 0 | if ((marker & 0xFFF0) == 0xFFC0 && marker != 0xFFC4 |
4073 | 0 | && marker != 0xFFC8 && marker != 0xFFCC) { |
4074 | | /* Skip 1 byte to height and width. */ |
4075 | 0 | fseek_err = fseek(stream, 1, SEEK_CUR); |
4076 | 0 | if (fseek_err) |
4077 | 0 | goto file_error; |
4078 | | |
4079 | 0 | if (fread(&height, sizeof(height), 1, stream) < 1) |
4080 | 0 | break; |
4081 | | |
4082 | 0 | if (fread(&width, sizeof(width), 1, stream) < 1) |
4083 | 0 | break; |
4084 | | |
4085 | 0 | height = LXW_UINT16_NETWORK(height); |
4086 | 0 | width = LXW_UINT16_NETWORK(width); |
4087 | |
|
4088 | 0 | offset -= 9; |
4089 | 0 | } |
4090 | | |
4091 | | /* Read the DPI in the 0xFFE0 element. */ |
4092 | 0 | if (marker == 0xFFE0) { |
4093 | 0 | uint16_t x_density = 0; |
4094 | 0 | uint16_t y_density = 0; |
4095 | 0 | uint8_t units = 1; |
4096 | |
|
4097 | 0 | fseek_err = fseek(stream, 7, SEEK_CUR); |
4098 | 0 | if (fseek_err) |
4099 | 0 | goto file_error; |
4100 | | |
4101 | 0 | if (fread(&units, sizeof(units), 1, stream) < 1) |
4102 | 0 | break; |
4103 | | |
4104 | 0 | if (fread(&x_density, sizeof(x_density), 1, stream) < 1) |
4105 | 0 | break; |
4106 | | |
4107 | 0 | if (fread(&y_density, sizeof(y_density), 1, stream) < 1) |
4108 | 0 | break; |
4109 | | |
4110 | 0 | x_density = LXW_UINT16_NETWORK(x_density); |
4111 | 0 | y_density = LXW_UINT16_NETWORK(y_density); |
4112 | |
|
4113 | 0 | if (units == 1) { |
4114 | 0 | x_dpi = x_density; |
4115 | 0 | y_dpi = y_density; |
4116 | 0 | } |
4117 | |
|
4118 | 0 | if (units == 2) { |
4119 | 0 | x_dpi = x_density * 2.54; |
4120 | 0 | y_dpi = y_density * 2.54; |
4121 | 0 | } |
4122 | |
|
4123 | 0 | offset -= 12; |
4124 | 0 | } |
4125 | | |
4126 | 0 | if (marker == 0xFFDA) |
4127 | 0 | break; |
4128 | | |
4129 | 0 | if (!feof(stream)) { |
4130 | 0 | fseek_err = fseek(stream, offset, SEEK_CUR); |
4131 | 0 | if (fseek_err) |
4132 | 0 | break; |
4133 | 0 | } |
4134 | 0 | } |
4135 | | |
4136 | | /* Ensure that we read some valid data from the file. */ |
4137 | 0 | if (width == 0) |
4138 | 0 | goto file_error; |
4139 | | |
4140 | | /* Set the image metadata. */ |
4141 | 0 | image_props->image_type = LXW_IMAGE_JPEG; |
4142 | 0 | image_props->width = width; |
4143 | 0 | image_props->height = height; |
4144 | 0 | image_props->x_dpi = x_dpi ? x_dpi : 96; |
4145 | 0 | image_props->y_dpi = y_dpi ? y_dpi : 96; |
4146 | 0 | image_props->extension = lxw_strdup("jpeg"); |
4147 | |
|
4148 | 0 | return LXW_NO_ERROR; |
4149 | | |
4150 | 0 | file_error: |
4151 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4152 | 0 | "no size data found in: %s.", image_props->filename); |
4153 | |
|
4154 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4155 | 0 | } |
4156 | | |
4157 | | /* |
4158 | | * Extract width and height information from a BMP file. |
4159 | | */ |
4160 | | STATIC lxw_error |
4161 | | _process_bmp(lxw_object_properties *image_props) |
4162 | 0 | { |
4163 | 0 | uint32_t width = 0; |
4164 | 0 | uint32_t height = 0; |
4165 | 0 | double x_dpi = 96; |
4166 | 0 | double y_dpi = 96; |
4167 | 0 | int fseek_err; |
4168 | |
|
4169 | 0 | FILE *stream = image_props->stream; |
4170 | | |
4171 | | /* Skip another 14 bytes to the start of the BMP height/width. */ |
4172 | 0 | fseek_err = fseek(stream, 14, SEEK_CUR); |
4173 | 0 | if (fseek_err) |
4174 | 0 | goto file_error; |
4175 | | |
4176 | 0 | if (fread(&width, sizeof(width), 1, stream) < 1) |
4177 | 0 | width = 0; |
4178 | |
|
4179 | 0 | if (fread(&height, sizeof(height), 1, stream) < 1) |
4180 | 0 | height = 0; |
4181 | | |
4182 | | /* Ensure that we read some valid data from the file. */ |
4183 | 0 | if (width == 0) |
4184 | 0 | goto file_error; |
4185 | | |
4186 | 0 | height = LXW_UINT32_HOST(height); |
4187 | 0 | width = LXW_UINT32_HOST(width); |
4188 | | |
4189 | | /* Set the image metadata. */ |
4190 | 0 | image_props->image_type = LXW_IMAGE_BMP; |
4191 | 0 | image_props->width = width; |
4192 | 0 | image_props->height = height; |
4193 | 0 | image_props->x_dpi = x_dpi; |
4194 | 0 | image_props->y_dpi = y_dpi; |
4195 | 0 | image_props->extension = lxw_strdup("bmp"); |
4196 | |
|
4197 | 0 | return LXW_NO_ERROR; |
4198 | | |
4199 | 0 | file_error: |
4200 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4201 | 0 | "no size data found in: %s.", image_props->filename); |
4202 | |
|
4203 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4204 | 0 | } |
4205 | | |
4206 | | /* |
4207 | | * Extract width and height information from a GIF file. |
4208 | | */ |
4209 | | STATIC lxw_error |
4210 | | _process_gif(lxw_object_properties *image_props) |
4211 | 0 | { |
4212 | 0 | uint16_t width = 0; |
4213 | 0 | uint16_t height = 0; |
4214 | 0 | double x_dpi = 96; |
4215 | 0 | double y_dpi = 96; |
4216 | 0 | int fseek_err; |
4217 | |
|
4218 | 0 | FILE *stream = image_props->stream; |
4219 | | |
4220 | | /* Skip another 2 bytes to the start of the GIF height/width. */ |
4221 | 0 | fseek_err = fseek(stream, 2, SEEK_CUR); |
4222 | 0 | if (fseek_err) |
4223 | 0 | goto file_error; |
4224 | | |
4225 | 0 | if (fread(&width, sizeof(width), 1, stream) < 1) |
4226 | 0 | width = 0; |
4227 | |
|
4228 | 0 | if (fread(&height, sizeof(height), 1, stream) < 1) |
4229 | 0 | height = 0; |
4230 | | |
4231 | | /* Ensure that we read some valid data from the file. */ |
4232 | 0 | if (width == 0) |
4233 | 0 | goto file_error; |
4234 | | |
4235 | 0 | height = LXW_UINT16_HOST(height); |
4236 | 0 | width = LXW_UINT16_HOST(width); |
4237 | | |
4238 | | /* Set the image metadata. */ |
4239 | 0 | image_props->image_type = LXW_IMAGE_GIF; |
4240 | 0 | image_props->width = width; |
4241 | 0 | image_props->height = height; |
4242 | 0 | image_props->x_dpi = x_dpi; |
4243 | 0 | image_props->y_dpi = y_dpi; |
4244 | 0 | image_props->extension = lxw_strdup("gif"); |
4245 | |
|
4246 | 0 | return LXW_NO_ERROR; |
4247 | | |
4248 | 0 | file_error: |
4249 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4250 | 0 | "no size data found in: %s.", image_props->filename); |
4251 | |
|
4252 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4253 | 0 | } |
4254 | | |
4255 | | /* |
4256 | | * Extract information from the image file such as dimension, type, filename, |
4257 | | * and extension. |
4258 | | */ |
4259 | | STATIC lxw_error |
4260 | | _get_image_properties(lxw_object_properties *image_props) |
4261 | 0 | { |
4262 | 0 | unsigned char signature[4]; |
4263 | 0 | #ifndef USE_NO_MD5 |
4264 | 0 | uint8_t i; |
4265 | 0 | MD5_CTX md5_context; |
4266 | 0 | size_t size_read; |
4267 | 0 | char buffer[LXW_IMAGE_BUFFER_SIZE]; |
4268 | 0 | unsigned char md5_checksum[LXW_MD5_SIZE]; |
4269 | 0 | #endif |
4270 | | |
4271 | | /* Read 4 bytes to look for the file header/signature. */ |
4272 | 0 | if (fread(signature, 1, 4, image_props->stream) < 4) { |
4273 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4274 | 0 | "couldn't read image type for: %s.", |
4275 | 0 | image_props->filename); |
4276 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4277 | 0 | } |
4278 | | |
4279 | 0 | if (memcmp(&signature[1], "PNG", 3) == 0) { |
4280 | 0 | if (_process_png(image_props) != LXW_NO_ERROR) |
4281 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4282 | 0 | } |
4283 | 0 | else if (signature[0] == 0xFF && signature[1] == 0xD8) { |
4284 | 0 | if (_process_jpeg(image_props) != LXW_NO_ERROR) |
4285 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4286 | 0 | } |
4287 | 0 | else if (memcmp(signature, "BM", 2) == 0) { |
4288 | 0 | if (_process_bmp(image_props) != LXW_NO_ERROR) |
4289 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4290 | 0 | } |
4291 | 0 | else if (memcmp(signature, "GIF8", 4) == 0) { |
4292 | 0 | if (_process_gif(image_props) != LXW_NO_ERROR) |
4293 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4294 | 0 | } |
4295 | 0 | else { |
4296 | 0 | LXW_WARN_FORMAT1("worksheet image insertion: " |
4297 | 0 | "unsupported image format for: %s.", |
4298 | 0 | image_props->filename); |
4299 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4300 | 0 | } |
4301 | | |
4302 | 0 | #ifndef USE_NO_MD5 |
4303 | | /* Calculate an MD5 checksum for the image so that we can remove duplicate |
4304 | | * images to reduce the xlsx file size.*/ |
4305 | 0 | rewind(image_props->stream); |
4306 | |
|
4307 | 0 | MD5_Init(&md5_context); |
4308 | |
|
4309 | 0 | size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); |
4310 | 0 | while (size_read) { |
4311 | 0 | MD5_Update(&md5_context, buffer, (unsigned long) size_read); |
4312 | 0 | size_read = |
4313 | 0 | fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); |
4314 | 0 | } |
4315 | |
|
4316 | 0 | MD5_Final(md5_checksum, &md5_context); |
4317 | | |
4318 | | /* Create a 32 char hex string buffer for the MD5 checksum. */ |
4319 | 0 | image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1); |
4320 | | |
4321 | | /* If this calloc fails we just return and don't remove duplicates. */ |
4322 | 0 | RETURN_ON_MEM_ERROR(image_props->md5, LXW_NO_ERROR); |
4323 | | |
4324 | | /* Convert the 16 byte MD5 buffer to a 32 char hex string. */ |
4325 | 0 | for (i = 0; i < LXW_MD5_SIZE; i++) { |
4326 | 0 | lxw_snprintf(&image_props->md5[2 * i], 3, "%02x", md5_checksum[i]); |
4327 | 0 | } |
4328 | 0 | #endif |
4329 | |
|
4330 | 0 | return LXW_NO_ERROR; |
4331 | 0 | } |
4332 | | |
4333 | | /* Conditional formats that refer to the same cell sqref range, like A or |
4334 | | * B1:B9, need to be written as part of one xml structure. Therefore we need |
4335 | | * to store them in a RB hash/tree keyed by sqref. Within the RB hash element |
4336 | | * we then store conditional formats that refer to sqref in a STAILQ list. */ |
4337 | | lxw_error |
4338 | | _store_conditional_format_object(lxw_worksheet *self, |
4339 | | lxw_cond_format_obj *cond_format) |
4340 | 0 | { |
4341 | 0 | lxw_cond_format_hash_element tmp_hash_element; |
4342 | 0 | lxw_cond_format_hash_element *found_hash_element = NULL; |
4343 | 0 | lxw_cond_format_hash_element *new_hash_element = NULL; |
4344 | | |
4345 | | /* Create a temp hash element to do the lookup. */ |
4346 | 0 | LXW_ATTRIBUTE_COPY(tmp_hash_element.sqref, cond_format->sqref); |
4347 | 0 | found_hash_element = RB_FIND(lxw_cond_format_hash, |
4348 | 0 | self->conditional_formats, |
4349 | 0 | &tmp_hash_element); |
4350 | |
|
4351 | 0 | if (found_hash_element) { |
4352 | | /* If the RB element exists then add the conditional format to the |
4353 | | * list for the sqref range.*/ |
4354 | 0 | STAILQ_INSERT_TAIL(found_hash_element->cond_formats, cond_format, |
4355 | 0 | list_pointers); |
4356 | 0 | } |
4357 | 0 | else { |
4358 | | /* Create a new RB hash element. */ |
4359 | 0 | new_hash_element = calloc(1, sizeof(lxw_cond_format_hash_element)); |
4360 | 0 | GOTO_LABEL_ON_MEM_ERROR(new_hash_element, mem_error); |
4361 | | |
4362 | | /* Use the sqref as the key. */ |
4363 | 0 | LXW_ATTRIBUTE_COPY(new_hash_element->sqref, cond_format->sqref); |
4364 | | |
4365 | | /* Also create the list where we store the cond format objects. */ |
4366 | 0 | new_hash_element->cond_formats = |
4367 | 0 | calloc(1, sizeof(struct lxw_cond_format_list)); |
4368 | 0 | GOTO_LABEL_ON_MEM_ERROR(new_hash_element->cond_formats, mem_error); |
4369 | | |
4370 | | /* Initialize the list and add the conditional format object. */ |
4371 | 0 | STAILQ_INIT(new_hash_element->cond_formats); |
4372 | 0 | STAILQ_INSERT_TAIL(new_hash_element->cond_formats, cond_format, |
4373 | 0 | list_pointers); |
4374 | | |
4375 | | /* Now insert the RB hash element into the tree. */ |
4376 | 0 | RB_INSERT(lxw_cond_format_hash, self->conditional_formats, |
4377 | 0 | new_hash_element); |
4378 | |
|
4379 | 0 | } |
4380 | | |
4381 | 0 | return LXW_NO_ERROR; |
4382 | | |
4383 | 0 | mem_error: |
4384 | 0 | free(new_hash_element); |
4385 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
4386 | 0 | } |
4387 | | |
4388 | | /***************************************************************************** |
4389 | | * |
4390 | | * XML file assembly functions. |
4391 | | * |
4392 | | ****************************************************************************/ |
4393 | | |
4394 | | /* |
4395 | | * Write out a number worksheet cell. Doesn't use the xml functions as an |
4396 | | * optimization in the inner cell writing loop. |
4397 | | */ |
4398 | | STATIC void |
4399 | | _write_number_cell(lxw_worksheet *self, char *range, |
4400 | | int32_t style_index, lxw_cell *cell) |
4401 | 0 | { |
4402 | | #ifdef USE_DTOA_LIBRARY |
4403 | | char data[LXW_ATTR_32]; |
4404 | | |
4405 | | lxw_sprintf_dbl(data, cell->u.number); |
4406 | | |
4407 | | if (style_index) |
4408 | | fprintf(self->file, |
4409 | | "<c r=\"%s\" s=\"%d\"><v>%s</v></c>", |
4410 | | range, style_index, data); |
4411 | | else |
4412 | | fprintf(self->file, "<c r=\"%s\"><v>%s</v></c>", range, data); |
4413 | | #else |
4414 | 0 | if (style_index) |
4415 | 0 | fprintf(self->file, |
4416 | 0 | "<c r=\"%s\" s=\"%d\"><v>%.16G</v></c>", |
4417 | 0 | range, style_index, cell->u.number); |
4418 | 0 | else |
4419 | 0 | fprintf(self->file, |
4420 | 0 | "<c r=\"%s\"><v>%.16G</v></c>", range, cell->u.number); |
4421 | |
|
4422 | 0 | #endif |
4423 | 0 | } |
4424 | | |
4425 | | /* |
4426 | | * Write out a string worksheet cell. Doesn't use the xml functions as an |
4427 | | * optimization in the inner cell writing loop. |
4428 | | */ |
4429 | | STATIC void |
4430 | | _write_string_cell(lxw_worksheet *self, char *range, |
4431 | | int32_t style_index, lxw_cell *cell) |
4432 | 66.9k | { |
4433 | | |
4434 | 66.9k | if (style_index) |
4435 | 0 | fprintf(self->file, |
4436 | 0 | "<c r=\"%s\" s=\"%d\" t=\"s\"><v>%d</v></c>", |
4437 | 0 | range, style_index, cell->u.string_id); |
4438 | 66.9k | else |
4439 | 66.9k | fprintf(self->file, |
4440 | 66.9k | "<c r=\"%s\" t=\"s\"><v>%d</v></c>", |
4441 | 66.9k | range, cell->u.string_id); |
4442 | 66.9k | } |
4443 | | |
4444 | | /* |
4445 | | * Write out an inline string. Doesn't use the xml functions as an |
4446 | | * optimization in the inner cell writing loop. |
4447 | | */ |
4448 | | STATIC void |
4449 | | _write_inline_string_cell(lxw_worksheet *self, char *range, |
4450 | | int32_t style_index, lxw_cell *cell) |
4451 | 0 | { |
4452 | 0 | char *string = lxw_escape_data(cell->u.string); |
4453 | | |
4454 | | /* Add attribute to preserve leading or trailing whitespace. */ |
4455 | 0 | if (isspace((unsigned char) string[0]) |
4456 | 0 | || isspace((unsigned char) string[strlen(string) - 1])) { |
4457 | |
|
4458 | 0 | if (style_index) |
4459 | 0 | fprintf(self->file, |
4460 | 0 | "<c r=\"%s\" s=\"%d\" t=\"inlineStr\"><is>" |
4461 | 0 | "<t xml:space=\"preserve\">%s</t></is></c>", |
4462 | 0 | range, style_index, string); |
4463 | 0 | else |
4464 | 0 | fprintf(self->file, |
4465 | 0 | "<c r=\"%s\" t=\"inlineStr\"><is>" |
4466 | 0 | "<t xml:space=\"preserve\">%s</t></is></c>", |
4467 | 0 | range, string); |
4468 | 0 | } |
4469 | 0 | else { |
4470 | 0 | if (style_index) |
4471 | 0 | fprintf(self->file, |
4472 | 0 | "<c r=\"%s\" s=\"%d\" t=\"inlineStr\">" |
4473 | 0 | "<is><t>%s</t></is></c>", range, style_index, string); |
4474 | 0 | else |
4475 | 0 | fprintf(self->file, |
4476 | 0 | "<c r=\"%s\" t=\"inlineStr\">" |
4477 | 0 | "<is><t>%s</t></is></c>", range, string); |
4478 | 0 | } |
4479 | |
|
4480 | 0 | free(string); |
4481 | 0 | } |
4482 | | |
4483 | | /* |
4484 | | * Write out an inline rich string. Doesn't use the xml functions as an |
4485 | | * optimization in the inner cell writing loop. |
4486 | | */ |
4487 | | STATIC void |
4488 | | _write_inline_rich_string_cell(lxw_worksheet *self, char *range, |
4489 | | int32_t style_index, lxw_cell *cell) |
4490 | 0 | { |
4491 | 0 | const char *string = cell->u.string; |
4492 | |
|
4493 | 0 | if (style_index) |
4494 | 0 | fprintf(self->file, |
4495 | 0 | "<c r=\"%s\" s=\"%d\" t=\"inlineStr\">" |
4496 | 0 | "<is>%s</is></c>", range, style_index, string); |
4497 | 0 | else |
4498 | 0 | fprintf(self->file, |
4499 | 0 | "<c r=\"%s\" t=\"inlineStr\">" |
4500 | 0 | "<is>%s</is></c>", range, string); |
4501 | 0 | } |
4502 | | |
4503 | | /* |
4504 | | * Write out a formula worksheet cell with a numeric result. |
4505 | | */ |
4506 | | STATIC void |
4507 | | _write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) |
4508 | 0 | { |
4509 | 0 | char data[LXW_ATTR_32]; |
4510 | |
|
4511 | 0 | lxw_sprintf_dbl(data, cell->formula_result); |
4512 | 0 | lxw_xml_data_element(self->file, "f", cell->u.string, NULL); |
4513 | 0 | lxw_xml_data_element(self->file, "v", data, NULL); |
4514 | 0 | } |
4515 | | |
4516 | | /* |
4517 | | * Write out a formula worksheet cell with a numeric result. |
4518 | | */ |
4519 | | STATIC void |
4520 | | _write_formula_str_cell(lxw_worksheet *self, lxw_cell *cell) |
4521 | 0 | { |
4522 | 0 | lxw_xml_data_element(self->file, "f", cell->u.string, NULL); |
4523 | 0 | lxw_xml_data_element(self->file, "v", cell->user_data2, NULL); |
4524 | 0 | } |
4525 | | |
4526 | | /* |
4527 | | * Write out an array formula worksheet cell with a numeric result. |
4528 | | */ |
4529 | | STATIC void |
4530 | | _write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell) |
4531 | 0 | { |
4532 | 0 | struct xml_attribute_list attributes; |
4533 | 0 | struct xml_attribute *attribute; |
4534 | 0 | char data[LXW_ATTR_32]; |
4535 | |
|
4536 | 0 | LXW_INIT_ATTRIBUTES(); |
4537 | 0 | LXW_PUSH_ATTRIBUTES_STR("t", "array"); |
4538 | 0 | LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1); |
4539 | |
|
4540 | 0 | lxw_sprintf_dbl(data, cell->formula_result); |
4541 | |
|
4542 | 0 | lxw_xml_data_element(self->file, "f", cell->u.string, &attributes); |
4543 | 0 | lxw_xml_data_element(self->file, "v", data, NULL); |
4544 | |
|
4545 | 0 | LXW_FREE_ATTRIBUTES(); |
4546 | 0 | } |
4547 | | |
4548 | | /* |
4549 | | * Write out a boolean worksheet cell. |
4550 | | */ |
4551 | | STATIC void |
4552 | | _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell) |
4553 | 0 | { |
4554 | 0 | char data[LXW_ATTR_32]; |
4555 | |
|
4556 | 0 | if (cell->u.number == 0.0) |
4557 | 0 | data[0] = '0'; |
4558 | 0 | else |
4559 | 0 | data[0] = '1'; |
4560 | |
|
4561 | 0 | data[1] = '\0'; |
4562 | |
|
4563 | 0 | lxw_xml_data_element(self->file, "v", data, NULL); |
4564 | 0 | } |
4565 | | |
4566 | | /* |
4567 | | * Write out a error worksheet cell. |
4568 | | */ |
4569 | | STATIC void |
4570 | | _write_error_cell(lxw_worksheet *self) |
4571 | 0 | { |
4572 | 0 | lxw_xml_data_element(self->file, "v", "#VALUE!", NULL); |
4573 | 0 | } |
4574 | | |
4575 | | /* |
4576 | | * Calculate the "spans" attribute of the <row> tag. This is an XLSX |
4577 | | * optimization and isn't strictly required. However, it makes comparing |
4578 | | * files easier. |
4579 | | * |
4580 | | * The span is the same for each block of 16 rows. |
4581 | | */ |
4582 | | STATIC void |
4583 | | _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num) |
4584 | 1.36k | { |
4585 | 1.36k | lxw_cell *cell_min = RB_MIN(lxw_table_cells, row->cells); |
4586 | 1.36k | lxw_cell *cell_max = RB_MAX(lxw_table_cells, row->cells); |
4587 | 1.36k | lxw_col_t span_col_min = cell_min->col_num; |
4588 | 1.36k | lxw_col_t span_col_max = cell_max->col_num; |
4589 | 1.36k | lxw_col_t col_min; |
4590 | 1.36k | lxw_col_t col_max; |
4591 | 1.36k | *block_num = row->row_num / 16; |
4592 | | |
4593 | 1.36k | row = RB_NEXT(lxw_table_rows, root, row); |
4594 | | |
4595 | 5.73k | while (row && (int32_t) (row->row_num / 16) == *block_num) { |
4596 | | |
4597 | 4.36k | if (!RB_EMPTY(row->cells)) { |
4598 | 4.36k | cell_min = RB_MIN(lxw_table_cells, row->cells); |
4599 | 4.36k | cell_max = RB_MAX(lxw_table_cells, row->cells); |
4600 | 4.36k | col_min = cell_min->col_num; |
4601 | 4.36k | col_max = cell_max->col_num; |
4602 | | |
4603 | 4.36k | if (col_min < span_col_min) |
4604 | 76 | span_col_min = col_min; |
4605 | | |
4606 | 4.36k | if (col_max > span_col_max) |
4607 | 516 | span_col_max = col_max; |
4608 | 4.36k | } |
4609 | | |
4610 | 4.36k | row = RB_NEXT(lxw_table_rows, root, row); |
4611 | 4.36k | } |
4612 | | |
4613 | 1.36k | lxw_snprintf(span, LXW_MAX_CELL_RANGE_LENGTH, |
4614 | 1.36k | "%d:%d", span_col_min + 1, span_col_max + 1); |
4615 | 1.36k | } |
4616 | | |
4617 | | /* |
4618 | | * Write out a generic worksheet cell. |
4619 | | */ |
4620 | | STATIC void |
4621 | | _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format) |
4622 | 66.9k | { |
4623 | 66.9k | struct xml_attribute_list attributes; |
4624 | 66.9k | struct xml_attribute *attribute; |
4625 | 66.9k | char range[LXW_MAX_CELL_NAME_LENGTH] = { 0 }; |
4626 | 66.9k | lxw_row_t row_num = cell->row_num; |
4627 | 66.9k | lxw_col_t col_num = cell->col_num; |
4628 | 66.9k | int32_t style_index = 0; |
4629 | | |
4630 | 66.9k | lxw_rowcol_to_cell(range, row_num, col_num); |
4631 | | |
4632 | 66.9k | if (cell->format) { |
4633 | 0 | style_index = lxw_format_get_xf_index(cell->format); |
4634 | 0 | } |
4635 | 66.9k | else if (row_format) { |
4636 | 0 | style_index = lxw_format_get_xf_index(row_format); |
4637 | 0 | } |
4638 | 66.9k | else if (col_num < self->col_formats_max && self->col_formats[col_num]) { |
4639 | 0 | style_index = lxw_format_get_xf_index(self->col_formats[col_num]); |
4640 | 0 | } |
4641 | | |
4642 | | /* Unrolled optimization for most commonly written cell types. */ |
4643 | 66.9k | if (cell->type == NUMBER_CELL) { |
4644 | 0 | _write_number_cell(self, range, style_index, cell); |
4645 | 0 | return; |
4646 | 0 | } |
4647 | | |
4648 | 66.9k | if (cell->type == STRING_CELL) { |
4649 | 66.9k | _write_string_cell(self, range, style_index, cell); |
4650 | 66.9k | return; |
4651 | 66.9k | } |
4652 | | |
4653 | 0 | if (cell->type == INLINE_STRING_CELL) { |
4654 | 0 | _write_inline_string_cell(self, range, style_index, cell); |
4655 | 0 | return; |
4656 | 0 | } |
4657 | | |
4658 | 0 | if (cell->type == INLINE_RICH_STRING_CELL) { |
4659 | 0 | _write_inline_rich_string_cell(self, range, style_index, cell); |
4660 | 0 | return; |
4661 | 0 | } |
4662 | | |
4663 | | /* For other cell types use the general functions. */ |
4664 | 0 | LXW_INIT_ATTRIBUTES(); |
4665 | 0 | LXW_PUSH_ATTRIBUTES_STR("r", range); |
4666 | |
|
4667 | 0 | if (style_index) |
4668 | 0 | LXW_PUSH_ATTRIBUTES_INT("s", style_index); |
4669 | |
|
4670 | 0 | if (cell->type == FORMULA_CELL) { |
4671 | | /* If user_data2 is set then the formula has a string result. */ |
4672 | 0 | if (cell->user_data2) |
4673 | 0 | LXW_PUSH_ATTRIBUTES_STR("t", "str"); |
4674 | |
|
4675 | 0 | lxw_xml_start_tag(self->file, "c", &attributes); |
4676 | |
|
4677 | 0 | if (cell->user_data2) |
4678 | 0 | _write_formula_str_cell(self, cell); |
4679 | 0 | else |
4680 | 0 | _write_formula_num_cell(self, cell); |
4681 | |
|
4682 | 0 | lxw_xml_end_tag(self->file, "c"); |
4683 | 0 | } |
4684 | 0 | else if (cell->type == BLANK_CELL) { |
4685 | 0 | if (cell->format) |
4686 | 0 | lxw_xml_empty_tag(self->file, "c", &attributes); |
4687 | 0 | } |
4688 | 0 | else if (cell->type == BOOLEAN_CELL) { |
4689 | 0 | LXW_PUSH_ATTRIBUTES_STR("t", "b"); |
4690 | 0 | lxw_xml_start_tag(self->file, "c", &attributes); |
4691 | 0 | _write_boolean_cell(self, cell); |
4692 | 0 | lxw_xml_end_tag(self->file, "c"); |
4693 | 0 | } |
4694 | 0 | else if (cell->type == ARRAY_FORMULA_CELL) { |
4695 | 0 | lxw_xml_start_tag(self->file, "c", &attributes); |
4696 | 0 | _write_array_formula_num_cell(self, cell); |
4697 | 0 | lxw_xml_end_tag(self->file, "c"); |
4698 | 0 | } |
4699 | 0 | else if (cell->type == DYNAMIC_ARRAY_FORMULA_CELL) { |
4700 | 0 | LXW_PUSH_ATTRIBUTES_STR("cm", "1"); |
4701 | 0 | lxw_xml_start_tag(self->file, "c", &attributes); |
4702 | 0 | _write_array_formula_num_cell(self, cell); |
4703 | 0 | lxw_xml_end_tag(self->file, "c"); |
4704 | 0 | } |
4705 | 0 | else if (cell->type == ERROR_CELL) { |
4706 | 0 | LXW_PUSH_ATTRIBUTES_STR("t", "e"); |
4707 | 0 | LXW_PUSH_ATTRIBUTES_DBL("vm", cell->u.number); |
4708 | 0 | lxw_xml_start_tag(self->file, "c", &attributes); |
4709 | 0 | _write_error_cell(self); |
4710 | 0 | lxw_xml_end_tag(self->file, "c"); |
4711 | 0 | } |
4712 | |
|
4713 | 0 | LXW_FREE_ATTRIBUTES(); |
4714 | 0 | } |
4715 | | |
4716 | | /* |
4717 | | * Write out the worksheet data as a series of rows and cells. |
4718 | | */ |
4719 | | STATIC void |
4720 | | _worksheet_write_rows(lxw_worksheet *self) |
4721 | 1.21k | { |
4722 | 1.21k | lxw_row *row; |
4723 | 1.21k | lxw_cell *cell; |
4724 | 1.21k | int32_t block_num = -1; |
4725 | 1.21k | char spans[LXW_MAX_CELL_RANGE_LENGTH] = { 0 }; |
4726 | | |
4727 | 5.73k | RB_FOREACH(row, lxw_table_rows, self->table) { |
4728 | | |
4729 | 5.73k | if (RB_EMPTY(row->cells)) { |
4730 | | /* Row contains no cells but has height, format or other data. */ |
4731 | | |
4732 | | /* Write a default span for default rows. */ |
4733 | 0 | if (self->default_row_set) |
4734 | 0 | _write_row(self, row, "1:1"); |
4735 | 0 | else |
4736 | 0 | _write_row(self, row, NULL); |
4737 | 0 | } |
4738 | 5.73k | else { |
4739 | | /* Row and cell data. */ |
4740 | 5.73k | if ((int32_t) row->row_num / 16 > block_num) |
4741 | 1.36k | _calculate_spans(row, spans, &block_num); |
4742 | | |
4743 | 5.73k | _write_row(self, row, spans); |
4744 | | |
4745 | 5.73k | if (row->data_changed) { |
4746 | 66.9k | RB_FOREACH(cell, lxw_table_cells, row->cells) { |
4747 | 66.9k | _write_cell(self, cell, row->format); |
4748 | 66.9k | } |
4749 | | |
4750 | 5.73k | lxw_xml_end_tag(self->file, "row"); |
4751 | 5.73k | } |
4752 | 5.73k | } |
4753 | 5.73k | } |
4754 | 1.21k | } |
4755 | | |
4756 | | /* |
4757 | | * Write out the worksheet data as a single row with cells. This method is |
4758 | | * used when memory optimization is on. A single row is written and the data |
4759 | | * array is reset. That way only one row of data is kept in memory at any one |
4760 | | * time. We don't write span data in the optimized case since it is optional. |
4761 | | */ |
4762 | | void |
4763 | | lxw_worksheet_write_single_row(lxw_worksheet *self) |
4764 | 1.30k | { |
4765 | 1.30k | lxw_row *row = self->optimize_row; |
4766 | 1.30k | lxw_col_t col; |
4767 | | |
4768 | | /* skip row if it doesn't contain row formatting, cell data or a comment. */ |
4769 | 1.30k | if (!(row->row_changed || row->data_changed)) |
4770 | 1.30k | return; |
4771 | | |
4772 | | /* Write the cells if the row contains data. */ |
4773 | 0 | if (!row->data_changed) { |
4774 | | /* Row data only. No cells. */ |
4775 | 0 | _write_row(self, row, NULL); |
4776 | 0 | } |
4777 | 0 | else { |
4778 | | /* Row and cell data. */ |
4779 | 0 | _write_row(self, row, NULL); |
4780 | |
|
4781 | 0 | for (col = self->dim_colmin; col <= self->dim_colmax; col++) { |
4782 | 0 | if (self->array[col]) { |
4783 | 0 | _write_cell(self, self->array[col], row->format); |
4784 | 0 | _free_cell(self->array[col]); |
4785 | 0 | self->array[col] = NULL; |
4786 | 0 | } |
4787 | 0 | } |
4788 | |
|
4789 | 0 | lxw_xml_end_tag(self->file, "row"); |
4790 | 0 | } |
4791 | | |
4792 | | /* Reset the row. */ |
4793 | 0 | row->height = LXW_DEF_ROW_HEIGHT; |
4794 | 0 | row->format = NULL; |
4795 | 0 | row->hidden = LXW_FALSE; |
4796 | 0 | row->level = 0; |
4797 | 0 | row->collapsed = LXW_FALSE; |
4798 | 0 | row->data_changed = LXW_FALSE; |
4799 | 0 | row->row_changed = LXW_FALSE; |
4800 | 0 | } |
4801 | | |
4802 | | /* Process a header/footer image and store it in the correct slot. */ |
4803 | | lxw_error |
4804 | | _worksheet_set_header_footer_image(lxw_worksheet *self, const char *filename, |
4805 | | uint8_t image_position) |
4806 | 0 | { |
4807 | 0 | FILE *image_stream; |
4808 | 0 | const char *description; |
4809 | 0 | lxw_object_properties *object_props; |
4810 | 0 | char *image_strings[] = { "LH", "CH", "RH", "LF", "CF", "RF" }; |
4811 | | |
4812 | | /* Not all slots will have image files. */ |
4813 | 0 | if (!filename) |
4814 | 0 | return LXW_NO_ERROR; |
4815 | | |
4816 | | /* Check that the image file exists and can be opened. */ |
4817 | 0 | image_stream = lxw_fopen(filename, "rb"); |
4818 | 0 | if (!image_stream) { |
4819 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
4820 | 0 | "file doesn't exist or can't be opened: %s.", |
4821 | 0 | filename); |
4822 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
4823 | 0 | } |
4824 | | |
4825 | | /* Use the filename as the default description, like Excel. */ |
4826 | 0 | description = lxw_basename(filename); |
4827 | 0 | if (!description) { |
4828 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
4829 | 0 | "couldn't get basename for file: %s.", filename); |
4830 | 0 | fclose(image_stream); |
4831 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
4832 | 0 | } |
4833 | | |
4834 | | /* Create a new object to hold the image properties. */ |
4835 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
4836 | 0 | if (!object_props) { |
4837 | 0 | fclose(image_stream); |
4838 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
4839 | 0 | } |
4840 | | |
4841 | | /* Copy other options or set defaults. */ |
4842 | 0 | object_props->filename = lxw_strdup(filename); |
4843 | 0 | object_props->description = lxw_strdup(description); |
4844 | 0 | object_props->stream = image_stream; |
4845 | | |
4846 | | /* Set VML image position string based on the header/footer/position. */ |
4847 | 0 | object_props->image_position = lxw_strdup(image_strings[image_position]); |
4848 | |
|
4849 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
4850 | 0 | *self->header_footer_objs[image_position] = object_props; |
4851 | 0 | self->has_header_vml = LXW_TRUE; |
4852 | 0 | fclose(image_stream); |
4853 | 0 | return LXW_NO_ERROR; |
4854 | 0 | } |
4855 | 0 | else { |
4856 | 0 | _free_object_properties(object_props); |
4857 | 0 | fclose(image_stream); |
4858 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
4859 | 0 | } |
4860 | 0 | } |
4861 | | |
4862 | | /* |
4863 | | * Write the <col> element. |
4864 | | */ |
4865 | | STATIC void |
4866 | | _worksheet_write_col_info(lxw_worksheet *self, lxw_col_options *options) |
4867 | 0 | { |
4868 | 0 | struct xml_attribute_list attributes; |
4869 | 0 | struct xml_attribute *attribute; |
4870 | |
|
4871 | 0 | double width = options->width; |
4872 | 0 | uint8_t has_custom_width = LXW_TRUE; |
4873 | 0 | int32_t xf_index = 0; |
4874 | 0 | double max_digit_width = 7.0; /* For Calabri 11. */ |
4875 | 0 | double padding = 5.0; |
4876 | | |
4877 | | /* Get the format index. */ |
4878 | 0 | if (options->format) { |
4879 | 0 | xf_index = lxw_format_get_xf_index(options->format); |
4880 | 0 | } |
4881 | | |
4882 | | /* Check if width is the Excel default. */ |
4883 | 0 | if (width == LXW_DEF_COL_WIDTH) { |
4884 | | |
4885 | | /* The default col width changes to 0 for hidden columns. */ |
4886 | 0 | if (options->hidden) |
4887 | 0 | width = 0; |
4888 | 0 | else |
4889 | 0 | has_custom_width = LXW_FALSE; |
4890 | |
|
4891 | 0 | } |
4892 | | |
4893 | | /* Convert column width from user units to character width. */ |
4894 | 0 | if (width > 0) { |
4895 | 0 | if (width < 1) { |
4896 | 0 | width = (uint16_t) (((uint16_t) |
4897 | 0 | (width * (max_digit_width + padding) + 0.5)) |
4898 | 0 | / max_digit_width * 256.0) / 256.0; |
4899 | 0 | } |
4900 | 0 | else { |
4901 | 0 | width = (uint16_t) (((uint16_t) |
4902 | 0 | (width * max_digit_width + 0.5) + padding) |
4903 | 0 | / max_digit_width * 256.0) / 256.0; |
4904 | 0 | } |
4905 | 0 | } |
4906 | |
|
4907 | 0 | LXW_INIT_ATTRIBUTES(); |
4908 | 0 | LXW_PUSH_ATTRIBUTES_INT("min", 1 + options->firstcol); |
4909 | 0 | LXW_PUSH_ATTRIBUTES_INT("max", 1 + options->lastcol); |
4910 | 0 | LXW_PUSH_ATTRIBUTES_DBL("width", width); |
4911 | |
|
4912 | 0 | if (xf_index) |
4913 | 0 | LXW_PUSH_ATTRIBUTES_INT("style", xf_index); |
4914 | |
|
4915 | 0 | if (options->hidden) |
4916 | 0 | LXW_PUSH_ATTRIBUTES_STR("hidden", "1"); |
4917 | |
|
4918 | 0 | if (has_custom_width) |
4919 | 0 | LXW_PUSH_ATTRIBUTES_STR("customWidth", "1"); |
4920 | |
|
4921 | 0 | if (options->level) |
4922 | 0 | LXW_PUSH_ATTRIBUTES_INT("outlineLevel", options->level); |
4923 | |
|
4924 | 0 | if (options->collapsed) |
4925 | 0 | LXW_PUSH_ATTRIBUTES_STR("collapsed", "1"); |
4926 | |
|
4927 | 0 | lxw_xml_empty_tag(self->file, "col", &attributes); |
4928 | |
|
4929 | 0 | LXW_FREE_ATTRIBUTES(); |
4930 | 0 | } |
4931 | | |
4932 | | /* |
4933 | | * Write the <cols> element and <col> sub elements. |
4934 | | */ |
4935 | | STATIC void |
4936 | | _worksheet_write_cols(lxw_worksheet *self) |
4937 | 1.30k | { |
4938 | 1.30k | lxw_col_t col; |
4939 | | |
4940 | 1.30k | if (!self->col_size_changed) |
4941 | 1.30k | return; |
4942 | | |
4943 | 0 | lxw_xml_start_tag(self->file, "cols", NULL); |
4944 | |
|
4945 | 0 | for (col = 0; col < self->col_options_max; col++) { |
4946 | 0 | if (self->col_options[col]) |
4947 | 0 | _worksheet_write_col_info(self, self->col_options[col]); |
4948 | 0 | } |
4949 | |
|
4950 | 0 | lxw_xml_end_tag(self->file, "cols"); |
4951 | 0 | } |
4952 | | |
4953 | | /* |
4954 | | * Write the <mergeCell> element. |
4955 | | */ |
4956 | | STATIC void |
4957 | | _worksheet_write_merge_cell(lxw_worksheet *self, |
4958 | | lxw_merged_range *merged_range) |
4959 | 0 | { |
4960 | |
|
4961 | 0 | struct xml_attribute_list attributes; |
4962 | 0 | struct xml_attribute *attribute; |
4963 | 0 | char ref[LXW_MAX_CELL_RANGE_LENGTH]; |
4964 | |
|
4965 | 0 | LXW_INIT_ATTRIBUTES(); |
4966 | | |
4967 | | /* Convert the merge dimensions to a cell range. */ |
4968 | 0 | lxw_rowcol_to_range(ref, merged_range->first_row, merged_range->first_col, |
4969 | 0 | merged_range->last_row, merged_range->last_col); |
4970 | |
|
4971 | 0 | LXW_PUSH_ATTRIBUTES_STR("ref", ref); |
4972 | |
|
4973 | 0 | lxw_xml_empty_tag(self->file, "mergeCell", &attributes); |
4974 | |
|
4975 | 0 | LXW_FREE_ATTRIBUTES(); |
4976 | 0 | } |
4977 | | |
4978 | | /* |
4979 | | * Write the <mergeCells> element. |
4980 | | */ |
4981 | | STATIC void |
4982 | | _worksheet_write_merge_cells(lxw_worksheet *self) |
4983 | 1.30k | { |
4984 | 1.30k | struct xml_attribute_list attributes; |
4985 | 1.30k | struct xml_attribute *attribute; |
4986 | 1.30k | lxw_merged_range *merged_range; |
4987 | | |
4988 | 1.30k | if (self->merged_range_count) { |
4989 | 0 | LXW_INIT_ATTRIBUTES(); |
4990 | |
|
4991 | 0 | LXW_PUSH_ATTRIBUTES_INT("count", self->merged_range_count); |
4992 | |
|
4993 | 0 | lxw_xml_start_tag(self->file, "mergeCells", &attributes); |
4994 | |
|
4995 | 0 | STAILQ_FOREACH(merged_range, self->merged_ranges, list_pointers) { |
4996 | 0 | _worksheet_write_merge_cell(self, merged_range); |
4997 | 0 | } |
4998 | 0 | lxw_xml_end_tag(self->file, "mergeCells"); |
4999 | |
|
5000 | 0 | LXW_FREE_ATTRIBUTES(); |
5001 | 0 | } |
5002 | 1.30k | } |
5003 | | |
5004 | | /* |
5005 | | * Write the <oddHeader> element. |
5006 | | */ |
5007 | | STATIC void |
5008 | | _worksheet_write_odd_header(lxw_worksheet *self) |
5009 | 0 | { |
5010 | 0 | lxw_xml_data_element(self->file, "oddHeader", self->header, NULL); |
5011 | 0 | } |
5012 | | |
5013 | | /* |
5014 | | * Write the <oddFooter> element. |
5015 | | */ |
5016 | | STATIC void |
5017 | | _worksheet_write_odd_footer(lxw_worksheet *self) |
5018 | 0 | { |
5019 | 0 | lxw_xml_data_element(self->file, "oddFooter", self->footer, NULL); |
5020 | 0 | } |
5021 | | |
5022 | | /* |
5023 | | * Write the <headerFooter> element. |
5024 | | */ |
5025 | | STATIC void |
5026 | | _worksheet_write_header_footer(lxw_worksheet *self) |
5027 | 1.30k | { |
5028 | 1.30k | if (!self->header_footer_changed) |
5029 | 1.30k | return; |
5030 | | |
5031 | 0 | lxw_xml_start_tag(self->file, "headerFooter", NULL); |
5032 | |
|
5033 | 0 | if (self->header) |
5034 | 0 | _worksheet_write_odd_header(self); |
5035 | |
|
5036 | 0 | if (self->footer) |
5037 | 0 | _worksheet_write_odd_footer(self); |
5038 | |
|
5039 | 0 | lxw_xml_end_tag(self->file, "headerFooter"); |
5040 | 0 | } |
5041 | | |
5042 | | /* |
5043 | | * Write the <pageSetUpPr> element. |
5044 | | */ |
5045 | | STATIC void |
5046 | | _worksheet_write_page_set_up_pr(lxw_worksheet *self) |
5047 | 0 | { |
5048 | 0 | struct xml_attribute_list attributes; |
5049 | 0 | struct xml_attribute *attribute; |
5050 | |
|
5051 | 0 | if (!self->fit_page) |
5052 | 0 | return; |
5053 | | |
5054 | 0 | LXW_INIT_ATTRIBUTES(); |
5055 | 0 | LXW_PUSH_ATTRIBUTES_STR("fitToPage", "1"); |
5056 | |
|
5057 | 0 | lxw_xml_empty_tag(self->file, "pageSetUpPr", &attributes); |
5058 | |
|
5059 | 0 | LXW_FREE_ATTRIBUTES(); |
5060 | |
|
5061 | 0 | } |
5062 | | |
5063 | | /* |
5064 | | * Write the <tabColor> element. |
5065 | | */ |
5066 | | STATIC void |
5067 | | _worksheet_write_tab_color(lxw_worksheet *self) |
5068 | 0 | { |
5069 | 0 | struct xml_attribute_list attributes; |
5070 | 0 | struct xml_attribute *attribute; |
5071 | 0 | char rgb_str[LXW_ATTR_32]; |
5072 | |
|
5073 | 0 | if (self->tab_color == LXW_COLOR_UNSET) |
5074 | 0 | return; |
5075 | | |
5076 | 0 | lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X", |
5077 | 0 | self->tab_color & LXW_COLOR_MASK); |
5078 | |
|
5079 | 0 | LXW_INIT_ATTRIBUTES(); |
5080 | 0 | LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str); |
5081 | |
|
5082 | 0 | lxw_xml_empty_tag(self->file, "tabColor", &attributes); |
5083 | |
|
5084 | 0 | LXW_FREE_ATTRIBUTES(); |
5085 | 0 | } |
5086 | | |
5087 | | /* |
5088 | | * Write the <outlinePr> element. |
5089 | | */ |
5090 | | STATIC void |
5091 | | _worksheet_write_outline_pr(lxw_worksheet *self) |
5092 | 0 | { |
5093 | 0 | struct xml_attribute_list attributes; |
5094 | 0 | struct xml_attribute *attribute; |
5095 | |
|
5096 | 0 | if (!self->outline_changed) |
5097 | 0 | return; |
5098 | | |
5099 | 0 | LXW_INIT_ATTRIBUTES(); |
5100 | |
|
5101 | 0 | if (self->outline_style) |
5102 | 0 | LXW_PUSH_ATTRIBUTES_STR("applyStyles", "1"); |
5103 | |
|
5104 | 0 | if (!self->outline_below) |
5105 | 0 | LXW_PUSH_ATTRIBUTES_STR("summaryBelow", "0"); |
5106 | |
|
5107 | 0 | if (!self->outline_right) |
5108 | 0 | LXW_PUSH_ATTRIBUTES_STR("summaryRight", "0"); |
5109 | |
|
5110 | 0 | if (!self->outline_on) |
5111 | 0 | LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0"); |
5112 | |
|
5113 | 0 | lxw_xml_empty_tag(self->file, "outlinePr", &attributes); |
5114 | |
|
5115 | 0 | LXW_FREE_ATTRIBUTES(); |
5116 | 0 | } |
5117 | | |
5118 | | /* |
5119 | | * Write the <sheetPr> element for Sheet level properties. |
5120 | | */ |
5121 | | STATIC void |
5122 | | _worksheet_write_sheet_pr(lxw_worksheet *self) |
5123 | 1.30k | { |
5124 | 1.30k | struct xml_attribute_list attributes; |
5125 | 1.30k | struct xml_attribute *attribute; |
5126 | | |
5127 | 1.30k | if (!self->fit_page |
5128 | 1.30k | && !self->filter_on |
5129 | 1.30k | && self->tab_color == LXW_COLOR_UNSET |
5130 | 1.30k | && !self->outline_changed |
5131 | 1.30k | && !self->vba_codename && !self->is_chartsheet) { |
5132 | 1.30k | return; |
5133 | 1.30k | } |
5134 | | |
5135 | 0 | LXW_INIT_ATTRIBUTES(); |
5136 | |
|
5137 | 0 | if (self->vba_codename) |
5138 | 0 | LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename); |
5139 | |
|
5140 | 0 | if (self->filter_on) |
5141 | 0 | LXW_PUSH_ATTRIBUTES_STR("filterMode", "1"); |
5142 | |
|
5143 | 0 | if (self->fit_page || self->tab_color != LXW_COLOR_UNSET |
5144 | 0 | || self->outline_changed) { |
5145 | 0 | lxw_xml_start_tag(self->file, "sheetPr", &attributes); |
5146 | 0 | _worksheet_write_tab_color(self); |
5147 | 0 | _worksheet_write_outline_pr(self); |
5148 | 0 | _worksheet_write_page_set_up_pr(self); |
5149 | 0 | lxw_xml_end_tag(self->file, "sheetPr"); |
5150 | 0 | } |
5151 | 0 | else { |
5152 | 0 | lxw_xml_empty_tag(self->file, "sheetPr", &attributes); |
5153 | 0 | } |
5154 | |
|
5155 | 0 | LXW_FREE_ATTRIBUTES(); |
5156 | |
|
5157 | 0 | } |
5158 | | |
5159 | | /* |
5160 | | * Write the <brk> element. |
5161 | | */ |
5162 | | STATIC void |
5163 | | _worksheet_write_brk(lxw_worksheet *self, uint32_t id, uint32_t max) |
5164 | 0 | { |
5165 | 0 | struct xml_attribute_list attributes; |
5166 | 0 | struct xml_attribute *attribute; |
5167 | |
|
5168 | 0 | LXW_INIT_ATTRIBUTES(); |
5169 | 0 | LXW_PUSH_ATTRIBUTES_INT("id", id); |
5170 | 0 | LXW_PUSH_ATTRIBUTES_INT("max", max); |
5171 | 0 | LXW_PUSH_ATTRIBUTES_STR("man", "1"); |
5172 | |
|
5173 | 0 | lxw_xml_empty_tag(self->file, "brk", &attributes); |
5174 | |
|
5175 | 0 | LXW_FREE_ATTRIBUTES(); |
5176 | 0 | } |
5177 | | |
5178 | | /* |
5179 | | * Write the <rowBreaks> element. |
5180 | | */ |
5181 | | STATIC void |
5182 | | _worksheet_write_row_breaks(lxw_worksheet *self) |
5183 | 1.30k | { |
5184 | 1.30k | struct xml_attribute_list attributes; |
5185 | 1.30k | struct xml_attribute *attribute; |
5186 | 1.30k | uint16_t count = self->hbreaks_count; |
5187 | 1.30k | uint16_t i; |
5188 | | |
5189 | 1.30k | if (!count) |
5190 | 1.30k | return; |
5191 | | |
5192 | 0 | LXW_INIT_ATTRIBUTES(); |
5193 | 0 | LXW_PUSH_ATTRIBUTES_INT("count", count); |
5194 | 0 | LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count); |
5195 | |
|
5196 | 0 | lxw_xml_start_tag(self->file, "rowBreaks", &attributes); |
5197 | |
|
5198 | 0 | for (i = 0; i < count; i++) |
5199 | 0 | _worksheet_write_brk(self, self->hbreaks[i], LXW_COL_MAX - 1); |
5200 | |
|
5201 | 0 | lxw_xml_end_tag(self->file, "rowBreaks"); |
5202 | |
|
5203 | 0 | LXW_FREE_ATTRIBUTES(); |
5204 | 0 | } |
5205 | | |
5206 | | /* |
5207 | | * Write the <colBreaks> element. |
5208 | | */ |
5209 | | STATIC void |
5210 | | _worksheet_write_col_breaks(lxw_worksheet *self) |
5211 | 1.30k | { |
5212 | 1.30k | struct xml_attribute_list attributes; |
5213 | 1.30k | struct xml_attribute *attribute; |
5214 | 1.30k | uint16_t count = self->vbreaks_count; |
5215 | 1.30k | uint16_t i; |
5216 | | |
5217 | 1.30k | if (!count) |
5218 | 1.30k | return; |
5219 | | |
5220 | 0 | LXW_INIT_ATTRIBUTES(); |
5221 | 0 | LXW_PUSH_ATTRIBUTES_INT("count", count); |
5222 | 0 | LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count); |
5223 | |
|
5224 | 0 | lxw_xml_start_tag(self->file, "colBreaks", &attributes); |
5225 | |
|
5226 | 0 | for (i = 0; i < count; i++) |
5227 | 0 | _worksheet_write_brk(self, self->vbreaks[i], LXW_ROW_MAX - 1); |
5228 | |
|
5229 | 0 | lxw_xml_end_tag(self->file, "colBreaks"); |
5230 | |
|
5231 | 0 | LXW_FREE_ATTRIBUTES(); |
5232 | 0 | } |
5233 | | |
5234 | | /* |
5235 | | * Write the <filter> element. |
5236 | | */ |
5237 | | STATIC void |
5238 | | _worksheet_write_filter(lxw_worksheet *self, const char *str, double num, |
5239 | | uint8_t criteria) |
5240 | 0 | { |
5241 | 0 | struct xml_attribute_list attributes; |
5242 | 0 | struct xml_attribute *attribute; |
5243 | |
|
5244 | 0 | if (criteria == LXW_FILTER_CRITERIA_BLANKS) |
5245 | 0 | return; |
5246 | | |
5247 | 0 | LXW_INIT_ATTRIBUTES(); |
5248 | |
|
5249 | 0 | if (str) |
5250 | 0 | LXW_PUSH_ATTRIBUTES_STR("val", str); |
5251 | 0 | else |
5252 | 0 | LXW_PUSH_ATTRIBUTES_DBL("val", num); |
5253 | |
|
5254 | 0 | lxw_xml_empty_tag(self->file, "filter", &attributes); |
5255 | |
|
5256 | 0 | LXW_FREE_ATTRIBUTES(); |
5257 | 0 | } |
5258 | | |
5259 | | /* |
5260 | | * Write the <filterColumn> element as simple equality. |
5261 | | */ |
5262 | | STATIC void |
5263 | | _worksheet_write_filter_standard(lxw_worksheet *self, |
5264 | | lxw_filter_rule_obj *filter) |
5265 | 0 | { |
5266 | 0 | struct xml_attribute_list attributes; |
5267 | 0 | struct xml_attribute *attribute; |
5268 | |
|
5269 | 0 | LXW_INIT_ATTRIBUTES(); |
5270 | |
|
5271 | 0 | if (filter->has_blanks) { |
5272 | 0 | LXW_PUSH_ATTRIBUTES_STR("blank", "1"); |
5273 | 0 | } |
5274 | |
|
5275 | 0 | if (filter->type == LXW_FILTER_TYPE_SINGLE && filter->has_blanks) { |
5276 | 0 | lxw_xml_empty_tag(self->file, "filters", &attributes); |
5277 | |
|
5278 | 0 | } |
5279 | 0 | else { |
5280 | 0 | lxw_xml_start_tag(self->file, "filters", &attributes); |
5281 | | |
5282 | | /* Write the filter element. */ |
5283 | 0 | if (filter->type == LXW_FILTER_TYPE_SINGLE) { |
5284 | 0 | _worksheet_write_filter(self, filter->value1_string, |
5285 | 0 | filter->value1, filter->criteria1); |
5286 | 0 | } |
5287 | 0 | else if (filter->type == LXW_FILTER_TYPE_AND |
5288 | 0 | || filter->type == LXW_FILTER_TYPE_OR) { |
5289 | 0 | _worksheet_write_filter(self, filter->value1_string, |
5290 | 0 | filter->value1, filter->criteria1); |
5291 | 0 | _worksheet_write_filter(self, filter->value2_string, |
5292 | 0 | filter->value2, filter->criteria2); |
5293 | 0 | } |
5294 | |
|
5295 | 0 | lxw_xml_end_tag(self->file, "filters"); |
5296 | 0 | } |
5297 | |
|
5298 | 0 | LXW_FREE_ATTRIBUTES(); |
5299 | 0 | } |
5300 | | |
5301 | | /* |
5302 | | * Write the <customFilter> element. |
5303 | | */ |
5304 | | STATIC void |
5305 | | _worksheet_write_custom_filter(lxw_worksheet *self, const char *str, |
5306 | | double num, uint8_t criteria) |
5307 | 0 | { |
5308 | 0 | struct xml_attribute_list attributes; |
5309 | 0 | struct xml_attribute *attribute; |
5310 | |
|
5311 | 0 | LXW_INIT_ATTRIBUTES(); |
5312 | |
|
5313 | 0 | if (criteria == LXW_FILTER_CRITERIA_NOT_EQUAL_TO) |
5314 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual"); |
5315 | 0 | if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN) |
5316 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan"); |
5317 | 0 | else if (criteria == LXW_FILTER_CRITERIA_GREATER_THAN_OR_EQUAL_TO) |
5318 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual"); |
5319 | 0 | else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN) |
5320 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan"); |
5321 | 0 | else if (criteria == LXW_FILTER_CRITERIA_LESS_THAN_OR_EQUAL_TO) |
5322 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual"); |
5323 | |
|
5324 | 0 | if (str) |
5325 | 0 | LXW_PUSH_ATTRIBUTES_STR("val", str); |
5326 | 0 | else |
5327 | 0 | LXW_PUSH_ATTRIBUTES_DBL("val", num); |
5328 | |
|
5329 | 0 | lxw_xml_empty_tag(self->file, "customFilter", &attributes); |
5330 | |
|
5331 | 0 | LXW_FREE_ATTRIBUTES(); |
5332 | 0 | } |
5333 | | |
5334 | | /* |
5335 | | * Write the <filterColumn> element as a list. |
5336 | | */ |
5337 | | STATIC void |
5338 | | _worksheet_write_filter_list(lxw_worksheet *self, lxw_filter_rule_obj *filter) |
5339 | 0 | { |
5340 | 0 | struct xml_attribute_list attributes; |
5341 | 0 | struct xml_attribute *attribute; |
5342 | 0 | uint16_t i; |
5343 | |
|
5344 | 0 | LXW_INIT_ATTRIBUTES(); |
5345 | |
|
5346 | 0 | if (filter->has_blanks) { |
5347 | 0 | LXW_PUSH_ATTRIBUTES_STR("blank", "1"); |
5348 | 0 | } |
5349 | |
|
5350 | 0 | lxw_xml_start_tag(self->file, "filters", &attributes); |
5351 | |
|
5352 | 0 | for (i = 0; i < filter->num_list_filters; i++) { |
5353 | 0 | _worksheet_write_filter(self, filter->list[i], 0, 0); |
5354 | 0 | } |
5355 | |
|
5356 | 0 | lxw_xml_end_tag(self->file, "filters"); |
5357 | |
|
5358 | 0 | LXW_FREE_ATTRIBUTES(); |
5359 | 0 | } |
5360 | | |
5361 | | /* |
5362 | | * Write the <filterColumn> element. |
5363 | | */ |
5364 | | STATIC void |
5365 | | _worksheet_write_filter_custom(lxw_worksheet *self, |
5366 | | lxw_filter_rule_obj *filter) |
5367 | 0 | { |
5368 | 0 | struct xml_attribute_list attributes; |
5369 | 0 | struct xml_attribute *attribute; |
5370 | |
|
5371 | 0 | LXW_INIT_ATTRIBUTES(); |
5372 | |
|
5373 | 0 | if (filter->type == LXW_FILTER_TYPE_AND) |
5374 | 0 | LXW_PUSH_ATTRIBUTES_STR("and", "1"); |
5375 | |
|
5376 | 0 | lxw_xml_start_tag(self->file, "customFilters", &attributes); |
5377 | | |
5378 | | /* Write the filter element. */ |
5379 | 0 | if (filter->type == LXW_FILTER_TYPE_SINGLE) { |
5380 | 0 | _worksheet_write_custom_filter(self, filter->value1_string, |
5381 | 0 | filter->value1, filter->criteria1); |
5382 | 0 | } |
5383 | 0 | else if (filter->type == LXW_FILTER_TYPE_AND |
5384 | 0 | || filter->type == LXW_FILTER_TYPE_OR) { |
5385 | 0 | _worksheet_write_custom_filter(self, filter->value1_string, |
5386 | 0 | filter->value1, filter->criteria1); |
5387 | 0 | _worksheet_write_custom_filter(self, filter->value2_string, |
5388 | 0 | filter->value2, filter->criteria2); |
5389 | 0 | } |
5390 | |
|
5391 | 0 | lxw_xml_end_tag(self->file, "customFilters"); |
5392 | |
|
5393 | 0 | LXW_FREE_ATTRIBUTES(); |
5394 | 0 | } |
5395 | | |
5396 | | /* |
5397 | | * Write the <filterColumn> element. |
5398 | | */ |
5399 | | STATIC void |
5400 | | _worksheet_write_filter_column(lxw_worksheet *self, |
5401 | | lxw_filter_rule_obj *filter) |
5402 | 0 | { |
5403 | 0 | struct xml_attribute_list attributes; |
5404 | 0 | struct xml_attribute *attribute; |
5405 | |
|
5406 | 0 | if (!filter) |
5407 | 0 | return; |
5408 | | |
5409 | 0 | LXW_INIT_ATTRIBUTES(); |
5410 | 0 | LXW_PUSH_ATTRIBUTES_INT("colId", filter->col_num); |
5411 | |
|
5412 | 0 | lxw_xml_start_tag(self->file, "filterColumn", &attributes); |
5413 | |
|
5414 | 0 | if (filter->list) |
5415 | 0 | _worksheet_write_filter_list(self, filter); |
5416 | 0 | else if (filter->is_custom) |
5417 | 0 | _worksheet_write_filter_custom(self, filter); |
5418 | 0 | else |
5419 | 0 | _worksheet_write_filter_standard(self, filter); |
5420 | |
|
5421 | 0 | lxw_xml_end_tag(self->file, "filterColumn"); |
5422 | |
|
5423 | 0 | LXW_FREE_ATTRIBUTES(); |
5424 | 0 | } |
5425 | | |
5426 | | /* |
5427 | | * Write the <autoFilter> element. |
5428 | | */ |
5429 | | STATIC void |
5430 | | _worksheet_write_auto_filter(lxw_worksheet *self) |
5431 | 1.30k | { |
5432 | 1.30k | struct xml_attribute_list attributes; |
5433 | 1.30k | struct xml_attribute *attribute; |
5434 | 1.30k | char range[LXW_MAX_CELL_RANGE_LENGTH]; |
5435 | 1.30k | uint16_t i; |
5436 | | |
5437 | 1.30k | if (!self->autofilter.in_use) |
5438 | 1.30k | return; |
5439 | | |
5440 | 0 | lxw_rowcol_to_range(range, |
5441 | 0 | self->autofilter.first_row, |
5442 | 0 | self->autofilter.first_col, |
5443 | 0 | self->autofilter.last_row, self->autofilter.last_col); |
5444 | |
|
5445 | 0 | LXW_INIT_ATTRIBUTES(); |
5446 | 0 | LXW_PUSH_ATTRIBUTES_STR("ref", range); |
5447 | |
|
5448 | 0 | if (self->autofilter.has_rules) { |
5449 | 0 | lxw_xml_start_tag(self->file, "autoFilter", &attributes); |
5450 | |
|
5451 | 0 | for (i = 0; i < self->num_filter_rules; i++) |
5452 | 0 | _worksheet_write_filter_column(self, self->filter_rules[i]); |
5453 | |
|
5454 | 0 | lxw_xml_end_tag(self->file, "autoFilter"); |
5455 | |
|
5456 | 0 | } |
5457 | 0 | else { |
5458 | 0 | lxw_xml_empty_tag(self->file, "autoFilter", &attributes); |
5459 | 0 | } |
5460 | |
|
5461 | 0 | LXW_FREE_ATTRIBUTES(); |
5462 | 0 | } |
5463 | | |
5464 | | /* |
5465 | | * Write the <hyperlink> element for external links. |
5466 | | */ |
5467 | | STATIC void |
5468 | | _worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num, |
5469 | | lxw_col_t col_num, const char *location, |
5470 | | const char *tooltip, uint16_t id) |
5471 | 0 | { |
5472 | 0 | struct xml_attribute_list attributes; |
5473 | 0 | struct xml_attribute *attribute; |
5474 | 0 | char ref[LXW_MAX_CELL_NAME_LENGTH]; |
5475 | 0 | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
5476 | |
|
5477 | 0 | lxw_rowcol_to_cell(ref, row_num, col_num); |
5478 | |
|
5479 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); |
5480 | |
|
5481 | 0 | LXW_INIT_ATTRIBUTES(); |
5482 | 0 | LXW_PUSH_ATTRIBUTES_STR("ref", ref); |
5483 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
5484 | |
|
5485 | 0 | if (location) |
5486 | 0 | LXW_PUSH_ATTRIBUTES_STR("location", location); |
5487 | |
|
5488 | 0 | if (tooltip) |
5489 | 0 | LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); |
5490 | |
|
5491 | 0 | lxw_xml_empty_tag(self->file, "hyperlink", &attributes); |
5492 | |
|
5493 | 0 | LXW_FREE_ATTRIBUTES(); |
5494 | 0 | } |
5495 | | |
5496 | | /* |
5497 | | * Write the <hyperlink> element for internal links. |
5498 | | */ |
5499 | | STATIC void |
5500 | | _worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num, |
5501 | | lxw_col_t col_num, const char *location, |
5502 | | const char *display, const char *tooltip) |
5503 | 0 | { |
5504 | 0 | struct xml_attribute_list attributes; |
5505 | 0 | struct xml_attribute *attribute; |
5506 | 0 | char ref[LXW_MAX_CELL_NAME_LENGTH]; |
5507 | |
|
5508 | 0 | lxw_rowcol_to_cell(ref, row_num, col_num); |
5509 | |
|
5510 | 0 | LXW_INIT_ATTRIBUTES(); |
5511 | 0 | LXW_PUSH_ATTRIBUTES_STR("ref", ref); |
5512 | |
|
5513 | 0 | if (location) |
5514 | 0 | LXW_PUSH_ATTRIBUTES_STR("location", location); |
5515 | |
|
5516 | 0 | if (tooltip) |
5517 | 0 | LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip); |
5518 | |
|
5519 | 0 | if (display) |
5520 | 0 | LXW_PUSH_ATTRIBUTES_STR("display", display); |
5521 | |
|
5522 | 0 | lxw_xml_empty_tag(self->file, "hyperlink", &attributes); |
5523 | |
|
5524 | 0 | LXW_FREE_ATTRIBUTES(); |
5525 | 0 | } |
5526 | | |
5527 | | /* |
5528 | | * Process any stored hyperlinks in row/col order and write the <hyperlinks> |
5529 | | * element. The attributes are different for internal and external links. |
5530 | | */ |
5531 | | STATIC void |
5532 | | _worksheet_write_hyperlinks(lxw_worksheet *self) |
5533 | 1.30k | { |
5534 | | |
5535 | 1.30k | lxw_row *row; |
5536 | 1.30k | lxw_cell *link; |
5537 | 1.30k | lxw_rel_tuple *relationship; |
5538 | | |
5539 | 1.30k | if (RB_EMPTY(self->hyperlinks)) |
5540 | 1.30k | return; |
5541 | | |
5542 | | /* Write the hyperlink elements. */ |
5543 | 0 | lxw_xml_start_tag(self->file, "hyperlinks", NULL); |
5544 | |
|
5545 | 0 | RB_FOREACH(row, lxw_table_rows, self->hyperlinks) { |
5546 | |
|
5547 | 0 | RB_FOREACH(link, lxw_table_cells, row->cells) { |
5548 | |
|
5549 | 0 | if (link->type == HYPERLINK_URL |
5550 | 0 | || link->type == HYPERLINK_EXTERNAL) { |
5551 | |
|
5552 | 0 | self->rel_count++; |
5553 | |
|
5554 | 0 | relationship = calloc(1, sizeof(lxw_rel_tuple)); |
5555 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error); |
5556 | | |
5557 | 0 | relationship->type = lxw_strdup("/hyperlink"); |
5558 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error); |
5559 | | |
5560 | 0 | relationship->target = lxw_strdup(link->u.string); |
5561 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error); |
5562 | | |
5563 | 0 | relationship->target_mode = lxw_strdup("External"); |
5564 | 0 | GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error); |
5565 | | |
5566 | 0 | STAILQ_INSERT_TAIL(self->external_hyperlinks, relationship, |
5567 | 0 | list_pointers); |
5568 | |
|
5569 | 0 | _worksheet_write_hyperlink_external(self, link->row_num, |
5570 | 0 | link->col_num, |
5571 | 0 | link->user_data1, |
5572 | 0 | link->user_data2, |
5573 | 0 | self->rel_count); |
5574 | 0 | } |
5575 | | |
5576 | 0 | if (link->type == HYPERLINK_INTERNAL) { |
5577 | |
|
5578 | 0 | _worksheet_write_hyperlink_internal(self, link->row_num, |
5579 | 0 | link->col_num, |
5580 | 0 | link->u.string, |
5581 | 0 | link->user_data1, |
5582 | 0 | link->user_data2); |
5583 | 0 | } |
5584 | |
|
5585 | 0 | } |
5586 | |
|
5587 | 0 | } |
5588 | | |
5589 | 0 | lxw_xml_end_tag(self->file, "hyperlinks"); |
5590 | 0 | return; |
5591 | | |
5592 | 0 | mem_error: |
5593 | 0 | if (relationship) { |
5594 | 0 | free(relationship->type); |
5595 | 0 | free(relationship->target); |
5596 | 0 | free(relationship->target_mode); |
5597 | 0 | free(relationship); |
5598 | 0 | } |
5599 | 0 | lxw_xml_end_tag(self->file, "hyperlinks"); |
5600 | 0 | } |
5601 | | |
5602 | | /* |
5603 | | * Write the <sheetProtection> element. |
5604 | | */ |
5605 | | STATIC void |
5606 | | _worksheet_write_sheet_protection(lxw_worksheet *self, |
5607 | | lxw_protection_obj *protect) |
5608 | 1.30k | { |
5609 | 1.30k | struct xml_attribute_list attributes; |
5610 | 1.30k | struct xml_attribute *attribute; |
5611 | | |
5612 | 1.30k | if (!protect->is_configured) |
5613 | 1.30k | return; |
5614 | | |
5615 | 0 | LXW_INIT_ATTRIBUTES(); |
5616 | |
|
5617 | 0 | if (*protect->hash) |
5618 | 0 | LXW_PUSH_ATTRIBUTES_STR("password", protect->hash); |
5619 | |
|
5620 | 0 | if (!protect->no_sheet) |
5621 | 0 | LXW_PUSH_ATTRIBUTES_INT("sheet", 1); |
5622 | |
|
5623 | 0 | if (!protect->no_content) |
5624 | 0 | LXW_PUSH_ATTRIBUTES_INT("content", 1); |
5625 | |
|
5626 | 0 | if (!protect->objects) |
5627 | 0 | LXW_PUSH_ATTRIBUTES_INT("objects", 1); |
5628 | |
|
5629 | 0 | if (!protect->scenarios) |
5630 | 0 | LXW_PUSH_ATTRIBUTES_INT("scenarios", 1); |
5631 | |
|
5632 | 0 | if (protect->format_cells) |
5633 | 0 | LXW_PUSH_ATTRIBUTES_INT("formatCells", 0); |
5634 | |
|
5635 | 0 | if (protect->format_columns) |
5636 | 0 | LXW_PUSH_ATTRIBUTES_INT("formatColumns", 0); |
5637 | |
|
5638 | 0 | if (protect->format_rows) |
5639 | 0 | LXW_PUSH_ATTRIBUTES_INT("formatRows", 0); |
5640 | |
|
5641 | 0 | if (protect->insert_columns) |
5642 | 0 | LXW_PUSH_ATTRIBUTES_INT("insertColumns", 0); |
5643 | |
|
5644 | 0 | if (protect->insert_rows) |
5645 | 0 | LXW_PUSH_ATTRIBUTES_INT("insertRows", 0); |
5646 | |
|
5647 | 0 | if (protect->insert_hyperlinks) |
5648 | 0 | LXW_PUSH_ATTRIBUTES_INT("insertHyperlinks", 0); |
5649 | |
|
5650 | 0 | if (protect->delete_columns) |
5651 | 0 | LXW_PUSH_ATTRIBUTES_INT("deleteColumns", 0); |
5652 | |
|
5653 | 0 | if (protect->delete_rows) |
5654 | 0 | LXW_PUSH_ATTRIBUTES_INT("deleteRows", 0); |
5655 | |
|
5656 | 0 | if (protect->no_select_locked_cells) |
5657 | 0 | LXW_PUSH_ATTRIBUTES_INT("selectLockedCells", 1); |
5658 | |
|
5659 | 0 | if (protect->sort) |
5660 | 0 | LXW_PUSH_ATTRIBUTES_INT("sort", 0); |
5661 | |
|
5662 | 0 | if (protect->autofilter) |
5663 | 0 | LXW_PUSH_ATTRIBUTES_INT("autoFilter", 0); |
5664 | |
|
5665 | 0 | if (protect->pivot_tables) |
5666 | 0 | LXW_PUSH_ATTRIBUTES_INT("pivotTables", 0); |
5667 | |
|
5668 | 0 | if (protect->no_select_unlocked_cells) |
5669 | 0 | LXW_PUSH_ATTRIBUTES_INT("selectUnlockedCells", 1); |
5670 | |
|
5671 | 0 | lxw_xml_empty_tag(self->file, "sheetProtection", &attributes); |
5672 | |
|
5673 | 0 | LXW_FREE_ATTRIBUTES(); |
5674 | 0 | } |
5675 | | |
5676 | | /* |
5677 | | * Write the <legacyDrawing> element. |
5678 | | */ |
5679 | | STATIC void |
5680 | | _worksheet_write_legacy_drawing(lxw_worksheet *self) |
5681 | 1.30k | { |
5682 | 1.30k | struct xml_attribute_list attributes; |
5683 | 1.30k | struct xml_attribute *attribute; |
5684 | 1.30k | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
5685 | | |
5686 | 1.30k | if (!self->has_vml) |
5687 | 1.30k | return; |
5688 | 0 | else |
5689 | 0 | self->rel_count++; |
5690 | | |
5691 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); |
5692 | 0 | LXW_INIT_ATTRIBUTES(); |
5693 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
5694 | |
|
5695 | 0 | lxw_xml_empty_tag(self->file, "legacyDrawing", &attributes); |
5696 | |
|
5697 | 0 | LXW_FREE_ATTRIBUTES(); |
5698 | |
|
5699 | 0 | } |
5700 | | |
5701 | | /* |
5702 | | * Write the <legacyDrawingHF> element. |
5703 | | */ |
5704 | | STATIC void |
5705 | | _worksheet_write_legacy_drawing_hf(lxw_worksheet *self) |
5706 | 1.30k | { |
5707 | 1.30k | struct xml_attribute_list attributes; |
5708 | 1.30k | struct xml_attribute *attribute; |
5709 | 1.30k | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
5710 | | |
5711 | 1.30k | if (!self->has_header_vml) |
5712 | 1.30k | return; |
5713 | 0 | else |
5714 | 0 | self->rel_count++; |
5715 | | |
5716 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); |
5717 | 0 | LXW_INIT_ATTRIBUTES(); |
5718 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
5719 | |
|
5720 | 0 | lxw_xml_empty_tag(self->file, "legacyDrawingHF", &attributes); |
5721 | |
|
5722 | 0 | LXW_FREE_ATTRIBUTES(); |
5723 | |
|
5724 | 0 | } |
5725 | | |
5726 | | /* |
5727 | | * Write the <picture> element. |
5728 | | */ |
5729 | | STATIC void |
5730 | | _worksheet_write_picture(lxw_worksheet *self) |
5731 | 1.30k | { |
5732 | 1.30k | struct xml_attribute_list attributes; |
5733 | 1.30k | struct xml_attribute *attribute; |
5734 | 1.30k | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
5735 | | |
5736 | 1.30k | if (!self->has_background_image) |
5737 | 1.30k | return; |
5738 | 0 | else |
5739 | 0 | self->rel_count++; |
5740 | | |
5741 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", self->rel_count); |
5742 | 0 | LXW_INIT_ATTRIBUTES(); |
5743 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
5744 | |
|
5745 | 0 | lxw_xml_empty_tag(self->file, "picture", &attributes); |
5746 | |
|
5747 | 0 | LXW_FREE_ATTRIBUTES(); |
5748 | 0 | } |
5749 | | |
5750 | | /* |
5751 | | * Write the <drawing> element. |
5752 | | */ |
5753 | | STATIC void |
5754 | | _worksheet_write_drawing(lxw_worksheet *self, uint16_t id) |
5755 | 0 | { |
5756 | 0 | struct xml_attribute_list attributes; |
5757 | 0 | struct xml_attribute *attribute; |
5758 | 0 | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
5759 | |
|
5760 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); |
5761 | 0 | LXW_INIT_ATTRIBUTES(); |
5762 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
5763 | |
|
5764 | 0 | lxw_xml_empty_tag(self->file, "drawing", &attributes); |
5765 | |
|
5766 | 0 | LXW_FREE_ATTRIBUTES(); |
5767 | |
|
5768 | 0 | } |
5769 | | |
5770 | | /* |
5771 | | * Write the <drawing> elements. |
5772 | | */ |
5773 | | STATIC void |
5774 | | _worksheet_write_drawings(lxw_worksheet *self) |
5775 | 1.30k | { |
5776 | 1.30k | if (!self->drawing) |
5777 | 1.30k | return; |
5778 | | |
5779 | 0 | self->rel_count++; |
5780 | |
|
5781 | 0 | _worksheet_write_drawing(self, self->rel_count); |
5782 | 0 | } |
5783 | | |
5784 | | /* |
5785 | | * Write the <formula1> element for numbers. |
5786 | | */ |
5787 | | STATIC void |
5788 | | _worksheet_write_formula1_num(lxw_worksheet *self, double number) |
5789 | 0 | { |
5790 | 0 | char data[LXW_ATTR_32]; |
5791 | |
|
5792 | 0 | lxw_sprintf_dbl(data, number); |
5793 | |
|
5794 | 0 | lxw_xml_data_element(self->file, "formula1", data, NULL); |
5795 | 0 | } |
5796 | | |
5797 | | /* |
5798 | | * Write the <formula1> element for strings/formulas. |
5799 | | */ |
5800 | | STATIC void |
5801 | | _worksheet_write_formula1_str(lxw_worksheet *self, char *str) |
5802 | 0 | { |
5803 | 0 | lxw_xml_data_element(self->file, "formula1", str, NULL); |
5804 | 0 | } |
5805 | | |
5806 | | /* |
5807 | | * Write the <formula2> element for numbers. |
5808 | | */ |
5809 | | STATIC void |
5810 | | _worksheet_write_formula2_num(lxw_worksheet *self, double number) |
5811 | 0 | { |
5812 | 0 | char data[LXW_ATTR_32]; |
5813 | |
|
5814 | 0 | lxw_sprintf_dbl(data, number); |
5815 | |
|
5816 | 0 | lxw_xml_data_element(self->file, "formula2", data, NULL); |
5817 | 0 | } |
5818 | | |
5819 | | /* |
5820 | | * Write the <formula2> element for strings/formulas. |
5821 | | */ |
5822 | | STATIC void |
5823 | | _worksheet_write_formula2_str(lxw_worksheet *self, char *str) |
5824 | 0 | { |
5825 | 0 | lxw_xml_data_element(self->file, "formula2", str, NULL); |
5826 | 0 | } |
5827 | | |
5828 | | /* |
5829 | | * Write the <dataValidation> element. |
5830 | | */ |
5831 | | STATIC void |
5832 | | _worksheet_write_data_validation(lxw_worksheet *self, |
5833 | | lxw_data_val_obj *validation) |
5834 | 0 | { |
5835 | 0 | struct xml_attribute_list attributes; |
5836 | 0 | struct xml_attribute *attribute; |
5837 | 0 | uint8_t is_between = 0; |
5838 | |
|
5839 | 0 | LXW_INIT_ATTRIBUTES(); |
5840 | |
|
5841 | 0 | switch (validation->validate) { |
5842 | 0 | case LXW_VALIDATION_TYPE_INTEGER: |
5843 | 0 | case LXW_VALIDATION_TYPE_INTEGER_FORMULA: |
5844 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "whole"); |
5845 | 0 | break; |
5846 | 0 | case LXW_VALIDATION_TYPE_DECIMAL: |
5847 | 0 | case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: |
5848 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "decimal"); |
5849 | 0 | break; |
5850 | 0 | case LXW_VALIDATION_TYPE_LIST: |
5851 | 0 | case LXW_VALIDATION_TYPE_LIST_FORMULA: |
5852 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "list"); |
5853 | 0 | break; |
5854 | 0 | case LXW_VALIDATION_TYPE_DATE: |
5855 | 0 | case LXW_VALIDATION_TYPE_DATE_FORMULA: |
5856 | 0 | case LXW_VALIDATION_TYPE_DATE_NUMBER: |
5857 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "date"); |
5858 | 0 | break; |
5859 | 0 | case LXW_VALIDATION_TYPE_TIME: |
5860 | 0 | case LXW_VALIDATION_TYPE_TIME_FORMULA: |
5861 | 0 | case LXW_VALIDATION_TYPE_TIME_NUMBER: |
5862 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "time"); |
5863 | 0 | break; |
5864 | 0 | case LXW_VALIDATION_TYPE_LENGTH: |
5865 | 0 | case LXW_VALIDATION_TYPE_LENGTH_FORMULA: |
5866 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "textLength"); |
5867 | 0 | break; |
5868 | 0 | case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: |
5869 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "custom"); |
5870 | 0 | break; |
5871 | 0 | } |
5872 | | |
5873 | 0 | switch (validation->criteria) { |
5874 | 0 | case LXW_VALIDATION_CRITERIA_EQUAL_TO: |
5875 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "equal"); |
5876 | 0 | break; |
5877 | 0 | case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO: |
5878 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual"); |
5879 | 0 | break; |
5880 | 0 | case LXW_VALIDATION_CRITERIA_LESS_THAN: |
5881 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan"); |
5882 | 0 | break; |
5883 | 0 | case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO: |
5884 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual"); |
5885 | 0 | break; |
5886 | 0 | case LXW_VALIDATION_CRITERIA_GREATER_THAN: |
5887 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan"); |
5888 | 0 | break; |
5889 | 0 | case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO: |
5890 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual"); |
5891 | 0 | break; |
5892 | 0 | case LXW_VALIDATION_CRITERIA_BETWEEN: |
5893 | | /* Between is the default for 2 formulas and isn't added. */ |
5894 | 0 | is_between = 1; |
5895 | 0 | break; |
5896 | 0 | case LXW_VALIDATION_CRITERIA_NOT_BETWEEN: |
5897 | 0 | is_between = 1; |
5898 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween"); |
5899 | 0 | break; |
5900 | 0 | } |
5901 | | |
5902 | 0 | if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING) |
5903 | 0 | LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning"); |
5904 | |
|
5905 | 0 | if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION) |
5906 | 0 | LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information"); |
5907 | |
|
5908 | 0 | if (validation->ignore_blank) |
5909 | 0 | LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1); |
5910 | |
|
5911 | 0 | if (validation->dropdown == LXW_VALIDATION_OFF) |
5912 | 0 | LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1); |
5913 | |
|
5914 | 0 | if (validation->show_input) |
5915 | 0 | LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1); |
5916 | |
|
5917 | 0 | if (validation->show_error) |
5918 | 0 | LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1); |
5919 | |
|
5920 | 0 | if (validation->error_title) |
5921 | 0 | LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title); |
5922 | |
|
5923 | 0 | if (validation->error_message) |
5924 | 0 | LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message); |
5925 | |
|
5926 | 0 | if (validation->input_title) |
5927 | 0 | LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title); |
5928 | |
|
5929 | 0 | if (validation->input_message) |
5930 | 0 | LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message); |
5931 | |
|
5932 | 0 | LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref); |
5933 | |
|
5934 | 0 | if (validation->validate == LXW_VALIDATION_TYPE_ANY) |
5935 | 0 | lxw_xml_empty_tag(self->file, "dataValidation", &attributes); |
5936 | 0 | else |
5937 | 0 | lxw_xml_start_tag(self->file, "dataValidation", &attributes); |
5938 | | |
5939 | | /* Write the formula1 and formula2 elements. */ |
5940 | 0 | switch (validation->validate) { |
5941 | 0 | case LXW_VALIDATION_TYPE_INTEGER: |
5942 | 0 | case LXW_VALIDATION_TYPE_DECIMAL: |
5943 | 0 | case LXW_VALIDATION_TYPE_LENGTH: |
5944 | 0 | case LXW_VALIDATION_TYPE_DATE: |
5945 | 0 | case LXW_VALIDATION_TYPE_TIME: |
5946 | 0 | case LXW_VALIDATION_TYPE_DATE_NUMBER: |
5947 | 0 | case LXW_VALIDATION_TYPE_TIME_NUMBER: |
5948 | 0 | _worksheet_write_formula1_num(self, validation->value_number); |
5949 | 0 | if (is_between) |
5950 | 0 | _worksheet_write_formula2_num(self, |
5951 | 0 | validation->maximum_number); |
5952 | 0 | break; |
5953 | 0 | case LXW_VALIDATION_TYPE_INTEGER_FORMULA: |
5954 | 0 | case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: |
5955 | 0 | case LXW_VALIDATION_TYPE_LENGTH_FORMULA: |
5956 | 0 | case LXW_VALIDATION_TYPE_DATE_FORMULA: |
5957 | 0 | case LXW_VALIDATION_TYPE_TIME_FORMULA: |
5958 | 0 | case LXW_VALIDATION_TYPE_LIST: |
5959 | 0 | case LXW_VALIDATION_TYPE_LIST_FORMULA: |
5960 | 0 | case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: |
5961 | 0 | _worksheet_write_formula1_str(self, validation->value_formula); |
5962 | 0 | if (is_between) |
5963 | 0 | _worksheet_write_formula2_str(self, |
5964 | 0 | validation->maximum_formula); |
5965 | 0 | break; |
5966 | 0 | } |
5967 | | |
5968 | 0 | if (validation->validate != LXW_VALIDATION_TYPE_ANY) |
5969 | 0 | lxw_xml_end_tag(self->file, "dataValidation"); |
5970 | |
|
5971 | 0 | LXW_FREE_ATTRIBUTES(); |
5972 | 0 | } |
5973 | | |
5974 | | /* |
5975 | | * Write the <dataValidations> element. |
5976 | | */ |
5977 | | STATIC void |
5978 | | _worksheet_write_data_validations(lxw_worksheet *self) |
5979 | 1.30k | { |
5980 | 1.30k | struct xml_attribute_list attributes; |
5981 | 1.30k | struct xml_attribute *attribute; |
5982 | 1.30k | lxw_data_val_obj *data_validation; |
5983 | | |
5984 | 1.30k | if (self->num_validations == 0) |
5985 | 1.30k | return; |
5986 | | |
5987 | 0 | LXW_INIT_ATTRIBUTES(); |
5988 | 0 | LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations); |
5989 | |
|
5990 | 0 | lxw_xml_start_tag(self->file, "dataValidations", &attributes); |
5991 | |
|
5992 | 0 | STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) { |
5993 | | /* Write the dataValidation element. */ |
5994 | 0 | _worksheet_write_data_validation(self, data_validation); |
5995 | 0 | } |
5996 | |
|
5997 | 0 | lxw_xml_end_tag(self->file, "dataValidations"); |
5998 | |
|
5999 | 0 | LXW_FREE_ATTRIBUTES(); |
6000 | 0 | } |
6001 | | |
6002 | | /* |
6003 | | * Write the <formula> element for strings. |
6004 | | */ |
6005 | | STATIC void |
6006 | | _worksheet_write_formula_str(lxw_worksheet *self, char *data) |
6007 | 0 | { |
6008 | 0 | lxw_xml_data_element(self->file, "formula", data, NULL); |
6009 | 0 | } |
6010 | | |
6011 | | /* |
6012 | | * Write the <formula> element for numbers. |
6013 | | */ |
6014 | | STATIC void |
6015 | | _worksheet_write_formula_num(lxw_worksheet *self, double num) |
6016 | 0 | { |
6017 | 0 | char data[LXW_ATTR_32]; |
6018 | |
|
6019 | 0 | lxw_sprintf_dbl(data, num); |
6020 | 0 | lxw_xml_data_element(self->file, "formula", data, NULL); |
6021 | 0 | } |
6022 | | |
6023 | | /* |
6024 | | * Write the <ext> element. |
6025 | | */ |
6026 | | STATIC void |
6027 | | _worksheet_write_ext(lxw_worksheet *self, char *uri) |
6028 | 0 | { |
6029 | 0 | struct xml_attribute_list attributes; |
6030 | 0 | struct xml_attribute *attribute; |
6031 | 0 | char xmlns_x_14[] = |
6032 | 0 | "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"; |
6033 | |
|
6034 | 0 | LXW_INIT_ATTRIBUTES(); |
6035 | 0 | LXW_PUSH_ATTRIBUTES_STR("xmlns:x14", xmlns_x_14); |
6036 | 0 | LXW_PUSH_ATTRIBUTES_STR("uri", uri); |
6037 | |
|
6038 | 0 | lxw_xml_start_tag(self->file, "ext", &attributes); |
6039 | |
|
6040 | 0 | LXW_FREE_ATTRIBUTES(); |
6041 | 0 | } |
6042 | | |
6043 | | /* |
6044 | | * Write the <extLst> dataBar extension element. |
6045 | | */ |
6046 | | STATIC void |
6047 | | _worksheet_write_data_bar_ext(lxw_worksheet *self, |
6048 | | lxw_cond_format_obj *cond_format) |
6049 | 0 | { |
6050 | | /* Create a pseudo GUID for each unique Excel 2010 data bar. */ |
6051 | 0 | cond_format->guid = calloc(1, LXW_GUID_LENGTH); |
6052 | 0 | lxw_snprintf(cond_format->guid, LXW_GUID_LENGTH, |
6053 | 0 | "{DA7ABA51-AAAA-BBBB-%04X-%012X}", |
6054 | 0 | self->index + 1, ++self->data_bar_2010_index); |
6055 | |
|
6056 | 0 | lxw_xml_start_tag(self->file, "extLst", NULL); |
6057 | |
|
6058 | 0 | _worksheet_write_ext(self, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"); |
6059 | |
|
6060 | 0 | lxw_xml_data_element(self->file, "x14:id", cond_format->guid, NULL); |
6061 | |
|
6062 | 0 | lxw_xml_end_tag(self->file, "ext"); |
6063 | 0 | lxw_xml_end_tag(self->file, "extLst"); |
6064 | 0 | } |
6065 | | |
6066 | | /* |
6067 | | * Write the <color> element. |
6068 | | */ |
6069 | | STATIC void |
6070 | | _worksheet_write_color(lxw_worksheet *self, lxw_color_t color) |
6071 | 0 | { |
6072 | 0 | struct xml_attribute_list attributes; |
6073 | 0 | struct xml_attribute *attribute; |
6074 | 0 | char rgb[LXW_ATTR_32]; |
6075 | |
|
6076 | 0 | lxw_snprintf(rgb, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); |
6077 | |
|
6078 | 0 | LXW_INIT_ATTRIBUTES(); |
6079 | 0 | LXW_PUSH_ATTRIBUTES_STR("rgb", rgb); |
6080 | |
|
6081 | 0 | lxw_xml_empty_tag(self->file, "color", &attributes); |
6082 | |
|
6083 | 0 | LXW_FREE_ATTRIBUTES(); |
6084 | 0 | } |
6085 | | |
6086 | | /* |
6087 | | * Write the <cfvo> element for strings. |
6088 | | */ |
6089 | | STATIC void |
6090 | | _worksheet_write_cfvo_str(lxw_worksheet *self, uint8_t rule_type, |
6091 | | char *value, uint8_t data_bar_2010) |
6092 | 0 | { |
6093 | 0 | struct xml_attribute_list attributes; |
6094 | 0 | struct xml_attribute *attribute; |
6095 | |
|
6096 | 0 | LXW_INIT_ATTRIBUTES(); |
6097 | |
|
6098 | 0 | if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) |
6099 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "min"); |
6100 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) |
6101 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "num"); |
6102 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) |
6103 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percent"); |
6104 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) |
6105 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); |
6106 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) |
6107 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "formula"); |
6108 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) |
6109 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "max"); |
6110 | |
|
6111 | 0 | if (!data_bar_2010 || (rule_type != LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
6112 | 0 | && rule_type != LXW_CONDITIONAL_RULE_TYPE_MAXIMUM)) |
6113 | 0 | LXW_PUSH_ATTRIBUTES_STR("val", value); |
6114 | |
|
6115 | 0 | lxw_xml_empty_tag(self->file, "cfvo", &attributes); |
6116 | |
|
6117 | 0 | LXW_FREE_ATTRIBUTES(); |
6118 | 0 | } |
6119 | | |
6120 | | /* |
6121 | | * Write the <cfvo> element for numbers. |
6122 | | */ |
6123 | | STATIC void |
6124 | | _worksheet_write_cfvo_num(lxw_worksheet *self, uint8_t rule_type, |
6125 | | double value, uint8_t data_bar_2010) |
6126 | 0 | { |
6127 | 0 | struct xml_attribute_list attributes; |
6128 | 0 | struct xml_attribute *attribute; |
6129 | |
|
6130 | 0 | LXW_INIT_ATTRIBUTES(); |
6131 | |
|
6132 | 0 | if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) |
6133 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "min"); |
6134 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) |
6135 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "num"); |
6136 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) |
6137 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percent"); |
6138 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) |
6139 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); |
6140 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) |
6141 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "formula"); |
6142 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) |
6143 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "max"); |
6144 | |
|
6145 | 0 | if (!data_bar_2010 || (rule_type != LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
6146 | 0 | && rule_type != LXW_CONDITIONAL_RULE_TYPE_MAXIMUM)) |
6147 | 0 | LXW_PUSH_ATTRIBUTES_DBL("val", value); |
6148 | |
|
6149 | 0 | lxw_xml_empty_tag(self->file, "cfvo", &attributes); |
6150 | |
|
6151 | 0 | LXW_FREE_ATTRIBUTES(); |
6152 | 0 | } |
6153 | | |
6154 | | /* |
6155 | | * Write the <iconSet> element. |
6156 | | */ |
6157 | | STATIC void |
6158 | | _worksheet_write_icon_set(lxw_worksheet *self, |
6159 | | lxw_cond_format_obj *cond_format) |
6160 | 0 | { |
6161 | 0 | struct xml_attribute_list attributes; |
6162 | 0 | struct xml_attribute *attribute; |
6163 | 0 | char *icon_set[] = { |
6164 | 0 | "3Arrows", |
6165 | 0 | "3ArrowsGray", |
6166 | 0 | "3Flags", |
6167 | 0 | "3TrafficLights", |
6168 | 0 | "3TrafficLights2", |
6169 | 0 | "3Signs", |
6170 | 0 | "3Symbols", |
6171 | 0 | "3Symbols2", |
6172 | 0 | "4Arrows", |
6173 | 0 | "4ArrowsGray", |
6174 | 0 | "4RedToBlack", |
6175 | 0 | "4Rating", |
6176 | 0 | "4TrafficLights", |
6177 | 0 | "5Arrows", |
6178 | 0 | "5ArrowsGray", |
6179 | 0 | "5Rating", |
6180 | 0 | "5Quarters", |
6181 | 0 | }; |
6182 | 0 | uint8_t percent = LXW_CONDITIONAL_RULE_TYPE_PERCENT; |
6183 | 0 | uint8_t style = cond_format->icon_style; |
6184 | |
|
6185 | 0 | LXW_INIT_ATTRIBUTES(); |
6186 | |
|
6187 | 0 | if (style != LXW_CONDITIONAL_ICONS_3_TRAFFIC_LIGHTS_UNRIMMED) |
6188 | 0 | LXW_PUSH_ATTRIBUTES_STR("iconSet", icon_set[style]); |
6189 | |
|
6190 | 0 | if (cond_format->reverse_icons == LXW_TRUE) |
6191 | 0 | LXW_PUSH_ATTRIBUTES_STR("reverse", "1"); |
6192 | |
|
6193 | 0 | if (cond_format->icons_only == LXW_TRUE) |
6194 | 0 | LXW_PUSH_ATTRIBUTES_STR("showValue", "0"); |
6195 | |
|
6196 | 0 | lxw_xml_start_tag(self->file, "iconSet", &attributes); |
6197 | |
|
6198 | 0 | if (style < LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED) { |
6199 | 0 | _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); |
6200 | 0 | _worksheet_write_cfvo_num(self, percent, 33, LXW_FALSE); |
6201 | 0 | _worksheet_write_cfvo_num(self, percent, 67, LXW_FALSE); |
6202 | 0 | } |
6203 | |
|
6204 | 0 | if (style >= LXW_CONDITIONAL_ICONS_4_ARROWS_COLORED |
6205 | 0 | && style < LXW_CONDITIONAL_ICONS_5_ARROWS_COLORED) { |
6206 | 0 | _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); |
6207 | 0 | _worksheet_write_cfvo_num(self, percent, 25, LXW_FALSE); |
6208 | 0 | _worksheet_write_cfvo_num(self, percent, 50, LXW_FALSE); |
6209 | 0 | _worksheet_write_cfvo_num(self, percent, 75, LXW_FALSE); |
6210 | 0 | } |
6211 | |
|
6212 | 0 | if (style >= LXW_CONDITIONAL_ICONS_5_ARROWS_COLORED |
6213 | 0 | && style <= LXW_CONDITIONAL_ICONS_5_QUARTERS) { |
6214 | 0 | _worksheet_write_cfvo_num(self, percent, 0, LXW_FALSE); |
6215 | 0 | _worksheet_write_cfvo_num(self, percent, 20, LXW_FALSE); |
6216 | 0 | _worksheet_write_cfvo_num(self, percent, 40, LXW_FALSE); |
6217 | 0 | _worksheet_write_cfvo_num(self, percent, 60, LXW_FALSE); |
6218 | 0 | _worksheet_write_cfvo_num(self, percent, 80, LXW_FALSE); |
6219 | 0 | } |
6220 | |
|
6221 | 0 | LXW_FREE_ATTRIBUTES(); |
6222 | 0 | } |
6223 | | |
6224 | | /* |
6225 | | * Write the <cfRule> element for data bar rules. |
6226 | | */ |
6227 | | STATIC void |
6228 | | _worksheet_write_cf_rule_icons(lxw_worksheet *self, |
6229 | | lxw_cond_format_obj *cond_format) |
6230 | 0 | { |
6231 | 0 | struct xml_attribute_list attributes; |
6232 | 0 | struct xml_attribute *attribute; |
6233 | |
|
6234 | 0 | LXW_INIT_ATTRIBUTES(); |
6235 | |
|
6236 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6237 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6238 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6239 | |
|
6240 | 0 | _worksheet_write_icon_set(self, cond_format); |
6241 | |
|
6242 | 0 | lxw_xml_end_tag(self->file, "iconSet"); |
6243 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6244 | |
|
6245 | 0 | LXW_FREE_ATTRIBUTES(); |
6246 | 0 | } |
6247 | | |
6248 | | /* |
6249 | | * Write the <dataBar> element. |
6250 | | */ |
6251 | | STATIC void |
6252 | | _worksheet_write_data_bar(lxw_worksheet *self, |
6253 | | lxw_cond_format_obj *cond_format) |
6254 | 0 | { |
6255 | 0 | struct xml_attribute_list attributes; |
6256 | 0 | struct xml_attribute *attribute; |
6257 | |
|
6258 | 0 | LXW_INIT_ATTRIBUTES(); |
6259 | |
|
6260 | 0 | if (cond_format->bar_only) |
6261 | 0 | LXW_PUSH_ATTRIBUTES_STR("showValue", "0"); |
6262 | |
|
6263 | 0 | lxw_xml_start_tag(self->file, "dataBar", &attributes); |
6264 | |
|
6265 | 0 | LXW_FREE_ATTRIBUTES(); |
6266 | 0 | } |
6267 | | |
6268 | | /* |
6269 | | * Write the <cfRule> element for data bar rules. |
6270 | | */ |
6271 | | STATIC void |
6272 | | _worksheet_write_cf_rule_data_bar(lxw_worksheet *self, |
6273 | | lxw_cond_format_obj *cond_format) |
6274 | 0 | { |
6275 | 0 | struct xml_attribute_list attributes; |
6276 | 0 | struct xml_attribute *attribute; |
6277 | |
|
6278 | 0 | LXW_INIT_ATTRIBUTES(); |
6279 | |
|
6280 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6281 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6282 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6283 | |
|
6284 | 0 | _worksheet_write_data_bar(self, cond_format); |
6285 | |
|
6286 | 0 | if (cond_format->min_value_string) { |
6287 | 0 | _worksheet_write_cfvo_str(self, cond_format->min_rule_type, |
6288 | 0 | cond_format->min_value_string, |
6289 | 0 | cond_format->data_bar_2010); |
6290 | 0 | } |
6291 | 0 | else { |
6292 | 0 | _worksheet_write_cfvo_num(self, cond_format->min_rule_type, |
6293 | 0 | cond_format->min_value, |
6294 | 0 | cond_format->data_bar_2010); |
6295 | 0 | } |
6296 | |
|
6297 | 0 | if (cond_format->max_value_string) { |
6298 | 0 | _worksheet_write_cfvo_str(self, cond_format->max_rule_type, |
6299 | 0 | cond_format->max_value_string, |
6300 | 0 | cond_format->data_bar_2010); |
6301 | 0 | } |
6302 | 0 | else { |
6303 | 0 | _worksheet_write_cfvo_num(self, cond_format->max_rule_type, |
6304 | 0 | cond_format->max_value, |
6305 | 0 | cond_format->data_bar_2010); |
6306 | 0 | } |
6307 | |
|
6308 | 0 | _worksheet_write_color(self, cond_format->bar_color); |
6309 | |
|
6310 | 0 | lxw_xml_end_tag(self->file, "dataBar"); |
6311 | |
|
6312 | 0 | if (cond_format->data_bar_2010) |
6313 | 0 | _worksheet_write_data_bar_ext(self, cond_format); |
6314 | |
|
6315 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6316 | |
|
6317 | 0 | LXW_FREE_ATTRIBUTES(); |
6318 | 0 | } |
6319 | | |
6320 | | /* |
6321 | | * Write the <cfRule> element for 2 and 3 color scale rules. |
6322 | | */ |
6323 | | STATIC void |
6324 | | _worksheet_write_cf_rule_color_scale(lxw_worksheet *self, |
6325 | | lxw_cond_format_obj *cond_format) |
6326 | 0 | { |
6327 | 0 | struct xml_attribute_list attributes; |
6328 | 0 | struct xml_attribute *attribute; |
6329 | |
|
6330 | 0 | LXW_INIT_ATTRIBUTES(); |
6331 | |
|
6332 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6333 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6334 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6335 | |
|
6336 | 0 | lxw_xml_start_tag(self->file, "colorScale", NULL); |
6337 | |
|
6338 | 0 | if (cond_format->min_value_string) { |
6339 | 0 | _worksheet_write_cfvo_str(self, cond_format->min_rule_type, |
6340 | 0 | cond_format->min_value_string, LXW_FALSE); |
6341 | 0 | } |
6342 | 0 | else { |
6343 | 0 | _worksheet_write_cfvo_num(self, cond_format->min_rule_type, |
6344 | 0 | cond_format->min_value, LXW_FALSE); |
6345 | 0 | } |
6346 | |
|
6347 | 0 | if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { |
6348 | 0 | if (cond_format->mid_value_string) { |
6349 | 0 | _worksheet_write_cfvo_str(self, cond_format->mid_rule_type, |
6350 | 0 | cond_format->mid_value_string, |
6351 | 0 | LXW_FALSE); |
6352 | 0 | } |
6353 | 0 | else { |
6354 | 0 | _worksheet_write_cfvo_num(self, cond_format->mid_rule_type, |
6355 | 0 | cond_format->mid_value, LXW_FALSE); |
6356 | 0 | } |
6357 | 0 | } |
6358 | |
|
6359 | 0 | if (cond_format->max_value_string) { |
6360 | 0 | _worksheet_write_cfvo_str(self, cond_format->max_rule_type, |
6361 | 0 | cond_format->max_value_string, LXW_FALSE); |
6362 | 0 | } |
6363 | 0 | else { |
6364 | 0 | _worksheet_write_cfvo_num(self, cond_format->max_rule_type, |
6365 | 0 | cond_format->max_value, LXW_FALSE); |
6366 | 0 | } |
6367 | |
|
6368 | 0 | _worksheet_write_color(self, cond_format->min_color); |
6369 | |
|
6370 | 0 | if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) |
6371 | 0 | _worksheet_write_color(self, cond_format->mid_color); |
6372 | |
|
6373 | 0 | _worksheet_write_color(self, cond_format->max_color); |
6374 | |
|
6375 | 0 | lxw_xml_end_tag(self->file, "colorScale"); |
6376 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6377 | |
|
6378 | 0 | LXW_FREE_ATTRIBUTES(); |
6379 | 0 | } |
6380 | | |
6381 | | /* |
6382 | | * Write the <cfRule> element for formula rules. |
6383 | | */ |
6384 | | STATIC void |
6385 | | _worksheet_write_cf_rule_formula(lxw_worksheet *self, |
6386 | | lxw_cond_format_obj *cond_format) |
6387 | 0 | { |
6388 | 0 | struct xml_attribute_list attributes; |
6389 | 0 | struct xml_attribute *attribute; |
6390 | |
|
6391 | 0 | LXW_INIT_ATTRIBUTES(); |
6392 | |
|
6393 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6394 | |
|
6395 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6396 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6397 | |
|
6398 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6399 | |
|
6400 | 0 | if (cond_format->stop_if_true) |
6401 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6402 | |
|
6403 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6404 | |
|
6405 | 0 | _worksheet_write_formula_str(self, cond_format->min_value_string); |
6406 | |
|
6407 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6408 | |
|
6409 | 0 | LXW_FREE_ATTRIBUTES(); |
6410 | 0 | } |
6411 | | |
6412 | | /* |
6413 | | * Write the <cfRule> element for top and bottom rules. |
6414 | | */ |
6415 | | STATIC void |
6416 | | _worksheet_write_cf_rule_top(lxw_worksheet *self, |
6417 | | lxw_cond_format_obj *cond_format) |
6418 | 0 | { |
6419 | 0 | struct xml_attribute_list attributes; |
6420 | 0 | struct xml_attribute *attribute; |
6421 | |
|
6422 | 0 | LXW_INIT_ATTRIBUTES(); |
6423 | |
|
6424 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6425 | |
|
6426 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6427 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6428 | |
|
6429 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6430 | |
|
6431 | 0 | if (cond_format->stop_if_true) |
6432 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6433 | |
|
6434 | 0 | if (cond_format->criteria == |
6435 | 0 | LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT) |
6436 | 0 | LXW_PUSH_ATTRIBUTES_INT("percent", 1); |
6437 | |
|
6438 | 0 | if (cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) |
6439 | 0 | LXW_PUSH_ATTRIBUTES_INT("bottom", 1); |
6440 | | |
6441 | | /* Rank must be an int in the range 1-1000 . */ |
6442 | 0 | if (cond_format->min_value < 1.0 || cond_format->min_value > 1000.0) |
6443 | 0 | LXW_PUSH_ATTRIBUTES_DBL("rank", 10); |
6444 | 0 | else |
6445 | 0 | LXW_PUSH_ATTRIBUTES_DBL("rank", (uint16_t) cond_format->min_value); |
6446 | |
|
6447 | 0 | lxw_xml_empty_tag(self->file, "cfRule", &attributes); |
6448 | |
|
6449 | 0 | LXW_FREE_ATTRIBUTES(); |
6450 | 0 | } |
6451 | | |
6452 | | /* |
6453 | | * Write the <cfRule> element for unique/duplicate rules. |
6454 | | */ |
6455 | | STATIC void |
6456 | | _worksheet_write_cf_rule_duplicate(lxw_worksheet *self, |
6457 | | lxw_cond_format_obj *cond_format) |
6458 | 0 | { |
6459 | 0 | struct xml_attribute_list attributes; |
6460 | 0 | struct xml_attribute *attribute; |
6461 | |
|
6462 | 0 | LXW_INIT_ATTRIBUTES(); |
6463 | |
|
6464 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6465 | | |
6466 | | /* Set the attributes common to all rule types. */ |
6467 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6468 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6469 | |
|
6470 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6471 | |
|
6472 | 0 | lxw_xml_empty_tag(self->file, "cfRule", &attributes); |
6473 | |
|
6474 | 0 | LXW_FREE_ATTRIBUTES(); |
6475 | 0 | } |
6476 | | |
6477 | | /* |
6478 | | * Write the <cfRule> element for averages rules. |
6479 | | */ |
6480 | | STATIC void |
6481 | | _worksheet_write_cf_rule_average(lxw_worksheet *self, |
6482 | | lxw_cond_format_obj *cond_format) |
6483 | 0 | { |
6484 | 0 | struct xml_attribute_list attributes; |
6485 | 0 | struct xml_attribute *attribute; |
6486 | 0 | uint8_t criteria = cond_format->criteria; |
6487 | |
|
6488 | 0 | LXW_INIT_ATTRIBUTES(); |
6489 | |
|
6490 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6491 | |
|
6492 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6493 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6494 | |
|
6495 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6496 | |
|
6497 | 0 | if (cond_format->stop_if_true) |
6498 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6499 | |
|
6500 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW |
6501 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL |
6502 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW |
6503 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW |
6504 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) |
6505 | 0 | LXW_PUSH_ATTRIBUTES_INT("aboveAverage", 0); |
6506 | |
|
6507 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE_OR_EQUAL |
6508 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL) |
6509 | 0 | LXW_PUSH_ATTRIBUTES_INT("equalAverage", 1); |
6510 | |
|
6511 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_ABOVE |
6512 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW) |
6513 | 0 | LXW_PUSH_ATTRIBUTES_INT("stdDev", 1); |
6514 | |
|
6515 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_ABOVE |
6516 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW) |
6517 | 0 | LXW_PUSH_ATTRIBUTES_INT("stdDev", 2); |
6518 | |
|
6519 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_ABOVE |
6520 | 0 | || criteria == LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) |
6521 | 0 | LXW_PUSH_ATTRIBUTES_INT("stdDev", 3); |
6522 | |
|
6523 | 0 | lxw_xml_empty_tag(self->file, "cfRule", &attributes); |
6524 | |
|
6525 | 0 | LXW_FREE_ATTRIBUTES(); |
6526 | 0 | } |
6527 | | |
6528 | | /* |
6529 | | * Write the <cfRule> element for time_period rules. |
6530 | | */ |
6531 | | STATIC void |
6532 | | _worksheet_write_cf_rule_time_period(lxw_worksheet *self, |
6533 | | lxw_cond_format_obj *cond_format) |
6534 | 0 | { |
6535 | 0 | struct xml_attribute_list attributes; |
6536 | 0 | struct xml_attribute *attribute; |
6537 | 0 | char formula[LXW_MAX_ATTRIBUTE_LENGTH]; |
6538 | 0 | uint8_t pos; |
6539 | 0 | uint8_t criteria = cond_format->criteria; |
6540 | 0 | char *first_cell = cond_format->first_cell; |
6541 | 0 | char *time_periods[] = { |
6542 | 0 | "yesterday", |
6543 | 0 | "today", |
6544 | 0 | "tomorrow", |
6545 | 0 | "last7Days", |
6546 | 0 | "lastWeek", |
6547 | 0 | "thisWeek", |
6548 | 0 | "nextWeek", |
6549 | 0 | "lastMonth", |
6550 | 0 | "thisMonth", |
6551 | 0 | "nextMonth", |
6552 | 0 | }; |
6553 | |
|
6554 | 0 | LXW_INIT_ATTRIBUTES(); |
6555 | |
|
6556 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6557 | |
|
6558 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6559 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6560 | |
|
6561 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6562 | |
|
6563 | 0 | pos = criteria - LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY; |
6564 | 0 | LXW_PUSH_ATTRIBUTES_STR("timePeriod", time_periods[pos]); |
6565 | |
|
6566 | 0 | if (cond_format->stop_if_true) |
6567 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6568 | |
|
6569 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6570 | |
|
6571 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY) { |
6572 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6573 | 0 | "FLOOR(%s,1)=TODAY()-1", first_cell); |
6574 | 0 | _worksheet_write_formula_str(self, formula); |
6575 | 0 | } |
6576 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TODAY) { |
6577 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6578 | 0 | "FLOOR(%s,1)=TODAY()", first_cell); |
6579 | 0 | _worksheet_write_formula_str(self, formula); |
6580 | 0 | } |
6581 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TOMORROW) { |
6582 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6583 | 0 | "FLOOR(%s,1)=TODAY()+1", first_cell); |
6584 | 0 | _worksheet_write_formula_str(self, formula); |
6585 | 0 | } |
6586 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_7_DAYS) { |
6587 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6588 | 0 | "AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())", |
6589 | 0 | first_cell, first_cell); |
6590 | 0 | _worksheet_write_formula_str(self, formula); |
6591 | 0 | } |
6592 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_WEEK) { |
6593 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6594 | 0 | "AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY()))," |
6595 | 0 | "TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))", |
6596 | 0 | first_cell, first_cell); |
6597 | 0 | _worksheet_write_formula_str(self, formula); |
6598 | 0 | } |
6599 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_WEEK) { |
6600 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6601 | 0 | "AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1," |
6602 | 0 | "ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))", |
6603 | 0 | first_cell, first_cell); |
6604 | 0 | _worksheet_write_formula_str(self, formula); |
6605 | 0 | } |
6606 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_WEEK) { |
6607 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6608 | 0 | "AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY()))," |
6609 | 0 | "ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))", |
6610 | 0 | first_cell, first_cell); |
6611 | 0 | _worksheet_write_formula_str(self, formula); |
6612 | 0 | } |
6613 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_MONTH) { |
6614 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6615 | 0 | "AND(MONTH(%s)=MONTH(TODAY())-1,OR(YEAR(%s)=YEAR(" |
6616 | 0 | "TODAY()),AND(MONTH(%s)=1,YEAR(A1)=YEAR(TODAY())-1)))", |
6617 | 0 | first_cell, first_cell, first_cell); |
6618 | 0 | _worksheet_write_formula_str(self, formula); |
6619 | 0 | } |
6620 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_MONTH) { |
6621 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6622 | 0 | "AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))", |
6623 | 0 | first_cell, first_cell); |
6624 | 0 | _worksheet_write_formula_str(self, formula); |
6625 | 0 | } |
6626 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH) { |
6627 | 0 | lxw_snprintf(formula, LXW_MAX_ATTRIBUTE_LENGTH, |
6628 | 0 | "AND(MONTH(%s)=MONTH(TODAY())+1,OR(YEAR(%s)=YEAR(" |
6629 | 0 | "TODAY()),AND(MONTH(%s)=12,YEAR(%s)=YEAR(TODAY())+1)))", |
6630 | 0 | first_cell, first_cell, first_cell, first_cell); |
6631 | 0 | _worksheet_write_formula_str(self, formula); |
6632 | 0 | } |
6633 | |
|
6634 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6635 | |
|
6636 | 0 | LXW_FREE_ATTRIBUTES(); |
6637 | 0 | } |
6638 | | |
6639 | | /* |
6640 | | * Write the <cfRule> element for blanks/no_blanks, errors/no_errors rules. |
6641 | | */ |
6642 | | STATIC void |
6643 | | _worksheet_write_cf_rule_blanks(lxw_worksheet *self, |
6644 | | lxw_cond_format_obj *cond_format) |
6645 | 0 | { |
6646 | 0 | struct xml_attribute_list attributes; |
6647 | 0 | struct xml_attribute *attribute; |
6648 | 0 | char formula[LXW_ATTR_32]; |
6649 | 0 | uint8_t type = cond_format->type; |
6650 | |
|
6651 | 0 | LXW_INIT_ATTRIBUTES(); |
6652 | |
|
6653 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6654 | |
|
6655 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6656 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6657 | |
|
6658 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6659 | |
|
6660 | 0 | if (cond_format->stop_if_true) |
6661 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6662 | |
|
6663 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6664 | |
|
6665 | 0 | if (type == LXW_CONDITIONAL_TYPE_BLANKS) { |
6666 | 0 | lxw_snprintf(formula, LXW_ATTR_32, "LEN(TRIM(%s))=0", |
6667 | 0 | cond_format->first_cell); |
6668 | 0 | _worksheet_write_formula_str(self, formula); |
6669 | 0 | } |
6670 | 0 | else if (type == LXW_CONDITIONAL_TYPE_NO_BLANKS) { |
6671 | 0 | lxw_snprintf(formula, LXW_ATTR_32, "LEN(TRIM(%s))>0", |
6672 | 0 | cond_format->first_cell); |
6673 | 0 | _worksheet_write_formula_str(self, formula); |
6674 | 0 | } |
6675 | 0 | else if (type == LXW_CONDITIONAL_TYPE_ERRORS) { |
6676 | 0 | lxw_snprintf(formula, LXW_ATTR_32, "ISERROR(%s)", |
6677 | 0 | cond_format->first_cell); |
6678 | 0 | _worksheet_write_formula_str(self, formula); |
6679 | 0 | } |
6680 | 0 | else if (type == LXW_CONDITIONAL_TYPE_NO_ERRORS) { |
6681 | 0 | lxw_snprintf(formula, LXW_ATTR_32, "NOT(ISERROR(%s))", |
6682 | 0 | cond_format->first_cell); |
6683 | 0 | _worksheet_write_formula_str(self, formula); |
6684 | 0 | } |
6685 | |
|
6686 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6687 | |
|
6688 | 0 | LXW_FREE_ATTRIBUTES(); |
6689 | 0 | } |
6690 | | |
6691 | | /* |
6692 | | * Write the <cfRule> element for text rules. |
6693 | | */ |
6694 | | STATIC void |
6695 | | _worksheet_write_cf_rule_text(lxw_worksheet *self, |
6696 | | lxw_cond_format_obj *cond_format) |
6697 | 0 | { |
6698 | 0 | struct xml_attribute_list attributes; |
6699 | 0 | struct xml_attribute *attribute; |
6700 | 0 | uint8_t pos; |
6701 | 0 | char formula[LXW_ATTR_32 * 2]; |
6702 | 0 | char *operators[] = { |
6703 | 0 | "containsText", |
6704 | 0 | "notContains", |
6705 | 0 | "beginsWith", |
6706 | 0 | "endsWith", |
6707 | 0 | }; |
6708 | 0 | uint8_t criteria = cond_format->criteria; |
6709 | |
|
6710 | 0 | LXW_INIT_ATTRIBUTES(); |
6711 | |
|
6712 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING) |
6713 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "containsText"); |
6714 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING) |
6715 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "notContainsText"); |
6716 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH) |
6717 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "beginsWith"); |
6718 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) |
6719 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "endsWith"); |
6720 | |
|
6721 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6722 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6723 | |
|
6724 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6725 | |
|
6726 | 0 | if (cond_format->stop_if_true) |
6727 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6728 | |
|
6729 | 0 | pos = criteria - LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING; |
6730 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", operators[pos]); |
6731 | |
|
6732 | 0 | LXW_PUSH_ATTRIBUTES_STR("text", cond_format->min_value_string); |
6733 | |
|
6734 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6735 | |
|
6736 | 0 | if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING) { |
6737 | 0 | lxw_snprintf(formula, LXW_ATTR_32 * 2, |
6738 | 0 | "NOT(ISERROR(SEARCH(\"%s\",%s)))", |
6739 | 0 | cond_format->min_value_string, cond_format->first_cell); |
6740 | 0 | _worksheet_write_formula_str(self, formula); |
6741 | 0 | } |
6742 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING) { |
6743 | 0 | lxw_snprintf(formula, LXW_ATTR_32 * 2, |
6744 | 0 | "ISERROR(SEARCH(\"%s\",%s))", |
6745 | 0 | cond_format->min_value_string, cond_format->first_cell); |
6746 | 0 | _worksheet_write_formula_str(self, formula); |
6747 | 0 | } |
6748 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH) { |
6749 | 0 | lxw_snprintf(formula, LXW_ATTR_32 * 2, |
6750 | 0 | "LEFT(%s,%d)=\"%s\"", |
6751 | 0 | cond_format->first_cell, |
6752 | 0 | (uint16_t) strlen(cond_format->min_value_string), |
6753 | 0 | cond_format->min_value_string); |
6754 | 0 | _worksheet_write_formula_str(self, formula); |
6755 | 0 | } |
6756 | 0 | else if (criteria == LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) { |
6757 | 0 | lxw_snprintf(formula, LXW_ATTR_32 * 2, |
6758 | 0 | "RIGHT(%s,%d)=\"%s\"", |
6759 | 0 | cond_format->first_cell, |
6760 | 0 | (uint16_t) strlen(cond_format->min_value_string), |
6761 | 0 | cond_format->min_value_string); |
6762 | 0 | _worksheet_write_formula_str(self, formula); |
6763 | 0 | } |
6764 | |
|
6765 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6766 | |
|
6767 | 0 | LXW_FREE_ATTRIBUTES(); |
6768 | 0 | } |
6769 | | |
6770 | | /* |
6771 | | * Write the <cfRule> element for cell rules. |
6772 | | */ |
6773 | | STATIC void |
6774 | | _worksheet_write_cf_rule_cell(lxw_worksheet *self, |
6775 | | lxw_cond_format_obj *cond_format) |
6776 | 0 | { |
6777 | 0 | struct xml_attribute_list attributes; |
6778 | 0 | struct xml_attribute *attribute; |
6779 | 0 | char *operators[] = { |
6780 | 0 | "none", |
6781 | 0 | "equal", |
6782 | 0 | "notEqual", |
6783 | 0 | "greaterThan", |
6784 | 0 | "lessThan", |
6785 | 0 | "greaterThanOrEqual", |
6786 | 0 | "lessThanOrEqual", |
6787 | 0 | "between", |
6788 | 0 | "notBetween", |
6789 | 0 | }; |
6790 | |
|
6791 | 0 | LXW_INIT_ATTRIBUTES(); |
6792 | |
|
6793 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", cond_format->type_string); |
6794 | |
|
6795 | 0 | if (cond_format->dxf_index != LXW_PROPERTY_UNSET) |
6796 | 0 | LXW_PUSH_ATTRIBUTES_INT("dxfId", cond_format->dxf_index); |
6797 | |
|
6798 | 0 | LXW_PUSH_ATTRIBUTES_INT("priority", cond_format->dxf_priority); |
6799 | |
|
6800 | 0 | if (cond_format->stop_if_true) |
6801 | 0 | LXW_PUSH_ATTRIBUTES_INT("stopIfTrue", 1); |
6802 | |
|
6803 | 0 | LXW_PUSH_ATTRIBUTES_STR("operator", operators[cond_format->criteria]); |
6804 | |
|
6805 | 0 | lxw_xml_start_tag(self->file, "cfRule", &attributes); |
6806 | |
|
6807 | 0 | if (cond_format->min_value_string) |
6808 | 0 | _worksheet_write_formula_str(self, cond_format->min_value_string); |
6809 | 0 | else |
6810 | 0 | _worksheet_write_formula_num(self, cond_format->min_value); |
6811 | |
|
6812 | 0 | if (cond_format->has_max) { |
6813 | 0 | if (cond_format->max_value_string) |
6814 | 0 | _worksheet_write_formula_str(self, cond_format->max_value_string); |
6815 | 0 | else |
6816 | 0 | _worksheet_write_formula_num(self, cond_format->max_value); |
6817 | 0 | } |
6818 | |
|
6819 | 0 | lxw_xml_end_tag(self->file, "cfRule"); |
6820 | |
|
6821 | 0 | LXW_FREE_ATTRIBUTES(); |
6822 | 0 | } |
6823 | | |
6824 | | /* |
6825 | | * Write the <cfRule> element. |
6826 | | */ |
6827 | | STATIC void |
6828 | | _worksheet_write_cf_rule(lxw_worksheet *self, |
6829 | | lxw_cond_format_obj *cond_format) |
6830 | 0 | { |
6831 | 0 | if (cond_format->type == LXW_CONDITIONAL_TYPE_CELL) { |
6832 | |
|
6833 | 0 | _worksheet_write_cf_rule_cell(self, cond_format); |
6834 | 0 | } |
6835 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TEXT) { |
6836 | |
|
6837 | 0 | _worksheet_write_cf_rule_text(self, cond_format); |
6838 | 0 | } |
6839 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) { |
6840 | |
|
6841 | 0 | _worksheet_write_cf_rule_time_period(self, cond_format); |
6842 | 0 | } |
6843 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_DUPLICATE |
6844 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_UNIQUE) { |
6845 | |
|
6846 | 0 | _worksheet_write_cf_rule_duplicate(self, cond_format); |
6847 | 0 | } |
6848 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_AVERAGE) { |
6849 | |
|
6850 | 0 | _worksheet_write_cf_rule_average(self, cond_format); |
6851 | 0 | } |
6852 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP |
6853 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) { |
6854 | |
|
6855 | 0 | _worksheet_write_cf_rule_top(self, cond_format); |
6856 | 0 | } |
6857 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_BLANKS |
6858 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_NO_BLANKS |
6859 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_ERRORS |
6860 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_NO_ERRORS) { |
6861 | |
|
6862 | 0 | _worksheet_write_cf_rule_blanks(self, cond_format); |
6863 | 0 | } |
6864 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_FORMULA) { |
6865 | |
|
6866 | 0 | _worksheet_write_cf_rule_formula(self, cond_format); |
6867 | 0 | } |
6868 | 0 | else if (cond_format->type == LXW_CONDITIONAL_2_COLOR_SCALE |
6869 | 0 | || cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { |
6870 | |
|
6871 | 0 | _worksheet_write_cf_rule_color_scale(self, cond_format); |
6872 | 0 | } |
6873 | 0 | else if (cond_format->type == LXW_CONDITIONAL_DATA_BAR) { |
6874 | |
|
6875 | 0 | _worksheet_write_cf_rule_data_bar(self, cond_format); |
6876 | 0 | } |
6877 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_ICON_SETS) { |
6878 | |
|
6879 | 0 | _worksheet_write_cf_rule_icons(self, cond_format); |
6880 | 0 | } |
6881 | |
|
6882 | 0 | } |
6883 | | |
6884 | | /* |
6885 | | * Write the <conditionalFormatting> element. |
6886 | | */ |
6887 | | STATIC void |
6888 | | _worksheet_write_conditional_formatting(lxw_worksheet *self, |
6889 | | lxw_cond_format_hash_element *element) |
6890 | 0 | { |
6891 | 0 | struct xml_attribute_list attributes; |
6892 | 0 | struct xml_attribute *attribute; |
6893 | 0 | lxw_cond_format_obj *cond_format; |
6894 | |
|
6895 | 0 | LXW_INIT_ATTRIBUTES(); |
6896 | 0 | LXW_PUSH_ATTRIBUTES_STR("sqref", element->sqref); |
6897 | |
|
6898 | 0 | lxw_xml_start_tag(self->file, "conditionalFormatting", &attributes); |
6899 | |
|
6900 | 0 | STAILQ_FOREACH(cond_format, element->cond_formats, list_pointers) { |
6901 | | /* Write the cfRule element. */ |
6902 | 0 | _worksheet_write_cf_rule(self, cond_format); |
6903 | 0 | } |
6904 | |
|
6905 | 0 | lxw_xml_end_tag(self->file, "conditionalFormatting"); |
6906 | |
|
6907 | 0 | LXW_FREE_ATTRIBUTES(); |
6908 | 0 | } |
6909 | | |
6910 | | /* |
6911 | | * Write the conditional formatting> elements. |
6912 | | */ |
6913 | | STATIC void |
6914 | | _worksheet_write_conditional_formats(lxw_worksheet *self) |
6915 | 1.30k | { |
6916 | 1.30k | lxw_cond_format_hash_element *element; |
6917 | 1.30k | lxw_cond_format_hash_element *next_element; |
6918 | | |
6919 | 1.30k | for (element = RB_MIN(lxw_cond_format_hash, self->conditional_formats); |
6920 | 1.30k | element; element = next_element) { |
6921 | |
|
6922 | 0 | _worksheet_write_conditional_formatting(self, element); |
6923 | |
|
6924 | 0 | next_element = |
6925 | 0 | RB_NEXT(lxw_cond_format_hash, self->conditional_formats, element); |
6926 | 0 | } |
6927 | 1.30k | } |
6928 | | |
6929 | | /* |
6930 | | * Write the <x14:xxxColor> elements for data bar conditional formats. |
6931 | | */ |
6932 | | STATIC void |
6933 | | _worksheet_write_x14_color(lxw_worksheet *self, char *type, lxw_color_t color) |
6934 | 0 | { |
6935 | 0 | struct xml_attribute_list attributes; |
6936 | 0 | struct xml_attribute *attribute; |
6937 | 0 | char rgb[LXW_ATTR_32]; |
6938 | |
|
6939 | 0 | lxw_snprintf(rgb, LXW_ATTR_32, "FF%06X", color & LXW_COLOR_MASK); |
6940 | |
|
6941 | 0 | LXW_INIT_ATTRIBUTES(); |
6942 | 0 | LXW_PUSH_ATTRIBUTES_STR("rgb", rgb); |
6943 | 0 | lxw_xml_empty_tag(self->file, type, &attributes); |
6944 | |
|
6945 | 0 | LXW_FREE_ATTRIBUTES(); |
6946 | 0 | } |
6947 | | |
6948 | | /* |
6949 | | * Write the <x14:cfvo> element. |
6950 | | */ |
6951 | | STATIC void |
6952 | | _worksheet_write_x14_cfvo(lxw_worksheet *self, uint8_t rule_type, |
6953 | | double number, char *string) |
6954 | 0 | { |
6955 | 0 | struct xml_attribute_list attributes; |
6956 | 0 | struct xml_attribute *attribute; |
6957 | 0 | char data[LXW_ATTR_32]; |
6958 | 0 | uint8_t has_value = LXW_FALSE; |
6959 | |
|
6960 | 0 | LXW_INIT_ATTRIBUTES(); |
6961 | |
|
6962 | 0 | if (!string) |
6963 | 0 | lxw_sprintf_dbl(data, number); |
6964 | |
|
6965 | 0 | if (rule_type == LXW_CONDITIONAL_RULE_TYPE_AUTO_MIN) { |
6966 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "autoMin"); |
6967 | 0 | has_value = LXW_FALSE; |
6968 | 0 | } |
6969 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MINIMUM) { |
6970 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "min"); |
6971 | 0 | has_value = LXW_FALSE; |
6972 | 0 | } |
6973 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_NUMBER) { |
6974 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "num"); |
6975 | 0 | has_value = LXW_TRUE; |
6976 | 0 | } |
6977 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENT) { |
6978 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percent"); |
6979 | 0 | has_value = LXW_TRUE; |
6980 | 0 | } |
6981 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_PERCENTILE) { |
6982 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "percentile"); |
6983 | 0 | has_value = LXW_TRUE; |
6984 | 0 | } |
6985 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_FORMULA) { |
6986 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "formula"); |
6987 | 0 | has_value = LXW_TRUE; |
6988 | 0 | } |
6989 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
6990 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "max"); |
6991 | 0 | has_value = LXW_FALSE; |
6992 | 0 | } |
6993 | 0 | else if (rule_type == LXW_CONDITIONAL_RULE_TYPE_AUTO_MAX) { |
6994 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "autoMax"); |
6995 | 0 | has_value = LXW_FALSE; |
6996 | 0 | } |
6997 | |
|
6998 | 0 | if (has_value) { |
6999 | 0 | lxw_xml_start_tag(self->file, "x14:cfvo", &attributes); |
7000 | |
|
7001 | 0 | if (string) |
7002 | 0 | lxw_xml_data_element(self->file, "xm:f", string, NULL); |
7003 | 0 | else |
7004 | 0 | lxw_xml_data_element(self->file, "xm:f", data, NULL); |
7005 | |
|
7006 | 0 | lxw_xml_end_tag(self->file, "x14:cfvo"); |
7007 | 0 | } |
7008 | 0 | else { |
7009 | 0 | lxw_xml_empty_tag(self->file, "x14:cfvo", &attributes); |
7010 | 0 | } |
7011 | 0 | LXW_FREE_ATTRIBUTES(); |
7012 | 0 | } |
7013 | | |
7014 | | /* |
7015 | | * Write the <x14:dataBar> element. |
7016 | | */ |
7017 | | STATIC void |
7018 | | _worksheet_write_x14_data_bar(lxw_worksheet *self, |
7019 | | lxw_cond_format_obj *cond_format) |
7020 | 0 | { |
7021 | 0 | struct xml_attribute_list attributes; |
7022 | 0 | struct xml_attribute *attribute; |
7023 | 0 | char min_length[] = "0"; |
7024 | 0 | char max_length[] = "100"; |
7025 | 0 | char border[] = "1"; |
7026 | |
|
7027 | 0 | LXW_INIT_ATTRIBUTES(); |
7028 | 0 | LXW_PUSH_ATTRIBUTES_STR("minLength", min_length); |
7029 | 0 | LXW_PUSH_ATTRIBUTES_STR("maxLength", max_length); |
7030 | |
|
7031 | 0 | if (!cond_format->bar_no_border) |
7032 | 0 | LXW_PUSH_ATTRIBUTES_STR("border", border); |
7033 | |
|
7034 | 0 | if (cond_format->bar_solid) |
7035 | 0 | LXW_PUSH_ATTRIBUTES_STR("gradient", "0"); |
7036 | |
|
7037 | 0 | if (cond_format->bar_direction == |
7038 | 0 | LXW_CONDITIONAL_BAR_DIRECTION_RIGHT_TO_LEFT) |
7039 | 0 | LXW_PUSH_ATTRIBUTES_STR("direction", "rightToLeft"); |
7040 | |
|
7041 | 0 | if (cond_format->bar_direction == |
7042 | 0 | LXW_CONDITIONAL_BAR_DIRECTION_LEFT_TO_RIGHT) |
7043 | 0 | LXW_PUSH_ATTRIBUTES_STR("direction", "leftToRight"); |
7044 | |
|
7045 | 0 | if (cond_format->bar_negative_color_same) |
7046 | 0 | LXW_PUSH_ATTRIBUTES_STR("negativeBarColorSameAsPositive", "1"); |
7047 | |
|
7048 | 0 | if (!cond_format->bar_no_border |
7049 | 0 | && !cond_format->bar_negative_border_color_same) |
7050 | 0 | LXW_PUSH_ATTRIBUTES_STR("negativeBarBorderColorSameAsPositive", "0"); |
7051 | |
|
7052 | 0 | if (cond_format->bar_axis_position == LXW_CONDITIONAL_BAR_AXIS_MIDPOINT) |
7053 | 0 | LXW_PUSH_ATTRIBUTES_STR("axisPosition", "middle"); |
7054 | |
|
7055 | 0 | if (cond_format->bar_axis_position == LXW_CONDITIONAL_BAR_AXIS_NONE) |
7056 | 0 | LXW_PUSH_ATTRIBUTES_STR("axisPosition", "none"); |
7057 | |
|
7058 | 0 | lxw_xml_start_tag(self->file, "x14:dataBar", &attributes); |
7059 | |
|
7060 | 0 | if (cond_format->auto_min) |
7061 | 0 | cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_AUTO_MIN; |
7062 | |
|
7063 | 0 | _worksheet_write_x14_cfvo(self, cond_format->min_rule_type, |
7064 | 0 | cond_format->min_value, |
7065 | 0 | cond_format->min_value_string); |
7066 | |
|
7067 | 0 | if (cond_format->auto_max) |
7068 | 0 | cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_AUTO_MAX; |
7069 | |
|
7070 | 0 | _worksheet_write_x14_cfvo(self, cond_format->max_rule_type, |
7071 | 0 | cond_format->max_value, |
7072 | 0 | cond_format->max_value_string); |
7073 | |
|
7074 | 0 | if (!cond_format->bar_no_border) |
7075 | 0 | _worksheet_write_x14_color(self, "x14:borderColor", |
7076 | 0 | cond_format->bar_border_color); |
7077 | |
|
7078 | 0 | if (!cond_format->bar_negative_color_same) |
7079 | 0 | _worksheet_write_x14_color(self, "x14:negativeFillColor", |
7080 | 0 | cond_format->bar_negative_color); |
7081 | |
|
7082 | 0 | if (!cond_format->bar_no_border |
7083 | 0 | && !cond_format->bar_negative_border_color_same) |
7084 | 0 | _worksheet_write_x14_color(self, "x14:negativeBorderColor", |
7085 | 0 | cond_format->bar_negative_border_color); |
7086 | |
|
7087 | 0 | if (cond_format->bar_axis_position != LXW_CONDITIONAL_BAR_AXIS_NONE) |
7088 | 0 | _worksheet_write_x14_color(self, "x14:axisColor", |
7089 | 0 | cond_format->bar_axis_color); |
7090 | |
|
7091 | 0 | LXW_FREE_ATTRIBUTES(); |
7092 | 0 | } |
7093 | | |
7094 | | /* |
7095 | | * Write the <x14:cfRule> element. |
7096 | | */ |
7097 | | STATIC void |
7098 | | _worksheet_write_x14_cf_rule(lxw_worksheet *self, |
7099 | | lxw_cond_format_obj *cond_format) |
7100 | 0 | { |
7101 | 0 | struct xml_attribute_list attributes; |
7102 | 0 | struct xml_attribute *attribute; |
7103 | |
|
7104 | 0 | LXW_INIT_ATTRIBUTES(); |
7105 | 0 | LXW_PUSH_ATTRIBUTES_STR("type", "dataBar"); |
7106 | 0 | LXW_PUSH_ATTRIBUTES_STR("id", cond_format->guid); |
7107 | |
|
7108 | 0 | lxw_xml_start_tag(self->file, "x14:cfRule", &attributes); |
7109 | | |
7110 | | /* Write the x14:dataBar element. */ |
7111 | 0 | _worksheet_write_x14_data_bar(self, cond_format); |
7112 | |
|
7113 | 0 | LXW_FREE_ATTRIBUTES(); |
7114 | 0 | } |
7115 | | |
7116 | | /* |
7117 | | * Write the <xm:sqref> element. |
7118 | | */ |
7119 | | STATIC void |
7120 | | _worksheet_write_xm_sqref(lxw_worksheet *self, |
7121 | | lxw_cond_format_obj *cond_format) |
7122 | 0 | { |
7123 | 0 | lxw_xml_data_element(self->file, "xm:sqref", cond_format->sqref, NULL); |
7124 | 0 | } |
7125 | | |
7126 | | /* |
7127 | | * Write the <conditionalFormatting> element. |
7128 | | */ |
7129 | | STATIC void |
7130 | | _worksheet_write_conditional_formatting_2010(lxw_worksheet *self, lxw_cond_format_hash_element |
7131 | | *element) |
7132 | 0 | { |
7133 | 0 | struct xml_attribute_list attributes; |
7134 | 0 | struct xml_attribute *attribute; |
7135 | 0 | lxw_cond_format_obj *cond_format; |
7136 | |
|
7137 | 0 | LXW_INIT_ATTRIBUTES(); |
7138 | 0 | LXW_PUSH_ATTRIBUTES_STR("xmlns:xm", |
7139 | 0 | "http://schemas.microsoft.com/office/excel/2006/main"); |
7140 | |
|
7141 | 0 | STAILQ_FOREACH(cond_format, element->cond_formats, list_pointers) { |
7142 | 0 | if (!cond_format->data_bar_2010) |
7143 | 0 | continue; |
7144 | | |
7145 | 0 | lxw_xml_start_tag(self->file, "x14:conditionalFormatting", |
7146 | 0 | &attributes); |
7147 | |
|
7148 | 0 | _worksheet_write_x14_cf_rule(self, cond_format); |
7149 | |
|
7150 | 0 | lxw_xml_end_tag(self->file, "x14:dataBar"); |
7151 | 0 | lxw_xml_end_tag(self->file, "x14:cfRule"); |
7152 | 0 | _worksheet_write_xm_sqref(self, cond_format); |
7153 | 0 | lxw_xml_end_tag(self->file, "x14:conditionalFormatting"); |
7154 | 0 | } |
7155 | |
|
7156 | 0 | LXW_FREE_ATTRIBUTES(); |
7157 | 0 | } |
7158 | | |
7159 | | /* |
7160 | | * Write the <extLst> element for Excel 2010 conditional formatting data bars. |
7161 | | */ |
7162 | | STATIC void |
7163 | | _worksheet_write_ext_list_data_bars(lxw_worksheet *self) |
7164 | 0 | { |
7165 | 0 | lxw_cond_format_hash_element *element; |
7166 | 0 | lxw_cond_format_hash_element *next_element; |
7167 | |
|
7168 | 0 | _worksheet_write_ext(self, "{78C0D931-6437-407d-A8EE-F0AAD7539E65}"); |
7169 | 0 | lxw_xml_start_tag(self->file, "x14:conditionalFormattings", NULL); |
7170 | |
|
7171 | 0 | for (element = RB_MIN(lxw_cond_format_hash, self->conditional_formats); |
7172 | 0 | element; element = next_element) { |
7173 | |
|
7174 | 0 | _worksheet_write_conditional_formatting_2010(self, element); |
7175 | |
|
7176 | 0 | next_element = |
7177 | 0 | RB_NEXT(lxw_cond_format_hash, self->conditional_formats, element); |
7178 | 0 | } |
7179 | |
|
7180 | 0 | lxw_xml_end_tag(self->file, "x14:conditionalFormattings"); |
7181 | 0 | lxw_xml_end_tag(self->file, "ext"); |
7182 | 0 | } |
7183 | | |
7184 | | /* |
7185 | | * Write the <extLst> element. |
7186 | | */ |
7187 | | STATIC void |
7188 | | _worksheet_write_ext_list(lxw_worksheet *self) |
7189 | 1.30k | { |
7190 | 1.30k | if (self->data_bar_2010_index == 0) |
7191 | 1.30k | return; |
7192 | | |
7193 | 0 | lxw_xml_start_tag(self->file, "extLst", NULL); |
7194 | |
|
7195 | 0 | _worksheet_write_ext_list_data_bars(self); |
7196 | |
|
7197 | 0 | lxw_xml_end_tag(self->file, "extLst"); |
7198 | 0 | } |
7199 | | |
7200 | | /* |
7201 | | * Write the <ignoredError> element. |
7202 | | */ |
7203 | | STATIC void |
7204 | | _worksheet_write_ignored_error(lxw_worksheet *self, char *ignore_error, |
7205 | | char *range) |
7206 | 0 | { |
7207 | 0 | struct xml_attribute_list attributes; |
7208 | 0 | struct xml_attribute *attribute; |
7209 | |
|
7210 | 0 | LXW_INIT_ATTRIBUTES(); |
7211 | 0 | LXW_PUSH_ATTRIBUTES_STR("sqref", range); |
7212 | 0 | LXW_PUSH_ATTRIBUTES_STR(ignore_error, "1"); |
7213 | |
|
7214 | 0 | lxw_xml_empty_tag(self->file, "ignoredError", &attributes); |
7215 | |
|
7216 | 0 | LXW_FREE_ATTRIBUTES(); |
7217 | 0 | } |
7218 | | |
7219 | | lxw_error |
7220 | | _validate_conditional_icons(lxw_conditional_format *user) |
7221 | 0 | { |
7222 | 0 | if (user->icon_style > LXW_CONDITIONAL_ICONS_5_QUARTERS) { |
7223 | |
|
7224 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7225 | 0 | "For type = LXW_CONDITIONAL_TYPE_ICON_SETS, " |
7226 | 0 | "invalid icon_style (%d).", user->icon_style); |
7227 | |
|
7228 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7229 | 0 | } |
7230 | 0 | else { |
7231 | 0 | return LXW_NO_ERROR; |
7232 | 0 | } |
7233 | 0 | } |
7234 | | |
7235 | | lxw_error |
7236 | | _validate_conditional_data_bar(lxw_worksheet *self, |
7237 | | lxw_cond_format_obj *cond_format, |
7238 | | lxw_conditional_format *user_options) |
7239 | 0 | { |
7240 | 0 | uint8_t min_rule_type = user_options->min_rule_type; |
7241 | 0 | uint8_t max_rule_type = user_options->max_rule_type; |
7242 | |
|
7243 | 0 | if (user_options->data_bar_2010 |
7244 | 0 | || user_options->bar_solid |
7245 | 0 | || user_options->bar_no_border |
7246 | 0 | || user_options->bar_direction |
7247 | 0 | || user_options->bar_axis_position |
7248 | 0 | || user_options->bar_negative_color_same |
7249 | 0 | || user_options->bar_negative_border_color_same |
7250 | 0 | || user_options->bar_negative_color |
7251 | 0 | || user_options->bar_border_color |
7252 | 0 | || user_options->bar_negative_border_color |
7253 | 0 | || user_options->bar_axis_color) { |
7254 | |
|
7255 | 0 | cond_format->data_bar_2010 = LXW_TRUE; |
7256 | 0 | self->excel_version = 2010; |
7257 | 0 | } |
7258 | |
|
7259 | 0 | if (min_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
7260 | 0 | && min_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
7261 | 0 | cond_format->min_rule_type = min_rule_type; |
7262 | 0 | cond_format->min_value = user_options->min_value; |
7263 | 0 | cond_format->min_value_string = |
7264 | 0 | lxw_strdup_formula(user_options->min_value_string); |
7265 | 0 | } |
7266 | 0 | else { |
7267 | 0 | cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_MINIMUM; |
7268 | 0 | cond_format->min_value = 0; |
7269 | 0 | } |
7270 | |
|
7271 | 0 | if (max_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
7272 | 0 | && max_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
7273 | 0 | cond_format->max_rule_type = max_rule_type; |
7274 | 0 | cond_format->max_value = user_options->max_value; |
7275 | 0 | cond_format->max_value_string = |
7276 | 0 | lxw_strdup_formula(user_options->max_value_string); |
7277 | 0 | } |
7278 | 0 | else { |
7279 | 0 | cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_MAXIMUM; |
7280 | 0 | cond_format->max_value = 0; |
7281 | 0 | } |
7282 | |
|
7283 | 0 | if (cond_format->data_bar_2010) { |
7284 | 0 | if (min_rule_type == LXW_CONDITIONAL_RULE_TYPE_NONE) |
7285 | 0 | cond_format->auto_min = LXW_TRUE; |
7286 | 0 | if (max_rule_type == LXW_CONDITIONAL_RULE_TYPE_NONE) |
7287 | 0 | cond_format->auto_max = LXW_TRUE; |
7288 | 0 | } |
7289 | |
|
7290 | 0 | cond_format->bar_only = user_options->bar_only; |
7291 | 0 | cond_format->bar_solid = user_options->bar_solid; |
7292 | 0 | cond_format->bar_no_border = user_options->bar_no_border; |
7293 | 0 | cond_format->bar_direction = user_options->bar_direction; |
7294 | 0 | cond_format->bar_axis_position = user_options->bar_axis_position; |
7295 | 0 | cond_format->bar_negative_color_same = |
7296 | 0 | user_options->bar_negative_color_same; |
7297 | 0 | cond_format->bar_negative_border_color_same = |
7298 | 0 | user_options->bar_negative_border_color_same; |
7299 | |
|
7300 | 0 | if (user_options->bar_color != LXW_COLOR_UNSET) |
7301 | 0 | cond_format->bar_color = user_options->bar_color; |
7302 | 0 | else |
7303 | 0 | cond_format->bar_color = 0x638EC6; |
7304 | |
|
7305 | 0 | if (user_options->bar_negative_color != LXW_COLOR_UNSET) |
7306 | 0 | cond_format->bar_negative_color = user_options->bar_negative_color; |
7307 | 0 | else |
7308 | 0 | cond_format->bar_negative_color = 0xFF0000; |
7309 | |
|
7310 | 0 | if (user_options->bar_border_color != LXW_COLOR_UNSET) |
7311 | 0 | cond_format->bar_border_color = user_options->bar_border_color; |
7312 | 0 | else |
7313 | 0 | cond_format->bar_border_color = cond_format->bar_color; |
7314 | |
|
7315 | 0 | if (user_options->bar_negative_border_color != LXW_COLOR_UNSET) |
7316 | 0 | cond_format->bar_negative_border_color = |
7317 | 0 | user_options->bar_negative_border_color; |
7318 | 0 | else |
7319 | 0 | cond_format->bar_negative_border_color = 0xFF0000; |
7320 | |
|
7321 | 0 | if (user_options->bar_axis_color != LXW_COLOR_UNSET) |
7322 | 0 | cond_format->bar_axis_color = user_options->bar_axis_color; |
7323 | 0 | else |
7324 | 0 | cond_format->bar_axis_color = 0x000000; |
7325 | |
|
7326 | 0 | return LXW_NO_ERROR; |
7327 | 0 | } |
7328 | | |
7329 | | lxw_error |
7330 | | _validate_conditional_scale(lxw_cond_format_obj *cond_format, |
7331 | | lxw_conditional_format *user_options) |
7332 | 0 | { |
7333 | 0 | uint8_t min_rule_type = user_options->min_rule_type; |
7334 | 0 | uint8_t mid_rule_type = user_options->mid_rule_type; |
7335 | 0 | uint8_t max_rule_type = user_options->max_rule_type; |
7336 | |
|
7337 | 0 | if (min_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
7338 | 0 | && min_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
7339 | 0 | cond_format->min_rule_type = min_rule_type; |
7340 | 0 | cond_format->min_value = user_options->min_value; |
7341 | 0 | cond_format->min_value_string = |
7342 | 0 | lxw_strdup_formula(user_options->min_value_string); |
7343 | 0 | } |
7344 | 0 | else { |
7345 | 0 | cond_format->min_rule_type = LXW_CONDITIONAL_RULE_TYPE_MINIMUM; |
7346 | 0 | cond_format->min_value = 0; |
7347 | 0 | } |
7348 | |
|
7349 | 0 | if (max_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
7350 | 0 | && max_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
7351 | 0 | cond_format->max_rule_type = max_rule_type; |
7352 | 0 | cond_format->max_value = user_options->max_value; |
7353 | 0 | cond_format->max_value_string = |
7354 | 0 | lxw_strdup_formula(user_options->max_value_string); |
7355 | 0 | } |
7356 | 0 | else { |
7357 | 0 | cond_format->max_rule_type = LXW_CONDITIONAL_RULE_TYPE_MAXIMUM; |
7358 | 0 | cond_format->max_value = 0; |
7359 | 0 | } |
7360 | |
|
7361 | 0 | if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { |
7362 | 0 | if (mid_rule_type > LXW_CONDITIONAL_RULE_TYPE_MINIMUM |
7363 | 0 | && mid_rule_type < LXW_CONDITIONAL_RULE_TYPE_MAXIMUM) { |
7364 | 0 | cond_format->mid_rule_type = mid_rule_type; |
7365 | 0 | cond_format->mid_value = user_options->mid_value; |
7366 | 0 | cond_format->mid_value_string = |
7367 | 0 | lxw_strdup_formula(user_options->mid_value_string); |
7368 | 0 | } |
7369 | 0 | else { |
7370 | 0 | cond_format->mid_rule_type = LXW_CONDITIONAL_RULE_TYPE_PERCENTILE; |
7371 | 0 | cond_format->mid_value = 50; |
7372 | 0 | } |
7373 | 0 | } |
7374 | |
|
7375 | 0 | if (user_options->min_color != LXW_COLOR_UNSET) |
7376 | 0 | cond_format->min_color = user_options->min_color; |
7377 | 0 | else |
7378 | 0 | cond_format->min_color = 0xFF7128; |
7379 | |
|
7380 | 0 | if (user_options->max_color != LXW_COLOR_UNSET) |
7381 | 0 | cond_format->max_color = user_options->max_color; |
7382 | 0 | else |
7383 | 0 | cond_format->max_color = 0xFFEF9C; |
7384 | |
|
7385 | 0 | if (cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { |
7386 | 0 | if (user_options->min_color == LXW_COLOR_UNSET) |
7387 | 0 | cond_format->min_color = 0xF8696B; |
7388 | |
|
7389 | 0 | if (user_options->mid_color != LXW_COLOR_UNSET) |
7390 | 0 | cond_format->mid_color = user_options->mid_color; |
7391 | 0 | else |
7392 | 0 | cond_format->mid_color = 0xFFEB84; |
7393 | |
|
7394 | 0 | if (user_options->max_color == LXW_COLOR_UNSET) |
7395 | 0 | cond_format->max_color = 0x63BE7B; |
7396 | 0 | } |
7397 | |
|
7398 | 0 | return LXW_NO_ERROR; |
7399 | 0 | } |
7400 | | |
7401 | | lxw_error |
7402 | | _validate_conditional_top(lxw_cond_format_obj *cond_format, |
7403 | | lxw_conditional_format *user_options) |
7404 | 0 | { |
7405 | | /* Restrict the range of rank values to Excel's allowed range. */ |
7406 | 0 | if (user_options->criteria == |
7407 | 0 | LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT) { |
7408 | 0 | if (user_options->value < 0.0 || user_options->value > 100.0) { |
7409 | |
|
7410 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7411 | 0 | "For type = LXW_CONDITIONAL_TYPE_TOP/BOTTOM, " |
7412 | 0 | "top/bottom percent (%g%%) must by in range 0-100", |
7413 | 0 | user_options->value); |
7414 | |
|
7415 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7416 | 0 | } |
7417 | 0 | } |
7418 | 0 | else { |
7419 | 0 | if (user_options->value < 1.0 || user_options->value > 1000.0) { |
7420 | |
|
7421 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7422 | 0 | "For type = LXW_CONDITIONAL_TYPE_TOP/BOTTOM, " |
7423 | 0 | "top/bottom items (%g) must by in range 1-1000", |
7424 | 0 | user_options->value); |
7425 | |
|
7426 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7427 | 0 | } |
7428 | 0 | } |
7429 | | |
7430 | 0 | cond_format->min_value = (uint16_t) user_options->value; |
7431 | |
|
7432 | 0 | return LXW_NO_ERROR; |
7433 | 0 | } |
7434 | | |
7435 | | lxw_error |
7436 | | _validate_conditional_average(lxw_conditional_format *user) |
7437 | 0 | { |
7438 | 0 | if (user->criteria < LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE || |
7439 | 0 | user->criteria > LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW) { |
7440 | |
|
7441 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7442 | 0 | "For type = LXW_CONDITIONAL_TYPE_AVERAGE, " |
7443 | 0 | "invalid criteria value (%d).", user->criteria); |
7444 | |
|
7445 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7446 | 0 | } |
7447 | 0 | else { |
7448 | 0 | return LXW_NO_ERROR; |
7449 | 0 | } |
7450 | 0 | } |
7451 | | |
7452 | | lxw_error |
7453 | | _validate_conditional_time_period(lxw_conditional_format *user) |
7454 | 0 | { |
7455 | 0 | if (user->criteria < LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY || |
7456 | 0 | user->criteria > LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH) { |
7457 | |
|
7458 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7459 | 0 | "For type = LXW_CONDITIONAL_TYPE_TIME_PERIOD, " |
7460 | 0 | "invalid criteria value (%d).", user->criteria); |
7461 | |
|
7462 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7463 | 0 | } |
7464 | 0 | else { |
7465 | 0 | return LXW_NO_ERROR; |
7466 | 0 | } |
7467 | 0 | } |
7468 | | |
7469 | | lxw_error |
7470 | | _validate_conditional_text(lxw_cond_format_obj *cond_format, |
7471 | | lxw_conditional_format *user_options) |
7472 | 0 | { |
7473 | 0 | if (!user_options->value_string) { |
7474 | |
|
7475 | 0 | LXW_WARN_FORMAT("worksheet_conditional_format_cell()/_range(): " |
7476 | 0 | "For type = LXW_CONDITIONAL_TYPE_TEXT, " |
7477 | 0 | "value_string can not be NULL. " |
7478 | 0 | "Text must be specified."); |
7479 | |
|
7480 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7481 | 0 | } |
7482 | | |
7483 | 0 | if (strlen(user_options->value_string) >= LXW_MAX_ATTRIBUTE_LENGTH) { |
7484 | |
|
7485 | 0 | LXW_WARN_FORMAT2("worksheet_conditional_format_cell()/_range(): " |
7486 | 0 | "For type = LXW_CONDITIONAL_TYPE_TEXT, " |
7487 | 0 | "value_string length (%d) must be less than %d.", |
7488 | 0 | (uint16_t) strlen(user_options->value_string), |
7489 | 0 | LXW_MAX_ATTRIBUTE_LENGTH); |
7490 | |
|
7491 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7492 | 0 | } |
7493 | | |
7494 | 0 | if (user_options->criteria < LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING || |
7495 | 0 | user_options->criteria > LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH) { |
7496 | |
|
7497 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
7498 | 0 | "For type = LXW_CONDITIONAL_TYPE_TEXT, " |
7499 | 0 | "invalid criteria value (%d).", |
7500 | 0 | user_options->criteria); |
7501 | |
|
7502 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7503 | 0 | } |
7504 | | |
7505 | 0 | cond_format->min_value_string = |
7506 | 0 | lxw_strdup_formula(user_options->value_string); |
7507 | |
|
7508 | 0 | return LXW_NO_ERROR; |
7509 | 0 | } |
7510 | | |
7511 | | lxw_error |
7512 | | _validate_conditional_formula(lxw_cond_format_obj *cond_format, |
7513 | | lxw_conditional_format *user_options) |
7514 | 0 | { |
7515 | 0 | if (!user_options->value_string) { |
7516 | |
|
7517 | 0 | LXW_WARN_FORMAT("worksheet_conditional_format_cell()/_range(): " |
7518 | 0 | "For type = LXW_CONDITIONAL_TYPE_FORMULA, " |
7519 | 0 | "value_string can not be NULL. " |
7520 | 0 | "Formula must be specified."); |
7521 | |
|
7522 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7523 | 0 | } |
7524 | | |
7525 | 0 | cond_format->min_value_string = |
7526 | 0 | lxw_strdup_formula(user_options->value_string); |
7527 | |
|
7528 | 0 | return LXW_NO_ERROR; |
7529 | 0 | } |
7530 | | |
7531 | | lxw_error |
7532 | | _validate_conditional_cell(lxw_cond_format_obj *cond_format, |
7533 | | lxw_conditional_format *user_options) |
7534 | 0 | { |
7535 | 0 | cond_format->min_value = user_options->value; |
7536 | 0 | cond_format->min_value_string = |
7537 | 0 | lxw_strdup_formula(user_options->value_string); |
7538 | |
|
7539 | 0 | if (cond_format->criteria == LXW_CONDITIONAL_CRITERIA_BETWEEN |
7540 | 0 | || cond_format->criteria == LXW_CONDITIONAL_CRITERIA_NOT_BETWEEN) { |
7541 | 0 | cond_format->has_max = LXW_TRUE; |
7542 | 0 | cond_format->min_value = user_options->min_value; |
7543 | 0 | cond_format->max_value = user_options->max_value; |
7544 | 0 | cond_format->min_value_string = |
7545 | 0 | lxw_strdup_formula(user_options->min_value_string); |
7546 | 0 | cond_format->max_value_string = |
7547 | 0 | lxw_strdup_formula(user_options->max_value_string); |
7548 | 0 | } |
7549 | |
|
7550 | 0 | return LXW_NO_ERROR; |
7551 | 0 | } |
7552 | | |
7553 | | /* Check that the correct criteria and used with the correct conditional format. */ |
7554 | | lxw_error |
7555 | | _validate_conditional_criteria(lxw_cond_format_obj *cond_format) |
7556 | 0 | { |
7557 | 0 | uint8_t criteria_mismatch = LXW_FALSE; |
7558 | |
|
7559 | 0 | if (cond_format->type == LXW_CONDITIONAL_TYPE_CELL) { |
7560 | 0 | switch (cond_format->criteria) { |
7561 | 0 | case LXW_CONDITIONAL_CRITERIA_EQUAL_TO: |
7562 | 0 | case LXW_CONDITIONAL_CRITERIA_NOT_EQUAL_TO: |
7563 | 0 | case LXW_CONDITIONAL_CRITERIA_GREATER_THAN: |
7564 | 0 | case LXW_CONDITIONAL_CRITERIA_LESS_THAN: |
7565 | 0 | case LXW_CONDITIONAL_CRITERIA_GREATER_THAN_OR_EQUAL_TO: |
7566 | 0 | case LXW_CONDITIONAL_CRITERIA_LESS_THAN_OR_EQUAL_TO: |
7567 | 0 | case LXW_CONDITIONAL_CRITERIA_BETWEEN: |
7568 | 0 | case LXW_CONDITIONAL_CRITERIA_NOT_BETWEEN: |
7569 | 0 | criteria_mismatch = LXW_FALSE; |
7570 | 0 | break; |
7571 | 0 | default: |
7572 | 0 | criteria_mismatch = LXW_TRUE; |
7573 | 0 | } |
7574 | 0 | } |
7575 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) { |
7576 | 0 | switch (cond_format->criteria) { |
7577 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY: |
7578 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TODAY: |
7579 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TOMORROW: |
7580 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_7_DAYS: |
7581 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_WEEK: |
7582 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_WEEK: |
7583 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_WEEK: |
7584 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_MONTH: |
7585 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_MONTH: |
7586 | 0 | case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH: |
7587 | 0 | criteria_mismatch = LXW_FALSE; |
7588 | 0 | break; |
7589 | 0 | default: |
7590 | 0 | criteria_mismatch = LXW_TRUE; |
7591 | 0 | } |
7592 | 0 | } |
7593 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TEXT) { |
7594 | 0 | switch (cond_format->criteria) { |
7595 | 0 | case LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING: |
7596 | 0 | case LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING: |
7597 | 0 | case LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH: |
7598 | 0 | case LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH: |
7599 | 0 | criteria_mismatch = LXW_FALSE; |
7600 | 0 | break; |
7601 | 0 | default: |
7602 | 0 | criteria_mismatch = LXW_TRUE; |
7603 | 0 | } |
7604 | 0 | } |
7605 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_AVERAGE) { |
7606 | 0 | switch (cond_format->criteria) { |
7607 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE: |
7608 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW: |
7609 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE_OR_EQUAL: |
7610 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL: |
7611 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_ABOVE: |
7612 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW: |
7613 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_ABOVE: |
7614 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW: |
7615 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_ABOVE: |
7616 | 0 | case LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW: |
7617 | 0 | criteria_mismatch = LXW_FALSE; |
7618 | 0 | break; |
7619 | 0 | default: |
7620 | 0 | criteria_mismatch = LXW_TRUE; |
7621 | 0 | } |
7622 | 0 | } |
7623 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP |
7624 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) { |
7625 | 0 | switch (cond_format->criteria) { |
7626 | 0 | case LXW_CONDITIONAL_CRITERIA_NONE: |
7627 | 0 | case LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT: |
7628 | 0 | criteria_mismatch = LXW_FALSE; |
7629 | 0 | break; |
7630 | 0 | default: |
7631 | 0 | criteria_mismatch = LXW_TRUE; |
7632 | 0 | } |
7633 | 0 | } |
7634 | 0 | else { |
7635 | | /* Any other conditional type should have a zero criteria. */ |
7636 | 0 | cond_format->criteria = LXW_CONDITIONAL_CRITERIA_NONE; |
7637 | 0 | } |
7638 | | |
7639 | 0 | if (criteria_mismatch) { |
7640 | 0 | LXW_WARN_FORMAT2("worksheet_conditional_format_cell()/_range(): " |
7641 | 0 | "LXW_CONDITIONAL_CRITERIA_* = %d is not valid for " |
7642 | 0 | "LXW_CONDITIONAL_TYPE_* = %d", cond_format->criteria, |
7643 | 0 | cond_format->type); |
7644 | |
|
7645 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
7646 | 0 | } |
7647 | 0 | else { |
7648 | 0 | return LXW_NO_ERROR; |
7649 | 0 | } |
7650 | 0 | } |
7651 | | |
7652 | | /* |
7653 | | * Write the <ignoredErrors> element. |
7654 | | */ |
7655 | | STATIC void |
7656 | | _worksheet_write_ignored_errors(lxw_worksheet *self) |
7657 | 1.30k | { |
7658 | 1.30k | if (!self->has_ignore_errors) |
7659 | 1.30k | return; |
7660 | | |
7661 | 0 | lxw_xml_start_tag(self->file, "ignoredErrors", NULL); |
7662 | |
|
7663 | 0 | if (self->ignore_number_stored_as_text) { |
7664 | 0 | _worksheet_write_ignored_error(self, "numberStoredAsText", |
7665 | 0 | self->ignore_number_stored_as_text); |
7666 | 0 | } |
7667 | |
|
7668 | 0 | if (self->ignore_eval_error) { |
7669 | 0 | _worksheet_write_ignored_error(self, "evalError", |
7670 | 0 | self->ignore_eval_error); |
7671 | 0 | } |
7672 | |
|
7673 | 0 | if (self->ignore_formula_differs) { |
7674 | 0 | _worksheet_write_ignored_error(self, "formula", |
7675 | 0 | self->ignore_formula_differs); |
7676 | 0 | } |
7677 | |
|
7678 | 0 | if (self->ignore_formula_range) { |
7679 | 0 | _worksheet_write_ignored_error(self, "formulaRange", |
7680 | 0 | self->ignore_formula_range); |
7681 | 0 | } |
7682 | |
|
7683 | 0 | if (self->ignore_formula_unlocked) { |
7684 | 0 | _worksheet_write_ignored_error(self, "unlockedFormula", |
7685 | 0 | self->ignore_formula_unlocked); |
7686 | 0 | } |
7687 | |
|
7688 | 0 | if (self->ignore_empty_cell_reference) { |
7689 | 0 | _worksheet_write_ignored_error(self, "emptyCellReference", |
7690 | 0 | self->ignore_empty_cell_reference); |
7691 | 0 | } |
7692 | |
|
7693 | 0 | if (self->ignore_list_data_validation) { |
7694 | 0 | _worksheet_write_ignored_error(self, "listDataValidation", |
7695 | 0 | self->ignore_list_data_validation); |
7696 | 0 | } |
7697 | |
|
7698 | 0 | if (self->ignore_calculated_column) { |
7699 | 0 | _worksheet_write_ignored_error(self, "calculatedColumn", |
7700 | 0 | self->ignore_calculated_column); |
7701 | 0 | } |
7702 | |
|
7703 | 0 | if (self->ignore_two_digit_text_year) { |
7704 | 0 | _worksheet_write_ignored_error(self, "twoDigitTextYear", |
7705 | 0 | self->ignore_two_digit_text_year); |
7706 | 0 | } |
7707 | |
|
7708 | 0 | lxw_xml_end_tag(self->file, "ignoredErrors"); |
7709 | 0 | } |
7710 | | |
7711 | | /* |
7712 | | * Write the <tablePart> element. |
7713 | | */ |
7714 | | STATIC void |
7715 | | _worksheet_write_table_part(lxw_worksheet *self, uint16_t id) |
7716 | 0 | { |
7717 | 0 | struct xml_attribute_list attributes; |
7718 | 0 | struct xml_attribute *attribute; |
7719 | 0 | char r_id[LXW_MAX_ATTRIBUTE_LENGTH]; |
7720 | |
|
7721 | 0 | lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id); |
7722 | |
|
7723 | 0 | LXW_INIT_ATTRIBUTES(); |
7724 | 0 | LXW_PUSH_ATTRIBUTES_STR("r:id", r_id); |
7725 | |
|
7726 | 0 | lxw_xml_empty_tag(self->file, "tablePart", &attributes); |
7727 | |
|
7728 | 0 | LXW_FREE_ATTRIBUTES(); |
7729 | 0 | } |
7730 | | |
7731 | | /* |
7732 | | * Write the <tableParts> element. |
7733 | | */ |
7734 | | STATIC void |
7735 | | _worksheet_write_table_parts(lxw_worksheet *self) |
7736 | 1.30k | { |
7737 | 1.30k | struct xml_attribute_list attributes; |
7738 | 1.30k | struct xml_attribute *attribute; |
7739 | 1.30k | lxw_table_obj *table_obj; |
7740 | | |
7741 | 1.30k | if (!self->table_count) |
7742 | 1.30k | return; |
7743 | | |
7744 | 0 | LXW_INIT_ATTRIBUTES(); |
7745 | 0 | LXW_PUSH_ATTRIBUTES_INT("count", self->table_count); |
7746 | |
|
7747 | 0 | lxw_xml_start_tag(self->file, "tableParts", &attributes); |
7748 | |
|
7749 | 0 | STAILQ_FOREACH(table_obj, self->table_objs, list_pointers) { |
7750 | 0 | self->rel_count++; |
7751 | | |
7752 | | /* Write the tablePart element. */ |
7753 | 0 | _worksheet_write_table_part(self, self->rel_count); |
7754 | 0 | } |
7755 | |
|
7756 | 0 | lxw_xml_end_tag(self->file, "tableParts"); |
7757 | |
|
7758 | 0 | LXW_FREE_ATTRIBUTES(); |
7759 | 0 | } |
7760 | | |
7761 | | /* |
7762 | | * External functions to call intern XML methods shared with chartsheet. |
7763 | | */ |
7764 | | void |
7765 | | lxw_worksheet_write_sheet_views(lxw_worksheet *self) |
7766 | 0 | { |
7767 | 0 | _worksheet_write_sheet_views(self); |
7768 | 0 | } |
7769 | | |
7770 | | void |
7771 | | lxw_worksheet_write_page_margins(lxw_worksheet *self) |
7772 | 0 | { |
7773 | 0 | _worksheet_write_page_margins(self); |
7774 | 0 | } |
7775 | | |
7776 | | void |
7777 | | lxw_worksheet_write_drawings(lxw_worksheet *self) |
7778 | 0 | { |
7779 | 0 | _worksheet_write_drawings(self); |
7780 | 0 | } |
7781 | | |
7782 | | void |
7783 | | lxw_worksheet_write_sheet_protection(lxw_worksheet *self, |
7784 | | lxw_protection_obj *protect) |
7785 | 0 | { |
7786 | 0 | _worksheet_write_sheet_protection(self, protect); |
7787 | 0 | } |
7788 | | |
7789 | | void |
7790 | | lxw_worksheet_write_sheet_pr(lxw_worksheet *self) |
7791 | 0 | { |
7792 | 0 | _worksheet_write_sheet_pr(self); |
7793 | 0 | } |
7794 | | |
7795 | | void |
7796 | | lxw_worksheet_write_page_setup(lxw_worksheet *self) |
7797 | 0 | { |
7798 | 0 | _worksheet_write_page_setup(self); |
7799 | 0 | } |
7800 | | |
7801 | | void |
7802 | | lxw_worksheet_write_header_footer(lxw_worksheet *self) |
7803 | 0 | { |
7804 | 0 | _worksheet_write_header_footer(self); |
7805 | 0 | } |
7806 | | |
7807 | | /* |
7808 | | * Assemble and write the XML file. |
7809 | | */ |
7810 | | void |
7811 | | lxw_worksheet_assemble_xml_file(lxw_worksheet *self) |
7812 | 1.30k | { |
7813 | | /* Write the XML declaration. */ |
7814 | 1.30k | _worksheet_xml_declaration(self); |
7815 | | |
7816 | | /* Write the worksheet element. */ |
7817 | 1.30k | _worksheet_write_worksheet(self); |
7818 | | |
7819 | | /* Write the worksheet properties. */ |
7820 | 1.30k | _worksheet_write_sheet_pr(self); |
7821 | | |
7822 | | /* Write the worksheet dimensions. */ |
7823 | 1.30k | _worksheet_write_dimension(self); |
7824 | | |
7825 | | /* Write the sheet view properties. */ |
7826 | 1.30k | _worksheet_write_sheet_views(self); |
7827 | | |
7828 | | /* Write the sheet format properties. */ |
7829 | 1.30k | _worksheet_write_sheet_format_pr(self); |
7830 | | |
7831 | | /* Write the sheet column info. */ |
7832 | 1.30k | _worksheet_write_cols(self); |
7833 | | |
7834 | | /* Write the sheetData element. */ |
7835 | 1.30k | if (!self->optimize) |
7836 | 1.30k | _worksheet_write_sheet_data(self); |
7837 | 0 | else |
7838 | 0 | _worksheet_write_optimized_sheet_data(self); |
7839 | | |
7840 | | /* Write the sheetProtection element. */ |
7841 | 1.30k | _worksheet_write_sheet_protection(self, &self->protection); |
7842 | | |
7843 | | /* Write the autoFilter element. */ |
7844 | 1.30k | _worksheet_write_auto_filter(self); |
7845 | | |
7846 | | /* Write the mergeCells element. */ |
7847 | 1.30k | _worksheet_write_merge_cells(self); |
7848 | | |
7849 | | /* Write the conditionalFormatting elements. */ |
7850 | 1.30k | _worksheet_write_conditional_formats(self); |
7851 | | |
7852 | | /* Write the dataValidations element. */ |
7853 | 1.30k | _worksheet_write_data_validations(self); |
7854 | | |
7855 | | /* Write the hyperlink element. */ |
7856 | 1.30k | _worksheet_write_hyperlinks(self); |
7857 | | |
7858 | | /* Write the printOptions element. */ |
7859 | 1.30k | _worksheet_write_print_options(self); |
7860 | | |
7861 | | /* Write the worksheet page_margins. */ |
7862 | 1.30k | _worksheet_write_page_margins(self); |
7863 | | |
7864 | | /* Write the worksheet page setup. */ |
7865 | 1.30k | _worksheet_write_page_setup(self); |
7866 | | |
7867 | | /* Write the headerFooter element. */ |
7868 | 1.30k | _worksheet_write_header_footer(self); |
7869 | | |
7870 | | /* Write the rowBreaks element. */ |
7871 | 1.30k | _worksheet_write_row_breaks(self); |
7872 | | |
7873 | | /* Write the colBreaks element. */ |
7874 | 1.30k | _worksheet_write_col_breaks(self); |
7875 | | |
7876 | | /* Write the ignoredErrors element. */ |
7877 | 1.30k | _worksheet_write_ignored_errors(self); |
7878 | | |
7879 | | /* Write the drawing element. */ |
7880 | 1.30k | _worksheet_write_drawings(self); |
7881 | | |
7882 | | /* Write the legacyDrawing element. */ |
7883 | 1.30k | _worksheet_write_legacy_drawing(self); |
7884 | | |
7885 | | /* Write the legacyDrawingHF element. */ |
7886 | 1.30k | _worksheet_write_legacy_drawing_hf(self); |
7887 | | |
7888 | | /* Write the picture element. */ |
7889 | 1.30k | _worksheet_write_picture(self); |
7890 | | |
7891 | | /* Write the tableParts element. */ |
7892 | 1.30k | _worksheet_write_table_parts(self); |
7893 | | |
7894 | | /* Write the extLst element. */ |
7895 | 1.30k | _worksheet_write_ext_list(self); |
7896 | | |
7897 | | /* Close the worksheet tag. */ |
7898 | 1.30k | lxw_xml_end_tag(self->file, "worksheet"); |
7899 | 1.30k | } |
7900 | | |
7901 | | /***************************************************************************** |
7902 | | * |
7903 | | * Public functions. |
7904 | | * |
7905 | | ****************************************************************************/ |
7906 | | |
7907 | | /* |
7908 | | * Write a number to a cell in Excel. |
7909 | | */ |
7910 | | lxw_error |
7911 | | worksheet_write_number(lxw_worksheet *self, |
7912 | | lxw_row_t row_num, |
7913 | | lxw_col_t col_num, double value, lxw_format *format) |
7914 | 0 | { |
7915 | 0 | lxw_cell *cell; |
7916 | 0 | lxw_error err; |
7917 | |
|
7918 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
7919 | 0 | if (err) |
7920 | 0 | return err; |
7921 | | |
7922 | 0 | cell = _new_number_cell(row_num, col_num, value, format); |
7923 | |
|
7924 | 0 | _insert_cell(self, row_num, col_num, cell); |
7925 | |
|
7926 | 0 | return LXW_NO_ERROR; |
7927 | 0 | } |
7928 | | |
7929 | | /* |
7930 | | * Write a string to an Excel file. |
7931 | | */ |
7932 | | lxw_error |
7933 | | worksheet_write_string(lxw_worksheet *self, |
7934 | | lxw_row_t row_num, |
7935 | | lxw_col_t col_num, const char *string, |
7936 | | lxw_format *format) |
7937 | 69.9k | { |
7938 | 69.9k | lxw_cell *cell; |
7939 | 69.9k | int32_t string_id; |
7940 | 69.9k | char *string_copy; |
7941 | 69.9k | struct sst_element *sst_element; |
7942 | 69.9k | lxw_error err; |
7943 | | |
7944 | 69.9k | if (!string || !*string) { |
7945 | | /* Treat a NULL or empty string with formatting as a blank cell. */ |
7946 | | /* Null strings without formats should be ignored. */ |
7947 | 2.92k | if (format) |
7948 | 0 | return worksheet_write_blank(self, row_num, col_num, format); |
7949 | 2.92k | else |
7950 | 2.92k | return LXW_NO_ERROR; |
7951 | 2.92k | } |
7952 | | |
7953 | 66.9k | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
7954 | 66.9k | if (err) |
7955 | 0 | return err; |
7956 | | |
7957 | 66.9k | if (lxw_utf8_strlen(string) > LXW_STR_MAX) |
7958 | 39 | return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; |
7959 | | |
7960 | 66.9k | if (!self->optimize) { |
7961 | | /* Get the SST element and string id. */ |
7962 | 66.9k | sst_element = lxw_get_sst_index(self->sst, string, LXW_FALSE); |
7963 | | |
7964 | 66.9k | if (!sst_element) |
7965 | 0 | return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; |
7966 | | |
7967 | 66.9k | string_id = sst_element->index; |
7968 | 66.9k | cell = _new_string_cell(row_num, col_num, string_id, |
7969 | 66.9k | sst_element->string, format); |
7970 | 66.9k | } |
7971 | 0 | else { |
7972 | | /* Look for and escape control chars in the string. */ |
7973 | 0 | if (lxw_has_control_characters(string)) { |
7974 | 0 | string_copy = lxw_escape_control_characters(string); |
7975 | 0 | } |
7976 | 0 | else { |
7977 | 0 | string_copy = lxw_strdup(string); |
7978 | 0 | } |
7979 | 0 | cell = _new_inline_string_cell(row_num, col_num, string_copy, format); |
7980 | 0 | } |
7981 | | |
7982 | 66.9k | _insert_cell(self, row_num, col_num, cell); |
7983 | | |
7984 | 66.9k | return LXW_NO_ERROR; |
7985 | 66.9k | } |
7986 | | |
7987 | | /* |
7988 | | * Write a formula with a numerical result to a cell in Excel. |
7989 | | */ |
7990 | | lxw_error |
7991 | | worksheet_write_formula_num(lxw_worksheet *self, |
7992 | | lxw_row_t row_num, |
7993 | | lxw_col_t col_num, |
7994 | | const char *formula, |
7995 | | lxw_format *format, double result) |
7996 | 0 | { |
7997 | 0 | lxw_cell *cell; |
7998 | 0 | char *formula_copy; |
7999 | 0 | lxw_error err; |
8000 | |
|
8001 | 0 | if (!formula) |
8002 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
8003 | | |
8004 | 0 | if (lxw_str_is_empty(formula)) |
8005 | 0 | return LXW_ERROR_PARAMETER_IS_EMPTY; |
8006 | | |
8007 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8008 | 0 | if (err) |
8009 | 0 | return err; |
8010 | | |
8011 | | /* Strip leading "=" from formula. */ |
8012 | 0 | if (formula[0] == '=') |
8013 | 0 | formula_copy = lxw_strdup(formula + 1); |
8014 | 0 | else |
8015 | 0 | formula_copy = lxw_strdup(formula); |
8016 | |
|
8017 | 0 | cell = _new_formula_cell(row_num, col_num, formula_copy, format); |
8018 | 0 | cell->formula_result = result; |
8019 | |
|
8020 | 0 | _insert_cell(self, row_num, col_num, cell); |
8021 | |
|
8022 | 0 | return LXW_NO_ERROR; |
8023 | 0 | } |
8024 | | |
8025 | | /* |
8026 | | * Write a formula with a string result to a cell in Excel. |
8027 | | */ |
8028 | | lxw_error |
8029 | | worksheet_write_formula_str(lxw_worksheet *self, |
8030 | | lxw_row_t row_num, |
8031 | | lxw_col_t col_num, |
8032 | | const char *formula, |
8033 | | lxw_format *format, const char *result) |
8034 | 0 | { |
8035 | 0 | lxw_cell *cell; |
8036 | 0 | char *formula_copy; |
8037 | 0 | lxw_error err; |
8038 | |
|
8039 | 0 | if (!formula) |
8040 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
8041 | | |
8042 | 0 | if (lxw_str_is_empty(formula)) |
8043 | 0 | return LXW_ERROR_PARAMETER_IS_EMPTY; |
8044 | | |
8045 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8046 | 0 | if (err) |
8047 | 0 | return err; |
8048 | | |
8049 | | /* Strip leading "=" from formula. */ |
8050 | 0 | if (formula[0] == '=') |
8051 | 0 | formula_copy = lxw_strdup(formula + 1); |
8052 | 0 | else |
8053 | 0 | formula_copy = lxw_strdup(formula); |
8054 | |
|
8055 | 0 | cell = _new_formula_cell(row_num, col_num, formula_copy, format); |
8056 | 0 | cell->user_data2 = lxw_strdup(result); |
8057 | |
|
8058 | 0 | _insert_cell(self, row_num, col_num, cell); |
8059 | |
|
8060 | 0 | return LXW_NO_ERROR; |
8061 | 0 | } |
8062 | | |
8063 | | /* |
8064 | | * Write a formula with a default result to a cell in Excel . |
8065 | | */ |
8066 | | lxw_error |
8067 | | worksheet_write_formula(lxw_worksheet *self, |
8068 | | lxw_row_t row_num, |
8069 | | lxw_col_t col_num, const char *formula, |
8070 | | lxw_format *format) |
8071 | 0 | { |
8072 | 0 | return worksheet_write_formula_num(self, row_num, col_num, formula, |
8073 | 0 | format, 0); |
8074 | 0 | } |
8075 | | |
8076 | | /* |
8077 | | * Internal shared function for various array formula functions. |
8078 | | */ |
8079 | | lxw_error |
8080 | | _store_array_formula(lxw_worksheet *self, |
8081 | | lxw_row_t first_row, |
8082 | | lxw_col_t first_col, |
8083 | | lxw_row_t last_row, |
8084 | | lxw_col_t last_col, |
8085 | | const char *formula, lxw_format *format, double result, |
8086 | | uint8_t is_dynamic) |
8087 | 0 | { |
8088 | 0 | lxw_cell *cell; |
8089 | 0 | lxw_row_t tmp_row; |
8090 | 0 | lxw_col_t tmp_col; |
8091 | 0 | char *formula_copy; |
8092 | 0 | char *range; |
8093 | 0 | lxw_error err; |
8094 | | |
8095 | | /* Swap last row/col with first row/col as necessary */ |
8096 | 0 | if (first_row > last_row) { |
8097 | 0 | tmp_row = last_row; |
8098 | 0 | last_row = first_row; |
8099 | 0 | first_row = tmp_row; |
8100 | 0 | } |
8101 | 0 | if (first_col > last_col) { |
8102 | 0 | tmp_col = last_col; |
8103 | 0 | last_col = first_col; |
8104 | 0 | first_col = tmp_col; |
8105 | 0 | } |
8106 | |
|
8107 | 0 | if (!formula) |
8108 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
8109 | | |
8110 | 0 | if (lxw_str_is_empty(formula)) |
8111 | 0 | return LXW_ERROR_PARAMETER_IS_EMPTY; |
8112 | | |
8113 | | /* Check that row and col are valid and store max and min values. */ |
8114 | 0 | err = _check_dimensions(self, first_row, first_col, LXW_FALSE, LXW_FALSE); |
8115 | 0 | if (err) |
8116 | 0 | return err; |
8117 | | |
8118 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); |
8119 | 0 | if (err) |
8120 | 0 | return err; |
8121 | | |
8122 | | /* Define the array range. */ |
8123 | 0 | range = calloc(1, LXW_MAX_CELL_RANGE_LENGTH); |
8124 | 0 | RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED); |
8125 | | |
8126 | 0 | if (first_row == last_row && first_col == last_col) |
8127 | 0 | lxw_rowcol_to_cell(range, first_row, first_col); |
8128 | 0 | else |
8129 | 0 | lxw_rowcol_to_range(range, first_row, first_col, last_row, last_col); |
8130 | | |
8131 | | /* Copy and trip leading "{=" from formula. */ |
8132 | 0 | if (formula[0] == '{') |
8133 | 0 | if (strlen(formula) >= 2 && formula[1] == '=') |
8134 | 0 | formula_copy = lxw_strdup(formula + 2); |
8135 | 0 | else |
8136 | 0 | formula_copy = lxw_strdup(formula + 1); |
8137 | 0 | else |
8138 | 0 | formula_copy = lxw_strdup_formula(formula); |
8139 | | |
8140 | | /* Strip trailing "}" from formula. */ |
8141 | 0 | if (strlen(formula_copy) > 0 |
8142 | 0 | && formula_copy[strlen(formula_copy) - 1] == '}') { |
8143 | 0 | formula_copy[strlen(formula_copy) - 1] = '\0'; |
8144 | 0 | } |
8145 | | |
8146 | | /* Check for empty formula that started as {=}. */ |
8147 | 0 | if (lxw_str_is_empty(formula_copy)) { |
8148 | 0 | free(formula_copy); |
8149 | 0 | free(range); |
8150 | 0 | return LXW_ERROR_PARAMETER_IS_EMPTY; |
8151 | 0 | } |
8152 | | |
8153 | | /* Create a new array formula cell object. */ |
8154 | 0 | cell = _new_array_formula_cell(first_row, first_col, |
8155 | 0 | formula_copy, range, format, is_dynamic); |
8156 | |
|
8157 | 0 | cell->formula_result = result; |
8158 | |
|
8159 | 0 | _insert_cell(self, first_row, first_col, cell); |
8160 | |
|
8161 | 0 | if (is_dynamic) |
8162 | 0 | self->has_dynamic_functions = LXW_TRUE; |
8163 | | |
8164 | | /* Pad out the rest of the area with formatted zeroes. */ |
8165 | 0 | if (!self->optimize) { |
8166 | 0 | for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) { |
8167 | 0 | for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) { |
8168 | 0 | if (tmp_row == first_row && tmp_col == first_col) |
8169 | 0 | continue; |
8170 | | |
8171 | 0 | worksheet_write_number(self, tmp_row, tmp_col, 0, format); |
8172 | 0 | } |
8173 | 0 | } |
8174 | 0 | } |
8175 | |
|
8176 | 0 | return LXW_NO_ERROR; |
8177 | 0 | } |
8178 | | |
8179 | | /* |
8180 | | * Write an array formula with a numerical result to a cell in Excel. |
8181 | | */ |
8182 | | lxw_error |
8183 | | worksheet_write_array_formula_num(lxw_worksheet *self, |
8184 | | lxw_row_t first_row, |
8185 | | lxw_col_t first_col, |
8186 | | lxw_row_t last_row, |
8187 | | lxw_col_t last_col, |
8188 | | const char *formula, |
8189 | | lxw_format *format, double result) |
8190 | 0 | { |
8191 | 0 | return _store_array_formula(self, first_row, first_col, |
8192 | 0 | last_row, last_col, formula, format, result, |
8193 | 0 | LXW_FALSE); |
8194 | 0 | } |
8195 | | |
8196 | | /* |
8197 | | * Write an array formula with a default result to a cell in Excel. |
8198 | | */ |
8199 | | lxw_error |
8200 | | worksheet_write_array_formula(lxw_worksheet *self, |
8201 | | lxw_row_t first_row, |
8202 | | lxw_col_t first_col, |
8203 | | lxw_row_t last_row, |
8204 | | lxw_col_t last_col, |
8205 | | const char *formula, lxw_format *format) |
8206 | 0 | { |
8207 | 0 | return _store_array_formula(self, first_row, first_col, |
8208 | 0 | last_row, last_col, formula, format, 0, |
8209 | 0 | LXW_FALSE); |
8210 | 0 | } |
8211 | | |
8212 | | /* |
8213 | | * Write a single cell dynamic array formula with a default result to a cell. |
8214 | | */ |
8215 | | lxw_error |
8216 | | worksheet_write_dynamic_formula(lxw_worksheet *self, |
8217 | | lxw_row_t row, |
8218 | | lxw_col_t col, |
8219 | | const char *formula, lxw_format *format) |
8220 | 0 | { |
8221 | 0 | return _store_array_formula(self, row, col, row, col, formula, format, 0, |
8222 | 0 | LXW_TRUE); |
8223 | 0 | } |
8224 | | |
8225 | | /* |
8226 | | * Write a single cell dynamic array formula with a numerical result to a cell. |
8227 | | */ |
8228 | | lxw_error |
8229 | | worksheet_write_dynamic_formula_num(lxw_worksheet *self, |
8230 | | lxw_row_t row, |
8231 | | lxw_col_t col, |
8232 | | const char *formula, |
8233 | | lxw_format *format, double result) |
8234 | 0 | { |
8235 | 0 | return _store_array_formula(self, row, col, row, col, formula, format, |
8236 | 0 | result, LXW_TRUE); |
8237 | 0 | } |
8238 | | |
8239 | | /* |
8240 | | * Write a dynamic array formula with a numerical result to a cell in Excel. |
8241 | | */ |
8242 | | lxw_error |
8243 | | worksheet_write_dynamic_array_formula_num(lxw_worksheet *self, |
8244 | | lxw_row_t first_row, |
8245 | | lxw_col_t first_col, |
8246 | | lxw_row_t last_row, |
8247 | | lxw_col_t last_col, |
8248 | | const char *formula, |
8249 | | lxw_format *format, double result) |
8250 | 0 | { |
8251 | 0 | return _store_array_formula(self, first_row, first_col, |
8252 | 0 | last_row, last_col, formula, format, result, |
8253 | 0 | LXW_TRUE); |
8254 | 0 | } |
8255 | | |
8256 | | /* |
8257 | | * Write a dynamic array formula with a default result to a cell in Excel. |
8258 | | */ |
8259 | | lxw_error |
8260 | | worksheet_write_dynamic_array_formula(lxw_worksheet *self, |
8261 | | lxw_row_t first_row, |
8262 | | lxw_col_t first_col, |
8263 | | lxw_row_t last_row, |
8264 | | lxw_col_t last_col, |
8265 | | const char *formula, lxw_format *format) |
8266 | 0 | { |
8267 | 0 | return _store_array_formula(self, first_row, first_col, |
8268 | 0 | last_row, last_col, formula, format, 0, |
8269 | 0 | LXW_TRUE); |
8270 | 0 | } |
8271 | | |
8272 | | /* |
8273 | | * Write a blank cell with a format to a cell in Excel. |
8274 | | */ |
8275 | | lxw_error |
8276 | | worksheet_write_blank(lxw_worksheet *self, |
8277 | | lxw_row_t row_num, lxw_col_t col_num, |
8278 | | lxw_format *format) |
8279 | 0 | { |
8280 | 0 | lxw_cell *cell; |
8281 | 0 | lxw_error err; |
8282 | | |
8283 | | /* Blank cells without formatting are ignored by Excel. */ |
8284 | 0 | if (!format) |
8285 | 0 | return LXW_NO_ERROR; |
8286 | | |
8287 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8288 | 0 | if (err) |
8289 | 0 | return err; |
8290 | | |
8291 | 0 | cell = _new_blank_cell(row_num, col_num, format); |
8292 | |
|
8293 | 0 | _insert_cell(self, row_num, col_num, cell); |
8294 | |
|
8295 | 0 | return LXW_NO_ERROR; |
8296 | 0 | } |
8297 | | |
8298 | | /* |
8299 | | * Write a boolean cell with a format to a cell in Excel. |
8300 | | */ |
8301 | | lxw_error |
8302 | | worksheet_write_boolean(lxw_worksheet *self, |
8303 | | lxw_row_t row_num, lxw_col_t col_num, |
8304 | | int value, lxw_format *format) |
8305 | 0 | { |
8306 | 0 | lxw_cell *cell; |
8307 | 0 | lxw_error err; |
8308 | |
|
8309 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8310 | 0 | if (err) |
8311 | 0 | return err; |
8312 | | |
8313 | 0 | cell = _new_boolean_cell(row_num, col_num, value, format); |
8314 | |
|
8315 | 0 | _insert_cell(self, row_num, col_num, cell); |
8316 | |
|
8317 | 0 | return LXW_NO_ERROR; |
8318 | 0 | } |
8319 | | |
8320 | | /* |
8321 | | * Write a date and or time to a cell in Excel. |
8322 | | */ |
8323 | | lxw_error |
8324 | | worksheet_write_datetime(lxw_worksheet *self, |
8325 | | lxw_row_t row_num, |
8326 | | lxw_col_t col_num, lxw_datetime *datetime, |
8327 | | lxw_format *format) |
8328 | 0 | { |
8329 | 0 | lxw_cell *cell; |
8330 | 0 | double excel_date; |
8331 | 0 | lxw_error err; |
8332 | |
|
8333 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8334 | 0 | if (err) |
8335 | 0 | return err; |
8336 | | |
8337 | 0 | err = lxw_datetime_validate(datetime); |
8338 | 0 | if (err) |
8339 | 0 | return err; |
8340 | | |
8341 | 0 | excel_date = |
8342 | 0 | lxw_datetime_to_excel_date_with_epoch(datetime, self->use_1904_epoch); |
8343 | |
|
8344 | 0 | cell = _new_number_cell(row_num, col_num, excel_date, format); |
8345 | |
|
8346 | 0 | _insert_cell(self, row_num, col_num, cell); |
8347 | |
|
8348 | 0 | return LXW_NO_ERROR; |
8349 | 0 | } |
8350 | | |
8351 | | /* |
8352 | | * Write a date and or time to a cell in Excel. |
8353 | | */ |
8354 | | lxw_error |
8355 | | worksheet_write_unixtime(lxw_worksheet *self, |
8356 | | lxw_row_t row_num, |
8357 | | lxw_col_t col_num, |
8358 | | int64_t unixtime, lxw_format *format) |
8359 | 0 | { |
8360 | 0 | lxw_cell *cell; |
8361 | 0 | double excel_date; |
8362 | 0 | lxw_error err; |
8363 | |
|
8364 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8365 | 0 | if (err) |
8366 | 0 | return err; |
8367 | | |
8368 | 0 | excel_date = |
8369 | 0 | lxw_unixtime_to_excel_date_with_epoch(unixtime, self->use_1904_epoch); |
8370 | |
|
8371 | 0 | cell = _new_number_cell(row_num, col_num, excel_date, format); |
8372 | |
|
8373 | 0 | _insert_cell(self, row_num, col_num, cell); |
8374 | |
|
8375 | 0 | return LXW_NO_ERROR; |
8376 | 0 | } |
8377 | | |
8378 | | /* |
8379 | | * Write a hyperlink/url to an Excel file. |
8380 | | */ |
8381 | | lxw_error |
8382 | | worksheet_write_url_opt(lxw_worksheet *self, |
8383 | | lxw_row_t row_num, |
8384 | | lxw_col_t col_num, const char *url, |
8385 | | lxw_format *user_format, const char *string, |
8386 | | const char *tooltip) |
8387 | 0 | { |
8388 | 0 | lxw_cell *link; |
8389 | 0 | char *string_copy = NULL; |
8390 | 0 | char *url_copy = NULL; |
8391 | 0 | char *url_external = NULL; |
8392 | 0 | char *url_string = NULL; |
8393 | 0 | char *tooltip_copy = NULL; |
8394 | 0 | char *found_string; |
8395 | 0 | char *tmp_string = NULL; |
8396 | 0 | lxw_format *format = NULL; |
8397 | 0 | size_t string_size; |
8398 | 0 | size_t i; |
8399 | 0 | lxw_error err = LXW_ERROR_MEMORY_MALLOC_FAILED; |
8400 | 0 | enum cell_types link_type = HYPERLINK_URL; |
8401 | |
|
8402 | 0 | if (!url || !*url) |
8403 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
8404 | | |
8405 | | /* Check the Excel limit of URLS per worksheet. */ |
8406 | 0 | if (self->hlink_count > LXW_MAX_NUMBER_URLS) { |
8407 | 0 | LXW_WARN("worksheet_write_url()/_opt(): URL ignored since it exceeds " |
8408 | 0 | "the maximum number of allowed worksheet URLs (65530)."); |
8409 | 0 | return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED; |
8410 | 0 | } |
8411 | | |
8412 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8413 | 0 | if (err) |
8414 | 0 | return err; |
8415 | | |
8416 | | /* Reset default error condition. */ |
8417 | 0 | err = LXW_ERROR_MEMORY_MALLOC_FAILED; |
8418 | | |
8419 | | /* Set the URI scheme from internal links. */ |
8420 | 0 | found_string = strstr(url, "internal:"); |
8421 | 0 | if (found_string) |
8422 | 0 | link_type = HYPERLINK_INTERNAL; |
8423 | | |
8424 | | /* Set the URI scheme from external links. */ |
8425 | 0 | found_string = strstr(url, "external:"); |
8426 | 0 | if (found_string) |
8427 | 0 | link_type = HYPERLINK_EXTERNAL; |
8428 | |
|
8429 | 0 | if (string) { |
8430 | 0 | string_copy = lxw_strdup(string); |
8431 | 0 | GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error); |
8432 | 0 | } |
8433 | 0 | else { |
8434 | 0 | if (link_type == HYPERLINK_URL) { |
8435 | | /* Strip the mailto header. */ |
8436 | 0 | found_string = strstr(url, "mailto:"); |
8437 | 0 | if (found_string) |
8438 | 0 | string_copy = lxw_strdup(url + sizeof("mailto")); |
8439 | 0 | else |
8440 | 0 | string_copy = lxw_strdup(url); |
8441 | 0 | } |
8442 | 0 | else { |
8443 | 0 | string_copy = lxw_strdup(url + sizeof("__ternal")); |
8444 | 0 | } |
8445 | 0 | GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error); |
8446 | 0 | } |
8447 | | |
8448 | 0 | if (url) { |
8449 | 0 | if (link_type == HYPERLINK_URL) |
8450 | 0 | url_copy = lxw_strdup(url); |
8451 | 0 | else |
8452 | 0 | url_copy = lxw_strdup(url + sizeof("__ternal")); |
8453 | |
|
8454 | 0 | GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); |
8455 | 0 | } |
8456 | | |
8457 | 0 | if (tooltip) { |
8458 | 0 | tooltip_copy = lxw_strdup(tooltip); |
8459 | 0 | GOTO_LABEL_ON_MEM_ERROR(tooltip_copy, mem_error); |
8460 | 0 | } |
8461 | | |
8462 | 0 | if (link_type == HYPERLINK_INTERNAL) { |
8463 | 0 | url_string = lxw_strdup(string_copy); |
8464 | 0 | GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error); |
8465 | 0 | } |
8466 | | |
8467 | | /* Split url into the link and optional anchor/location. */ |
8468 | 0 | found_string = strchr(url_copy, '#'); |
8469 | |
|
8470 | 0 | if (found_string) { |
8471 | 0 | free(url_string); |
8472 | 0 | url_string = lxw_strdup(found_string + 1); |
8473 | 0 | GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error); |
8474 | | |
8475 | 0 | *found_string = '\0'; |
8476 | 0 | } |
8477 | | |
8478 | | /* Escape the URL. */ |
8479 | 0 | if (link_type == HYPERLINK_URL || link_type == HYPERLINK_EXTERNAL) { |
8480 | 0 | tmp_string = lxw_escape_url_characters(url_copy, LXW_FALSE); |
8481 | 0 | GOTO_LABEL_ON_MEM_ERROR(tmp_string, mem_error); |
8482 | | |
8483 | 0 | free(url_copy); |
8484 | 0 | url_copy = tmp_string; |
8485 | 0 | } |
8486 | | |
8487 | 0 | if (link_type == HYPERLINK_EXTERNAL) { |
8488 | | /* External Workbook links need to be modified into the right format. |
8489 | | * The URL will look something like "c:\temp\file.xlsx#Sheet!A1". */ |
8490 | | |
8491 | | /* For external links change the dir separator from Unix to DOS. */ |
8492 | 0 | for (i = 0; i <= strlen(url_copy); i++) |
8493 | 0 | if (url_copy[i] == '/') |
8494 | 0 | url_copy[i] = '\\'; |
8495 | |
|
8496 | 0 | for (i = 0; i <= strlen(string_copy); i++) |
8497 | 0 | if (string_copy[i] == '/') |
8498 | 0 | string_copy[i] = '\\'; |
8499 | | |
8500 | | /* Look for Windows style "C:/" link or Windows share "\\" link. */ |
8501 | 0 | found_string = strchr(url_copy, ':'); |
8502 | 0 | if (!found_string) |
8503 | 0 | found_string = strstr(url_copy, "\\\\"); |
8504 | |
|
8505 | 0 | if (found_string) { |
8506 | | /* Add the file:/// URI to the url if non-local. */ |
8507 | 0 | string_size = sizeof("file:///") + strlen(url_copy); |
8508 | 0 | url_external = calloc(1, string_size); |
8509 | 0 | GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error); |
8510 | | |
8511 | 0 | lxw_snprintf(url_external, string_size, "file:///%s", url_copy); |
8512 | |
|
8513 | 0 | } |
8514 | | |
8515 | | /* Convert a ./dir/file.xlsx link to dir/file.xlsx. */ |
8516 | 0 | found_string = strstr(url_copy, ".\\"); |
8517 | 0 | if (found_string == url_copy) |
8518 | 0 | memmove(url_copy, url_copy + 2, strlen(url_copy) - 1); |
8519 | |
|
8520 | 0 | if (url_external) { |
8521 | 0 | free(url_copy); |
8522 | 0 | url_copy = lxw_strdup(url_external); |
8523 | 0 | GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error); |
8524 | | |
8525 | 0 | free(url_external); |
8526 | 0 | url_external = NULL; |
8527 | 0 | } |
8528 | |
|
8529 | 0 | } |
8530 | | |
8531 | | /* Check if URL exceeds Excel's length limit. */ |
8532 | 0 | if (lxw_utf8_strlen(url_copy) > self->max_url_length) { |
8533 | 0 | LXW_WARN_FORMAT2("worksheet_write_url()/_opt(): URL exceeds " |
8534 | 0 | "Excel's allowable length of %d characters: %s", |
8535 | 0 | self->max_url_length, url_copy); |
8536 | 0 | err = LXW_ERROR_WORKSHEET_MAX_URL_LENGTH_EXCEEDED; |
8537 | 0 | goto mem_error; |
8538 | 0 | } |
8539 | | |
8540 | | /* Use the default URL format if none is specified. */ |
8541 | 0 | if (!user_format) |
8542 | 0 | format = self->default_url_format; |
8543 | 0 | else |
8544 | 0 | format = user_format; |
8545 | |
|
8546 | 0 | if (!self->storing_embedded_image) { |
8547 | 0 | err = |
8548 | 0 | worksheet_write_string(self, row_num, col_num, string_copy, |
8549 | 0 | format); |
8550 | 0 | if (err) |
8551 | 0 | goto mem_error; |
8552 | 0 | } |
8553 | | |
8554 | | /* Reset default error condition. */ |
8555 | 0 | err = LXW_ERROR_MEMORY_MALLOC_FAILED; |
8556 | |
|
8557 | 0 | link = _new_hyperlink_cell(row_num, col_num, link_type, url_copy, |
8558 | 0 | url_string, tooltip_copy); |
8559 | 0 | GOTO_LABEL_ON_MEM_ERROR(link, mem_error); |
8560 | | |
8561 | 0 | _insert_hyperlink(self, row_num, col_num, link); |
8562 | |
|
8563 | 0 | free(string_copy); |
8564 | 0 | self->hlink_count++; |
8565 | 0 | return LXW_NO_ERROR; |
8566 | | |
8567 | 0 | mem_error: |
8568 | 0 | free(string_copy); |
8569 | 0 | free(url_copy); |
8570 | 0 | free(url_external); |
8571 | 0 | free(url_string); |
8572 | 0 | free(tooltip_copy); |
8573 | 0 | return err; |
8574 | 0 | } |
8575 | | |
8576 | | /* |
8577 | | * Write a hyperlink/url to an Excel file. |
8578 | | */ |
8579 | | lxw_error |
8580 | | worksheet_write_url(lxw_worksheet *self, |
8581 | | lxw_row_t row_num, |
8582 | | lxw_col_t col_num, const char *url, lxw_format *format) |
8583 | 0 | { |
8584 | 0 | return worksheet_write_url_opt(self, row_num, col_num, url, format, NULL, |
8585 | 0 | NULL); |
8586 | 0 | } |
8587 | | |
8588 | | /* |
8589 | | * Write a rich string to an Excel file. |
8590 | | * |
8591 | | * Rather than duplicate several of the styles.c font xml methods of styles.c |
8592 | | * and write the data to a memory buffer this function creates a temporary |
8593 | | * styles object and uses it to write the data to a file. It then reads that |
8594 | | * data back into memory and closes the file. |
8595 | | */ |
8596 | | lxw_error |
8597 | | worksheet_write_rich_string(lxw_worksheet *self, |
8598 | | lxw_row_t row_num, |
8599 | | lxw_col_t col_num, |
8600 | | lxw_rich_string_tuple *rich_strings[], |
8601 | | lxw_format *format) |
8602 | 0 | { |
8603 | 0 | lxw_cell *cell; |
8604 | 0 | int32_t string_id; |
8605 | 0 | struct sst_element *sst_element; |
8606 | 0 | lxw_error err; |
8607 | 0 | uint8_t i; |
8608 | 0 | long file_size; |
8609 | 0 | char *rich_string = NULL; |
8610 | 0 | const char *string_copy = NULL; |
8611 | 0 | lxw_styles *styles = NULL; |
8612 | 0 | lxw_format *default_format = NULL; |
8613 | 0 | lxw_rich_string_tuple *rich_string_tuple = NULL; |
8614 | 0 | FILE *tmpfile; |
8615 | |
|
8616 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8617 | 0 | if (err) |
8618 | 0 | return err; |
8619 | | |
8620 | | /* Iterate through rich string fragments to check for input errors. */ |
8621 | 0 | i = 0; |
8622 | 0 | err = LXW_NO_ERROR; |
8623 | 0 | while ((rich_string_tuple = rich_strings[i++]) != NULL) { |
8624 | | |
8625 | | /* Check for NULL or empty strings. */ |
8626 | 0 | if (!rich_string_tuple->string || !*rich_string_tuple->string) { |
8627 | 0 | err = LXW_ERROR_PARAMETER_VALIDATION; |
8628 | 0 | } |
8629 | 0 | } |
8630 | | |
8631 | | /* If there are less than 2 fragments it isn't a rich string. */ |
8632 | 0 | if (i <= 2) |
8633 | 0 | err = LXW_ERROR_PARAMETER_VALIDATION; |
8634 | |
|
8635 | 0 | if (err) |
8636 | 0 | return err; |
8637 | | |
8638 | | /* Create a tmp file for the styles object. */ |
8639 | 0 | tmpfile = lxw_get_filehandle(&rich_string, NULL, self->tmpdir); |
8640 | 0 | if (!tmpfile) |
8641 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
8642 | | |
8643 | | /* Create a temp styles object for writing the font data. */ |
8644 | 0 | styles = lxw_styles_new(); |
8645 | 0 | GOTO_LABEL_ON_MEM_ERROR(styles, mem_error); |
8646 | 0 | styles->file = tmpfile; |
8647 | | |
8648 | | /* Create a default format for non-formatted text. */ |
8649 | 0 | default_format = lxw_format_new(); |
8650 | 0 | GOTO_LABEL_ON_MEM_ERROR(default_format, mem_error); |
8651 | | |
8652 | | /* Iterate through the rich string fragments and write each one out. */ |
8653 | 0 | i = 0; |
8654 | 0 | while ((rich_string_tuple = rich_strings[i++]) != NULL) { |
8655 | 0 | lxw_xml_start_tag(tmpfile, "r", NULL); |
8656 | |
|
8657 | 0 | if (rich_string_tuple->format) { |
8658 | | /* Write the user defined font format. */ |
8659 | 0 | lxw_styles_write_rich_font(styles, rich_string_tuple->format); |
8660 | 0 | } |
8661 | 0 | else { |
8662 | | /* Write a default font format. Except for the first fragment. */ |
8663 | 0 | if (i > 1) |
8664 | 0 | lxw_styles_write_rich_font(styles, default_format); |
8665 | 0 | } |
8666 | |
|
8667 | 0 | lxw_styles_write_string_fragment(styles, rich_string_tuple->string); |
8668 | 0 | lxw_xml_end_tag(tmpfile, "r"); |
8669 | 0 | } |
8670 | | |
8671 | | /* Free the temp objects. */ |
8672 | 0 | lxw_styles_free(styles); |
8673 | 0 | lxw_format_free(default_format); |
8674 | | |
8675 | | /* Flush the file. */ |
8676 | 0 | fflush(tmpfile); |
8677 | |
|
8678 | 0 | if (!rich_string) { |
8679 | | /* Read the size to calculate the required memory. */ |
8680 | 0 | file_size = ftell(tmpfile); |
8681 | | /* Allocate a buffer for the rich string xml data. */ |
8682 | 0 | rich_string = calloc(file_size + 1, 1); |
8683 | 0 | GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error); |
8684 | | |
8685 | | /* Rewind the file and read the data into the memory buffer. */ |
8686 | 0 | rewind(tmpfile); |
8687 | 0 | if (fread((void *) rich_string, file_size, 1, tmpfile) < 1) { |
8688 | 0 | fclose(tmpfile); |
8689 | 0 | free((void *) rich_string); |
8690 | 0 | return LXW_ERROR_READING_TMPFILE; |
8691 | 0 | } |
8692 | 0 | } |
8693 | | |
8694 | | /* Close the temp file. */ |
8695 | 0 | fclose(tmpfile); |
8696 | |
|
8697 | 0 | if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) { |
8698 | 0 | free((void *) rich_string); |
8699 | 0 | return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; |
8700 | 0 | } |
8701 | | |
8702 | 0 | if (!self->optimize) { |
8703 | | /* Get the SST element and string id. */ |
8704 | 0 | sst_element = lxw_get_sst_index(self->sst, rich_string, LXW_TRUE); |
8705 | 0 | free((void *) rich_string); |
8706 | |
|
8707 | 0 | if (!sst_element) |
8708 | 0 | return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND; |
8709 | | |
8710 | 0 | string_id = sst_element->index; |
8711 | 0 | cell = _new_string_cell(row_num, col_num, string_id, |
8712 | 0 | sst_element->string, format); |
8713 | 0 | } |
8714 | 0 | else { |
8715 | | /* Look for and escape control chars in the string. */ |
8716 | 0 | if (lxw_has_control_characters(rich_string)) { |
8717 | 0 | string_copy = lxw_escape_control_characters(rich_string); |
8718 | 0 | free((void *) rich_string); |
8719 | 0 | } |
8720 | 0 | else { |
8721 | 0 | string_copy = rich_string; |
8722 | 0 | } |
8723 | 0 | cell = _new_inline_rich_string_cell(row_num, col_num, string_copy, |
8724 | 0 | format); |
8725 | 0 | } |
8726 | | |
8727 | 0 | _insert_cell(self, row_num, col_num, cell); |
8728 | |
|
8729 | 0 | return LXW_NO_ERROR; |
8730 | | |
8731 | 0 | mem_error: |
8732 | 0 | lxw_styles_free(styles); |
8733 | 0 | lxw_format_free(default_format); |
8734 | 0 | fclose(tmpfile); |
8735 | |
|
8736 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
8737 | 0 | } |
8738 | | |
8739 | | /* |
8740 | | * Write a comment to a worksheet cell in Excel. |
8741 | | */ |
8742 | | lxw_error |
8743 | | worksheet_write_comment_opt(lxw_worksheet *self, |
8744 | | lxw_row_t row_num, lxw_col_t col_num, |
8745 | | const char *text, lxw_comment_options *options) |
8746 | 0 | { |
8747 | 0 | lxw_cell *cell; |
8748 | 0 | lxw_error err; |
8749 | 0 | lxw_vml_obj *comment; |
8750 | |
|
8751 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
8752 | 0 | if (err) |
8753 | 0 | return err; |
8754 | | |
8755 | 0 | if (!text) |
8756 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
8757 | | |
8758 | 0 | if (lxw_str_is_empty(text)) |
8759 | 0 | return LXW_ERROR_PARAMETER_IS_EMPTY; |
8760 | | |
8761 | 0 | if (lxw_utf8_strlen(text) > LXW_STR_MAX) |
8762 | 0 | return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED; |
8763 | | |
8764 | 0 | comment = calloc(1, sizeof(lxw_vml_obj)); |
8765 | 0 | GOTO_LABEL_ON_MEM_ERROR(comment, mem_error); |
8766 | | |
8767 | 0 | comment->text = lxw_strdup(text); |
8768 | 0 | GOTO_LABEL_ON_MEM_ERROR(comment->text, mem_error); |
8769 | | |
8770 | 0 | comment->row = row_num; |
8771 | 0 | comment->col = col_num; |
8772 | |
|
8773 | 0 | cell = _new_comment_cell(row_num, col_num, comment); |
8774 | 0 | GOTO_LABEL_ON_MEM_ERROR(cell, mem_error); |
8775 | | |
8776 | 0 | _insert_comment(self, row_num, col_num, cell); |
8777 | | |
8778 | | /* Set user and default parameters for the comment. */ |
8779 | 0 | _get_comment_params(comment, options); |
8780 | |
|
8781 | 0 | self->has_vml = LXW_TRUE; |
8782 | 0 | self->has_comments = LXW_TRUE; |
8783 | | |
8784 | | /* Insert a placeholder in the cell RB table in the same position so |
8785 | | * that the worksheet row "spans" calculations are correct. */ |
8786 | 0 | _insert_cell_placeholder(self, row_num, col_num); |
8787 | |
|
8788 | 0 | return LXW_NO_ERROR; |
8789 | | |
8790 | 0 | mem_error: |
8791 | 0 | if (comment) |
8792 | 0 | _free_vml_object(comment); |
8793 | |
|
8794 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
8795 | 0 | } |
8796 | | |
8797 | | /* |
8798 | | * Write a comment to a worksheet cell in Excel. |
8799 | | */ |
8800 | | lxw_error |
8801 | | worksheet_write_comment(lxw_worksheet *self, |
8802 | | lxw_row_t row_num, lxw_col_t col_num, |
8803 | | const char *string) |
8804 | 0 | { |
8805 | 0 | return worksheet_write_comment_opt(self, row_num, col_num, string, NULL); |
8806 | 0 | } |
8807 | | |
8808 | | /* |
8809 | | * Set the properties of a single column or a range of columns with options. |
8810 | | */ |
8811 | | lxw_error |
8812 | | worksheet_set_column_opt(lxw_worksheet *self, |
8813 | | lxw_col_t firstcol, |
8814 | | lxw_col_t lastcol, |
8815 | | double width, |
8816 | | lxw_format *format, |
8817 | | lxw_row_col_options *user_options) |
8818 | 0 | { |
8819 | 0 | lxw_col_options *copied_options; |
8820 | 0 | uint8_t ignore_row = LXW_TRUE; |
8821 | 0 | uint8_t ignore_col = LXW_TRUE; |
8822 | 0 | uint8_t hidden = LXW_FALSE; |
8823 | 0 | uint8_t level = 0; |
8824 | 0 | uint8_t collapsed = LXW_FALSE; |
8825 | 0 | lxw_col_t col; |
8826 | 0 | lxw_error err; |
8827 | |
|
8828 | 0 | if (user_options) { |
8829 | 0 | hidden = user_options->hidden; |
8830 | 0 | level = user_options->level; |
8831 | 0 | collapsed = user_options->collapsed; |
8832 | 0 | } |
8833 | | |
8834 | | /* Ensure second col is larger than first. */ |
8835 | 0 | if (firstcol > lastcol) { |
8836 | 0 | lxw_col_t tmp = firstcol; |
8837 | 0 | firstcol = lastcol; |
8838 | 0 | lastcol = tmp; |
8839 | 0 | } |
8840 | | |
8841 | | /* Ensure that the cols are valid and store max and min values. |
8842 | | * NOTE: The check shouldn't modify the row dimensions and should only |
8843 | | * modify the column dimensions in certain cases. */ |
8844 | 0 | if (format != NULL || (width != LXW_DEF_COL_WIDTH && hidden)) |
8845 | 0 | ignore_col = LXW_FALSE; |
8846 | |
|
8847 | 0 | err = _check_dimensions(self, 0, firstcol, ignore_row, ignore_col); |
8848 | |
|
8849 | 0 | if (!err) |
8850 | 0 | err = _check_dimensions(self, 0, lastcol, ignore_row, ignore_col); |
8851 | |
|
8852 | 0 | if (err) |
8853 | 0 | return err; |
8854 | | |
8855 | | /* Resize the col_options array if required. */ |
8856 | 0 | if (firstcol >= self->col_options_max) { |
8857 | 0 | lxw_col_t col_tmp; |
8858 | 0 | lxw_col_t old_size = self->col_options_max; |
8859 | 0 | lxw_col_t new_size = _next_power_of_two(firstcol + 1); |
8860 | 0 | lxw_col_options **new_ptr = realloc(self->col_options, |
8861 | 0 | new_size * |
8862 | 0 | sizeof(lxw_col_options *)); |
8863 | |
|
8864 | 0 | if (new_ptr) { |
8865 | 0 | for (col_tmp = old_size; col_tmp < new_size; col_tmp++) |
8866 | 0 | new_ptr[col_tmp] = NULL; |
8867 | |
|
8868 | 0 | self->col_options = new_ptr; |
8869 | 0 | self->col_options_max = new_size; |
8870 | 0 | } |
8871 | 0 | else { |
8872 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
8873 | 0 | } |
8874 | 0 | } |
8875 | | |
8876 | | /* Resize the col_formats array if required. */ |
8877 | 0 | if (lastcol >= self->col_formats_max) { |
8878 | 0 | lxw_col_t col; |
8879 | 0 | lxw_col_t old_size = self->col_formats_max; |
8880 | 0 | lxw_col_t new_size = _next_power_of_two(lastcol + 1); |
8881 | 0 | lxw_format **new_ptr = realloc(self->col_formats, |
8882 | 0 | new_size * sizeof(lxw_format *)); |
8883 | |
|
8884 | 0 | if (new_ptr) { |
8885 | 0 | for (col = old_size; col < new_size; col++) |
8886 | 0 | new_ptr[col] = NULL; |
8887 | |
|
8888 | 0 | self->col_formats = new_ptr; |
8889 | 0 | self->col_formats_max = new_size; |
8890 | 0 | } |
8891 | 0 | else { |
8892 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
8893 | 0 | } |
8894 | 0 | } |
8895 | | |
8896 | | /* Store the column options. */ |
8897 | 0 | copied_options = calloc(1, sizeof(lxw_col_options)); |
8898 | 0 | RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED); |
8899 | | |
8900 | | /* Ensure the level is <= 7). */ |
8901 | 0 | if (level > 7) |
8902 | 0 | level = 7; |
8903 | |
|
8904 | 0 | if (level > self->outline_col_level) |
8905 | 0 | self->outline_col_level = level; |
8906 | | |
8907 | | /* Set the column properties. */ |
8908 | 0 | copied_options->firstcol = firstcol; |
8909 | 0 | copied_options->lastcol = lastcol; |
8910 | 0 | copied_options->width = width; |
8911 | 0 | copied_options->format = format; |
8912 | 0 | copied_options->hidden = hidden; |
8913 | 0 | copied_options->level = level; |
8914 | 0 | copied_options->collapsed = collapsed; |
8915 | |
|
8916 | 0 | free(self->col_options[firstcol]); |
8917 | 0 | self->col_options[firstcol] = copied_options; |
8918 | | |
8919 | | /* Store the column formats for use when writing cell data. */ |
8920 | 0 | for (col = firstcol; col <= lastcol; col++) { |
8921 | 0 | self->col_formats[col] = format; |
8922 | 0 | } |
8923 | | |
8924 | | /* Store the column change to allow optimizations. */ |
8925 | 0 | self->col_size_changed = LXW_TRUE; |
8926 | |
|
8927 | 0 | return LXW_NO_ERROR; |
8928 | 0 | } |
8929 | | |
8930 | | /* |
8931 | | * Set the properties of a single column or a range of columns. |
8932 | | */ |
8933 | | lxw_error |
8934 | | worksheet_set_column(lxw_worksheet *self, |
8935 | | lxw_col_t firstcol, |
8936 | | lxw_col_t lastcol, double width, lxw_format *format) |
8937 | 0 | { |
8938 | 0 | return worksheet_set_column_opt(self, firstcol, lastcol, width, format, |
8939 | 0 | NULL); |
8940 | 0 | } |
8941 | | |
8942 | | /* |
8943 | | * Set the properties of a single column or a range of columns, with the |
8944 | | * width in pixels. |
8945 | | */ |
8946 | | lxw_error |
8947 | | worksheet_set_column_pixels(lxw_worksheet *self, |
8948 | | lxw_col_t firstcol, |
8949 | | lxw_col_t lastcol, |
8950 | | uint32_t pixels, lxw_format *format) |
8951 | 0 | { |
8952 | 0 | double width = _pixels_to_width(pixels); |
8953 | |
|
8954 | 0 | return worksheet_set_column_opt(self, firstcol, lastcol, width, format, |
8955 | 0 | NULL); |
8956 | 0 | } |
8957 | | |
8958 | | /* |
8959 | | * Set the properties of a single column or a range of columns with options, |
8960 | | * with the width in pixels. |
8961 | | */ |
8962 | | lxw_error |
8963 | | worksheet_set_column_pixels_opt(lxw_worksheet *self, |
8964 | | lxw_col_t firstcol, |
8965 | | lxw_col_t lastcol, |
8966 | | uint32_t pixels, |
8967 | | lxw_format *format, |
8968 | | lxw_row_col_options *user_options) |
8969 | 0 | { |
8970 | 0 | double width = _pixels_to_width(pixels); |
8971 | |
|
8972 | 0 | return worksheet_set_column_opt(self, firstcol, lastcol, width, format, |
8973 | 0 | user_options); |
8974 | 0 | } |
8975 | | |
8976 | | /* |
8977 | | * Set the properties of a row with options. |
8978 | | */ |
8979 | | lxw_error |
8980 | | worksheet_set_row_opt(lxw_worksheet *self, |
8981 | | lxw_row_t row_num, |
8982 | | double height, |
8983 | | lxw_format *format, lxw_row_col_options *user_options) |
8984 | 0 | { |
8985 | |
|
8986 | 0 | lxw_col_t min_col; |
8987 | 0 | uint8_t hidden = LXW_FALSE; |
8988 | 0 | uint8_t level = 0; |
8989 | 0 | uint8_t collapsed = LXW_FALSE; |
8990 | 0 | lxw_row *row; |
8991 | 0 | lxw_error err; |
8992 | |
|
8993 | 0 | if (user_options) { |
8994 | 0 | hidden = user_options->hidden; |
8995 | 0 | level = user_options->level; |
8996 | 0 | collapsed = user_options->collapsed; |
8997 | 0 | } |
8998 | | |
8999 | | /* Use minimum col in _check_dimensions(). */ |
9000 | 0 | if (self->dim_colmin != LXW_COL_MAX) |
9001 | 0 | min_col = self->dim_colmin; |
9002 | 0 | else |
9003 | 0 | min_col = 0; |
9004 | |
|
9005 | 0 | err = _check_dimensions(self, row_num, min_col, LXW_FALSE, LXW_FALSE); |
9006 | 0 | if (err) |
9007 | 0 | return err; |
9008 | | |
9009 | | /* If the height is 0 the row is hidden and the height is the default. */ |
9010 | 0 | if (height == 0) { |
9011 | 0 | hidden = LXW_TRUE; |
9012 | 0 | height = self->default_row_height; |
9013 | 0 | } |
9014 | | |
9015 | | /* Ensure the level is <= 7). */ |
9016 | 0 | if (level > 7) |
9017 | 0 | level = 7; |
9018 | |
|
9019 | 0 | if (level > self->outline_row_level) |
9020 | 0 | self->outline_row_level = level; |
9021 | | |
9022 | | /* Store the row properties. */ |
9023 | 0 | row = _get_row(self, row_num); |
9024 | |
|
9025 | 0 | row->height = height; |
9026 | 0 | row->format = format; |
9027 | 0 | row->hidden = hidden; |
9028 | 0 | row->level = level; |
9029 | 0 | row->collapsed = collapsed; |
9030 | 0 | row->row_changed = LXW_TRUE; |
9031 | |
|
9032 | 0 | if (height != self->default_row_height) |
9033 | 0 | row->height_changed = LXW_TRUE; |
9034 | |
|
9035 | 0 | return LXW_NO_ERROR; |
9036 | 0 | } |
9037 | | |
9038 | | /* |
9039 | | * Set the properties of a row. |
9040 | | */ |
9041 | | lxw_error |
9042 | | worksheet_set_row(lxw_worksheet *self, |
9043 | | lxw_row_t row_num, double height, lxw_format *format) |
9044 | 0 | { |
9045 | 0 | return worksheet_set_row_opt(self, row_num, height, format, NULL); |
9046 | 0 | } |
9047 | | |
9048 | | /* |
9049 | | * Set the properties of a row, with the height in pixels. |
9050 | | */ |
9051 | | lxw_error |
9052 | | worksheet_set_row_pixels(lxw_worksheet *self, |
9053 | | lxw_row_t row_num, uint32_t pixels, |
9054 | | lxw_format *format) |
9055 | 0 | { |
9056 | 0 | double height = _pixels_to_height(pixels); |
9057 | |
|
9058 | 0 | return worksheet_set_row_opt(self, row_num, height, format, NULL); |
9059 | 0 | } |
9060 | | |
9061 | | /* |
9062 | | * Set the properties of a row with options, with the height in pixels. |
9063 | | */ |
9064 | | lxw_error |
9065 | | worksheet_set_row_pixels_opt(lxw_worksheet *self, |
9066 | | lxw_row_t row_num, |
9067 | | uint32_t pixels, |
9068 | | lxw_format *format, |
9069 | | lxw_row_col_options *user_options) |
9070 | 0 | { |
9071 | 0 | double height = _pixels_to_height(pixels); |
9072 | |
|
9073 | 0 | return worksheet_set_row_opt(self, row_num, height, format, user_options); |
9074 | 0 | } |
9075 | | |
9076 | | /* |
9077 | | * Merge a range of cells. The first cell should contain the data and the others |
9078 | | * should be blank. All cells should contain the same format. |
9079 | | */ |
9080 | | lxw_error |
9081 | | worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row, |
9082 | | lxw_col_t first_col, lxw_row_t last_row, |
9083 | | lxw_col_t last_col, const char *string, |
9084 | | lxw_format *format) |
9085 | 0 | { |
9086 | 0 | lxw_merged_range *merged_range; |
9087 | 0 | lxw_row_t tmp_row; |
9088 | 0 | lxw_col_t tmp_col; |
9089 | 0 | lxw_error err; |
9090 | | |
9091 | | /* Excel doesn't allow a single cell to be merged */ |
9092 | 0 | if (first_row == last_row && first_col == last_col) |
9093 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9094 | | |
9095 | | /* Swap last row/col with first row/col as necessary */ |
9096 | 0 | if (first_row > last_row) { |
9097 | 0 | tmp_row = last_row; |
9098 | 0 | last_row = first_row; |
9099 | 0 | first_row = tmp_row; |
9100 | 0 | } |
9101 | 0 | if (first_col > last_col) { |
9102 | 0 | tmp_col = last_col; |
9103 | 0 | last_col = first_col; |
9104 | 0 | first_col = tmp_col; |
9105 | 0 | } |
9106 | | |
9107 | | /* Check that column number is valid and store the max value */ |
9108 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); |
9109 | 0 | if (err) |
9110 | 0 | return err; |
9111 | | |
9112 | | /* Store the merge range. */ |
9113 | 0 | merged_range = calloc(1, sizeof(lxw_merged_range)); |
9114 | 0 | RETURN_ON_MEM_ERROR(merged_range, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9115 | | |
9116 | 0 | merged_range->first_row = first_row; |
9117 | 0 | merged_range->first_col = first_col; |
9118 | 0 | merged_range->last_row = last_row; |
9119 | 0 | merged_range->last_col = last_col; |
9120 | |
|
9121 | 0 | STAILQ_INSERT_TAIL(self->merged_ranges, merged_range, list_pointers); |
9122 | 0 | self->merged_range_count++; |
9123 | | |
9124 | | /* Write the first cell */ |
9125 | 0 | worksheet_write_string(self, first_row, first_col, string, format); |
9126 | | |
9127 | | /* Pad out the rest of the area with formatted blank cells. */ |
9128 | 0 | for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) { |
9129 | 0 | for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) { |
9130 | 0 | if (tmp_row == first_row && tmp_col == first_col) |
9131 | 0 | continue; |
9132 | | |
9133 | 0 | worksheet_write_blank(self, tmp_row, tmp_col, format); |
9134 | 0 | } |
9135 | 0 | } |
9136 | |
|
9137 | 0 | return LXW_NO_ERROR; |
9138 | 0 | } |
9139 | | |
9140 | | /* |
9141 | | * Set the autofilter area in the worksheet. |
9142 | | */ |
9143 | | lxw_error |
9144 | | worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row, |
9145 | | lxw_col_t first_col, lxw_row_t last_row, |
9146 | | lxw_col_t last_col) |
9147 | 0 | { |
9148 | 0 | lxw_row_t tmp_row; |
9149 | 0 | lxw_col_t tmp_col; |
9150 | 0 | lxw_error err; |
9151 | 0 | lxw_filter_rule_obj **filter_rules; |
9152 | 0 | lxw_col_t num_filter_rules; |
9153 | | |
9154 | | /* Swap last row/col with first row/col as necessary */ |
9155 | 0 | if (first_row > last_row) { |
9156 | 0 | tmp_row = last_row; |
9157 | 0 | last_row = first_row; |
9158 | 0 | first_row = tmp_row; |
9159 | 0 | } |
9160 | 0 | if (first_col > last_col) { |
9161 | 0 | tmp_col = last_col; |
9162 | 0 | last_col = first_col; |
9163 | 0 | first_col = tmp_col; |
9164 | 0 | } |
9165 | | |
9166 | | /* Check that column number is valid and store the max value */ |
9167 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE); |
9168 | 0 | if (err) |
9169 | 0 | return err; |
9170 | | |
9171 | | /* Create a array to hold filter rules. */ |
9172 | 0 | self->autofilter.in_use = LXW_FALSE; |
9173 | 0 | self->autofilter.has_rules = LXW_FALSE; |
9174 | 0 | _free_filter_rules(self); |
9175 | 0 | num_filter_rules = last_col - first_col + 1; |
9176 | 0 | filter_rules = calloc(num_filter_rules, sizeof(lxw_filter_rule_obj *)); |
9177 | 0 | RETURN_ON_MEM_ERROR(filter_rules, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9178 | | |
9179 | 0 | self->autofilter.in_use = LXW_TRUE; |
9180 | 0 | self->autofilter.first_row = first_row; |
9181 | 0 | self->autofilter.first_col = first_col; |
9182 | 0 | self->autofilter.last_row = last_row; |
9183 | 0 | self->autofilter.last_col = last_col; |
9184 | |
|
9185 | 0 | self->filter_rules = filter_rules; |
9186 | 0 | self->num_filter_rules = num_filter_rules; |
9187 | |
|
9188 | 0 | return LXW_NO_ERROR; |
9189 | 0 | } |
9190 | | |
9191 | | /* |
9192 | | * Set a autofilter rule for a filter column. |
9193 | | */ |
9194 | | lxw_error |
9195 | | worksheet_filter_column(lxw_worksheet *self, lxw_col_t col, |
9196 | | lxw_filter_rule *rule) |
9197 | 0 | { |
9198 | 0 | lxw_filter_rule_obj *rule_obj; |
9199 | 0 | uint16_t rule_index; |
9200 | |
|
9201 | 0 | if (!rule) { |
9202 | 0 | LXW_WARN("worksheet_filter_column(): rule parameter cannot be NULL"); |
9203 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9204 | 0 | } |
9205 | | |
9206 | 0 | if (self->autofilter.in_use == LXW_FALSE) { |
9207 | 0 | LXW_WARN("worksheet_filter_column(): " |
9208 | 0 | "Worksheet autofilter range hasn't been defined. " |
9209 | 0 | "Use worksheet_autofilter() first."); |
9210 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9211 | 0 | } |
9212 | | |
9213 | 0 | if (col < self->autofilter.first_col || col > self->autofilter.last_col) { |
9214 | 0 | LXW_WARN_FORMAT3("worksheet_filter_column(): " |
9215 | 0 | "Column '%d' is outside autofilter range " |
9216 | 0 | "'%d <= col <= %d'.", col, |
9217 | 0 | self->autofilter.first_col, |
9218 | 0 | self->autofilter.last_col); |
9219 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9220 | 0 | } |
9221 | | |
9222 | | /* Free any previous rule in the column slot. */ |
9223 | 0 | rule_index = col - self->autofilter.first_col; |
9224 | 0 | _free_filter_rule(self->filter_rules[rule_index]); |
9225 | | |
9226 | | /* Create a new rule and copy user input. */ |
9227 | 0 | rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); |
9228 | 0 | RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9229 | | |
9230 | 0 | rule_obj->col_num = rule_index; |
9231 | 0 | rule_obj->type = LXW_FILTER_TYPE_SINGLE; |
9232 | 0 | rule_obj->criteria1 = rule->criteria; |
9233 | 0 | rule_obj->value1 = rule->value; |
9234 | |
|
9235 | 0 | if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { |
9236 | 0 | rule_obj->value1_string = lxw_strdup(rule->value_string); |
9237 | 0 | } |
9238 | 0 | else { |
9239 | 0 | rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; |
9240 | 0 | rule_obj->value1_string = lxw_strdup(" "); |
9241 | 0 | } |
9242 | |
|
9243 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) |
9244 | 0 | rule_obj->has_blanks = LXW_TRUE; |
9245 | |
|
9246 | 0 | _set_custom_filter(rule_obj); |
9247 | |
|
9248 | 0 | self->filter_rules[rule_index] = rule_obj; |
9249 | 0 | self->filter_on = LXW_TRUE; |
9250 | 0 | self->autofilter.has_rules = LXW_TRUE; |
9251 | |
|
9252 | 0 | return LXW_NO_ERROR; |
9253 | 0 | } |
9254 | | |
9255 | | /* |
9256 | | * Set two autofilter rules for a filter column. |
9257 | | */ |
9258 | | lxw_error |
9259 | | worksheet_filter_column2(lxw_worksheet *self, lxw_col_t col, |
9260 | | lxw_filter_rule *rule1, lxw_filter_rule *rule2, |
9261 | | uint8_t and_or) |
9262 | 0 | { |
9263 | 0 | lxw_filter_rule_obj *rule_obj; |
9264 | 0 | uint16_t rule_index; |
9265 | |
|
9266 | 0 | if (!rule1 || !rule2) { |
9267 | 0 | LXW_WARN("worksheet_filter_column2(): rule parameter cannot be NULL"); |
9268 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9269 | 0 | } |
9270 | | |
9271 | 0 | if (self->autofilter.in_use == LXW_FALSE) { |
9272 | 0 | LXW_WARN("worksheet_filter_column2(): " |
9273 | 0 | "Worksheet autofilter range hasn't been defined. " |
9274 | 0 | "Use worksheet_autofilter() first."); |
9275 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9276 | 0 | } |
9277 | | |
9278 | 0 | if (col < self->autofilter.first_col || col > self->autofilter.last_col) { |
9279 | 0 | LXW_WARN_FORMAT3("worksheet_filter_column2(): " |
9280 | 0 | "Column '%d' is outside autofilter range " |
9281 | 0 | "'%d <= col <= %d'.", col, |
9282 | 0 | self->autofilter.first_col, |
9283 | 0 | self->autofilter.last_col); |
9284 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9285 | 0 | } |
9286 | | |
9287 | | /* Free any previous rule in the column slot. */ |
9288 | 0 | rule_index = col - self->autofilter.first_col; |
9289 | 0 | _free_filter_rule(self->filter_rules[rule_index]); |
9290 | | |
9291 | | /* Create a new rule and copy user input. */ |
9292 | 0 | rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); |
9293 | 0 | RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9294 | | |
9295 | 0 | if (and_or == LXW_FILTER_AND) |
9296 | 0 | rule_obj->type = LXW_FILTER_TYPE_AND; |
9297 | 0 | else |
9298 | 0 | rule_obj->type = LXW_FILTER_TYPE_OR; |
9299 | |
|
9300 | 0 | rule_obj->col_num = rule_index; |
9301 | |
|
9302 | 0 | rule_obj->criteria1 = rule1->criteria; |
9303 | 0 | rule_obj->value1 = rule1->value; |
9304 | |
|
9305 | 0 | rule_obj->criteria2 = rule2->criteria; |
9306 | 0 | rule_obj->value2 = rule2->value; |
9307 | |
|
9308 | 0 | if (rule_obj->criteria1 != LXW_FILTER_CRITERIA_NON_BLANKS) { |
9309 | 0 | rule_obj->value1_string = lxw_strdup(rule1->value_string); |
9310 | 0 | } |
9311 | 0 | else { |
9312 | 0 | rule_obj->criteria1 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; |
9313 | 0 | rule_obj->value1_string = lxw_strdup(" "); |
9314 | 0 | } |
9315 | |
|
9316 | 0 | if (rule_obj->criteria2 != LXW_FILTER_CRITERIA_NON_BLANKS) { |
9317 | 0 | rule_obj->value2_string = lxw_strdup(rule2->value_string); |
9318 | 0 | } |
9319 | 0 | else { |
9320 | 0 | rule_obj->criteria2 = LXW_FILTER_CRITERIA_NOT_EQUAL_TO; |
9321 | 0 | rule_obj->value2_string = lxw_strdup(" "); |
9322 | 0 | } |
9323 | |
|
9324 | 0 | if (rule_obj->criteria1 == LXW_FILTER_CRITERIA_BLANKS) |
9325 | 0 | rule_obj->has_blanks = LXW_TRUE; |
9326 | |
|
9327 | 0 | if (rule_obj->criteria2 == LXW_FILTER_CRITERIA_BLANKS) |
9328 | 0 | rule_obj->has_blanks = LXW_TRUE; |
9329 | |
|
9330 | 0 | _set_custom_filter(rule_obj); |
9331 | |
|
9332 | 0 | self->filter_rules[rule_index] = rule_obj; |
9333 | 0 | self->filter_on = LXW_TRUE; |
9334 | 0 | self->autofilter.has_rules = LXW_TRUE; |
9335 | |
|
9336 | 0 | return LXW_NO_ERROR; |
9337 | 0 | } |
9338 | | |
9339 | | /* |
9340 | | * Set two autofilter rules for a filter column. |
9341 | | */ |
9342 | | lxw_error |
9343 | | worksheet_filter_list(lxw_worksheet *self, lxw_col_t col, const char **list) |
9344 | 0 | { |
9345 | 0 | lxw_filter_rule_obj *rule_obj; |
9346 | 0 | uint16_t rule_index; |
9347 | 0 | uint8_t has_blanks = LXW_FALSE; |
9348 | 0 | uint16_t num_filters = 0; |
9349 | 0 | uint16_t input_list_index; |
9350 | 0 | uint16_t rule_obj_list_index; |
9351 | 0 | const char *str; |
9352 | 0 | char **tmp_list; |
9353 | |
|
9354 | 0 | if (!list) { |
9355 | 0 | LXW_WARN("worksheet_filter_list(): list parameter cannot be NULL"); |
9356 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9357 | 0 | } |
9358 | | |
9359 | 0 | if (self->autofilter.in_use == LXW_FALSE) { |
9360 | 0 | LXW_WARN("worksheet_filter_list(): " |
9361 | 0 | "Worksheet autofilter range hasn't been defined. " |
9362 | 0 | "Use worksheet_autofilter() first."); |
9363 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9364 | 0 | } |
9365 | | |
9366 | 0 | if (col < self->autofilter.first_col || col > self->autofilter.last_col) { |
9367 | 0 | LXW_WARN_FORMAT3("worksheet_filter_list(): " |
9368 | 0 | "Column '%d' is outside autofilter range " |
9369 | 0 | "'%d <= col <= %d'.", col, |
9370 | 0 | self->autofilter.first_col, |
9371 | 0 | self->autofilter.last_col); |
9372 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9373 | 0 | } |
9374 | | |
9375 | | /* Count the number of non "Blanks" strings in the input list. */ |
9376 | 0 | input_list_index = 0; |
9377 | 0 | while ((str = list[input_list_index]) != NULL) { |
9378 | 0 | if (strncmp(str, "Blanks", 6) == 0) |
9379 | 0 | has_blanks = LXW_TRUE; |
9380 | 0 | else |
9381 | 0 | num_filters++; |
9382 | |
|
9383 | 0 | input_list_index++; |
9384 | 0 | } |
9385 | | |
9386 | | /* There should be at least one filter string. */ |
9387 | 0 | if (num_filters == 0) { |
9388 | 0 | LXW_WARN("worksheet_filter_list(): " |
9389 | 0 | "list must have at least 1 non-blanks item."); |
9390 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9391 | 0 | } |
9392 | | |
9393 | | /* Free any previous rule in the column slot. */ |
9394 | 0 | rule_index = col - self->autofilter.first_col; |
9395 | 0 | _free_filter_rule(self->filter_rules[rule_index]); |
9396 | | |
9397 | | /* Create a new rule and copy user input. */ |
9398 | 0 | rule_obj = calloc(1, sizeof(lxw_filter_rule_obj)); |
9399 | 0 | RETURN_ON_MEM_ERROR(rule_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9400 | | |
9401 | 0 | tmp_list = calloc(num_filters + 1, sizeof(char *)); |
9402 | 0 | GOTO_LABEL_ON_MEM_ERROR(tmp_list, mem_error); |
9403 | | |
9404 | | /* Copy input list (without any "Blanks" command) to an internal list. */ |
9405 | 0 | input_list_index = 0; |
9406 | 0 | rule_obj_list_index = 0; |
9407 | 0 | while ((str = list[input_list_index]) != NULL) { |
9408 | 0 | if (strncmp(str, "Blanks", 6) != 0) { |
9409 | 0 | tmp_list[rule_obj_list_index] = lxw_strdup(str); |
9410 | 0 | rule_obj_list_index++; |
9411 | 0 | } |
9412 | |
|
9413 | 0 | input_list_index++; |
9414 | 0 | } |
9415 | |
|
9416 | 0 | rule_obj->list = tmp_list; |
9417 | 0 | rule_obj->num_list_filters = num_filters; |
9418 | 0 | rule_obj->is_custom = LXW_FALSE; |
9419 | 0 | rule_obj->col_num = rule_index; |
9420 | 0 | rule_obj->type = LXW_FILTER_TYPE_STRING_LIST; |
9421 | 0 | rule_obj->has_blanks = has_blanks; |
9422 | |
|
9423 | 0 | self->filter_rules[rule_index] = rule_obj; |
9424 | 0 | self->filter_on = LXW_TRUE; |
9425 | 0 | self->autofilter.has_rules = LXW_TRUE; |
9426 | |
|
9427 | 0 | return LXW_NO_ERROR; |
9428 | | |
9429 | 0 | mem_error: |
9430 | 0 | free(rule_obj); |
9431 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
9432 | |
|
9433 | 0 | } |
9434 | | |
9435 | | /* |
9436 | | * Add an Excel table to the worksheet. |
9437 | | */ |
9438 | | lxw_error |
9439 | | worksheet_add_table(lxw_worksheet *self, lxw_row_t first_row, |
9440 | | lxw_col_t first_col, lxw_row_t last_row, |
9441 | | lxw_col_t last_col, lxw_table_options *user_options) |
9442 | 0 | { |
9443 | 0 | lxw_row_t tmp_row; |
9444 | 0 | lxw_col_t tmp_col; |
9445 | 0 | lxw_col_t num_cols; |
9446 | 0 | lxw_error err; |
9447 | 0 | lxw_table_obj *table_obj; |
9448 | 0 | lxw_table_column **columns; |
9449 | |
|
9450 | 0 | if (self->optimize) { |
9451 | 0 | LXW_WARN_FORMAT("worksheet_add_table(): " |
9452 | 0 | "worksheet tables aren't supported in " |
9453 | 0 | "'constant_memory' mode"); |
9454 | 0 | return LXW_ERROR_FEATURE_NOT_SUPPORTED; |
9455 | 0 | } |
9456 | | |
9457 | | /* Swap last row/col with first row/col as necessary */ |
9458 | 0 | if (first_row > last_row) { |
9459 | 0 | tmp_row = last_row; |
9460 | 0 | last_row = first_row; |
9461 | 0 | first_row = tmp_row; |
9462 | 0 | } |
9463 | 0 | if (first_col > last_col) { |
9464 | 0 | tmp_col = last_col; |
9465 | 0 | last_col = first_col; |
9466 | 0 | first_col = tmp_col; |
9467 | 0 | } |
9468 | | |
9469 | | /* Check that column number is valid and store the max value */ |
9470 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); |
9471 | 0 | if (err) |
9472 | 0 | return err; |
9473 | | |
9474 | 0 | num_cols = last_col - first_col + 1; |
9475 | | |
9476 | | /* Check that there are sufficient data rows. */ |
9477 | 0 | err = _check_table_rows(first_row, last_row, user_options); |
9478 | 0 | if (err) |
9479 | 0 | return err; |
9480 | | |
9481 | | /* Check that the the table name is valid. */ |
9482 | 0 | err = _check_table_name(user_options); |
9483 | 0 | if (err) |
9484 | 0 | return err; |
9485 | | |
9486 | | /* Create a table object to copy from the user options. */ |
9487 | 0 | table_obj = calloc(1, sizeof(lxw_table_obj)); |
9488 | 0 | RETURN_ON_MEM_ERROR(table_obj, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9489 | | |
9490 | 0 | columns = calloc(num_cols, sizeof(lxw_table_column *)); |
9491 | 0 | GOTO_LABEL_ON_MEM_ERROR(columns, error); |
9492 | | |
9493 | 0 | table_obj->columns = columns; |
9494 | 0 | table_obj->num_cols = num_cols; |
9495 | 0 | table_obj->first_row = first_row; |
9496 | 0 | table_obj->first_col = first_col; |
9497 | 0 | table_obj->last_row = last_row; |
9498 | 0 | table_obj->last_col = last_col; |
9499 | |
|
9500 | 0 | err = _set_default_table_columns(table_obj); |
9501 | 0 | if (err) |
9502 | 0 | goto error; |
9503 | | |
9504 | | /* Create the table range. */ |
9505 | 0 | lxw_rowcol_to_range(table_obj->sqref, |
9506 | 0 | first_row, first_col, last_row, last_col); |
9507 | 0 | lxw_rowcol_to_range(table_obj->filter_sqref, |
9508 | 0 | first_row, first_col, last_row, last_col); |
9509 | | |
9510 | | /* Validate and copy user options to an internal object. */ |
9511 | 0 | if (user_options) { |
9512 | |
|
9513 | 0 | _check_and_copy_table_style(table_obj, user_options); |
9514 | |
|
9515 | 0 | table_obj->total_row = user_options->total_row; |
9516 | 0 | table_obj->last_column = user_options->last_column; |
9517 | 0 | table_obj->first_column = user_options->first_column; |
9518 | 0 | table_obj->no_autofilter = user_options->no_autofilter; |
9519 | 0 | table_obj->no_header_row = user_options->no_header_row; |
9520 | 0 | table_obj->no_banded_rows = user_options->no_banded_rows; |
9521 | 0 | table_obj->banded_columns = user_options->banded_columns; |
9522 | |
|
9523 | 0 | if (user_options->no_header_row) |
9524 | 0 | table_obj->no_autofilter = LXW_TRUE; |
9525 | |
|
9526 | 0 | if (user_options->columns) { |
9527 | 0 | err = _set_custom_table_columns(table_obj, user_options); |
9528 | 0 | if (err) |
9529 | 0 | goto error; |
9530 | 0 | } |
9531 | | |
9532 | 0 | if (user_options->total_row) { |
9533 | 0 | lxw_rowcol_to_range(table_obj->filter_sqref, |
9534 | 0 | first_row, first_col, last_row - 1, last_col); |
9535 | 0 | } |
9536 | |
|
9537 | 0 | if (user_options->name) { |
9538 | 0 | table_obj->name = lxw_strdup(user_options->name); |
9539 | 0 | if (!table_obj->name) { |
9540 | 0 | err = LXW_ERROR_MEMORY_MALLOC_FAILED; |
9541 | 0 | goto error; |
9542 | 0 | } |
9543 | 0 | } |
9544 | 0 | } |
9545 | | |
9546 | 0 | _write_table_column_data(self, table_obj); |
9547 | |
|
9548 | 0 | STAILQ_INSERT_TAIL(self->table_objs, table_obj, list_pointers); |
9549 | 0 | self->table_count++; |
9550 | |
|
9551 | 0 | return LXW_NO_ERROR; |
9552 | | |
9553 | 0 | error: |
9554 | 0 | _free_worksheet_table(table_obj); |
9555 | 0 | return err; |
9556 | |
|
9557 | 0 | } |
9558 | | |
9559 | | /* |
9560 | | * Set this worksheet as a selected worksheet, i.e. the worksheet has its tab |
9561 | | * highlighted. |
9562 | | */ |
9563 | | void |
9564 | | worksheet_select(lxw_worksheet *self) |
9565 | 0 | { |
9566 | 0 | self->selected = LXW_TRUE; |
9567 | | |
9568 | | /* Selected worksheet can't be hidden. */ |
9569 | 0 | self->hidden = LXW_FALSE; |
9570 | 0 | } |
9571 | | |
9572 | | /* |
9573 | | * Set this worksheet as the active worksheet, i.e. the worksheet that is |
9574 | | * displayed when the workbook is opened. Also set it as selected. |
9575 | | */ |
9576 | | void |
9577 | | worksheet_activate(lxw_worksheet *self) |
9578 | 0 | { |
9579 | 0 | self->selected = LXW_TRUE; |
9580 | 0 | self->active = LXW_TRUE; |
9581 | | |
9582 | | /* Active worksheet can't be hidden. */ |
9583 | 0 | self->hidden = LXW_FALSE; |
9584 | |
|
9585 | 0 | *self->active_sheet = self->index; |
9586 | 0 | } |
9587 | | |
9588 | | /* |
9589 | | * Set this worksheet as the first visible sheet. This is necessary |
9590 | | * when there are a large number of worksheets and the activated |
9591 | | * worksheet is not visible on the screen. |
9592 | | */ |
9593 | | void |
9594 | | worksheet_set_first_sheet(lxw_worksheet *self) |
9595 | 0 | { |
9596 | | /* Active worksheet can't be hidden. */ |
9597 | 0 | self->hidden = LXW_FALSE; |
9598 | |
|
9599 | 0 | *self->first_sheet = self->index; |
9600 | 0 | } |
9601 | | |
9602 | | /* |
9603 | | * Hide this worksheet. |
9604 | | */ |
9605 | | void |
9606 | | worksheet_hide(lxw_worksheet *self) |
9607 | 0 | { |
9608 | 0 | self->hidden = LXW_TRUE; |
9609 | | |
9610 | | /* A hidden worksheet shouldn't be active or selected. */ |
9611 | 0 | self->selected = LXW_FALSE; |
9612 | | |
9613 | | /* If this is active_sheet or first_sheet reset the workbook value. */ |
9614 | 0 | if (*self->first_sheet == self->index) |
9615 | 0 | *self->first_sheet = 0; |
9616 | |
|
9617 | 0 | if (*self->active_sheet == self->index) |
9618 | 0 | *self->active_sheet = 0; |
9619 | 0 | } |
9620 | | |
9621 | | /* |
9622 | | * Set which cell or cells are selected in a worksheet. |
9623 | | */ |
9624 | | lxw_error |
9625 | | worksheet_set_selection(lxw_worksheet *self, |
9626 | | lxw_row_t first_row, lxw_col_t first_col, |
9627 | | lxw_row_t last_row, lxw_col_t last_col) |
9628 | 0 | { |
9629 | 0 | lxw_selection *selection; |
9630 | 0 | lxw_row_t tmp_row; |
9631 | 0 | lxw_col_t tmp_col; |
9632 | 0 | lxw_error err; |
9633 | 0 | char active_cell[LXW_MAX_CELL_RANGE_LENGTH]; |
9634 | 0 | char sqref[LXW_MAX_CELL_RANGE_LENGTH]; |
9635 | | |
9636 | | /* Only allow selection to be set once to avoid freeing/re-creating it. */ |
9637 | 0 | if (!STAILQ_EMPTY(self->selections)) |
9638 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9639 | | |
9640 | | /* Excel doesn't set a selection for cell A1 since it is the default. */ |
9641 | 0 | if (first_row == 0 && first_col == 0 && last_row == 0 && last_col == 0) |
9642 | 0 | return LXW_NO_ERROR; |
9643 | | |
9644 | 0 | selection = calloc(1, sizeof(lxw_selection)); |
9645 | 0 | RETURN_ON_MEM_ERROR(selection, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9646 | | |
9647 | | /* Check that row and col are valid without storing. */ |
9648 | 0 | err = _check_dimensions(self, first_row, first_col, LXW_TRUE, LXW_TRUE); |
9649 | 0 | if (err) { |
9650 | 0 | free(selection); |
9651 | 0 | return err; |
9652 | 0 | } |
9653 | | |
9654 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); |
9655 | 0 | if (err) { |
9656 | 0 | free(selection); |
9657 | 0 | return err; |
9658 | 0 | } |
9659 | | |
9660 | | /* Set the cell range selection. Do this before swapping max/min to */ |
9661 | | /* allow the selection direction to be reversed. */ |
9662 | 0 | lxw_rowcol_to_cell(active_cell, first_row, first_col); |
9663 | | |
9664 | | /* Swap last row/col for first row/col if necessary. */ |
9665 | 0 | if (first_row > last_row) { |
9666 | 0 | tmp_row = first_row; |
9667 | 0 | first_row = last_row; |
9668 | 0 | last_row = tmp_row; |
9669 | 0 | } |
9670 | |
|
9671 | 0 | if (first_col > last_col) { |
9672 | 0 | tmp_col = first_col; |
9673 | 0 | first_col = last_col; |
9674 | 0 | last_col = tmp_col; |
9675 | 0 | } |
9676 | | |
9677 | | /* If the first and last cell are the same write a single cell. */ |
9678 | 0 | if ((first_row == last_row) && (first_col == last_col)) |
9679 | 0 | lxw_rowcol_to_cell(sqref, first_row, first_col); |
9680 | 0 | else |
9681 | 0 | lxw_rowcol_to_range(sqref, first_row, first_col, last_row, last_col); |
9682 | |
|
9683 | 0 | lxw_strcpy(selection->pane, ""); |
9684 | 0 | lxw_strcpy(selection->active_cell, active_cell); |
9685 | 0 | lxw_strcpy(selection->sqref, sqref); |
9686 | |
|
9687 | 0 | STAILQ_INSERT_TAIL(self->selections, selection, list_pointers); |
9688 | |
|
9689 | 0 | return LXW_NO_ERROR; |
9690 | 0 | } |
9691 | | |
9692 | | /* |
9693 | | * Set the first visible cell at the top left of the worksheet. |
9694 | | */ |
9695 | | void |
9696 | | worksheet_set_top_left_cell(lxw_worksheet *self, lxw_row_t row, lxw_col_t col) |
9697 | 0 | { |
9698 | 0 | if (row == 0 && col == 0) |
9699 | 0 | return; |
9700 | | |
9701 | 0 | lxw_rowcol_to_cell(self->top_left_cell, row, col); |
9702 | 0 | } |
9703 | | |
9704 | | /* |
9705 | | * Set panes and mark them as frozen. With extra options. |
9706 | | */ |
9707 | | void |
9708 | | worksheet_freeze_panes_opt(lxw_worksheet *self, |
9709 | | lxw_row_t first_row, lxw_col_t first_col, |
9710 | | lxw_row_t top_row, lxw_col_t left_col, |
9711 | | uint8_t type) |
9712 | 0 | { |
9713 | 0 | self->panes.first_row = first_row; |
9714 | 0 | self->panes.first_col = first_col; |
9715 | 0 | self->panes.top_row = top_row; |
9716 | 0 | self->panes.left_col = left_col; |
9717 | 0 | self->panes.x_split = 0.0; |
9718 | 0 | self->panes.y_split = 0.0; |
9719 | |
|
9720 | 0 | if (type) |
9721 | 0 | self->panes.type = FREEZE_SPLIT_PANES; |
9722 | 0 | else |
9723 | 0 | self->panes.type = FREEZE_PANES; |
9724 | 0 | } |
9725 | | |
9726 | | /* |
9727 | | * Set panes and mark them as frozen. |
9728 | | */ |
9729 | | void |
9730 | | worksheet_freeze_panes(lxw_worksheet *self, |
9731 | | lxw_row_t first_row, lxw_col_t first_col) |
9732 | 0 | { |
9733 | 0 | worksheet_freeze_panes_opt(self, first_row, first_col, |
9734 | 0 | first_row, first_col, 0); |
9735 | 0 | } |
9736 | | |
9737 | | /* |
9738 | | * Set panes and mark them as split.With extra options. |
9739 | | */ |
9740 | | void |
9741 | | worksheet_split_panes_opt(lxw_worksheet *self, |
9742 | | double y_split, double x_split, |
9743 | | lxw_row_t top_row, lxw_col_t left_col) |
9744 | 0 | { |
9745 | 0 | self->panes.first_row = 0; |
9746 | 0 | self->panes.first_col = 0; |
9747 | 0 | self->panes.top_row = top_row; |
9748 | 0 | self->panes.left_col = left_col; |
9749 | 0 | self->panes.x_split = x_split; |
9750 | 0 | self->panes.y_split = y_split; |
9751 | 0 | self->panes.type = SPLIT_PANES; |
9752 | 0 | } |
9753 | | |
9754 | | /* |
9755 | | * Set panes and mark them as split. |
9756 | | */ |
9757 | | void |
9758 | | worksheet_split_panes(lxw_worksheet *self, double y_split, double x_split) |
9759 | 0 | { |
9760 | 0 | worksheet_split_panes_opt(self, y_split, x_split, 0, 0); |
9761 | 0 | } |
9762 | | |
9763 | | /* |
9764 | | * Set the page orientation as portrait. |
9765 | | */ |
9766 | | void |
9767 | | worksheet_set_portrait(lxw_worksheet *self) |
9768 | 0 | { |
9769 | 0 | self->orientation = LXW_PORTRAIT; |
9770 | 0 | self->page_setup_changed = LXW_TRUE; |
9771 | 0 | } |
9772 | | |
9773 | | /* |
9774 | | * Set the page orientation as landscape. |
9775 | | */ |
9776 | | void |
9777 | | worksheet_set_landscape(lxw_worksheet *self) |
9778 | 0 | { |
9779 | 0 | self->orientation = LXW_LANDSCAPE; |
9780 | 0 | self->page_setup_changed = LXW_TRUE; |
9781 | 0 | } |
9782 | | |
9783 | | /* |
9784 | | * Set the page view mode for Mac Excel. |
9785 | | */ |
9786 | | void |
9787 | | worksheet_set_page_view(lxw_worksheet *self) |
9788 | 0 | { |
9789 | 0 | self->page_view = LXW_TRUE; |
9790 | 0 | } |
9791 | | |
9792 | | /* |
9793 | | * Set the paper type. Example. 1 = US Letter, 9 = A4 |
9794 | | */ |
9795 | | void |
9796 | | worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size) |
9797 | 0 | { |
9798 | 0 | if (paper_size > 118) { |
9799 | 0 | LXW_WARN_FORMAT1("worksheet_set_paper(): invalid paper size: %d. " |
9800 | 0 | "Valid range is 0-118", paper_size); |
9801 | 0 | return; |
9802 | 0 | } |
9803 | | |
9804 | 0 | self->paper_size = paper_size; |
9805 | 0 | self->page_setup_changed = LXW_TRUE; |
9806 | 0 | } |
9807 | | |
9808 | | /* |
9809 | | * Set the order in which pages are printed. |
9810 | | */ |
9811 | | void |
9812 | | worksheet_print_across(lxw_worksheet *self) |
9813 | 0 | { |
9814 | 0 | self->page_order = LXW_PRINT_ACROSS; |
9815 | 0 | self->page_setup_changed = LXW_TRUE; |
9816 | 0 | } |
9817 | | |
9818 | | /* |
9819 | | * Set all the page margins in inches. |
9820 | | */ |
9821 | | void |
9822 | | worksheet_set_margins(lxw_worksheet *self, double left, double right, |
9823 | | double top, double bottom) |
9824 | 0 | { |
9825 | |
|
9826 | 0 | if (left >= 0) |
9827 | 0 | self->margin_left = left; |
9828 | |
|
9829 | 0 | if (right >= 0) |
9830 | 0 | self->margin_right = right; |
9831 | |
|
9832 | 0 | if (top >= 0) |
9833 | 0 | self->margin_top = top; |
9834 | |
|
9835 | 0 | if (bottom >= 0) |
9836 | 0 | self->margin_bottom = bottom; |
9837 | 0 | } |
9838 | | |
9839 | | /* |
9840 | | * Set the page header caption and options. |
9841 | | */ |
9842 | | lxw_error |
9843 | | worksheet_set_header_opt(lxw_worksheet *self, const char *string, |
9844 | | lxw_header_footer_options *options) |
9845 | 0 | { |
9846 | 0 | lxw_error err; |
9847 | 0 | char *tmp_header; |
9848 | 0 | char *found_string; |
9849 | 0 | char *offset_string; |
9850 | 0 | uint8_t placeholder_count = 0; |
9851 | 0 | uint8_t image_count = 0; |
9852 | |
|
9853 | 0 | if (!string) { |
9854 | 0 | LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " |
9855 | 0 | "header/footer string cannot be NULL."); |
9856 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
9857 | 0 | } |
9858 | | |
9859 | 0 | if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { |
9860 | 0 | LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " |
9861 | 0 | "header/footer string exceeds Excel's limit of " |
9862 | 0 | "255 characters."); |
9863 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
9864 | 0 | } |
9865 | | |
9866 | 0 | tmp_header = lxw_strdup(string); |
9867 | 0 | RETURN_ON_MEM_ERROR(tmp_header, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9868 | | |
9869 | | /* Replace &[Picture] with &G which is used internally by Excel. */ |
9870 | 0 | while ((found_string = strstr(tmp_header, "&[Picture]"))) { |
9871 | 0 | found_string++; |
9872 | 0 | *found_string = 'G'; |
9873 | |
|
9874 | 0 | do { |
9875 | 0 | found_string++; |
9876 | 0 | offset_string = found_string + sizeof("Picture"); |
9877 | 0 | *found_string = *offset_string; |
9878 | 0 | } while (*offset_string); |
9879 | 0 | } |
9880 | | |
9881 | | /* Count &G placeholders and ensure there are sufficient images. */ |
9882 | 0 | found_string = tmp_header; |
9883 | 0 | while (*found_string) { |
9884 | 0 | if (*found_string == '&' && *(found_string + 1) == 'G') |
9885 | 0 | placeholder_count++; |
9886 | 0 | found_string++; |
9887 | 0 | } |
9888 | |
|
9889 | 0 | if (placeholder_count > 0 && !options) { |
9890 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
9891 | 0 | "the number of &G/&[Picture] placeholders in option " |
9892 | 0 | "string \"%s\" does not match the number of supplied " |
9893 | 0 | "images.", string); |
9894 | |
|
9895 | 0 | free(tmp_header); |
9896 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9897 | 0 | } |
9898 | | |
9899 | | /* Free any previous header string so we can overwrite it. */ |
9900 | 0 | free(self->header); |
9901 | 0 | self->header = NULL; |
9902 | |
|
9903 | 0 | if (options) { |
9904 | | /* Ensure there are enough images to match the placeholders. There is |
9905 | | * a potential bug where there are sufficient images but in the wrong |
9906 | | * positions but we don't currently try to deal with that.*/ |
9907 | 0 | if (options->image_left) |
9908 | 0 | image_count++; |
9909 | 0 | if (options->image_center) |
9910 | 0 | image_count++; |
9911 | 0 | if (options->image_right) |
9912 | 0 | image_count++; |
9913 | |
|
9914 | 0 | if (placeholder_count != image_count) { |
9915 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
9916 | 0 | "the number of &G/&[Picture] placeholders in option " |
9917 | 0 | "string \"%s\" does not match the number of supplied " |
9918 | 0 | "images.", string); |
9919 | |
|
9920 | 0 | free(tmp_header); |
9921 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
9922 | 0 | } |
9923 | | |
9924 | | /* Free any existing header image objects. */ |
9925 | 0 | _free_object_properties(self->header_left_object_props); |
9926 | 0 | _free_object_properties(self->header_center_object_props); |
9927 | 0 | _free_object_properties(self->header_right_object_props); |
9928 | |
|
9929 | 0 | if (options->margin > 0.0) |
9930 | 0 | self->margin_header = options->margin; |
9931 | |
|
9932 | 0 | err = _worksheet_set_header_footer_image(self, |
9933 | 0 | options->image_left, |
9934 | 0 | HEADER_LEFT); |
9935 | 0 | if (err) { |
9936 | 0 | free(tmp_header); |
9937 | 0 | return err; |
9938 | 0 | } |
9939 | | |
9940 | 0 | err = _worksheet_set_header_footer_image(self, |
9941 | 0 | options->image_center, |
9942 | 0 | HEADER_CENTER); |
9943 | 0 | if (err) { |
9944 | 0 | free(tmp_header); |
9945 | 0 | return err; |
9946 | 0 | } |
9947 | | |
9948 | 0 | err = _worksheet_set_header_footer_image(self, |
9949 | 0 | options->image_right, |
9950 | 0 | HEADER_RIGHT); |
9951 | 0 | if (err) { |
9952 | 0 | free(tmp_header); |
9953 | 0 | return err; |
9954 | 0 | } |
9955 | 0 | } |
9956 | | |
9957 | 0 | self->header = tmp_header; |
9958 | 0 | self->header_footer_changed = LXW_TRUE; |
9959 | |
|
9960 | 0 | return LXW_NO_ERROR; |
9961 | 0 | } |
9962 | | |
9963 | | /* |
9964 | | * Set the page footer caption and options. |
9965 | | */ |
9966 | | lxw_error |
9967 | | worksheet_set_footer_opt(lxw_worksheet *self, const char *string, |
9968 | | lxw_header_footer_options *options) |
9969 | 0 | { |
9970 | 0 | lxw_error err; |
9971 | 0 | char *tmp_footer; |
9972 | 0 | char *found_string; |
9973 | 0 | char *offset_string; |
9974 | 0 | uint8_t placeholder_count = 0; |
9975 | 0 | uint8_t image_count = 0; |
9976 | |
|
9977 | 0 | if (!string) { |
9978 | 0 | LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " |
9979 | 0 | "header/footer string cannot be NULL."); |
9980 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
9981 | 0 | } |
9982 | | |
9983 | 0 | if (lxw_utf8_strlen(string) > LXW_HEADER_FOOTER_MAX) { |
9984 | 0 | LXW_WARN_FORMAT("worksheet_set_header_opt/footer_opt(): " |
9985 | 0 | "header/footer string exceeds Excel's limit of " |
9986 | 0 | "255 characters."); |
9987 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
9988 | 0 | } |
9989 | | |
9990 | 0 | tmp_footer = lxw_strdup(string); |
9991 | 0 | RETURN_ON_MEM_ERROR(tmp_footer, LXW_ERROR_MEMORY_MALLOC_FAILED); |
9992 | | |
9993 | | /* Replace &[Picture] with &G which is used internally by Excel. */ |
9994 | 0 | while ((found_string = strstr(tmp_footer, "&[Picture]"))) { |
9995 | 0 | found_string++; |
9996 | 0 | *found_string = 'G'; |
9997 | |
|
9998 | 0 | do { |
9999 | 0 | found_string++; |
10000 | 0 | offset_string = found_string + sizeof("Picture"); |
10001 | 0 | *found_string = *offset_string; |
10002 | 0 | } while (*offset_string); |
10003 | 0 | } |
10004 | | |
10005 | | /* Count &G placeholders and ensure there are sufficient images. */ |
10006 | 0 | found_string = tmp_footer; |
10007 | 0 | while (*found_string) { |
10008 | 0 | if (*found_string == '&' && *(found_string + 1) == 'G') |
10009 | 0 | placeholder_count++; |
10010 | 0 | found_string++; |
10011 | 0 | } |
10012 | |
|
10013 | 0 | if (placeholder_count > 0 && !options) { |
10014 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
10015 | 0 | "the number of &G/&[Picture] placeholders in option " |
10016 | 0 | "string \"%s\" does not match the number of supplied " |
10017 | 0 | "images.", string); |
10018 | |
|
10019 | 0 | free(tmp_footer); |
10020 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10021 | 0 | } |
10022 | | |
10023 | | /* Free any previous footer string so we can overwrite it. */ |
10024 | 0 | free(self->footer); |
10025 | 0 | self->footer = NULL; |
10026 | |
|
10027 | 0 | if (options) { |
10028 | | /* Ensure there are enough images to match the placeholders. There is |
10029 | | * a potential bug where there are sufficient images but in the wrong |
10030 | | * positions but we don't currently try to deal with that.*/ |
10031 | 0 | if (options->image_left) |
10032 | 0 | image_count++; |
10033 | 0 | if (options->image_center) |
10034 | 0 | image_count++; |
10035 | 0 | if (options->image_right) |
10036 | 0 | image_count++; |
10037 | |
|
10038 | 0 | if (placeholder_count != image_count) { |
10039 | 0 | LXW_WARN_FORMAT1("worksheet_set_header_opt/footer_opt(): " |
10040 | 0 | "the number of &G/&[Picture] placeholders in option " |
10041 | 0 | "string \"%s\" does not match the number of supplied " |
10042 | 0 | "images.", string); |
10043 | |
|
10044 | 0 | free(tmp_footer); |
10045 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10046 | 0 | } |
10047 | | |
10048 | | /* Free any existing footer image objects. */ |
10049 | 0 | _free_object_properties(self->footer_left_object_props); |
10050 | 0 | _free_object_properties(self->footer_center_object_props); |
10051 | 0 | _free_object_properties(self->footer_right_object_props); |
10052 | |
|
10053 | 0 | if (options->margin > 0.0) |
10054 | 0 | self->margin_footer = options->margin; |
10055 | |
|
10056 | 0 | err = _worksheet_set_header_footer_image(self, |
10057 | 0 | options->image_left, |
10058 | 0 | FOOTER_LEFT); |
10059 | 0 | if (err) { |
10060 | 0 | free(tmp_footer); |
10061 | 0 | return err; |
10062 | 0 | } |
10063 | | |
10064 | 0 | err = _worksheet_set_header_footer_image(self, |
10065 | 0 | options->image_center, |
10066 | 0 | FOOTER_CENTER); |
10067 | 0 | if (err) { |
10068 | 0 | free(tmp_footer); |
10069 | 0 | return err; |
10070 | 0 | } |
10071 | | |
10072 | 0 | err = _worksheet_set_header_footer_image(self, |
10073 | 0 | options->image_right, |
10074 | 0 | FOOTER_RIGHT); |
10075 | 0 | if (err) { |
10076 | 0 | free(tmp_footer); |
10077 | 0 | return err; |
10078 | 0 | } |
10079 | 0 | } |
10080 | | |
10081 | 0 | self->footer = tmp_footer; |
10082 | 0 | self->header_footer_changed = LXW_TRUE; |
10083 | |
|
10084 | 0 | return LXW_NO_ERROR; |
10085 | 0 | } |
10086 | | |
10087 | | /* |
10088 | | * Set the page header caption. |
10089 | | */ |
10090 | | lxw_error |
10091 | | worksheet_set_header(lxw_worksheet *self, const char *string) |
10092 | 0 | { |
10093 | 0 | return worksheet_set_header_opt(self, string, NULL); |
10094 | 0 | } |
10095 | | |
10096 | | /* |
10097 | | * Set the page footer caption. |
10098 | | */ |
10099 | | lxw_error |
10100 | | worksheet_set_footer(lxw_worksheet *self, const char *string) |
10101 | 0 | { |
10102 | 0 | return worksheet_set_footer_opt(self, string, NULL); |
10103 | 0 | } |
10104 | | |
10105 | | /* |
10106 | | * Set the option to show/hide gridlines on the screen and the printed page. |
10107 | | */ |
10108 | | void |
10109 | | worksheet_gridlines(lxw_worksheet *self, uint8_t option) |
10110 | 0 | { |
10111 | 0 | if (option == LXW_HIDE_ALL_GRIDLINES) { |
10112 | 0 | self->print_gridlines = 0; |
10113 | 0 | self->screen_gridlines = 0; |
10114 | 0 | } |
10115 | |
|
10116 | 0 | if (option & LXW_SHOW_SCREEN_GRIDLINES) { |
10117 | 0 | self->screen_gridlines = 1; |
10118 | 0 | } |
10119 | |
|
10120 | 0 | if (option & LXW_SHOW_PRINT_GRIDLINES) { |
10121 | 0 | self->print_gridlines = 1; |
10122 | 0 | self->print_options_changed = 1; |
10123 | 0 | } |
10124 | 0 | } |
10125 | | |
10126 | | /* |
10127 | | * Center the page horizontally. |
10128 | | */ |
10129 | | void |
10130 | | worksheet_center_horizontally(lxw_worksheet *self) |
10131 | 0 | { |
10132 | 0 | self->print_options_changed = 1; |
10133 | 0 | self->hcenter = 1; |
10134 | 0 | } |
10135 | | |
10136 | | /* |
10137 | | * Center the page horizontally. |
10138 | | */ |
10139 | | void |
10140 | | worksheet_center_vertically(lxw_worksheet *self) |
10141 | 0 | { |
10142 | 0 | self->print_options_changed = 1; |
10143 | 0 | self->vcenter = 1; |
10144 | 0 | } |
10145 | | |
10146 | | /* |
10147 | | * Set the option to print the row and column headers on the printed page. |
10148 | | */ |
10149 | | void |
10150 | | worksheet_print_row_col_headers(lxw_worksheet *self) |
10151 | 0 | { |
10152 | 0 | self->print_headers = 1; |
10153 | 0 | self->print_options_changed = 1; |
10154 | 0 | } |
10155 | | |
10156 | | /* |
10157 | | * Set the rows to repeat at the top of each printed page. |
10158 | | */ |
10159 | | lxw_error |
10160 | | worksheet_repeat_rows(lxw_worksheet *self, lxw_row_t first_row, |
10161 | | lxw_row_t last_row) |
10162 | 0 | { |
10163 | 0 | lxw_row_t tmp_row; |
10164 | 0 | lxw_error err; |
10165 | |
|
10166 | 0 | if (first_row > last_row) { |
10167 | 0 | tmp_row = last_row; |
10168 | 0 | last_row = first_row; |
10169 | 0 | first_row = tmp_row; |
10170 | 0 | } |
10171 | |
|
10172 | 0 | err = _check_dimensions(self, last_row, 0, LXW_IGNORE, LXW_IGNORE); |
10173 | 0 | if (err) |
10174 | 0 | return err; |
10175 | | |
10176 | 0 | self->repeat_rows.in_use = LXW_TRUE; |
10177 | 0 | self->repeat_rows.first_row = first_row; |
10178 | 0 | self->repeat_rows.last_row = last_row; |
10179 | |
|
10180 | 0 | return LXW_NO_ERROR; |
10181 | 0 | } |
10182 | | |
10183 | | /* |
10184 | | * Set the columns to repeat at the left hand side of each printed page. |
10185 | | */ |
10186 | | lxw_error |
10187 | | worksheet_repeat_columns(lxw_worksheet *self, lxw_col_t first_col, |
10188 | | lxw_col_t last_col) |
10189 | 0 | { |
10190 | 0 | lxw_col_t tmp_col; |
10191 | 0 | lxw_error err; |
10192 | |
|
10193 | 0 | if (first_col > last_col) { |
10194 | 0 | tmp_col = last_col; |
10195 | 0 | last_col = first_col; |
10196 | 0 | first_col = tmp_col; |
10197 | 0 | } |
10198 | |
|
10199 | 0 | err = _check_dimensions(self, last_col, 0, LXW_IGNORE, LXW_IGNORE); |
10200 | 0 | if (err) |
10201 | 0 | return err; |
10202 | | |
10203 | 0 | self->repeat_cols.in_use = LXW_TRUE; |
10204 | 0 | self->repeat_cols.first_col = first_col; |
10205 | 0 | self->repeat_cols.last_col = last_col; |
10206 | |
|
10207 | 0 | return LXW_NO_ERROR; |
10208 | 0 | } |
10209 | | |
10210 | | /* |
10211 | | * Set the print area in the current worksheet. |
10212 | | */ |
10213 | | lxw_error |
10214 | | worksheet_print_area(lxw_worksheet *self, lxw_row_t first_row, |
10215 | | lxw_col_t first_col, lxw_row_t last_row, |
10216 | | lxw_col_t last_col) |
10217 | 0 | { |
10218 | 0 | lxw_row_t tmp_row; |
10219 | 0 | lxw_col_t tmp_col; |
10220 | 0 | lxw_error err; |
10221 | |
|
10222 | 0 | if (first_row > last_row) { |
10223 | 0 | tmp_row = last_row; |
10224 | 0 | last_row = first_row; |
10225 | 0 | first_row = tmp_row; |
10226 | 0 | } |
10227 | |
|
10228 | 0 | if (first_col > last_col) { |
10229 | 0 | tmp_col = last_col; |
10230 | 0 | last_col = first_col; |
10231 | 0 | first_col = tmp_col; |
10232 | 0 | } |
10233 | |
|
10234 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_IGNORE, LXW_IGNORE); |
10235 | 0 | if (err) |
10236 | 0 | return err; |
10237 | | |
10238 | | /* Ignore max area since it is the same as no print area in Excel. */ |
10239 | 0 | if (first_row == 0 && first_col == 0 && last_row == LXW_ROW_MAX - 1 |
10240 | 0 | && last_col == LXW_COL_MAX - 1) { |
10241 | 0 | return LXW_NO_ERROR; |
10242 | 0 | } |
10243 | | |
10244 | 0 | self->print_area.in_use = LXW_TRUE; |
10245 | 0 | self->print_area.first_row = first_row; |
10246 | 0 | self->print_area.last_row = last_row; |
10247 | 0 | self->print_area.first_col = first_col; |
10248 | 0 | self->print_area.last_col = last_col; |
10249 | |
|
10250 | 0 | return LXW_NO_ERROR; |
10251 | 0 | } |
10252 | | |
10253 | | /* Store the vertical and horizontal number of pages that will define the |
10254 | | * maximum area printed. |
10255 | | */ |
10256 | | void |
10257 | | worksheet_fit_to_pages(lxw_worksheet *self, uint16_t width, uint16_t height) |
10258 | 0 | { |
10259 | 0 | self->fit_page = 1; |
10260 | 0 | self->fit_width = width; |
10261 | 0 | self->fit_height = height; |
10262 | 0 | self->page_setup_changed = 1; |
10263 | 0 | } |
10264 | | |
10265 | | /* |
10266 | | * Set the start page number. |
10267 | | */ |
10268 | | void |
10269 | | worksheet_set_start_page(lxw_worksheet *self, uint16_t start_page) |
10270 | 0 | { |
10271 | 0 | self->page_start = start_page; |
10272 | 0 | } |
10273 | | |
10274 | | /* |
10275 | | * Set the scale factor for the printed page. |
10276 | | */ |
10277 | | void |
10278 | | worksheet_set_print_scale(lxw_worksheet *self, uint16_t scale) |
10279 | 0 | { |
10280 | | /* Confine the scale to Excel"s range */ |
10281 | 0 | if (scale < 10 || scale > 400) |
10282 | 0 | return; |
10283 | | |
10284 | | /* Turn off "fit to page" option. */ |
10285 | 0 | self->fit_page = LXW_FALSE; |
10286 | |
|
10287 | 0 | self->print_scale = scale; |
10288 | 0 | self->page_setup_changed = LXW_TRUE; |
10289 | 0 | } |
10290 | | |
10291 | | /* |
10292 | | * Set the print in black and white option. |
10293 | | */ |
10294 | | void |
10295 | | worksheet_print_black_and_white(lxw_worksheet *self) |
10296 | 0 | { |
10297 | 0 | self->black_white = LXW_TRUE; |
10298 | 0 | self->page_setup_changed = LXW_TRUE; |
10299 | 0 | } |
10300 | | |
10301 | | /* |
10302 | | * Store the horizontal page breaks on a worksheet. |
10303 | | */ |
10304 | | lxw_error |
10305 | | worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[]) |
10306 | 0 | { |
10307 | 0 | uint16_t count = 0; |
10308 | |
|
10309 | 0 | if (hbreaks == NULL) |
10310 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10311 | | |
10312 | 0 | while (hbreaks[count]) |
10313 | 0 | count++; |
10314 | | |
10315 | | /* The Excel 2007 specification says that the maximum number of page |
10316 | | * breaks is 1026. However, in practice it is actually 1023. */ |
10317 | 0 | if (count > LXW_BREAKS_MAX) |
10318 | 0 | count = LXW_BREAKS_MAX; |
10319 | |
|
10320 | 0 | self->hbreaks = calloc(count, sizeof(lxw_row_t)); |
10321 | 0 | RETURN_ON_MEM_ERROR(self->hbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED); |
10322 | 0 | memcpy(self->hbreaks, hbreaks, count * sizeof(lxw_row_t)); |
10323 | 0 | self->hbreaks_count = count; |
10324 | |
|
10325 | 0 | return LXW_NO_ERROR; |
10326 | 0 | } |
10327 | | |
10328 | | /* |
10329 | | * Store the vertical page breaks on a worksheet. |
10330 | | */ |
10331 | | lxw_error |
10332 | | worksheet_set_v_pagebreaks(lxw_worksheet *self, lxw_col_t vbreaks[]) |
10333 | 0 | { |
10334 | 0 | uint16_t count = 0; |
10335 | |
|
10336 | 0 | if (vbreaks == NULL) |
10337 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10338 | | |
10339 | 0 | while (vbreaks[count]) |
10340 | 0 | count++; |
10341 | | |
10342 | | /* The Excel 2007 specification says that the maximum number of page |
10343 | | * breaks is 1026. However, in practice it is actually 1023. */ |
10344 | 0 | if (count > LXW_BREAKS_MAX) |
10345 | 0 | count = LXW_BREAKS_MAX; |
10346 | |
|
10347 | 0 | self->vbreaks = calloc(count, sizeof(lxw_col_t)); |
10348 | 0 | RETURN_ON_MEM_ERROR(self->vbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED); |
10349 | 0 | memcpy(self->vbreaks, vbreaks, count * sizeof(lxw_col_t)); |
10350 | 0 | self->vbreaks_count = count; |
10351 | |
|
10352 | 0 | return LXW_NO_ERROR; |
10353 | 0 | } |
10354 | | |
10355 | | /* |
10356 | | * Set the worksheet zoom factor. |
10357 | | */ |
10358 | | void |
10359 | | worksheet_set_zoom(lxw_worksheet *self, uint16_t scale) |
10360 | 0 | { |
10361 | | /* Confine the scale to Excel"s range */ |
10362 | 0 | if (scale < 10 || scale > 400) { |
10363 | 0 | LXW_WARN("worksheet_set_zoom(): " |
10364 | 0 | "Zoom factor scale outside range: 10 <= zoom <= 400."); |
10365 | 0 | return; |
10366 | 0 | } |
10367 | | |
10368 | 0 | self->zoom = scale; |
10369 | 0 | } |
10370 | | |
10371 | | /* |
10372 | | * Hide cell zero values. |
10373 | | */ |
10374 | | void |
10375 | | worksheet_hide_zero(lxw_worksheet *self) |
10376 | 0 | { |
10377 | 0 | self->show_zeros = LXW_FALSE; |
10378 | 0 | } |
10379 | | |
10380 | | /* |
10381 | | * Display the worksheet right to left for some eastern versions of Excel. |
10382 | | */ |
10383 | | void |
10384 | | worksheet_right_to_left(lxw_worksheet *self) |
10385 | 0 | { |
10386 | 0 | self->right_to_left = LXW_TRUE; |
10387 | 0 | } |
10388 | | |
10389 | | /* |
10390 | | * Set the color of the worksheet tab. |
10391 | | */ |
10392 | | void |
10393 | | worksheet_set_tab_color(lxw_worksheet *self, lxw_color_t color) |
10394 | 0 | { |
10395 | 0 | self->tab_color = color; |
10396 | 0 | } |
10397 | | |
10398 | | /* |
10399 | | * Set the worksheet protection flags to prevent modification of worksheet |
10400 | | * objects. |
10401 | | */ |
10402 | | void |
10403 | | worksheet_protect(lxw_worksheet *self, const char *password, |
10404 | | lxw_protection *options) |
10405 | 0 | { |
10406 | 0 | struct lxw_protection_obj *protect = &self->protection; |
10407 | | |
10408 | | /* Copy any user parameters to the internal structure. */ |
10409 | 0 | if (options) { |
10410 | 0 | protect->no_select_locked_cells = options->no_select_locked_cells; |
10411 | 0 | protect->no_select_unlocked_cells = options->no_select_unlocked_cells; |
10412 | 0 | protect->format_cells = options->format_cells; |
10413 | 0 | protect->format_columns = options->format_columns; |
10414 | 0 | protect->format_rows = options->format_rows; |
10415 | 0 | protect->insert_columns = options->insert_columns; |
10416 | 0 | protect->insert_rows = options->insert_rows; |
10417 | 0 | protect->insert_hyperlinks = options->insert_hyperlinks; |
10418 | 0 | protect->delete_columns = options->delete_columns; |
10419 | 0 | protect->delete_rows = options->delete_rows; |
10420 | 0 | protect->sort = options->sort; |
10421 | 0 | protect->autofilter = options->autofilter; |
10422 | 0 | protect->pivot_tables = options->pivot_tables; |
10423 | 0 | protect->scenarios = options->scenarios; |
10424 | 0 | protect->objects = options->objects; |
10425 | 0 | } |
10426 | |
|
10427 | 0 | if (password) { |
10428 | 0 | uint16_t hash = lxw_hash_password(password); |
10429 | 0 | lxw_snprintf(protect->hash, 5, "%X", hash); |
10430 | 0 | } |
10431 | |
|
10432 | 0 | protect->no_sheet = LXW_FALSE; |
10433 | 0 | protect->no_content = LXW_TRUE; |
10434 | 0 | protect->is_configured = LXW_TRUE; |
10435 | 0 | } |
10436 | | |
10437 | | /* |
10438 | | * Set the worksheet properties for outlines and grouping. |
10439 | | */ |
10440 | | void |
10441 | | worksheet_outline_settings(lxw_worksheet *self, |
10442 | | uint8_t visible, |
10443 | | uint8_t symbols_below, |
10444 | | uint8_t symbols_right, uint8_t auto_style) |
10445 | 0 | { |
10446 | 0 | self->outline_on = visible; |
10447 | 0 | self->outline_below = symbols_below; |
10448 | 0 | self->outline_right = symbols_right; |
10449 | 0 | self->outline_style = auto_style; |
10450 | |
|
10451 | 0 | self->outline_changed = LXW_TRUE; |
10452 | 0 | } |
10453 | | |
10454 | | /* |
10455 | | * Set the default row properties |
10456 | | */ |
10457 | | void |
10458 | | worksheet_set_default_row(lxw_worksheet *self, double height, |
10459 | | uint8_t hide_unused_rows) |
10460 | 0 | { |
10461 | 0 | if (height < 0) |
10462 | 0 | height = self->default_row_height; |
10463 | |
|
10464 | 0 | if (height != self->default_row_height) { |
10465 | 0 | self->default_row_height = height; |
10466 | 0 | self->row_size_changed = LXW_TRUE; |
10467 | 0 | } |
10468 | |
|
10469 | 0 | if (hide_unused_rows) |
10470 | 0 | self->default_row_zeroed = LXW_TRUE; |
10471 | |
|
10472 | 0 | self->default_row_set = LXW_TRUE; |
10473 | 0 | } |
10474 | | |
10475 | | /* |
10476 | | * Insert an image with options into the worksheet. |
10477 | | */ |
10478 | | lxw_error |
10479 | | worksheet_insert_image_opt(lxw_worksheet *self, |
10480 | | lxw_row_t row_num, lxw_col_t col_num, |
10481 | | const char *filename, |
10482 | | lxw_image_options *user_options) |
10483 | 0 | { |
10484 | 0 | FILE *image_stream; |
10485 | 0 | const char *description; |
10486 | 0 | lxw_object_properties *object_props; |
10487 | |
|
10488 | 0 | if (!filename) { |
10489 | 0 | LXW_WARN("worksheet_insert_image()/_opt(): " |
10490 | 0 | "filename must be specified."); |
10491 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10492 | 0 | } |
10493 | | |
10494 | | /* Check that the image file exists and can be opened. */ |
10495 | 0 | image_stream = lxw_fopen(filename, "rb"); |
10496 | 0 | if (!image_stream) { |
10497 | 0 | LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " |
10498 | 0 | "file doesn't exist or can't be opened: %s.", |
10499 | 0 | filename); |
10500 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10501 | 0 | } |
10502 | | |
10503 | | /* Use the filename as the default description, like Excel. */ |
10504 | 0 | description = lxw_basename(filename); |
10505 | 0 | if (!description) { |
10506 | 0 | LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): " |
10507 | 0 | "couldn't get basename for file: %s.", filename); |
10508 | 0 | fclose(image_stream); |
10509 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10510 | 0 | } |
10511 | | |
10512 | | /* Create a new object to hold the image properties. */ |
10513 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
10514 | 0 | if (!object_props) { |
10515 | 0 | fclose(image_stream); |
10516 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10517 | 0 | } |
10518 | | |
10519 | 0 | if (user_options) { |
10520 | 0 | object_props->x_offset = user_options->x_offset; |
10521 | 0 | object_props->y_offset = user_options->y_offset; |
10522 | 0 | object_props->x_scale = user_options->x_scale; |
10523 | 0 | object_props->y_scale = user_options->y_scale; |
10524 | 0 | object_props->url = lxw_strdup(user_options->url); |
10525 | 0 | object_props->tip = lxw_strdup(user_options->tip); |
10526 | 0 | object_props->object_position = user_options->object_position; |
10527 | 0 | object_props->decorative = user_options->decorative; |
10528 | |
|
10529 | 0 | if (user_options->description) |
10530 | 0 | description = user_options->description; |
10531 | 0 | } |
10532 | | |
10533 | | /* Copy other options or set defaults. */ |
10534 | 0 | object_props->filename = lxw_strdup(filename); |
10535 | 0 | object_props->description = lxw_strdup(description); |
10536 | 0 | object_props->stream = image_stream; |
10537 | 0 | object_props->row = row_num; |
10538 | 0 | object_props->col = col_num; |
10539 | |
|
10540 | 0 | if (object_props->x_scale == 0.0) |
10541 | 0 | object_props->x_scale = 1; |
10542 | |
|
10543 | 0 | if (object_props->y_scale == 0.0) |
10544 | 0 | object_props->y_scale = 1; |
10545 | |
|
10546 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
10547 | 0 | STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); |
10548 | 0 | fclose(image_stream); |
10549 | 0 | return LXW_NO_ERROR; |
10550 | 0 | } |
10551 | 0 | else { |
10552 | 0 | _free_object_properties(object_props); |
10553 | 0 | fclose(image_stream); |
10554 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
10555 | 0 | } |
10556 | 0 | } |
10557 | | |
10558 | | /* |
10559 | | * Insert an image into the worksheet. |
10560 | | */ |
10561 | | lxw_error |
10562 | | worksheet_insert_image(lxw_worksheet *self, |
10563 | | lxw_row_t row_num, lxw_col_t col_num, |
10564 | | const char *filename) |
10565 | 0 | { |
10566 | 0 | return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL); |
10567 | 0 | } |
10568 | | |
10569 | | /* |
10570 | | * Insert an image buffer, with options, into the worksheet. |
10571 | | */ |
10572 | | lxw_error |
10573 | | worksheet_insert_image_buffer_opt(lxw_worksheet *self, |
10574 | | lxw_row_t row_num, |
10575 | | lxw_col_t col_num, |
10576 | | const unsigned char *image_buffer, |
10577 | | size_t image_size, |
10578 | | lxw_image_options *user_options) |
10579 | 0 | { |
10580 | 0 | FILE *image_stream; |
10581 | 0 | lxw_object_properties *object_props; |
10582 | |
|
10583 | 0 | if (!image_size) { |
10584 | 0 | LXW_WARN("worksheet_insert_image_buffer()/_opt(): " |
10585 | 0 | "size must be non-zero."); |
10586 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10587 | 0 | } |
10588 | | |
10589 | | /* Write the image buffer to a file (preferably in memory) so we can read |
10590 | | * the dimensions like an ordinary file. */ |
10591 | | #ifdef USE_FMEMOPEN |
10592 | | image_stream = fmemopen((void *) image_buffer, image_size, "rb"); |
10593 | | |
10594 | | if (!image_stream) |
10595 | | return LXW_ERROR_CREATING_TMPFILE; |
10596 | | #else |
10597 | 0 | image_stream = lxw_tmpfile(self->tmpdir); |
10598 | |
|
10599 | 0 | if (!image_stream) |
10600 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
10601 | | |
10602 | 0 | if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { |
10603 | 0 | fclose(image_stream); |
10604 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
10605 | 0 | } |
10606 | | |
10607 | 0 | rewind(image_stream); |
10608 | 0 | #endif |
10609 | | |
10610 | | /* Create a new object to hold the image properties. */ |
10611 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
10612 | 0 | if (!object_props) { |
10613 | 0 | fclose(image_stream); |
10614 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10615 | 0 | } |
10616 | | |
10617 | | /* Store the image data in the properties structure. */ |
10618 | 0 | object_props->image_buffer = calloc(1, image_size); |
10619 | 0 | if (!object_props->image_buffer) { |
10620 | 0 | _free_object_properties(object_props); |
10621 | 0 | fclose(image_stream); |
10622 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10623 | 0 | } |
10624 | 0 | else { |
10625 | 0 | memcpy(object_props->image_buffer, image_buffer, image_size); |
10626 | 0 | object_props->image_buffer_size = image_size; |
10627 | 0 | object_props->is_image_buffer = LXW_TRUE; |
10628 | 0 | } |
10629 | | |
10630 | 0 | if (user_options) { |
10631 | 0 | object_props->x_offset = user_options->x_offset; |
10632 | 0 | object_props->y_offset = user_options->y_offset; |
10633 | 0 | object_props->x_scale = user_options->x_scale; |
10634 | 0 | object_props->y_scale = user_options->y_scale; |
10635 | 0 | object_props->url = lxw_strdup(user_options->url); |
10636 | 0 | object_props->tip = lxw_strdup(user_options->tip); |
10637 | 0 | object_props->object_position = user_options->object_position; |
10638 | 0 | object_props->description = lxw_strdup(user_options->description); |
10639 | 0 | object_props->decorative = user_options->decorative; |
10640 | 0 | } |
10641 | | |
10642 | | /* Copy other options or set defaults. */ |
10643 | 0 | object_props->filename = lxw_strdup("image_buffer"); |
10644 | 0 | object_props->stream = image_stream; |
10645 | 0 | object_props->row = row_num; |
10646 | 0 | object_props->col = col_num; |
10647 | |
|
10648 | 0 | if (object_props->x_scale == 0.0) |
10649 | 0 | object_props->x_scale = 1; |
10650 | |
|
10651 | 0 | if (object_props->y_scale == 0.0) |
10652 | 0 | object_props->y_scale = 1; |
10653 | |
|
10654 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
10655 | 0 | STAILQ_INSERT_TAIL(self->image_props, object_props, list_pointers); |
10656 | 0 | fclose(image_stream); |
10657 | 0 | return LXW_NO_ERROR; |
10658 | 0 | } |
10659 | 0 | else { |
10660 | 0 | _free_object_properties(object_props); |
10661 | 0 | fclose(image_stream); |
10662 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
10663 | 0 | } |
10664 | 0 | } |
10665 | | |
10666 | | /* |
10667 | | * Insert an image buffer into the worksheet. |
10668 | | */ |
10669 | | lxw_error |
10670 | | worksheet_insert_image_buffer(lxw_worksheet *self, |
10671 | | lxw_row_t row_num, |
10672 | | lxw_col_t col_num, |
10673 | | const unsigned char *image_buffer, |
10674 | | size_t image_size) |
10675 | 0 | { |
10676 | 0 | return worksheet_insert_image_buffer_opt(self, row_num, col_num, |
10677 | 0 | image_buffer, image_size, NULL); |
10678 | 0 | } |
10679 | | |
10680 | | /* |
10681 | | * Embed an image with options into the worksheet. |
10682 | | */ |
10683 | | lxw_error |
10684 | | worksheet_embed_image_opt(lxw_worksheet *self, |
10685 | | lxw_row_t row_num, lxw_col_t col_num, |
10686 | | const char *filename, |
10687 | | lxw_image_options *user_options) |
10688 | 0 | { |
10689 | 0 | FILE *image_stream; |
10690 | 0 | lxw_object_properties *object_props; |
10691 | 0 | lxw_error err; |
10692 | |
|
10693 | 0 | if (!filename) { |
10694 | 0 | LXW_WARN("worksheet_embed_image()/_opt(): " |
10695 | 0 | "filename must be specified."); |
10696 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10697 | 0 | } |
10698 | | |
10699 | | /* Check that the image file exists and can be opened. */ |
10700 | 0 | image_stream = lxw_fopen(filename, "rb"); |
10701 | 0 | if (!image_stream) { |
10702 | 0 | LXW_WARN_FORMAT1("worksheet_embed_image()/_opt(): " |
10703 | 0 | "file doesn't exist or can't be opened: %s.", |
10704 | 0 | filename); |
10705 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10706 | 0 | } |
10707 | | |
10708 | | /* Check and store the cell dimensions. */ |
10709 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
10710 | 0 | if (err) { |
10711 | 0 | fclose(image_stream); |
10712 | 0 | return err; |
10713 | 0 | } |
10714 | | |
10715 | | /* Create a new object to hold the image properties. */ |
10716 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
10717 | 0 | if (!object_props) { |
10718 | 0 | fclose(image_stream); |
10719 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10720 | 0 | } |
10721 | | |
10722 | | /* We only copy/use a limited number of options for embedded images. */ |
10723 | 0 | if (user_options) { |
10724 | 0 | if (user_options->cell_format) |
10725 | 0 | object_props->format = user_options->cell_format; |
10726 | | |
10727 | | /* The url for embedded images is written as a cell url. */ |
10728 | 0 | if (user_options->url) { |
10729 | 0 | if (!user_options->cell_format) |
10730 | 0 | object_props->format = self->default_url_format; |
10731 | |
|
10732 | 0 | self->storing_embedded_image = LXW_TRUE; |
10733 | 0 | err = worksheet_write_url(self, |
10734 | 0 | row_num, |
10735 | 0 | col_num, |
10736 | 0 | user_options->url, |
10737 | 0 | object_props->format); |
10738 | 0 | if (err) { |
10739 | 0 | _free_object_properties(object_props); |
10740 | 0 | fclose(image_stream); |
10741 | 0 | return err; |
10742 | 0 | } |
10743 | | |
10744 | 0 | self->storing_embedded_image = LXW_FALSE; |
10745 | 0 | } |
10746 | | |
10747 | 0 | object_props->decorative = user_options->decorative; |
10748 | 0 | if (user_options->description) |
10749 | 0 | object_props->description = lxw_strdup(user_options->description); |
10750 | 0 | } |
10751 | | |
10752 | | /* Copy other options or set defaults. */ |
10753 | 0 | object_props->filename = lxw_strdup(filename); |
10754 | 0 | object_props->stream = image_stream; |
10755 | 0 | object_props->row = row_num; |
10756 | 0 | object_props->col = col_num; |
10757 | |
|
10758 | 0 | if (object_props->x_scale == 0.0) |
10759 | 0 | object_props->x_scale = 1; |
10760 | |
|
10761 | 0 | if (object_props->y_scale == 0.0) |
10762 | 0 | object_props->y_scale = 1; |
10763 | |
|
10764 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
10765 | 0 | STAILQ_INSERT_TAIL(self->embedded_image_props, object_props, |
10766 | 0 | list_pointers); |
10767 | 0 | fclose(image_stream); |
10768 | |
|
10769 | 0 | return LXW_NO_ERROR; |
10770 | 0 | } |
10771 | 0 | else { |
10772 | 0 | _free_object_properties(object_props); |
10773 | 0 | fclose(image_stream); |
10774 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
10775 | 0 | } |
10776 | 0 | } |
10777 | | |
10778 | | /* |
10779 | | * Embed an image into the worksheet. |
10780 | | */ |
10781 | | lxw_error |
10782 | | worksheet_embed_image(lxw_worksheet *self, |
10783 | | lxw_row_t row_num, lxw_col_t col_num, |
10784 | | const char *filename) |
10785 | 0 | { |
10786 | 0 | return worksheet_embed_image_opt(self, row_num, col_num, filename, NULL); |
10787 | 0 | } |
10788 | | |
10789 | | /* |
10790 | | * Embed an image buffer, with options, into the worksheet. |
10791 | | */ |
10792 | | lxw_error |
10793 | | worksheet_embed_image_buffer_opt(lxw_worksheet *self, |
10794 | | lxw_row_t row_num, |
10795 | | lxw_col_t col_num, |
10796 | | const unsigned char *image_buffer, |
10797 | | size_t image_size, |
10798 | | lxw_image_options *user_options) |
10799 | 0 | { |
10800 | 0 | FILE *image_stream; |
10801 | 0 | lxw_object_properties *object_props; |
10802 | 0 | lxw_error err; |
10803 | |
|
10804 | 0 | if (!image_size) { |
10805 | 0 | LXW_WARN("worksheet_embed_image_buffer()/_opt(): " |
10806 | 0 | "size must be non-zero."); |
10807 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10808 | 0 | } |
10809 | | |
10810 | | /* Write the image buffer to a file (preferably in memory) so we can read |
10811 | | * the dimensions like an ordinary file. For embedded images we really only |
10812 | | * need the image type. */ |
10813 | | #ifdef USE_FMEMOPEN |
10814 | | image_stream = fmemopen((void *) image_buffer, image_size, "rb"); |
10815 | | |
10816 | | if (!image_stream) |
10817 | | return LXW_ERROR_CREATING_TMPFILE; |
10818 | | #else |
10819 | 0 | image_stream = lxw_tmpfile(self->tmpdir); |
10820 | |
|
10821 | 0 | if (!image_stream) |
10822 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
10823 | | |
10824 | 0 | if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { |
10825 | 0 | fclose(image_stream); |
10826 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
10827 | 0 | } |
10828 | | |
10829 | 0 | rewind(image_stream); |
10830 | 0 | #endif |
10831 | | |
10832 | | /* Check and store the cell dimensions. */ |
10833 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE); |
10834 | 0 | if (err) |
10835 | 0 | return err; |
10836 | | |
10837 | | /* Create a new object to hold the image properties. */ |
10838 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
10839 | 0 | if (!object_props) { |
10840 | 0 | fclose(image_stream); |
10841 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10842 | 0 | } |
10843 | | |
10844 | | /* Store the image data in the properties structure. */ |
10845 | 0 | object_props->image_buffer = calloc(1, image_size); |
10846 | 0 | if (!object_props->image_buffer) { |
10847 | 0 | _free_object_properties(object_props); |
10848 | 0 | fclose(image_stream); |
10849 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10850 | 0 | } |
10851 | 0 | else { |
10852 | 0 | memcpy(object_props->image_buffer, image_buffer, image_size); |
10853 | 0 | object_props->image_buffer_size = image_size; |
10854 | 0 | object_props->is_image_buffer = LXW_TRUE; |
10855 | 0 | } |
10856 | | |
10857 | | /* We only copy/use a limited number of options for embedded images. */ |
10858 | 0 | if (user_options) { |
10859 | 0 | if (user_options->cell_format) |
10860 | 0 | object_props->format = user_options->cell_format; |
10861 | | |
10862 | | /* The url for embedded images is written as a cell url. */ |
10863 | 0 | if (user_options->url) { |
10864 | 0 | if (!user_options->cell_format) |
10865 | 0 | object_props->format = self->default_url_format; |
10866 | |
|
10867 | 0 | self->storing_embedded_image = LXW_TRUE; |
10868 | 0 | err = worksheet_write_url(self, |
10869 | 0 | row_num, |
10870 | 0 | col_num, |
10871 | 0 | user_options->url, |
10872 | 0 | object_props->format); |
10873 | 0 | if (err) { |
10874 | 0 | _free_object_properties(object_props); |
10875 | 0 | fclose(image_stream); |
10876 | 0 | return err; |
10877 | 0 | } |
10878 | | |
10879 | 0 | self->storing_embedded_image = LXW_FALSE; |
10880 | 0 | } |
10881 | | |
10882 | 0 | object_props->decorative = user_options->decorative; |
10883 | 0 | if (user_options->description) |
10884 | 0 | object_props->description = lxw_strdup(user_options->description); |
10885 | 0 | } |
10886 | | |
10887 | | /* Copy other options or set defaults. */ |
10888 | 0 | object_props->filename = lxw_strdup("image_buffer"); |
10889 | 0 | object_props->stream = image_stream; |
10890 | 0 | object_props->row = row_num; |
10891 | 0 | object_props->col = col_num; |
10892 | |
|
10893 | 0 | if (object_props->x_scale == 0.0) |
10894 | 0 | object_props->x_scale = 1; |
10895 | |
|
10896 | 0 | if (object_props->y_scale == 0.0) |
10897 | 0 | object_props->y_scale = 1; |
10898 | |
|
10899 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
10900 | 0 | STAILQ_INSERT_TAIL(self->embedded_image_props, object_props, |
10901 | 0 | list_pointers); |
10902 | 0 | fclose(image_stream); |
10903 | |
|
10904 | 0 | return LXW_NO_ERROR; |
10905 | 0 | } |
10906 | 0 | else { |
10907 | 0 | _free_object_properties(object_props); |
10908 | 0 | fclose(image_stream); |
10909 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
10910 | 0 | } |
10911 | 0 | } |
10912 | | |
10913 | | /* |
10914 | | * Insert an image buffer into the worksheet. |
10915 | | */ |
10916 | | lxw_error |
10917 | | worksheet_embed_image_buffer(lxw_worksheet *self, |
10918 | | lxw_row_t row_num, |
10919 | | lxw_col_t col_num, |
10920 | | const unsigned char *image_buffer, |
10921 | | size_t image_size) |
10922 | 0 | { |
10923 | 0 | return worksheet_embed_image_buffer_opt(self, row_num, col_num, |
10924 | 0 | image_buffer, image_size, NULL); |
10925 | 0 | } |
10926 | | |
10927 | | /* |
10928 | | * Set an image as a worksheet background. |
10929 | | */ |
10930 | | lxw_error |
10931 | | worksheet_set_background(lxw_worksheet *self, const char *filename) |
10932 | 0 | { |
10933 | 0 | FILE *image_stream; |
10934 | 0 | lxw_object_properties *object_props; |
10935 | |
|
10936 | 0 | if (!filename) { |
10937 | 0 | LXW_WARN("worksheet_set_background(): " |
10938 | 0 | "filename must be specified."); |
10939 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10940 | 0 | } |
10941 | | |
10942 | | /* Check that the image file exists and can be opened. */ |
10943 | 0 | image_stream = lxw_fopen(filename, "rb"); |
10944 | 0 | if (!image_stream) { |
10945 | 0 | LXW_WARN_FORMAT1("worksheet_set_background(): " |
10946 | 0 | "file doesn't exist or can't be opened: %s.", |
10947 | 0 | filename); |
10948 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
10949 | 0 | } |
10950 | | |
10951 | | /* Create a new object to hold the image properties. */ |
10952 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
10953 | 0 | if (!object_props) { |
10954 | 0 | fclose(image_stream); |
10955 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
10956 | 0 | } |
10957 | | |
10958 | | /* Copy other options or set defaults. */ |
10959 | 0 | object_props->filename = lxw_strdup(filename); |
10960 | 0 | object_props->stream = image_stream; |
10961 | 0 | object_props->is_background = LXW_TRUE; |
10962 | |
|
10963 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
10964 | 0 | _free_object_properties(self->background_image); |
10965 | 0 | self->background_image = object_props; |
10966 | 0 | self->has_background_image = LXW_TRUE; |
10967 | 0 | fclose(image_stream); |
10968 | 0 | return LXW_NO_ERROR; |
10969 | 0 | } |
10970 | 0 | else { |
10971 | 0 | _free_object_properties(object_props); |
10972 | 0 | fclose(image_stream); |
10973 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
10974 | 0 | } |
10975 | 0 | } |
10976 | | |
10977 | | /* |
10978 | | * Set an image buffer as a worksheet background. |
10979 | | */ |
10980 | | lxw_error |
10981 | | worksheet_set_background_buffer(lxw_worksheet *self, |
10982 | | const unsigned char *image_buffer, |
10983 | | size_t image_size) |
10984 | 0 | { |
10985 | 0 | FILE *image_stream; |
10986 | 0 | lxw_object_properties *object_props; |
10987 | |
|
10988 | 0 | if (!image_size) { |
10989 | 0 | LXW_WARN("worksheet_set_background(): " "size must be non-zero."); |
10990 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
10991 | 0 | } |
10992 | | |
10993 | | /* Write the image buffer to a file (preferably in memory) so we can read |
10994 | | * the dimensions like an ordinary file. */ |
10995 | | #ifdef USE_FMEMOPEN |
10996 | | image_stream = fmemopen((void *) image_buffer, image_size, "rb"); |
10997 | | |
10998 | | if (!image_stream) |
10999 | | return LXW_ERROR_CREATING_TMPFILE; |
11000 | | #else |
11001 | 0 | image_stream = lxw_tmpfile(self->tmpdir); |
11002 | |
|
11003 | 0 | if (!image_stream) |
11004 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
11005 | | |
11006 | 0 | if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) { |
11007 | 0 | fclose(image_stream); |
11008 | 0 | return LXW_ERROR_CREATING_TMPFILE; |
11009 | 0 | } |
11010 | | |
11011 | 0 | rewind(image_stream); |
11012 | 0 | #endif |
11013 | | |
11014 | | /* Create a new object to hold the image properties. */ |
11015 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
11016 | 0 | if (!object_props) { |
11017 | 0 | fclose(image_stream); |
11018 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
11019 | 0 | } |
11020 | | |
11021 | | /* Store the image data in the properties structure. */ |
11022 | 0 | object_props->image_buffer = calloc(1, image_size); |
11023 | 0 | if (!object_props->image_buffer) { |
11024 | 0 | _free_object_properties(object_props); |
11025 | 0 | fclose(image_stream); |
11026 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
11027 | 0 | } |
11028 | 0 | else { |
11029 | 0 | memcpy(object_props->image_buffer, image_buffer, image_size); |
11030 | 0 | object_props->image_buffer_size = image_size; |
11031 | 0 | object_props->is_image_buffer = LXW_TRUE; |
11032 | 0 | } |
11033 | | |
11034 | | /* Copy other options or set defaults. */ |
11035 | 0 | object_props->filename = lxw_strdup("image_buffer"); |
11036 | 0 | object_props->stream = image_stream; |
11037 | 0 | object_props->is_background = LXW_TRUE; |
11038 | |
|
11039 | 0 | if (_get_image_properties(object_props) == LXW_NO_ERROR) { |
11040 | 0 | _free_object_properties(self->background_image); |
11041 | 0 | self->background_image = object_props; |
11042 | 0 | self->has_background_image = LXW_TRUE; |
11043 | 0 | fclose(image_stream); |
11044 | 0 | return LXW_NO_ERROR; |
11045 | 0 | } |
11046 | 0 | else { |
11047 | 0 | _free_object_properties(object_props); |
11048 | 0 | fclose(image_stream); |
11049 | 0 | return LXW_ERROR_IMAGE_DIMENSIONS; |
11050 | 0 | } |
11051 | 0 | } |
11052 | | |
11053 | | /* |
11054 | | * Insert an chart into the worksheet. |
11055 | | */ |
11056 | | lxw_error |
11057 | | worksheet_insert_chart_opt(lxw_worksheet *self, |
11058 | | lxw_row_t row_num, lxw_col_t col_num, |
11059 | | lxw_chart *chart, lxw_chart_options *user_options) |
11060 | 0 | { |
11061 | 0 | lxw_object_properties *object_props; |
11062 | 0 | lxw_chart_series *series; |
11063 | |
|
11064 | 0 | if (!chart) { |
11065 | 0 | LXW_WARN("worksheet_insert_chart()/_opt(): chart must be non-NULL."); |
11066 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
11067 | 0 | } |
11068 | | |
11069 | | /* Check that the chart isn't being used more than once. */ |
11070 | 0 | if (chart->in_use) { |
11071 | 0 | LXW_WARN("worksheet_insert_chart()/_opt(): the same chart object " |
11072 | 0 | "cannot be inserted in a worksheet more than once."); |
11073 | |
|
11074 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11075 | 0 | } |
11076 | | |
11077 | | /* Check that the chart has a data series. */ |
11078 | 0 | if (STAILQ_EMPTY(chart->series_list)) { |
11079 | 0 | LXW_WARN |
11080 | 0 | ("worksheet_insert_chart()/_opt(): chart must have a series."); |
11081 | |
|
11082 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11083 | 0 | } |
11084 | | |
11085 | | /* Check that the chart has a 'values' series. */ |
11086 | 0 | STAILQ_FOREACH(series, chart->series_list, list_pointers) { |
11087 | 0 | if (!series->values->formula && !series->values->sheetname) { |
11088 | 0 | LXW_WARN("worksheet_insert_chart()/_opt(): chart must have a " |
11089 | 0 | "'values' series."); |
11090 | |
|
11091 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11092 | 0 | } |
11093 | 0 | } |
11094 | | |
11095 | | /* Create a new object to hold the chart image properties. */ |
11096 | 0 | object_props = calloc(1, sizeof(lxw_object_properties)); |
11097 | 0 | RETURN_ON_MEM_ERROR(object_props, LXW_ERROR_MEMORY_MALLOC_FAILED); |
11098 | | |
11099 | 0 | if (user_options) { |
11100 | 0 | object_props->x_offset = user_options->x_offset; |
11101 | 0 | object_props->y_offset = user_options->y_offset; |
11102 | 0 | object_props->x_scale = user_options->x_scale; |
11103 | 0 | object_props->y_scale = user_options->y_scale; |
11104 | 0 | object_props->object_position = user_options->object_position; |
11105 | 0 | object_props->description = lxw_strdup(user_options->description); |
11106 | 0 | object_props->decorative = user_options->decorative; |
11107 | 0 | } |
11108 | | |
11109 | | /* Copy other options or set defaults. */ |
11110 | 0 | object_props->row = row_num; |
11111 | 0 | object_props->col = col_num; |
11112 | |
|
11113 | 0 | object_props->width = 480; |
11114 | 0 | object_props->height = 288; |
11115 | |
|
11116 | 0 | if (object_props->x_scale == 0.0) |
11117 | 0 | object_props->x_scale = 1; |
11118 | |
|
11119 | 0 | if (object_props->y_scale == 0.0) |
11120 | 0 | object_props->y_scale = 1; |
11121 | | |
11122 | | /* Store chart references so they can be ordered in the workbook. */ |
11123 | 0 | object_props->chart = chart; |
11124 | |
|
11125 | 0 | STAILQ_INSERT_TAIL(self->chart_data, object_props, list_pointers); |
11126 | |
|
11127 | 0 | chart->in_use = LXW_TRUE; |
11128 | |
|
11129 | 0 | return LXW_NO_ERROR; |
11130 | 0 | } |
11131 | | |
11132 | | /* |
11133 | | * Insert an image into the worksheet. |
11134 | | */ |
11135 | | lxw_error |
11136 | | worksheet_insert_chart(lxw_worksheet *self, |
11137 | | lxw_row_t row_num, lxw_col_t col_num, lxw_chart *chart) |
11138 | 0 | { |
11139 | 0 | return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL); |
11140 | 0 | } |
11141 | | |
11142 | | /* |
11143 | | * Add a data validation to a worksheet, for a range. Ironically this requires |
11144 | | * a lot of validation of the user input. |
11145 | | */ |
11146 | | lxw_error |
11147 | | worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row, |
11148 | | lxw_col_t first_col, |
11149 | | lxw_row_t last_row, |
11150 | | lxw_col_t last_col, |
11151 | | lxw_data_validation *validation) |
11152 | 0 | { |
11153 | 0 | lxw_data_val_obj *copy; |
11154 | 0 | uint8_t is_between = LXW_FALSE; |
11155 | 0 | uint8_t is_formula = LXW_FALSE; |
11156 | 0 | uint8_t has_criteria = LXW_TRUE; |
11157 | 0 | lxw_error err; |
11158 | 0 | lxw_row_t tmp_row; |
11159 | 0 | lxw_col_t tmp_col; |
11160 | 0 | size_t length; |
11161 | | |
11162 | | /* No action is required for validation type 'any' unless there are |
11163 | | * input messages to display.*/ |
11164 | 0 | if (validation->validate == LXW_VALIDATION_TYPE_ANY |
11165 | 0 | && !(validation->input_title || validation->input_message)) { |
11166 | |
|
11167 | 0 | return LXW_NO_ERROR; |
11168 | 0 | } |
11169 | | |
11170 | | /* Check for formula types. */ |
11171 | 0 | switch (validation->validate) { |
11172 | 0 | case LXW_VALIDATION_TYPE_INTEGER_FORMULA: |
11173 | 0 | case LXW_VALIDATION_TYPE_DECIMAL_FORMULA: |
11174 | 0 | case LXW_VALIDATION_TYPE_LIST_FORMULA: |
11175 | 0 | case LXW_VALIDATION_TYPE_LENGTH_FORMULA: |
11176 | 0 | case LXW_VALIDATION_TYPE_DATE_FORMULA: |
11177 | 0 | case LXW_VALIDATION_TYPE_TIME_FORMULA: |
11178 | 0 | case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: |
11179 | 0 | is_formula = LXW_TRUE; |
11180 | 0 | break; |
11181 | 0 | } |
11182 | | |
11183 | | /* Check for types without a criteria. */ |
11184 | 0 | switch (validation->validate) { |
11185 | 0 | case LXW_VALIDATION_TYPE_LIST: |
11186 | 0 | case LXW_VALIDATION_TYPE_LIST_FORMULA: |
11187 | 0 | case LXW_VALIDATION_TYPE_ANY: |
11188 | 0 | case LXW_VALIDATION_TYPE_CUSTOM_FORMULA: |
11189 | 0 | has_criteria = LXW_FALSE; |
11190 | 0 | break; |
11191 | 0 | } |
11192 | | |
11193 | | /* Check that a validation parameter has been specified |
11194 | | * except for 'list', 'any' and 'custom'. */ |
11195 | 0 | if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) { |
11196 | |
|
11197 | 0 | LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " |
11198 | 0 | "criteria parameter must be specified."); |
11199 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11200 | 0 | } |
11201 | | |
11202 | | /* Check for "between" criteria so we can do additional checks. */ |
11203 | 0 | if (has_criteria |
11204 | 0 | && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN |
11205 | 0 | || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) { |
11206 | |
|
11207 | 0 | is_between = LXW_TRUE; |
11208 | 0 | } |
11209 | | |
11210 | | /* Check that formula values are non NULL. */ |
11211 | 0 | if (is_formula) { |
11212 | 0 | if (is_between) { |
11213 | 0 | if (!validation->minimum_formula) { |
11214 | 0 | LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " |
11215 | 0 | "minimum_formula parameter cannot be NULL."); |
11216 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11217 | 0 | } |
11218 | 0 | if (!validation->maximum_formula) { |
11219 | 0 | LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " |
11220 | 0 | "maximum_formula parameter cannot be NULL."); |
11221 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11222 | 0 | } |
11223 | 0 | } |
11224 | 0 | else { |
11225 | 0 | if (!validation->value_formula) { |
11226 | 0 | LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " |
11227 | 0 | "formula parameter cannot be NULL."); |
11228 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11229 | 0 | } |
11230 | 0 | } |
11231 | 0 | } |
11232 | | |
11233 | | /* Check Excel limitations on input strings. */ |
11234 | 0 | if (validation->input_title) { |
11235 | 0 | length = lxw_utf8_strlen(validation->input_title); |
11236 | 0 | if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) { |
11237 | 0 | LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " |
11238 | 0 | "input_title length > Excel limit of %d.", |
11239 | 0 | LXW_VALIDATION_MAX_TITLE_LENGTH); |
11240 | 0 | return LXW_ERROR_32_STRING_LENGTH_EXCEEDED; |
11241 | 0 | } |
11242 | 0 | } |
11243 | | |
11244 | 0 | if (validation->error_title) { |
11245 | 0 | length = lxw_utf8_strlen(validation->error_title); |
11246 | 0 | if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) { |
11247 | 0 | LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " |
11248 | 0 | "error_title length > Excel limit of %d.", |
11249 | 0 | LXW_VALIDATION_MAX_TITLE_LENGTH); |
11250 | 0 | return LXW_ERROR_32_STRING_LENGTH_EXCEEDED; |
11251 | 0 | } |
11252 | 0 | } |
11253 | | |
11254 | 0 | if (validation->input_message) { |
11255 | 0 | length = lxw_utf8_strlen(validation->input_message); |
11256 | 0 | if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { |
11257 | 0 | LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " |
11258 | 0 | "input_message length > Excel limit of %d.", |
11259 | 0 | LXW_VALIDATION_MAX_STRING_LENGTH); |
11260 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
11261 | 0 | } |
11262 | 0 | } |
11263 | | |
11264 | 0 | if (validation->error_message) { |
11265 | 0 | length = lxw_utf8_strlen(validation->error_message); |
11266 | 0 | if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { |
11267 | 0 | LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " |
11268 | 0 | "error_message length > Excel limit of %d.", |
11269 | 0 | LXW_VALIDATION_MAX_STRING_LENGTH); |
11270 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
11271 | 0 | } |
11272 | 0 | } |
11273 | | |
11274 | 0 | if (validation->validate == LXW_VALIDATION_TYPE_LIST) { |
11275 | 0 | length = _validation_list_length(validation->value_list); |
11276 | |
|
11277 | 0 | if (length == 0) { |
11278 | 0 | LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): " |
11279 | 0 | "list parameters cannot be zero."); |
11280 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11281 | 0 | } |
11282 | | |
11283 | 0 | if (length > LXW_VALIDATION_MAX_STRING_LENGTH) { |
11284 | 0 | LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): " |
11285 | 0 | "list length with commas > Excel limit of %d.", |
11286 | 0 | LXW_VALIDATION_MAX_STRING_LENGTH); |
11287 | 0 | return LXW_ERROR_255_STRING_LENGTH_EXCEEDED; |
11288 | 0 | } |
11289 | 0 | } |
11290 | | |
11291 | | /* Swap last row/col with first row/col as necessary */ |
11292 | 0 | if (first_row > last_row) { |
11293 | 0 | tmp_row = last_row; |
11294 | 0 | last_row = first_row; |
11295 | 0 | first_row = tmp_row; |
11296 | 0 | } |
11297 | 0 | if (first_col > last_col) { |
11298 | 0 | tmp_col = last_col; |
11299 | 0 | last_col = first_col; |
11300 | 0 | first_col = tmp_col; |
11301 | 0 | } |
11302 | | |
11303 | | /* Check that dimensions are valid but don't store them. */ |
11304 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); |
11305 | 0 | if (err) |
11306 | 0 | return err; |
11307 | | |
11308 | | /* Create a copy of the parameters from the user data validation. */ |
11309 | 0 | copy = calloc(1, sizeof(lxw_data_val_obj)); |
11310 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy, mem_error); |
11311 | | |
11312 | | /* Create the data validation range. */ |
11313 | 0 | if (first_row == last_row && first_col == last_col) |
11314 | 0 | lxw_rowcol_to_cell(copy->sqref, first_row, first_col); |
11315 | 0 | else |
11316 | 0 | lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row, |
11317 | 0 | last_col); |
11318 | | |
11319 | | /* Copy the parameters from the user data validation. */ |
11320 | 0 | copy->validate = validation->validate; |
11321 | 0 | copy->value_number = validation->value_number; |
11322 | 0 | copy->error_type = validation->error_type; |
11323 | 0 | copy->dropdown = validation->dropdown; |
11324 | |
|
11325 | 0 | if (has_criteria) |
11326 | 0 | copy->criteria = validation->criteria; |
11327 | |
|
11328 | 0 | if (is_between) { |
11329 | 0 | copy->value_number = validation->minimum_number; |
11330 | 0 | copy->maximum_number = validation->maximum_number; |
11331 | 0 | } |
11332 | | |
11333 | | /* Copy the input/error titles and messages. */ |
11334 | 0 | if (validation->input_title) { |
11335 | 0 | copy->input_title = lxw_strdup_formula(validation->input_title); |
11336 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error); |
11337 | 0 | } |
11338 | | |
11339 | 0 | if (validation->input_message) { |
11340 | 0 | copy->input_message = lxw_strdup_formula(validation->input_message); |
11341 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error); |
11342 | 0 | } |
11343 | | |
11344 | 0 | if (validation->error_title) { |
11345 | 0 | copy->error_title = lxw_strdup_formula(validation->error_title); |
11346 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error); |
11347 | 0 | } |
11348 | | |
11349 | 0 | if (validation->error_message) { |
11350 | 0 | copy->error_message = lxw_strdup_formula(validation->error_message); |
11351 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error); |
11352 | 0 | } |
11353 | | |
11354 | | /* Copy the formula strings. */ |
11355 | 0 | if (is_formula) { |
11356 | 0 | if (is_between) { |
11357 | 0 | copy->value_formula = |
11358 | 0 | lxw_strdup_formula(validation->minimum_formula); |
11359 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); |
11360 | 0 | copy->maximum_formula = |
11361 | 0 | lxw_strdup_formula(validation->maximum_formula); |
11362 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error); |
11363 | 0 | } |
11364 | 0 | else { |
11365 | 0 | copy->value_formula = |
11366 | 0 | lxw_strdup_formula(validation->value_formula); |
11367 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); |
11368 | 0 | } |
11369 | 0 | } |
11370 | | |
11371 | | /* Copy the validation list as a csv string. */ |
11372 | 0 | if (validation->validate == LXW_VALIDATION_TYPE_LIST) { |
11373 | 0 | copy->value_formula = _validation_list_to_csv(validation->value_list); |
11374 | 0 | GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error); |
11375 | 0 | } |
11376 | | |
11377 | 0 | if (validation->validate == LXW_VALIDATION_TYPE_DATE |
11378 | 0 | || validation->validate == LXW_VALIDATION_TYPE_TIME) { |
11379 | 0 | if (is_between) { |
11380 | 0 | copy->value_number = |
11381 | 0 | lxw_datetime_to_excel_date_with_epoch |
11382 | 0 | (&validation->minimum_datetime, self->use_1904_epoch); |
11383 | 0 | copy->maximum_number = |
11384 | 0 | lxw_datetime_to_excel_date_with_epoch |
11385 | 0 | (&validation->maximum_datetime, self->use_1904_epoch); |
11386 | 0 | } |
11387 | 0 | else { |
11388 | 0 | copy->value_number = |
11389 | 0 | lxw_datetime_to_excel_date_with_epoch |
11390 | 0 | (&validation->value_datetime, self->use_1904_epoch); |
11391 | 0 | } |
11392 | 0 | } |
11393 | | |
11394 | | /* These options are on by default so we can't take plain booleans. */ |
11395 | 0 | copy->ignore_blank = validation->ignore_blank ^ 1; |
11396 | 0 | copy->show_input = validation->show_input ^ 1; |
11397 | 0 | copy->show_error = validation->show_error ^ 1; |
11398 | |
|
11399 | 0 | STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers); |
11400 | |
|
11401 | 0 | self->num_validations++; |
11402 | |
|
11403 | 0 | return LXW_NO_ERROR; |
11404 | | |
11405 | 0 | mem_error: |
11406 | 0 | _free_data_validation(copy); |
11407 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
11408 | 0 | } |
11409 | | |
11410 | | /* |
11411 | | * Add a data validation to a worksheet, for a cell. |
11412 | | */ |
11413 | | lxw_error |
11414 | | worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row, |
11415 | | lxw_col_t col, lxw_data_validation *validation) |
11416 | 0 | { |
11417 | 0 | return worksheet_data_validation_range(self, row, col, |
11418 | 0 | row, col, validation); |
11419 | 0 | } |
11420 | | |
11421 | | /* |
11422 | | * Add a conditional format to a worksheet, for a range. |
11423 | | */ |
11424 | | lxw_error |
11425 | | worksheet_conditional_format_range(lxw_worksheet *self, lxw_row_t first_row, |
11426 | | lxw_col_t first_col, |
11427 | | lxw_row_t last_row, |
11428 | | lxw_col_t last_col, |
11429 | | lxw_conditional_format *user_options) |
11430 | 0 | { |
11431 | 0 | lxw_cond_format_obj *cond_format; |
11432 | 0 | lxw_row_t tmp_row; |
11433 | 0 | lxw_col_t tmp_col; |
11434 | 0 | lxw_error err = LXW_NO_ERROR; |
11435 | 0 | char *type_strings[] = { |
11436 | 0 | "none", |
11437 | 0 | "cellIs", |
11438 | 0 | "containsText", |
11439 | 0 | "timePeriod", |
11440 | 0 | "aboveAverage", |
11441 | 0 | "duplicateValues", |
11442 | 0 | "uniqueValues", |
11443 | 0 | "top10", |
11444 | 0 | "top10", |
11445 | 0 | "containsBlanks", |
11446 | 0 | "notContainsBlanks", |
11447 | 0 | "containsErrors", |
11448 | 0 | "notContainsErrors", |
11449 | 0 | "expression", |
11450 | 0 | "colorScale", |
11451 | 0 | "colorScale", |
11452 | 0 | "dataBar", |
11453 | 0 | "iconSet", |
11454 | 0 | }; |
11455 | | |
11456 | | /* Swap last row/col with first row/col as necessary */ |
11457 | 0 | if (first_row > last_row) { |
11458 | 0 | tmp_row = last_row; |
11459 | 0 | last_row = first_row; |
11460 | 0 | first_row = tmp_row; |
11461 | 0 | } |
11462 | 0 | if (first_col > last_col) { |
11463 | 0 | tmp_col = last_col; |
11464 | 0 | last_col = first_col; |
11465 | 0 | first_col = tmp_col; |
11466 | 0 | } |
11467 | | |
11468 | | /* Check that dimensions are valid but don't store them. */ |
11469 | 0 | err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE); |
11470 | 0 | if (err) |
11471 | 0 | return err; |
11472 | | |
11473 | | /* Check the validation type is in correct enum range. */ |
11474 | 0 | if (user_options->type <= LXW_CONDITIONAL_TYPE_NONE || |
11475 | 0 | user_options->type >= LXW_CONDITIONAL_TYPE_LAST) { |
11476 | |
|
11477 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
11478 | 0 | "invalid type value (%d).", user_options->type); |
11479 | |
|
11480 | 0 | return LXW_ERROR_PARAMETER_VALIDATION; |
11481 | 0 | } |
11482 | | |
11483 | | /* Create a copy of the parameters from the user data validation. */ |
11484 | 0 | cond_format = calloc(1, sizeof(lxw_cond_format_obj)); |
11485 | 0 | GOTO_LABEL_ON_MEM_ERROR(cond_format, error); |
11486 | | |
11487 | | /* Create the data validation range. */ |
11488 | 0 | if (first_row == last_row && first_col == last_col) |
11489 | 0 | lxw_rowcol_to_cell(cond_format->sqref, first_row, first_col); |
11490 | 0 | else |
11491 | 0 | lxw_rowcol_to_range(cond_format->sqref, first_row, first_col, |
11492 | 0 | last_row, last_col); |
11493 | | |
11494 | | /* Store the first cell string for text and date rules. */ |
11495 | 0 | lxw_rowcol_to_cell(cond_format->first_cell, first_row, first_col); |
11496 | | |
11497 | | /* Overwrite the sqref range with a user supplied set of ranges. */ |
11498 | 0 | if (user_options->multi_range) { |
11499 | |
|
11500 | 0 | if (strlen(user_options->multi_range) >= LXW_MAX_ATTRIBUTE_LENGTH) { |
11501 | 0 | LXW_WARN_FORMAT1("worksheet_conditional_format_cell()/_range(): " |
11502 | 0 | "multi_range >= limit = %d.", |
11503 | 0 | LXW_MAX_ATTRIBUTE_LENGTH); |
11504 | 0 | err = LXW_ERROR_PARAMETER_VALIDATION; |
11505 | 0 | goto error; |
11506 | 0 | } |
11507 | | |
11508 | 0 | LXW_ATTRIBUTE_COPY(cond_format->sqref, user_options->multi_range); |
11509 | 0 | } |
11510 | | |
11511 | | /* Get the conditional format dxf format index. */ |
11512 | 0 | if (user_options->format) |
11513 | 0 | cond_format->dxf_index = |
11514 | 0 | lxw_format_get_dxf_index(user_options->format); |
11515 | 0 | else |
11516 | 0 | cond_format->dxf_index = LXW_PROPERTY_UNSET; |
11517 | | |
11518 | | /* Set some common option for all validation types. */ |
11519 | 0 | cond_format->type = user_options->type; |
11520 | 0 | cond_format->criteria = user_options->criteria; |
11521 | 0 | cond_format->stop_if_true = user_options->stop_if_true; |
11522 | 0 | cond_format->type_string = lxw_strdup(type_strings[cond_format->type]); |
11523 | | |
11524 | | /* Check that the criteria matches the conditional type. */ |
11525 | 0 | err = _validate_conditional_criteria(cond_format); |
11526 | 0 | if (err) |
11527 | 0 | goto error; |
11528 | | |
11529 | | /* Validate the user input for various types of rules. */ |
11530 | 0 | if (user_options->type == LXW_CONDITIONAL_TYPE_CELL |
11531 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_DUPLICATE |
11532 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_UNIQUE) { |
11533 | |
|
11534 | 0 | err = _validate_conditional_cell(cond_format, user_options); |
11535 | 0 | if (err) |
11536 | 0 | goto error; |
11537 | 0 | } |
11538 | 0 | else if (user_options->type == LXW_CONDITIONAL_TYPE_TEXT) { |
11539 | |
|
11540 | 0 | err = _validate_conditional_text(cond_format, user_options); |
11541 | 0 | if (err) |
11542 | 0 | goto error; |
11543 | 0 | } |
11544 | 0 | else if (user_options->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) { |
11545 | |
|
11546 | 0 | err = _validate_conditional_time_period(user_options); |
11547 | 0 | if (err) |
11548 | 0 | goto error; |
11549 | 0 | } |
11550 | 0 | else if (user_options->type == LXW_CONDITIONAL_TYPE_AVERAGE) { |
11551 | |
|
11552 | 0 | err = _validate_conditional_average(user_options); |
11553 | 0 | if (err) |
11554 | 0 | goto error; |
11555 | 0 | } |
11556 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP |
11557 | 0 | || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) { |
11558 | |
|
11559 | 0 | err = _validate_conditional_top(cond_format, user_options); |
11560 | 0 | if (err) |
11561 | 0 | goto error; |
11562 | 0 | } |
11563 | 0 | else if (user_options->type == LXW_CONDITIONAL_TYPE_FORMULA) { |
11564 | |
|
11565 | 0 | err = _validate_conditional_formula(cond_format, user_options); |
11566 | 0 | if (err) |
11567 | 0 | goto error; |
11568 | 0 | } |
11569 | 0 | else if (cond_format->type == LXW_CONDITIONAL_2_COLOR_SCALE |
11570 | 0 | || cond_format->type == LXW_CONDITIONAL_3_COLOR_SCALE) { |
11571 | |
|
11572 | 0 | err = _validate_conditional_scale(cond_format, user_options); |
11573 | 0 | if (err) |
11574 | 0 | goto error; |
11575 | 0 | } |
11576 | 0 | else if (cond_format->type == LXW_CONDITIONAL_DATA_BAR) { |
11577 | |
|
11578 | 0 | err = _validate_conditional_data_bar(self, cond_format, user_options); |
11579 | 0 | if (err) |
11580 | 0 | goto error; |
11581 | 0 | } |
11582 | 0 | else if (cond_format->type == LXW_CONDITIONAL_TYPE_ICON_SETS) { |
11583 | |
|
11584 | 0 | err = _validate_conditional_icons(user_options); |
11585 | 0 | if (err) |
11586 | 0 | goto error; |
11587 | | |
11588 | 0 | cond_format->icon_style = user_options->icon_style; |
11589 | 0 | cond_format->reverse_icons = user_options->reverse_icons; |
11590 | 0 | cond_format->icons_only = user_options->icons_only; |
11591 | 0 | } |
11592 | | |
11593 | | /* Set the priority based on the order of adding. */ |
11594 | 0 | cond_format->dxf_priority = ++self->dxf_priority; |
11595 | | |
11596 | | /* Store the conditional format object. */ |
11597 | 0 | err = _store_conditional_format_object(self, cond_format); |
11598 | |
|
11599 | 0 | if (err) |
11600 | 0 | goto error; |
11601 | 0 | else |
11602 | 0 | return LXW_NO_ERROR; |
11603 | | |
11604 | 0 | error: |
11605 | 0 | _free_cond_format(cond_format); |
11606 | 0 | return err; |
11607 | 0 | } |
11608 | | |
11609 | | /* |
11610 | | * Add a conditional format to a worksheet, for a cell. |
11611 | | */ |
11612 | | lxw_error |
11613 | | worksheet_conditional_format_cell(lxw_worksheet *self, |
11614 | | lxw_row_t row, |
11615 | | lxw_col_t col, |
11616 | | lxw_conditional_format *options) |
11617 | 0 | { |
11618 | 0 | return worksheet_conditional_format_range(self, row, col, |
11619 | 0 | row, col, options); |
11620 | 0 | } |
11621 | | |
11622 | | /* |
11623 | | * Insert a button object into the worksheet. |
11624 | | */ |
11625 | | lxw_error |
11626 | | worksheet_insert_button(lxw_worksheet *self, lxw_row_t row_num, |
11627 | | lxw_col_t col_num, lxw_button_options *options) |
11628 | 0 | { |
11629 | 0 | lxw_error err; |
11630 | 0 | lxw_vml_obj *button; |
11631 | |
|
11632 | 0 | err = _check_dimensions(self, row_num, col_num, LXW_TRUE, LXW_TRUE); |
11633 | 0 | if (err) |
11634 | 0 | return err; |
11635 | | |
11636 | 0 | button = calloc(1, sizeof(lxw_vml_obj)); |
11637 | 0 | GOTO_LABEL_ON_MEM_ERROR(button, mem_error); |
11638 | | |
11639 | 0 | button->row = row_num; |
11640 | 0 | button->col = col_num; |
11641 | | |
11642 | | /* Set user and default parameters for the button. */ |
11643 | 0 | err = _get_button_params(button, 1 + self->num_buttons, options); |
11644 | 0 | if (err) |
11645 | 0 | goto mem_error; |
11646 | | |
11647 | | /* Calculate the worksheet position of the button. */ |
11648 | 0 | _worksheet_position_vml_object(self, button); |
11649 | |
|
11650 | 0 | self->has_vml = LXW_TRUE; |
11651 | 0 | self->has_buttons = LXW_TRUE; |
11652 | 0 | self->num_buttons++; |
11653 | |
|
11654 | 0 | STAILQ_INSERT_TAIL(self->button_objs, button, list_pointers); |
11655 | |
|
11656 | 0 | return LXW_NO_ERROR; |
11657 | | |
11658 | 0 | mem_error: |
11659 | 0 | if (button) |
11660 | 0 | _free_vml_object(button); |
11661 | |
|
11662 | 0 | return LXW_ERROR_MEMORY_MALLOC_FAILED; |
11663 | 0 | } |
11664 | | |
11665 | | /* |
11666 | | * Set the VBA name for the worksheet. |
11667 | | */ |
11668 | | lxw_error |
11669 | | worksheet_set_vba_name(lxw_worksheet *self, const char *name) |
11670 | 0 | { |
11671 | 0 | if (!name) { |
11672 | 0 | LXW_WARN("worksheet_set_vba_name(): " "name must be specified."); |
11673 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
11674 | 0 | } |
11675 | | |
11676 | 0 | self->vba_codename = lxw_strdup(name); |
11677 | |
|
11678 | 0 | return LXW_NO_ERROR; |
11679 | 0 | } |
11680 | | |
11681 | | /* |
11682 | | * Set the default author of the cell comments. |
11683 | | */ |
11684 | | void |
11685 | | worksheet_set_comments_author(lxw_worksheet *self, const char *author) |
11686 | 0 | { |
11687 | 0 | self->comment_author = lxw_strdup(author); |
11688 | 0 | } |
11689 | | |
11690 | | /* |
11691 | | * Make any comments in the worksheet visible, unless explicitly hidden. |
11692 | | */ |
11693 | | void |
11694 | | worksheet_show_comments(lxw_worksheet *self) |
11695 | 0 | { |
11696 | 0 | self->comment_display_default = LXW_COMMENT_DISPLAY_VISIBLE; |
11697 | 0 | } |
11698 | | |
11699 | | /* |
11700 | | * Ignore various Excel errors/warnings in a worksheet for user defined ranges. |
11701 | | */ |
11702 | | lxw_error |
11703 | | worksheet_ignore_errors(lxw_worksheet *self, uint8_t type, const char *range) |
11704 | 0 | { |
11705 | 0 | if (!range) { |
11706 | 0 | LXW_WARN("worksheet_ignore_errors(): " "'range' must be specified."); |
11707 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
11708 | 0 | } |
11709 | | |
11710 | 0 | if (type <= 0 || type >= LXW_IGNORE_LAST_OPTION) { |
11711 | 0 | LXW_WARN("worksheet_ignore_errors(): " "unknown option in 'type'."); |
11712 | 0 | return LXW_ERROR_NULL_PARAMETER_IGNORED; |
11713 | 0 | } |
11714 | | |
11715 | | /* Set the ranges to be ignored. */ |
11716 | 0 | if (type == LXW_IGNORE_NUMBER_STORED_AS_TEXT) { |
11717 | 0 | free(self->ignore_number_stored_as_text); |
11718 | 0 | self->ignore_number_stored_as_text = lxw_strdup(range); |
11719 | 0 | } |
11720 | 0 | else if (type == LXW_IGNORE_EVAL_ERROR) { |
11721 | 0 | free(self->ignore_eval_error); |
11722 | 0 | self->ignore_eval_error = lxw_strdup(range); |
11723 | 0 | } |
11724 | 0 | else if (type == LXW_IGNORE_FORMULA_DIFFERS) { |
11725 | 0 | free(self->ignore_formula_differs); |
11726 | 0 | self->ignore_formula_differs = lxw_strdup(range); |
11727 | 0 | } |
11728 | 0 | else if (type == LXW_IGNORE_FORMULA_RANGE) { |
11729 | 0 | free(self->ignore_formula_range); |
11730 | 0 | self->ignore_formula_range = lxw_strdup(range); |
11731 | 0 | } |
11732 | 0 | else if (type == LXW_IGNORE_FORMULA_UNLOCKED) { |
11733 | 0 | free(self->ignore_formula_unlocked); |
11734 | 0 | self->ignore_formula_unlocked = lxw_strdup(range); |
11735 | 0 | } |
11736 | 0 | else if (type == LXW_IGNORE_EMPTY_CELL_REFERENCE) { |
11737 | 0 | free(self->ignore_empty_cell_reference); |
11738 | 0 | self->ignore_empty_cell_reference = lxw_strdup(range); |
11739 | 0 | } |
11740 | 0 | else if (type == LXW_IGNORE_LIST_DATA_VALIDATION) { |
11741 | 0 | free(self->ignore_list_data_validation); |
11742 | 0 | self->ignore_list_data_validation = lxw_strdup(range); |
11743 | 0 | } |
11744 | 0 | else if (type == LXW_IGNORE_CALCULATED_COLUMN) { |
11745 | 0 | free(self->ignore_calculated_column); |
11746 | 0 | self->ignore_calculated_column = lxw_strdup(range); |
11747 | 0 | } |
11748 | 0 | else if (type == LXW_IGNORE_TWO_DIGIT_TEXT_YEAR) { |
11749 | 0 | free(self->ignore_two_digit_text_year); |
11750 | 0 | self->ignore_two_digit_text_year = lxw_strdup(range); |
11751 | 0 | } |
11752 | |
|
11753 | 0 | self->has_ignore_errors = LXW_TRUE; |
11754 | |
|
11755 | 0 | return LXW_NO_ERROR; |
11756 | 0 | } |
11757 | | |
11758 | | /* |
11759 | | * Write an error cell for versions of Excel that don't support embedded images. |
11760 | | */ |
11761 | | void |
11762 | | worksheet_set_error_cell(lxw_worksheet *self, |
11763 | | lxw_object_properties *object_props, uint32_t ref_id) |
11764 | 0 | { |
11765 | 0 | lxw_row_t row_num = object_props->row; |
11766 | 0 | lxw_col_t col_num = object_props->col; |
11767 | |
|
11768 | 0 | lxw_cell *cell = |
11769 | 0 | _new_error_cell(row_num, col_num, ref_id, object_props->format); |
11770 | 0 | _insert_cell(self, row_num, col_num, cell); |
11771 | |
|
11772 | 0 | } |