fast_excel 0.1.4 → 0.1.5

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