fast_excel 0.2.3 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile.lock +16 -16
  6. data/README.md +1 -1
  7. data/Rakefile +1 -1
  8. data/fast_excel.gemspec +1 -1
  9. data/lib/fast_excel.rb +21 -5
  10. data/lib/fast_excel/binding/format.rb +3 -3
  11. data/libxlsxwriter/.drone.yml +27 -0
  12. data/libxlsxwriter/.indent.pro +3 -0
  13. data/libxlsxwriter/.travis.yml +12 -0
  14. data/libxlsxwriter/CMakeLists.txt +348 -0
  15. data/libxlsxwriter/Changes.txt +78 -0
  16. data/libxlsxwriter/LICENSE.txt +65 -4
  17. data/libxlsxwriter/Makefile +27 -7
  18. data/libxlsxwriter/Readme.md +2 -0
  19. data/libxlsxwriter/appveyor.yml +65 -0
  20. data/libxlsxwriter/cmake/FindMINIZIP.cmake +121 -0
  21. data/libxlsxwriter/cmake/FindPackage.cmake +183 -0
  22. data/libxlsxwriter/cmake/FindZLIB.cmake +123 -0
  23. data/libxlsxwriter/cmake/i686-toolchain.cmake +7 -0
  24. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +1 -1
  25. data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
  26. data/libxlsxwriter/include/xlsxwriter.h +2 -2
  27. data/libxlsxwriter/include/xlsxwriter/app.h +1 -1
  28. data/libxlsxwriter/include/xlsxwriter/chart.h +109 -8
  29. data/libxlsxwriter/include/xlsxwriter/common.h +10 -2
  30. data/libxlsxwriter/include/xlsxwriter/content_types.h +1 -1
  31. data/libxlsxwriter/include/xlsxwriter/core.h +1 -1
  32. data/libxlsxwriter/include/xlsxwriter/custom.h +1 -1
  33. data/libxlsxwriter/include/xlsxwriter/drawing.h +1 -1
  34. data/libxlsxwriter/include/xlsxwriter/format.h +6 -6
  35. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  36. data/libxlsxwriter/include/xlsxwriter/packager.h +6 -1
  37. data/libxlsxwriter/include/xlsxwriter/relationships.h +1 -1
  38. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +1 -1
  39. data/libxlsxwriter/include/xlsxwriter/styles.h +2 -2
  40. data/libxlsxwriter/include/xlsxwriter/theme.h +1 -1
  41. data/libxlsxwriter/include/xlsxwriter/utility.h +11 -5
  42. data/libxlsxwriter/include/xlsxwriter/workbook.h +3 -3
  43. data/libxlsxwriter/include/xlsxwriter/worksheet.h +517 -39
  44. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +2 -2
  45. data/libxlsxwriter/libxlsxwriter.podspec +4 -2
  46. data/libxlsxwriter/src/Makefile +30 -5
  47. data/libxlsxwriter/src/app.c +1 -1
  48. data/libxlsxwriter/src/chart.c +76 -19
  49. data/libxlsxwriter/src/content_types.c +1 -1
  50. data/libxlsxwriter/src/core.c +10 -10
  51. data/libxlsxwriter/src/custom.c +2 -2
  52. data/libxlsxwriter/src/drawing.c +1 -1
  53. data/libxlsxwriter/src/format.c +3 -3
  54. data/libxlsxwriter/src/hash_table.c +1 -1
  55. data/libxlsxwriter/src/packager.c +20 -7
  56. data/libxlsxwriter/src/relationships.c +1 -1
  57. data/libxlsxwriter/src/shared_strings.c +1 -1
  58. data/libxlsxwriter/src/styles.c +4 -4
  59. data/libxlsxwriter/src/theme.c +1 -1
  60. data/libxlsxwriter/src/utility.c +41 -1
  61. data/libxlsxwriter/src/workbook.c +8 -6
  62. data/libxlsxwriter/src/worksheet.c +748 -31
  63. data/libxlsxwriter/src/xmlwriter.c +2 -2
  64. data/libxlsxwriter/third_party/minizip/Makefile +6 -1
  65. data/libxlsxwriter/version.txt +1 -1
  66. data/test/reopen_test.rb +22 -0
  67. data/test/test_helper.rb +8 -5
  68. data/test/validations_test.rb +27 -0
  69. metadata +11 -2
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -37,7 +37,12 @@ STATIC lxw_error _add_file_to_zip(lxw_packager *self, FILE * file,
37
37
  #undef SLIST_ENTRY
38
38
 
39
39
  #include <windows.h>
40
+
41
+ #ifdef USE_SYSTEM_MINIZIP
42
+ #include "minizip/iowin32.h"
43
+ #else
40
44
  #include "../third_party/minizip/iowin32.h"
45
+ #endif
41
46
 
42
47
  zipFile
43
48
  _open_zipfile_win32(const char *filename)
@@ -57,7 +62,7 @@ _open_zipfile_win32(const char *filename)
57
62
  }
58
63
 
59
64
  /* Use the native Win32 file handling functions with minizip. */
60
- fill_win32_filefunc64(&filefunc);
65
+ fill_win32_filefunc64W(&filefunc);
61
66
 
62
67
  return zipOpen2_64(wide_filename, 0, NULL, &filefunc);
63
68
  }
@@ -196,6 +201,7 @@ _write_image_files(lxw_packager *self)
196
201
  lxw_worksheet *worksheet;
197
202
  lxw_image_options *image;
198
203
  lxw_error err;
204
+ FILE *image_stream;
199
205
 
200
206
  char filename[LXW_FILENAME_LENGTH] = { 0 };
201
207
  uint16_t index = 1;
@@ -210,12 +216,19 @@ _write_image_files(lxw_packager *self)
210
216
  lxw_snprintf(filename, LXW_FILENAME_LENGTH,
211
217
  "xl/media/image%d.%s", index++, image->extension);
212
218
 
213
- rewind(image->stream);
219
+ /* Check that the image file exists and can be opened. */
220
+ image_stream = fopen(image->filename, "rb");
221
+ if (!image_stream) {
222
+ LXW_WARN_FORMAT1("Error adding image to xlsx file: file "
223
+ "doesn't exist or can't be opened: %s.",
224
+ image->filename);
225
+ return LXW_ERROR_CREATING_TMPFILE;
226
+ }
227
+
228
+ err = _add_file_to_zip(self, image_stream, filename);
229
+ fclose(image_stream);
214
230
 
215
- err = _add_file_to_zip(self, image->stream, filename);
216
231
  RETURN_ON_ERROR(err);
217
-
218
- fclose(image->stream);
219
232
  }
220
233
  }
221
234
 
@@ -934,7 +947,7 @@ lxw_create_package(lxw_packager *self)
934
947
  RETURN_ON_ERROR(error);
935
948
 
936
949
  error = _write_image_files(self);
937
- RETURN_ON_ERROR(error);;
950
+ RETURN_ON_ERROR(error);
938
951
 
939
952
  error = _write_root_rels_file(self);
940
953
  RETURN_ON_ERROR(error);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -153,13 +153,13 @@ _write_num_fmts(lxw_styles *self)
153
153
  * Write the <sz> element.
154
154
  */
155
155
  STATIC void
156
- _write_font_size(lxw_styles *self, uint16_t font_size)
156
+ _write_font_size(lxw_styles *self, double font_size)
157
157
  {
158
158
  struct xml_attribute_list attributes;
159
159
  struct xml_attribute *attribute;
160
160
 
161
161
  LXW_INIT_ATTRIBUTES();
162
- LXW_PUSH_ATTRIBUTES_INT("val", font_size);
162
+ LXW_PUSH_ATTRIBUTES_DBL("val", font_size);
163
163
 
164
164
  lxw_xml_empty_tag(self->file, "sz", &attributes);
165
165
 
@@ -337,7 +337,7 @@ _write_font(lxw_styles *self, lxw_format *format)
337
337
  if (format->font_script == LXW_FONT_SUBSCRIPT)
338
338
  _write_vert_align(self, "subscript");
339
339
 
340
- if (format->font_size)
340
+ if (format->font_size > 0.0)
341
341
  _write_font_size(self, format->font_size);
342
342
 
343
343
  if (format->theme)
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -28,6 +28,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
28
28
  "Worksheet name exceeds Excel's limit of 31 characters.",
29
29
  "Worksheet name contains invalid Excel character: '[]:*?/\\'",
30
30
  "Worksheet name is already in use.",
31
+ "Parameter exceeds Excel's limit of 32 characters.",
31
32
  "Parameter exceeds Excel's limit of 128 characters.",
32
33
  "Parameter exceeds Excel's limit of 255 characters.",
33
34
  "String exceeds Excel's limit of 32,767 characters.",
@@ -419,6 +420,19 @@ lxw_strdup(const char *str)
419
420
  return copy;
420
421
  }
421
422
 
423
+ /* Simple function to strdup() a formula string without the leading "=". */
424
+ char *
425
+ lxw_strdup_formula(const char *formula)
426
+ {
427
+ if (!formula)
428
+ return NULL;
429
+
430
+ if (formula[0] == '=')
431
+ return lxw_strdup(formula + 1);
432
+ else
433
+ return lxw_strdup(formula);
434
+ }
435
+
422
436
  /* Simple strlen that counts UTF-8 characters. Assumes well formed UTF-8. */
423
437
  size_t
424
438
  lxw_utf8_strlen(const char *str)
@@ -513,3 +527,29 @@ lxw_tmpfile(char *tmpdir)
513
527
  return tmpfile();
514
528
  #endif
515
529
  }
530
+
531
+ /*
532
+ * Sample function to handle sprintf of doubles for locale portable code. This
533
+ * is usually handled by a lxw_sprintf_dbl() macro but it can be replaced with
534
+ * a function of the same name.
535
+ *
536
+ * The code below is a simplified example that changes numbers like 123,45 to
537
+ * 123.45. End-users can replace this with something more rigorous if
538
+ * required.
539
+ */
540
+ #ifdef USE_DOUBLE_FUNCTION
541
+ int
542
+ lxw_sprintf_dbl(char *data, double number)
543
+ {
544
+ char *tmp;
545
+
546
+ lxw_snprintf(data, LXW_ATTR_32, "%.16g", number);
547
+
548
+ /* Replace comma with decimal point. */
549
+ tmp = strchr(data, ',');
550
+ if (tmp)
551
+ *tmp = '.';
552
+
553
+ return 0;
554
+ }
555
+ #endif
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -404,7 +404,6 @@ _prepare_num_formats(lxw_workbook *self)
404
404
  lxw_hash_element *used_format_element;
405
405
  uint16_t index = 0xA4;
406
406
  uint16_t num_format_count = 0;
407
- char *num_format;
408
407
  uint16_t *num_format_index;
409
408
 
410
409
  LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
@@ -415,12 +414,14 @@ _prepare_num_formats(lxw_workbook *self)
415
414
  continue;
416
415
 
417
416
  /* Check if there is a user defined number format string. */
418
- num_format = format->num_format;
417
+ if (*format->num_format) {
418
+ char num_format[LXW_FORMAT_FIELD_LEN] = { 0 };
419
+ lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s",
420
+ format->num_format);
419
421
 
420
- if (*num_format) {
421
422
  /* Look up the num_format in the hash table. */
422
423
  hash_element = lxw_hash_key_exists(num_formats, num_format,
423
- strlen(num_format));
424
+ LXW_FORMAT_FIELD_LEN);
424
425
 
425
426
  if (hash_element) {
426
427
  /* Num_Format has already been used. */
@@ -432,7 +433,8 @@ _prepare_num_formats(lxw_workbook *self)
432
433
  *num_format_index = index;
433
434
  format->num_format_index = index;
434
435
  lxw_insert_hash_element(num_formats, num_format,
435
- num_format_index, strlen(num_format));
436
+ num_format_index,
437
+ LXW_FORMAT_FIELD_LEN);
436
438
  index++;
437
439
  num_format_count++;
438
440
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2018, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -15,11 +15,13 @@
15
15
  #include "xlsxwriter/utility.h"
16
16
  #include "xlsxwriter/relationships.h"
17
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
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
+ #define LXW_VALIDATION_MAX_TITLE_LENGTH 32
24
+ #define LXW_VALIDATION_MAX_STRING_LENGTH 255
23
25
 
24
26
  /*
25
27
  * Forward declarations.
@@ -123,6 +125,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
123
125
  GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error);
124
126
  STAILQ_INIT(worksheet->selections);
125
127
 
128
+ worksheet->data_validations =
129
+ calloc(1, sizeof(struct lxw_data_validations));
130
+ GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error);
131
+ STAILQ_INIT(worksheet->data_validations);
132
+
126
133
  worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
127
134
  GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
128
135
  STAILQ_INIT(worksheet->external_hyperlinks);
@@ -187,6 +194,9 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
187
194
  worksheet->zoom_scale_normal = LXW_TRUE;
188
195
  worksheet->show_zeros = LXW_TRUE;
189
196
  worksheet->outline_on = LXW_TRUE;
197
+ worksheet->outline_style = LXW_TRUE;
198
+ worksheet->outline_below = LXW_TRUE;
199
+ worksheet->outline_right = LXW_FALSE;
190
200
  worksheet->tab_color = LXW_COLOR_UNSET;
191
201
 
192
202
  if (init_data) {
@@ -268,6 +278,26 @@ _free_image_options(lxw_image_options *image)
268
278
  free(image);
269
279
  }
270
280
 
281
+ /*
282
+ * Free a worksheet data_validation.
283
+ */
284
+ STATIC void
285
+ _free_data_validation(lxw_data_validation *data_validation)
286
+ {
287
+ if (!data_validation)
288
+ return;
289
+
290
+ free(data_validation->value_formula);
291
+ free(data_validation->maximum_formula);
292
+ free(data_validation->input_title);
293
+ free(data_validation->input_message);
294
+ free(data_validation->error_title);
295
+ free(data_validation->error_message);
296
+ free(data_validation->minimum_formula);
297
+
298
+ free(data_validation);
299
+ }
300
+
271
301
  /*
272
302
  * Free a worksheet object.
273
303
  */
@@ -280,6 +310,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
280
310
  lxw_merged_range *merged_range;
281
311
  lxw_image_options *image_options;
282
312
  lxw_selection *selection;
313
+ lxw_data_validation *data_validation;
283
314
  lxw_rel_tuple *relationship;
284
315
 
285
316
  if (!worksheet)
@@ -309,7 +340,6 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
309
340
  }
310
341
 
311
342
  if (worksheet->hyperlinks) {
312
-
313
343
  for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row;
314
344
  row = next_row) {
315
345
 
@@ -361,6 +391,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
361
391
  free(worksheet->selections);
362
392
  }
363
393
 
394
+ if (worksheet->data_validations) {
395
+ while (!STAILQ_EMPTY(worksheet->data_validations)) {
396
+ data_validation = STAILQ_FIRST(worksheet->data_validations);
397
+ STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers);
398
+ _free_data_validation(data_validation);
399
+ }
400
+
401
+ free(worksheet->data_validations);
402
+ }
403
+
364
404
  /* TODO. Add function for freeing the relationship lists. */
365
405
  while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
366
406
  relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
@@ -860,6 +900,61 @@ lxw_basename(const char *path)
860
900
  return back_slash + 1;
861
901
  }
862
902
 
903
+ /* Function to count the total concatenated length of the strings in a
904
+ * validation list array, including commas. */
905
+ size_t
906
+ _validation_list_length(char **list)
907
+ {
908
+ uint8_t i = 0;
909
+ size_t length = 0;
910
+
911
+ if (!list || !list[0])
912
+ return 0;
913
+
914
+ while (list[i] && length <= LXW_VALIDATION_MAX_STRING_LENGTH) {
915
+ /* Include commas in the length. */
916
+ length += 1 + lxw_utf8_strlen(list[i]);
917
+ i++;
918
+ }
919
+
920
+ /* Adjust the count for extraneous comma at end. */
921
+ length--;
922
+
923
+ return length;
924
+ }
925
+
926
+ /* Function to convert an array of strings into a CSV string for data
927
+ * validation lists. */
928
+ char *
929
+ _validation_list_to_csv(char **list)
930
+ {
931
+ uint8_t i = 0;
932
+ char *str;
933
+
934
+ /* Create a buffer for the concatenated, and quoted, string. */
935
+ /* Add +3 for quotes and EOL. */
936
+ str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH + 3);
937
+ if (!str)
938
+ return NULL;
939
+
940
+ /* Add the start quote and first element. */
941
+ strcat(str, "\"");
942
+ strcat(str, list[0]);
943
+
944
+ /* Add the other elements preceded by a comma. */
945
+ i = 1;
946
+ while (list[i]) {
947
+ strcat(str, ",");
948
+ strcat(str, list[i]);
949
+ i++;
950
+ }
951
+
952
+ /* Add the end quote. */
953
+ strcat(str, "\"");
954
+
955
+ return str;
956
+ }
957
+
863
958
  /*****************************************************************************
864
959
  *
865
960
  * XML functions.
@@ -1372,6 +1467,12 @@ _worksheet_write_sheet_format_pr(lxw_worksheet *self)
1372
1467
  if (self->default_row_zeroed)
1373
1468
  LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1");
1374
1469
 
1470
+ if (self->outline_row_level)
1471
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevelRow", self->outline_row_level);
1472
+
1473
+ if (self->outline_col_level)
1474
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevelCol", self->outline_col_level);
1475
+
1375
1476
  lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes);
1376
1477
 
1377
1478
  LXW_FREE_ATTRIBUTES();
@@ -1608,6 +1709,9 @@ _write_row(lxw_worksheet *self, lxw_row *row, char *spans)
1608
1709
  if (height != LXW_DEF_ROW_HEIGHT)
1609
1710
  LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
1610
1711
 
1712
+ if (row->level)
1713
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevel", row->level);
1714
+
1611
1715
  if (row->collapsed)
1612
1716
  LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
1613
1717
 
@@ -2183,8 +2287,7 @@ _process_jpeg(lxw_image_options *image_options)
2183
2287
  if (fseek_err)
2184
2288
  goto file_error;
2185
2289
 
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. */
2290
+ /* Search through the image data and read the JPEG markers. */
2188
2291
  while (!feof(stream)) {
2189
2292
 
2190
2293
  /* Read the JPEG marker and length fields for the sub-section. */
@@ -2201,7 +2304,10 @@ _process_jpeg(lxw_image_options *image_options)
2201
2304
  /* The offset for next fseek() is the field length + type length. */
2202
2305
  offset = length - 2;
2203
2306
 
2204
- if (marker == 0xFFC0 || marker == 0xFFC2) {
2307
+ /* Read the height and width in the 0xFFCn elements (except C4, C8 */
2308
+ /* and CC which aren't SOF markers). */
2309
+ if ((marker & 0xFFF0) == 0xFFC0 && marker != 0xFFC4
2310
+ && marker != 0xFFC8 && marker != 0xFFCC) {
2205
2311
  /* Skip 1 byte to height and width. */
2206
2312
  fseek_err = fseek(stream, 1, SEEK_CUR);
2207
2313
  if (fseek_err)
@@ -2219,6 +2325,7 @@ _process_jpeg(lxw_image_options *image_options)
2219
2325
  offset -= 9;
2220
2326
  }
2221
2327
 
2328
+ /* Read the DPI in the 0xFFE0 element. */
2222
2329
  if (marker == 0xFFE0) {
2223
2330
  uint16_t x_density = 0;
2224
2331
  uint16_t y_density = 0;
@@ -2314,6 +2421,9 @@ _process_bmp(lxw_image_options *image_options)
2314
2421
  if (width == 0)
2315
2422
  goto file_error;
2316
2423
 
2424
+ height = LXW_UINT32_HOST(height);
2425
+ width = LXW_UINT32_HOST(width);
2426
+
2317
2427
  /* Set the image metadata. */
2318
2428
  image_options->image_type = LXW_IMAGE_BMP;
2319
2429
  image_options->width = width;
@@ -2385,6 +2495,18 @@ STATIC void
2385
2495
  _write_number_cell(lxw_worksheet *self, char *range,
2386
2496
  int32_t style_index, lxw_cell *cell)
2387
2497
  {
2498
+ #ifdef USE_DOUBLE_FUNCTION
2499
+ char data[LXW_ATTR_32];
2500
+
2501
+ lxw_sprintf_dbl(data, cell->u.number);
2502
+
2503
+ if (style_index)
2504
+ fprintf(self->file,
2505
+ "<c r=\"%s\" s=\"%d\"><v>%s</v></c>",
2506
+ range, style_index, data);
2507
+ else
2508
+ fprintf(self->file, "<c r=\"%s\"><v>%s</v></c>", range, data);
2509
+ #else
2388
2510
  if (style_index)
2389
2511
  fprintf(self->file,
2390
2512
  "<c r=\"%s\" s=\"%d\"><v>%.16g</v></c>",
@@ -2392,6 +2514,8 @@ _write_number_cell(lxw_worksheet *self, char *range,
2392
2514
  else
2393
2515
  fprintf(self->file,
2394
2516
  "<c r=\"%s\"><v>%.16g</v></c>", range, cell->u.number);
2517
+
2518
+ #endif
2395
2519
  }
2396
2520
 
2397
2521
  /*
@@ -2402,6 +2526,7 @@ STATIC void
2402
2526
  _write_string_cell(lxw_worksheet *self, char *range,
2403
2527
  int32_t style_index, lxw_cell *cell)
2404
2528
  {
2529
+
2405
2530
  if (style_index)
2406
2531
  fprintf(self->file,
2407
2532
  "<c r=\"%s\" s=\"%d\" t=\"s\"><v>%d</v></c>",
@@ -2459,8 +2584,7 @@ _write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
2459
2584
  {
2460
2585
  char data[LXW_ATTR_32];
2461
2586
 
2462
- lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
2463
-
2587
+ lxw_sprintf_dbl(data, cell->formula_result);
2464
2588
  lxw_xml_data_element(self->file, "f", cell->u.string, NULL);
2465
2589
  lxw_xml_data_element(self->file, "v", data, NULL);
2466
2590
  }
@@ -2479,7 +2603,7 @@ _write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
2479
2603
  LXW_PUSH_ATTRIBUTES_STR("t", "array");
2480
2604
  LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1);
2481
2605
 
2482
- lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
2606
+ lxw_sprintf_dbl(data, cell->formula_result);
2483
2607
 
2484
2608
  lxw_xml_data_element(self->file, "f", cell->u.string, &attributes);
2485
2609
  lxw_xml_data_element(self->file, "v", data, NULL);
@@ -2515,8 +2639,10 @@ _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell)
2515
2639
  STATIC void
2516
2640
  _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
2517
2641
  {
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;
2642
+ lxw_cell *cell_min = RB_MIN(lxw_table_cells, row->cells);
2643
+ lxw_cell *cell_max = RB_MAX(lxw_table_cells, row->cells);
2644
+ lxw_col_t span_col_min = cell_min->col_num;
2645
+ lxw_col_t span_col_max = cell_max->col_num;
2520
2646
  lxw_col_t col_min;
2521
2647
  lxw_col_t col_max;
2522
2648
  *block_num = row->row_num / 16;
@@ -2526,8 +2652,10 @@ _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
2526
2652
  while (row && (int32_t) (row->row_num / 16) == *block_num) {
2527
2653
 
2528
2654
  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;
2655
+ cell_min = RB_MIN(lxw_table_cells, row->cells);
2656
+ cell_max = RB_MAX(lxw_table_cells, row->cells);
2657
+ col_min = cell_min->col_num;
2658
+ col_max = cell_max->col_num;
2531
2659
 
2532
2660
  if (col_min < span_col_min)
2533
2661
  span_col_min = col_min;
@@ -2922,6 +3050,37 @@ _worksheet_write_tab_color(lxw_worksheet *self)
2922
3050
  LXW_FREE_ATTRIBUTES();
2923
3051
  }
2924
3052
 
3053
+ /*
3054
+ * Write the <outlinePr> element.
3055
+ */
3056
+ STATIC void
3057
+ _worksheet_write_outline_pr(lxw_worksheet *self)
3058
+ {
3059
+ struct xml_attribute_list attributes;
3060
+ struct xml_attribute *attribute;
3061
+
3062
+ if (!self->outline_changed)
3063
+ return;
3064
+
3065
+ LXW_INIT_ATTRIBUTES();
3066
+
3067
+ if (self->outline_style)
3068
+ LXW_PUSH_ATTRIBUTES_STR("applyStyles", "1");
3069
+
3070
+ if (!self->outline_below)
3071
+ LXW_PUSH_ATTRIBUTES_STR("summaryBelow", "0");
3072
+
3073
+ if (!self->outline_right)
3074
+ LXW_PUSH_ATTRIBUTES_STR("summaryRight", "0");
3075
+
3076
+ if (!self->outline_on)
3077
+ LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0");
3078
+
3079
+ lxw_xml_empty_tag(self->file, "outlinePr", &attributes);
3080
+
3081
+ LXW_FREE_ATTRIBUTES();
3082
+ }
3083
+
2925
3084
  /*
2926
3085
  * Write the <sheetPr> element for Sheet level properties.
2927
3086
  */
@@ -2950,7 +3109,7 @@ _worksheet_write_sheet_pr(lxw_worksheet *self)
2950
3109
  || self->outline_changed) {
2951
3110
  lxw_xml_start_tag(self->file, "sheetPr", &attributes);
2952
3111
  _worksheet_write_tab_color(self);
2953
- /* _worksheet_write_outline_pr(self); */
3112
+ _worksheet_write_outline_pr(self);
2954
3113
  _worksheet_write_page_set_up_pr(self);
2955
3114
  lxw_xml_end_tag(self->file, "sheetPr");
2956
3115
  }
@@ -3312,6 +3471,224 @@ _write_drawings(lxw_worksheet *self)
3312
3471
  _write_drawing(self, self->rel_count);
3313
3472
  }
3314
3473
 
3474
+ /*
3475
+ * Write the <formula1> element for numbers.
3476
+ */
3477
+ STATIC void
3478
+ _worksheet_write_formula1_num(lxw_worksheet *self, double number)
3479
+ {
3480
+ char data[LXW_ATTR_32];
3481
+
3482
+ lxw_sprintf_dbl(data, number);
3483
+
3484
+ lxw_xml_data_element(self->file, "formula1", data, NULL);
3485
+ }
3486
+
3487
+ /*
3488
+ * Write the <formula1> element for strings/formulas.
3489
+ */
3490
+ STATIC void
3491
+ _worksheet_write_formula1_str(lxw_worksheet *self, char *str)
3492
+ {
3493
+ lxw_xml_data_element(self->file, "formula1", str, NULL);
3494
+ }
3495
+
3496
+ /*
3497
+ * Write the <formula2> element for numbers.
3498
+ */
3499
+ STATIC void
3500
+ _worksheet_write_formula2_num(lxw_worksheet *self, double number)
3501
+ {
3502
+ char data[LXW_ATTR_32];
3503
+
3504
+ lxw_sprintf_dbl(data, number);
3505
+
3506
+ lxw_xml_data_element(self->file, "formula2", data, NULL);
3507
+ }
3508
+
3509
+ /*
3510
+ * Write the <formula2> element for strings/formulas.
3511
+ */
3512
+ STATIC void
3513
+ _worksheet_write_formula2_str(lxw_worksheet *self, char *str)
3514
+ {
3515
+ lxw_xml_data_element(self->file, "formula2", str, NULL);
3516
+ }
3517
+
3518
+ /*
3519
+ * Write the <dataValidation> element.
3520
+ */
3521
+ STATIC void
3522
+ _worksheet_write_data_validation(lxw_worksheet *self,
3523
+ lxw_data_validation *validation)
3524
+ {
3525
+ struct xml_attribute_list attributes;
3526
+ struct xml_attribute *attribute;
3527
+ uint8_t is_between = 0;
3528
+
3529
+ LXW_INIT_ATTRIBUTES();
3530
+
3531
+ switch (validation->validate) {
3532
+ case LXW_VALIDATION_TYPE_INTEGER:
3533
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3534
+ LXW_PUSH_ATTRIBUTES_STR("type", "whole");
3535
+ break;
3536
+ case LXW_VALIDATION_TYPE_DECIMAL:
3537
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3538
+ LXW_PUSH_ATTRIBUTES_STR("type", "decimal");
3539
+ break;
3540
+ case LXW_VALIDATION_TYPE_LIST:
3541
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3542
+ LXW_PUSH_ATTRIBUTES_STR("type", "list");
3543
+ break;
3544
+ case LXW_VALIDATION_TYPE_DATE:
3545
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3546
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3547
+ LXW_PUSH_ATTRIBUTES_STR("type", "date");
3548
+ break;
3549
+ case LXW_VALIDATION_TYPE_TIME:
3550
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3551
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3552
+ LXW_PUSH_ATTRIBUTES_STR("type", "time");
3553
+ break;
3554
+ case LXW_VALIDATION_TYPE_LENGTH:
3555
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3556
+ LXW_PUSH_ATTRIBUTES_STR("type", "textLength");
3557
+ break;
3558
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3559
+ LXW_PUSH_ATTRIBUTES_STR("type", "custom");
3560
+ break;
3561
+ }
3562
+
3563
+ switch (validation->criteria) {
3564
+ case LXW_VALIDATION_CRITERIA_EQUAL_TO:
3565
+ LXW_PUSH_ATTRIBUTES_STR("operator", "equal");
3566
+ break;
3567
+ case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO:
3568
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual");
3569
+ break;
3570
+ case LXW_VALIDATION_CRITERIA_LESS_THAN:
3571
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan");
3572
+ break;
3573
+ case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO:
3574
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual");
3575
+ break;
3576
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN:
3577
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan");
3578
+ break;
3579
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO:
3580
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual");
3581
+ break;
3582
+ case LXW_VALIDATION_CRITERIA_BETWEEN:
3583
+ /* Between is the default for 2 formulas and isn't added. */
3584
+ is_between = 1;
3585
+ break;
3586
+ case LXW_VALIDATION_CRITERIA_NOT_BETWEEN:
3587
+ is_between = 1;
3588
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween");
3589
+ break;
3590
+ }
3591
+
3592
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING)
3593
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning");
3594
+
3595
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION)
3596
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information");
3597
+
3598
+ if (validation->ignore_blank)
3599
+ LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1);
3600
+
3601
+ if (validation->dropdown == LXW_VALIDATION_OFF)
3602
+ LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1);
3603
+
3604
+ if (validation->show_input)
3605
+ LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1);
3606
+
3607
+ if (validation->show_error)
3608
+ LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1);
3609
+
3610
+ if (validation->error_title)
3611
+ LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title);
3612
+
3613
+ if (validation->error_message)
3614
+ LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message);
3615
+
3616
+ if (validation->input_title)
3617
+ LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title);
3618
+
3619
+ if (validation->input_message)
3620
+ LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message);
3621
+
3622
+ LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref);
3623
+
3624
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY)
3625
+ lxw_xml_empty_tag(self->file, "dataValidation", &attributes);
3626
+ else
3627
+ lxw_xml_start_tag(self->file, "dataValidation", &attributes);
3628
+
3629
+ /* Write the formula1 and formula2 elements. */
3630
+ switch (validation->validate) {
3631
+ case LXW_VALIDATION_TYPE_INTEGER:
3632
+ case LXW_VALIDATION_TYPE_DECIMAL:
3633
+ case LXW_VALIDATION_TYPE_LENGTH:
3634
+ case LXW_VALIDATION_TYPE_DATE:
3635
+ case LXW_VALIDATION_TYPE_TIME:
3636
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3637
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3638
+ _worksheet_write_formula1_num(self, validation->value_number);
3639
+ if (is_between)
3640
+ _worksheet_write_formula2_num(self,
3641
+ validation->maximum_number);
3642
+ break;
3643
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3644
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3645
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3646
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3647
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3648
+ case LXW_VALIDATION_TYPE_LIST:
3649
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3650
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3651
+ _worksheet_write_formula1_str(self, validation->value_formula);
3652
+ if (is_between)
3653
+ _worksheet_write_formula2_str(self,
3654
+ validation->maximum_formula);
3655
+ break;
3656
+ }
3657
+
3658
+ if (validation->validate != LXW_VALIDATION_TYPE_ANY)
3659
+ lxw_xml_end_tag(self->file, "dataValidation");
3660
+
3661
+ LXW_FREE_ATTRIBUTES();
3662
+ }
3663
+
3664
+ /*
3665
+ * Write the <dataValidations> element.
3666
+ */
3667
+ STATIC void
3668
+ _worksheet_write_data_validations(lxw_worksheet *self)
3669
+ {
3670
+ struct xml_attribute_list attributes;
3671
+ struct xml_attribute *attribute;
3672
+ lxw_data_validation *data_validation;
3673
+
3674
+ if (self->num_validations == 0)
3675
+ return;
3676
+
3677
+ LXW_INIT_ATTRIBUTES();
3678
+ LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations);
3679
+
3680
+ lxw_xml_start_tag(self->file, "dataValidations", &attributes);
3681
+
3682
+ STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) {
3683
+ /* Write the dataValidation element. */
3684
+ _worksheet_write_data_validation(self, data_validation);
3685
+ }
3686
+
3687
+ lxw_xml_end_tag(self->file, "dataValidations");
3688
+
3689
+ LXW_FREE_ATTRIBUTES();
3690
+ }
3691
+
3315
3692
  /*
3316
3693
  * Assemble and write the XML file.
3317
3694
  */
@@ -3354,6 +3731,9 @@ lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
3354
3731
  /* Write the mergeCells element. */
3355
3732
  _worksheet_write_merge_cells(self);
3356
3733
 
3734
+ /* Write the dataValidations element. */
3735
+ _worksheet_write_data_validations(self);
3736
+
3357
3737
  /* Write the hyperlink element. */
3358
3738
  _worksheet_write_hyperlinks(self);
3359
3739
 
@@ -3431,7 +3811,7 @@ worksheet_write_string(lxw_worksheet *self,
3431
3811
  if (format)
3432
3812
  return worksheet_write_blank(self, row_num, col_num, format);
3433
3813
  else
3434
- return LXW_ERROR_NULL_PARAMETER_IGNORED;
3814
+ return LXW_NO_ERROR;
3435
3815
  }
3436
3816
 
3437
3817
  err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
@@ -4010,6 +4390,14 @@ worksheet_set_column_opt(lxw_worksheet *self,
4010
4390
  copied_options = calloc(1, sizeof(lxw_col_options));
4011
4391
  RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED);
4012
4392
 
4393
+ /* Ensure the level is <= 7). */
4394
+ if (level > 7)
4395
+ level = 7;
4396
+
4397
+ if (level > self->outline_col_level)
4398
+ self->outline_col_level = level;
4399
+
4400
+ /* Set the column properties. */
4013
4401
  copied_options->firstcol = firstcol;
4014
4402
  copied_options->lastcol = lastcol;
4015
4403
  copied_options->width = width;
@@ -4082,6 +4470,14 @@ worksheet_set_row_opt(lxw_worksheet *self,
4082
4470
  height = self->default_row_height;
4083
4471
  }
4084
4472
 
4473
+ /* Ensure the level is <= 7). */
4474
+ if (level > 7)
4475
+ level = 7;
4476
+
4477
+ if (level > self->outline_row_level)
4478
+ self->outline_row_level = level;
4479
+
4480
+ /* Store the row properties. */
4085
4481
  row = _get_row(self, row_num);
4086
4482
 
4087
4483
  row->height = height;
@@ -4467,7 +4863,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string,
4467
4863
  lxw_header_footer_options *options)
4468
4864
  {
4469
4865
  if (options) {
4470
- if (options->margin > 0)
4866
+ if (options->margin >= 0.0)
4471
4867
  self->margin_header = options->margin;
4472
4868
  }
4473
4869
 
@@ -4491,7 +4887,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string,
4491
4887
  lxw_header_footer_options *options)
4492
4888
  {
4493
4889
  if (options) {
4494
- if (options->margin > 0)
4890
+ if (options->margin >= 0.0)
4495
4891
  self->margin_footer = options->margin;
4496
4892
  }
4497
4893
 
@@ -4819,11 +5215,23 @@ worksheet_protect(lxw_worksheet *self, const char *password,
4819
5215
  struct lxw_protection *protect = &self->protection;
4820
5216
 
4821
5217
  /* 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';
5218
+ if (options) {
5219
+ protect->no_select_locked_cells = options->no_select_locked_cells;
5220
+ protect->no_select_unlocked_cells = options->no_select_unlocked_cells;
5221
+ protect->format_cells = options->format_cells;
5222
+ protect->format_columns = options->format_columns;
5223
+ protect->format_rows = options->format_rows;
5224
+ protect->insert_columns = options->insert_columns;
5225
+ protect->insert_rows = options->insert_rows;
5226
+ protect->insert_hyperlinks = options->insert_hyperlinks;
5227
+ protect->delete_columns = options->delete_columns;
5228
+ protect->delete_rows = options->delete_rows;
5229
+ protect->sort = options->sort;
5230
+ protect->autofilter = options->autofilter;
5231
+ protect->pivot_tables = options->pivot_tables;
5232
+ protect->scenarios = options->scenarios;
5233
+ protect->objects = options->objects;
5234
+ }
4827
5235
 
4828
5236
  if (password) {
4829
5237
  uint16_t hash = _hash_password(password);
@@ -4833,6 +5241,23 @@ worksheet_protect(lxw_worksheet *self, const char *password,
4833
5241
  protect->is_configured = LXW_TRUE;
4834
5242
  }
4835
5243
 
5244
+ /*
5245
+ * Set the worksheet properties for outlines and grouping.
5246
+ */
5247
+ void
5248
+ worksheet_outline_settings(lxw_worksheet *self,
5249
+ uint8_t visible,
5250
+ uint8_t symbols_below,
5251
+ uint8_t symbols_right, uint8_t auto_style)
5252
+ {
5253
+ self->outline_on = visible;
5254
+ self->outline_below = symbols_below;
5255
+ self->outline_right = symbols_right;
5256
+ self->outline_style = auto_style;
5257
+
5258
+ self->outline_changed = LXW_TRUE;
5259
+ }
5260
+
4836
5261
  /*
4837
5262
  * Set the default row properties
4838
5263
  */
@@ -4899,9 +5324,10 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4899
5324
  }
4900
5325
 
4901
5326
  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);
5327
+ options->x_offset = user_options->x_offset;
5328
+ options->y_offset = user_options->y_offset;
5329
+ options->x_scale = user_options->x_scale;
5330
+ options->y_scale = user_options->y_scale;
4905
5331
  }
4906
5332
 
4907
5333
  /* Copy other options or set defaults. */
@@ -4919,10 +5345,12 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4919
5345
 
4920
5346
  if (_get_image_properties(options) == LXW_NO_ERROR) {
4921
5347
  STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
5348
+ fclose(image_stream);
4922
5349
  return LXW_NO_ERROR;
4923
5350
  }
4924
5351
  else {
4925
5352
  free(options);
5353
+ fclose(image_stream);
4926
5354
  return LXW_ERROR_IMAGE_DIMENSIONS;
4927
5355
  }
4928
5356
  }
@@ -4984,8 +5412,12 @@ worksheet_insert_chart_opt(lxw_worksheet *self,
4984
5412
  options = calloc(1, sizeof(lxw_image_options));
4985
5413
  RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED);
4986
5414
 
4987
- if (user_options)
4988
- memcpy(options, user_options, sizeof(lxw_image_options));
5415
+ if (user_options) {
5416
+ options->x_offset = user_options->x_offset;
5417
+ options->y_offset = user_options->y_offset;
5418
+ options->x_scale = user_options->x_scale;
5419
+ options->y_scale = user_options->y_scale;
5420
+ }
4989
5421
 
4990
5422
  /* Copy other options or set defaults. */
4991
5423
  options->row = row_num;
@@ -5020,3 +5452,288 @@ worksheet_insert_chart(lxw_worksheet *self,
5020
5452
  {
5021
5453
  return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
5022
5454
  }
5455
+
5456
+ /*
5457
+ * Add a data validation to a worksheet, for a range. Ironically this requires
5458
+ * a lot of validation of the user input.
5459
+ */
5460
+ lxw_error
5461
+ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row,
5462
+ lxw_col_t first_col,
5463
+ lxw_row_t last_row,
5464
+ lxw_col_t last_col,
5465
+ lxw_data_validation *validation)
5466
+ {
5467
+ lxw_data_validation *copy;
5468
+ uint8_t is_between = LXW_FALSE;
5469
+ uint8_t is_formula = LXW_FALSE;
5470
+ uint8_t has_criteria = LXW_TRUE;
5471
+ lxw_error err;
5472
+ lxw_row_t tmp_row;
5473
+ lxw_col_t tmp_col;
5474
+ size_t length;
5475
+
5476
+ /* No action is required for validation type 'any' unless there are
5477
+ * input messages to display.*/
5478
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY
5479
+ && !(validation->input_title || validation->input_message)) {
5480
+
5481
+ return LXW_NO_ERROR;
5482
+ }
5483
+
5484
+ /* Check for formula types. */
5485
+ switch (validation->validate) {
5486
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
5487
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
5488
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5489
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
5490
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
5491
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
5492
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5493
+ is_formula = LXW_TRUE;
5494
+ break;
5495
+ }
5496
+
5497
+ /* Check for types without a criteria. */
5498
+ switch (validation->validate) {
5499
+ case LXW_VALIDATION_TYPE_LIST:
5500
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5501
+ case LXW_VALIDATION_TYPE_ANY:
5502
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5503
+ has_criteria = LXW_FALSE;
5504
+ break;
5505
+ }
5506
+
5507
+ /* Check that a validation parameter has been specified
5508
+ * except for 'list', 'any' and 'custom'. */
5509
+ if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) {
5510
+
5511
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5512
+ "criteria parameter must be specified.");
5513
+ return LXW_ERROR_PARAMETER_VALIDATION;
5514
+ }
5515
+
5516
+ /* Check for "between" criteria so we can do additional checks. */
5517
+ if (has_criteria
5518
+ && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN
5519
+ || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) {
5520
+
5521
+ is_between = LXW_TRUE;
5522
+ }
5523
+
5524
+ /* Check that formula values are non NULL. */
5525
+ if (is_formula) {
5526
+ if (is_between) {
5527
+ if (!validation->minimum_formula) {
5528
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5529
+ "minimum_formula parameter cannot be NULL.");
5530
+ return LXW_ERROR_PARAMETER_VALIDATION;
5531
+ }
5532
+ if (!validation->maximum_formula) {
5533
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5534
+ "maximum_formula parameter cannot be NULL.");
5535
+ return LXW_ERROR_PARAMETER_VALIDATION;
5536
+ }
5537
+ }
5538
+ else {
5539
+ if (!validation->value_formula) {
5540
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5541
+ "formula parameter cannot be NULL.");
5542
+ return LXW_ERROR_PARAMETER_VALIDATION;
5543
+ }
5544
+ }
5545
+ }
5546
+
5547
+ /* Check Excel limitations on input strings. */
5548
+ if (validation->input_title) {
5549
+ length = lxw_utf8_strlen(validation->input_title);
5550
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5551
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5552
+ "input_title length > Excel limit of %d.",
5553
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5554
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5555
+ }
5556
+ }
5557
+
5558
+ if (validation->error_title) {
5559
+ length = lxw_utf8_strlen(validation->error_title);
5560
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5561
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5562
+ "error_title length > Excel limit of %d.",
5563
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5564
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5565
+ }
5566
+ }
5567
+
5568
+ if (validation->input_message) {
5569
+ length = lxw_utf8_strlen(validation->input_message);
5570
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5571
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5572
+ "input_message length > Excel limit of %d.",
5573
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5574
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5575
+ }
5576
+ }
5577
+
5578
+ if (validation->error_message) {
5579
+ length = lxw_utf8_strlen(validation->error_message);
5580
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5581
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5582
+ "error_message length > Excel limit of %d.",
5583
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5584
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5585
+ }
5586
+ }
5587
+
5588
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
5589
+ length = _validation_list_length(validation->value_list);
5590
+
5591
+ if (length == 0) {
5592
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5593
+ "list parameters cannot be zero.");
5594
+ return LXW_ERROR_PARAMETER_VALIDATION;
5595
+ }
5596
+
5597
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5598
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5599
+ "list length with commas > Excel limit of %d.",
5600
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5601
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5602
+ }
5603
+ }
5604
+
5605
+ /* Swap last row/col with first row/col as necessary */
5606
+ if (first_row > last_row) {
5607
+ tmp_row = last_row;
5608
+ last_row = first_row;
5609
+ first_row = tmp_row;
5610
+ }
5611
+ if (first_col > last_col) {
5612
+ tmp_col = last_col;
5613
+ last_col = first_col;
5614
+ first_col = tmp_col;
5615
+ }
5616
+
5617
+ /* Check that dimensions are valid but don't store them. */
5618
+ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE);
5619
+ if (err)
5620
+ return err;
5621
+
5622
+ /* Create a copy of the parameters from the user data validation. */
5623
+ copy = calloc(1, sizeof(lxw_data_validation));
5624
+ GOTO_LABEL_ON_MEM_ERROR(copy, mem_error);
5625
+
5626
+ /* Create the data validation range. */
5627
+ if (first_row == last_row && first_col == last_col)
5628
+ lxw_rowcol_to_cell(copy->sqref, first_row, last_col);
5629
+ else
5630
+ lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row,
5631
+ last_col);
5632
+
5633
+ /* Copy the parameters from the user data validation. */
5634
+ copy->validate = validation->validate;
5635
+ copy->value_number = validation->value_number;
5636
+ copy->error_type = validation->error_type;
5637
+ copy->dropdown = validation->dropdown;
5638
+ copy->is_between = is_between;
5639
+
5640
+ if (has_criteria)
5641
+ copy->criteria = validation->criteria;
5642
+
5643
+ if (is_between) {
5644
+ copy->value_number = validation->minimum_number;
5645
+ copy->maximum_number = validation->maximum_number;
5646
+ }
5647
+
5648
+ /* Copy the input/error titles and messages. */
5649
+ if (validation->input_title) {
5650
+ copy->input_title = lxw_strdup_formula(validation->input_title);
5651
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error);
5652
+ }
5653
+
5654
+ if (validation->input_message) {
5655
+ copy->input_message = lxw_strdup_formula(validation->input_message);
5656
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error);
5657
+ }
5658
+
5659
+ if (validation->error_title) {
5660
+ copy->error_title = lxw_strdup_formula(validation->error_title);
5661
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error);
5662
+ }
5663
+
5664
+ if (validation->error_message) {
5665
+ copy->error_message = lxw_strdup_formula(validation->error_message);
5666
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error);
5667
+ }
5668
+
5669
+ /* Copy the formula strings. */
5670
+ if (is_formula) {
5671
+ if (is_between) {
5672
+ copy->value_formula =
5673
+ lxw_strdup_formula(validation->minimum_formula);
5674
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5675
+ copy->maximum_formula =
5676
+ lxw_strdup_formula(validation->maximum_formula);
5677
+ GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error);
5678
+ }
5679
+ else {
5680
+ copy->value_formula =
5681
+ lxw_strdup_formula(validation->value_formula);
5682
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5683
+ }
5684
+ }
5685
+
5686
+ /* Copy the validation list as a csv string. */
5687
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
5688
+ copy->value_formula = _validation_list_to_csv(validation->value_list);
5689
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5690
+ }
5691
+
5692
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST_FORMULA) {
5693
+ copy->value_formula = lxw_strdup_formula(validation->value_formula);
5694
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
5695
+ }
5696
+
5697
+ if (validation->validate == LXW_VALIDATION_TYPE_DATE
5698
+ || validation->validate == LXW_VALIDATION_TYPE_TIME) {
5699
+ if (is_between) {
5700
+ copy->value_number =
5701
+ lxw_datetime_to_excel_date(&validation->minimum_datetime,
5702
+ LXW_EPOCH_1900);
5703
+ copy->maximum_number =
5704
+ lxw_datetime_to_excel_date(&validation->maximum_datetime,
5705
+ LXW_EPOCH_1900);
5706
+ }
5707
+ else {
5708
+ copy->value_number =
5709
+ lxw_datetime_to_excel_date(&validation->value_datetime,
5710
+ LXW_EPOCH_1900);
5711
+ }
5712
+ }
5713
+
5714
+ /* These options are on by default so we can't take plain booleans. */
5715
+ copy->ignore_blank = validation->ignore_blank ^ 1;
5716
+ copy->show_input = validation->show_input ^ 1;
5717
+ copy->show_error = validation->show_error ^ 1;
5718
+
5719
+ STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers);
5720
+
5721
+ self->num_validations++;
5722
+
5723
+ return LXW_NO_ERROR;
5724
+
5725
+ mem_error:
5726
+ _free_data_validation(copy);
5727
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5728
+ }
5729
+
5730
+ /*
5731
+ * Add a data validation to a worksheet, for a cell.
5732
+ */
5733
+ lxw_error
5734
+ worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row,
5735
+ lxw_col_t col, lxw_data_validation *validation)
5736
+ {
5737
+ return worksheet_data_validation_range(self, row, col,
5738
+ row, col, validation);
5739
+ }