fast_excel 0.6.0 → 0.6.1

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +3 -1
  4. data/Rakefile +4 -1
  5. data/fast_excel.gemspec +1 -1
  6. data/lib/fast_excel/binding/workbook.rb +9 -0
  7. data/lib/fast_excel/binding/worksheet.rb +2 -2
  8. data/lib/fast_excel/binding.rb +16 -13
  9. data/libxlsxwriter/.cirrus.yml +9 -0
  10. data/libxlsxwriter/.github/workflows/cmake_actions.yml +10 -6
  11. data/libxlsxwriter/.github/workflows/code_style.yml +2 -0
  12. data/libxlsxwriter/.github/workflows/make_32_actions.yml +43 -0
  13. data/libxlsxwriter/.github/workflows/windows_build.yml +7 -6
  14. data/libxlsxwriter/.github/workflows/zig_build.yml +1 -3
  15. data/libxlsxwriter/.gitignore +9 -7
  16. data/libxlsxwriter/.indent.pro +6 -0
  17. data/libxlsxwriter/CMakeLists.txt +377 -238
  18. data/libxlsxwriter/Changes.txt +189 -0
  19. data/libxlsxwriter/LICENSE.txt +1 -1
  20. data/libxlsxwriter/Makefile +36 -4
  21. data/libxlsxwriter/Readme.md +1 -1
  22. data/libxlsxwriter/build.zig +181 -92
  23. data/libxlsxwriter/build.zig.zon +15 -4
  24. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +4 -0
  25. data/libxlsxwriter/include/xlsxwriter/app.h +2 -1
  26. data/libxlsxwriter/include/xlsxwriter/chart.h +134 -1
  27. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +2 -1
  28. data/libxlsxwriter/include/xlsxwriter/comment.h +2 -1
  29. data/libxlsxwriter/include/xlsxwriter/common.h +12 -8
  30. data/libxlsxwriter/include/xlsxwriter/content_types.h +3 -1
  31. data/libxlsxwriter/include/xlsxwriter/core.h +2 -1
  32. data/libxlsxwriter/include/xlsxwriter/custom.h +2 -1
  33. data/libxlsxwriter/include/xlsxwriter/drawing.h +2 -1
  34. data/libxlsxwriter/include/xlsxwriter/format.h +34 -3
  35. data/libxlsxwriter/include/xlsxwriter/hash_table.h +2 -1
  36. data/libxlsxwriter/include/xlsxwriter/metadata.h +5 -1
  37. data/libxlsxwriter/include/xlsxwriter/packager.h +6 -1
  38. data/libxlsxwriter/include/xlsxwriter/relationships.h +3 -1
  39. data/libxlsxwriter/include/xlsxwriter/rich_value.h +51 -0
  40. data/libxlsxwriter/include/xlsxwriter/rich_value_rel.h +50 -0
  41. data/libxlsxwriter/include/xlsxwriter/rich_value_structure.h +53 -0
  42. data/libxlsxwriter/include/xlsxwriter/rich_value_types.h +49 -0
  43. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +2 -1
  44. data/libxlsxwriter/include/xlsxwriter/styles.h +2 -1
  45. data/libxlsxwriter/include/xlsxwriter/table.h +2 -1
  46. data/libxlsxwriter/include/xlsxwriter/theme.h +2 -1
  47. data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +4 -4
  48. data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +3 -3
  49. data/libxlsxwriter/include/xlsxwriter/utility.h +58 -7
  50. data/libxlsxwriter/include/xlsxwriter/vml.h +2 -1
  51. data/libxlsxwriter/include/xlsxwriter/workbook.h +59 -2
  52. data/libxlsxwriter/include/xlsxwriter/worksheet.h +159 -13
  53. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +11 -10
  54. data/libxlsxwriter/include/xlsxwriter.h +5 -4
  55. data/libxlsxwriter/libxlsxwriter.podspec +6 -6
  56. data/libxlsxwriter/src/Makefile +2 -1
  57. data/libxlsxwriter/src/app.c +2 -1
  58. data/libxlsxwriter/src/chart.c +360 -63
  59. data/libxlsxwriter/src/chartsheet.c +2 -1
  60. data/libxlsxwriter/src/comment.c +2 -1
  61. data/libxlsxwriter/src/content_types.c +22 -1
  62. data/libxlsxwriter/src/core.c +2 -1
  63. data/libxlsxwriter/src/custom.c +2 -1
  64. data/libxlsxwriter/src/drawing.c +7 -3
  65. data/libxlsxwriter/src/format.c +21 -1
  66. data/libxlsxwriter/src/hash_table.c +2 -1
  67. data/libxlsxwriter/src/metadata.c +176 -20
  68. data/libxlsxwriter/src/packager.c +306 -6
  69. data/libxlsxwriter/src/relationships.c +25 -1
  70. data/libxlsxwriter/src/rich_value.c +191 -0
  71. data/libxlsxwriter/src/rich_value_rel.c +142 -0
  72. data/libxlsxwriter/src/rich_value_structure.c +162 -0
  73. data/libxlsxwriter/src/rich_value_types.c +199 -0
  74. data/libxlsxwriter/src/shared_strings.c +2 -1
  75. data/libxlsxwriter/src/styles.c +31 -7
  76. data/libxlsxwriter/src/table.c +2 -1
  77. data/libxlsxwriter/src/theme.c +2 -1
  78. data/libxlsxwriter/src/utility.c +136 -46
  79. data/libxlsxwriter/src/vml.c +2 -1
  80. data/libxlsxwriter/src/workbook.c +191 -10
  81. data/libxlsxwriter/src/worksheet.c +514 -34
  82. data/libxlsxwriter/src/xmlwriter.c +17 -22
  83. data/libxlsxwriter/third_party/minizip/Makefile.orig +1 -1
  84. data/libxlsxwriter/third_party/minizip/configure.ac +1 -1
  85. data/libxlsxwriter/third_party/minizip/ioapi.h +6 -6
  86. data/libxlsxwriter/third_party/minizip/miniunz.c +16 -2
  87. data/libxlsxwriter/third_party/minizip/unzip.c +4 -4
  88. data/libxlsxwriter/third_party/minizip/unzip.h +1 -1
  89. data/libxlsxwriter/third_party/minizip/zip.c +5 -5
  90. data/libxlsxwriter/third_party/minizip/zip.h +3 -3
  91. data/libxlsxwriter/version.txt +1 -1
  92. data/test/validations_test.rb +3 -3
  93. data/test/worksheet_test.rb +10 -2
  94. metadata +11 -4
  95. data/libxlsxwriter/cmake/FindMINIZIP.cmake +0 -121
  96. data/libxlsxwriter/cmake/FindPackage.cmake +0 -183
  97. data/libxlsxwriter/cmake/i686-toolchain.cmake +0 -7
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * SPDX-License-Identifier: BSD-2-Clause
7
+ * Copyright 2014-2026, John McNamara, jmcnamara@cpan.org.
7
8
  *
8
9
  */
9
10
 
@@ -141,6 +142,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
141
142
  GOTO_LABEL_ON_MEM_ERROR(worksheet->image_props, mem_error);
142
143
  STAILQ_INIT(worksheet->image_props);
143
144
 
145
+ worksheet->embedded_image_props =
146
+ calloc(1, sizeof(struct lxw_embedded_image_props));
147
+ GOTO_LABEL_ON_MEM_ERROR(worksheet->embedded_image_props, mem_error);
148
+ STAILQ_INIT(worksheet->embedded_image_props);
149
+
144
150
  worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_props));
145
151
  GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error);
146
152
  STAILQ_INIT(worksheet->chart_data);
@@ -288,6 +294,7 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
288
294
  worksheet->first_sheet = init_data->first_sheet;
289
295
  worksheet->default_url_format = init_data->default_url_format;
290
296
  worksheet->max_url_length = init_data->max_url_length;
297
+ worksheet->use_1904_epoch = init_data->use_1904_epoch;
291
298
  }
292
299
 
293
300
  return worksheet;
@@ -367,7 +374,8 @@ _free_cell(lxw_cell *cell)
367
374
  return;
368
375
 
369
376
  if (cell->type != NUMBER_CELL && cell->type != STRING_CELL
370
- && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) {
377
+ && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL
378
+ && cell->type != ERROR_CELL) {
371
379
 
372
380
  free((void *) cell->u.string);
373
381
  }
@@ -606,6 +614,17 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
606
614
  free(worksheet->image_props);
607
615
  }
608
616
 
617
+ if (worksheet->embedded_image_props) {
618
+ while (!STAILQ_EMPTY(worksheet->embedded_image_props)) {
619
+ object_props = STAILQ_FIRST(worksheet->embedded_image_props);
620
+ STAILQ_REMOVE_HEAD(worksheet->embedded_image_props,
621
+ list_pointers);
622
+ _free_object_properties(object_props);
623
+ }
624
+
625
+ free(worksheet->embedded_image_props);
626
+ }
627
+
609
628
  if (worksheet->chart_data) {
610
629
  while (!STAILQ_EMPTY(worksheet->chart_data)) {
611
630
  object_props = STAILQ_FIRST(worksheet->chart_data);
@@ -990,6 +1009,25 @@ _new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value,
990
1009
  return cell;
991
1010
  }
992
1011
 
1012
+ /*
1013
+ * Create a new worksheet error cell object.
1014
+ */
1015
+ STATIC lxw_cell *
1016
+ _new_error_cell(lxw_row_t row_num, lxw_col_t col_num, uint32_t value,
1017
+ lxw_format *format)
1018
+ {
1019
+ lxw_cell *cell = calloc(1, sizeof(lxw_cell));
1020
+ RETURN_ON_MEM_ERROR(cell, cell);
1021
+
1022
+ cell->row_num = row_num;
1023
+ cell->col_num = col_num;
1024
+ cell->type = ERROR_CELL;
1025
+ cell->format = format;
1026
+ cell->u.number = value;
1027
+
1028
+ return cell;
1029
+ }
1030
+
993
1031
  /*
994
1032
  * Create a new comment cell object.
995
1033
  */
@@ -1454,13 +1492,13 @@ lxw_basename(const char *path)
1454
1492
  size_t
1455
1493
  _validation_list_length(const char **list)
1456
1494
  {
1457
- uint8_t i = 0;
1495
+ size_t i = 0;
1458
1496
  size_t length = 0;
1459
1497
 
1460
1498
  if (!list || !list[0])
1461
1499
  return 0;
1462
1500
 
1463
- while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) {
1501
+ while (list[i]) {
1464
1502
  /* Include commas in the length. */
1465
1503
  length += 1 + lxw_utf8_strlen(list[i]);
1466
1504
  i++;
@@ -1477,7 +1515,7 @@ _validation_list_length(const char **list)
1477
1515
  char *
1478
1516
  _validation_list_to_csv(const char **list)
1479
1517
  {
1480
- uint8_t i = 0;
1518
+ size_t i = 0;
1481
1519
  char *str;
1482
1520
 
1483
1521
  /* Create a buffer for the concatenated, and quoted, string. */
@@ -1651,7 +1689,7 @@ _set_default_table_columns(lxw_table_obj *table_obj)
1651
1689
  for (i = 0; i < num_cols; i++) {
1652
1690
  lxw_snprintf(col_name, LXW_ATTR_32, "Column%d", i + 1);
1653
1691
 
1654
- column = calloc(num_cols, sizeof(lxw_table_column));
1692
+ column = calloc(1, sizeof(lxw_table_column));
1655
1693
  RETURN_ON_MEM_ERROR(column, LXW_ERROR_MEMORY_MALLOC_FAILED);
1656
1694
 
1657
1695
  header = lxw_strdup(col_name);
@@ -1680,9 +1718,11 @@ _expand_table_formula(const char *formula)
1680
1718
 
1681
1719
  ptr = formula;
1682
1720
 
1683
- while (*ptr++) {
1721
+ while (*ptr) {
1684
1722
  if (*ptr == '@')
1685
1723
  ref_count++;
1724
+
1725
+ ptr++;
1686
1726
  }
1687
1727
 
1688
1728
  if (ref_count == 0) {
@@ -2967,13 +3007,15 @@ _worksheet_position_object_pixels(lxw_worksheet *self,
2967
3007
  height = height + y1;
2968
3008
 
2969
3009
  /* Subtract the underlying cell widths to find the end cell. */
2970
- while (width >= _worksheet_size_col(self, col_end, anchor)) {
3010
+ while (width >= _worksheet_size_col(self, col_end, anchor)
3011
+ && col_end < LXW_COL_MAX) {
2971
3012
  width -= _worksheet_size_col(self, col_end, anchor);
2972
3013
  col_end++;
2973
3014
  }
2974
3015
 
2975
3016
  /* Subtract the underlying cell heights to find the end cell. */
2976
- while (height >= _worksheet_size_row(self, row_end, anchor)) {
3017
+ while (height >= _worksheet_size_row(self, row_end, anchor)
3018
+ && row_end < LXW_ROW_MAX) {
2977
3019
  height -= _worksheet_size_row(self, row_end, anchor);
2978
3020
  row_end++;
2979
3021
  }
@@ -4138,11 +4180,13 @@ _process_bmp(lxw_object_properties *image_props)
4138
4180
  height = 0;
4139
4181
 
4140
4182
  /* Ensure that we read some valid data from the file. */
4141
- if (width == 0)
4183
+ if (width == 0 || height == 0)
4142
4184
  goto file_error;
4143
4185
 
4144
- height = LXW_UINT32_HOST(height);
4145
- width = LXW_UINT32_HOST(width);
4186
+ /* The height can be stored as negative for a top-down DIB so we need to
4187
+ * take the absolute value. */
4188
+ height = (uint32_t) abs(LXW_INT32_HOST(height));
4189
+ width = (uint32_t) LXW_INT32_HOST(width);
4146
4190
 
4147
4191
  /* Set the image metadata. */
4148
4192
  image_props->image_type = LXW_IMAGE_BMP;
@@ -4521,6 +4565,15 @@ _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell)
4521
4565
  lxw_xml_data_element(self->file, "v", data, NULL);
4522
4566
  }
4523
4567
 
4568
+ /*
4569
+ * Write out a error worksheet cell.
4570
+ */
4571
+ STATIC void
4572
+ _write_error_cell(lxw_worksheet *self)
4573
+ {
4574
+ lxw_xml_data_element(self->file, "v", "#VALUE!", NULL);
4575
+ }
4576
+
4524
4577
  /*
4525
4578
  * Calculate the "spans" attribute of the <row> tag. This is an XLSX
4526
4579
  * optimization and isn't strictly required. However, it makes comparing
@@ -4651,6 +4704,13 @@ _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format)
4651
4704
  _write_array_formula_num_cell(self, cell);
4652
4705
  lxw_xml_end_tag(self->file, "c");
4653
4706
  }
4707
+ else if (cell->type == ERROR_CELL) {
4708
+ LXW_PUSH_ATTRIBUTES_STR("t", "e");
4709
+ LXW_PUSH_ATTRIBUTES_DBL("vm", cell->u.number);
4710
+ lxw_xml_start_tag(self->file, "c", &attributes);
4711
+ _write_error_cell(self);
4712
+ lxw_xml_end_tag(self->file, "c");
4713
+ }
4654
4714
 
4655
4715
  LXW_FREE_ATTRIBUTES();
4656
4716
  }
@@ -7492,6 +7552,105 @@ _validate_conditional_cell(lxw_cond_format_obj *cond_format,
7492
7552
  return LXW_NO_ERROR;
7493
7553
  }
7494
7554
 
7555
+ /* Check that the correct criteria and used with the correct conditional format. */
7556
+ lxw_error
7557
+ _validate_conditional_criteria(lxw_cond_format_obj *cond_format)
7558
+ {
7559
+ uint8_t criteria_mismatch = LXW_FALSE;
7560
+
7561
+ if (cond_format->type == LXW_CONDITIONAL_TYPE_CELL) {
7562
+ switch (cond_format->criteria) {
7563
+ case LXW_CONDITIONAL_CRITERIA_EQUAL_TO:
7564
+ case LXW_CONDITIONAL_CRITERIA_NOT_EQUAL_TO:
7565
+ case LXW_CONDITIONAL_CRITERIA_GREATER_THAN:
7566
+ case LXW_CONDITIONAL_CRITERIA_LESS_THAN:
7567
+ case LXW_CONDITIONAL_CRITERIA_GREATER_THAN_OR_EQUAL_TO:
7568
+ case LXW_CONDITIONAL_CRITERIA_LESS_THAN_OR_EQUAL_TO:
7569
+ case LXW_CONDITIONAL_CRITERIA_BETWEEN:
7570
+ case LXW_CONDITIONAL_CRITERIA_NOT_BETWEEN:
7571
+ criteria_mismatch = LXW_FALSE;
7572
+ break;
7573
+ default:
7574
+ criteria_mismatch = LXW_TRUE;
7575
+ }
7576
+ }
7577
+ else if (cond_format->type == LXW_CONDITIONAL_TYPE_TIME_PERIOD) {
7578
+ switch (cond_format->criteria) {
7579
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_YESTERDAY:
7580
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TODAY:
7581
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_TOMORROW:
7582
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_7_DAYS:
7583
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_WEEK:
7584
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_WEEK:
7585
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_WEEK:
7586
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_LAST_MONTH:
7587
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_THIS_MONTH:
7588
+ case LXW_CONDITIONAL_CRITERIA_TIME_PERIOD_NEXT_MONTH:
7589
+ criteria_mismatch = LXW_FALSE;
7590
+ break;
7591
+ default:
7592
+ criteria_mismatch = LXW_TRUE;
7593
+ }
7594
+ }
7595
+ else if (cond_format->type == LXW_CONDITIONAL_TYPE_TEXT) {
7596
+ switch (cond_format->criteria) {
7597
+ case LXW_CONDITIONAL_CRITERIA_TEXT_CONTAINING:
7598
+ case LXW_CONDITIONAL_CRITERIA_TEXT_NOT_CONTAINING:
7599
+ case LXW_CONDITIONAL_CRITERIA_TEXT_BEGINS_WITH:
7600
+ case LXW_CONDITIONAL_CRITERIA_TEXT_ENDS_WITH:
7601
+ criteria_mismatch = LXW_FALSE;
7602
+ break;
7603
+ default:
7604
+ criteria_mismatch = LXW_TRUE;
7605
+ }
7606
+ }
7607
+ else if (cond_format->type == LXW_CONDITIONAL_TYPE_AVERAGE) {
7608
+ switch (cond_format->criteria) {
7609
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE:
7610
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW:
7611
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_ABOVE_OR_EQUAL:
7612
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_BELOW_OR_EQUAL:
7613
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_ABOVE:
7614
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_1_STD_DEV_BELOW:
7615
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_ABOVE:
7616
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_2_STD_DEV_BELOW:
7617
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_ABOVE:
7618
+ case LXW_CONDITIONAL_CRITERIA_AVERAGE_3_STD_DEV_BELOW:
7619
+ criteria_mismatch = LXW_FALSE;
7620
+ break;
7621
+ default:
7622
+ criteria_mismatch = LXW_TRUE;
7623
+ }
7624
+ }
7625
+ else if (cond_format->type == LXW_CONDITIONAL_TYPE_TOP
7626
+ || cond_format->type == LXW_CONDITIONAL_TYPE_BOTTOM) {
7627
+ switch (cond_format->criteria) {
7628
+ case LXW_CONDITIONAL_CRITERIA_NONE:
7629
+ case LXW_CONDITIONAL_CRITERIA_TOP_OR_BOTTOM_PERCENT:
7630
+ criteria_mismatch = LXW_FALSE;
7631
+ break;
7632
+ default:
7633
+ criteria_mismatch = LXW_TRUE;
7634
+ }
7635
+ }
7636
+ else {
7637
+ /* Any other conditional type should have a zero criteria. */
7638
+ cond_format->criteria = LXW_CONDITIONAL_CRITERIA_NONE;
7639
+ }
7640
+
7641
+ if (criteria_mismatch) {
7642
+ LXW_WARN_FORMAT2("worksheet_conditional_format_cell()/_range(): "
7643
+ "LXW_CONDITIONAL_CRITERIA_* = %d is not valid for "
7644
+ "LXW_CONDITIONAL_TYPE_* = %d", cond_format->criteria,
7645
+ cond_format->type);
7646
+
7647
+ return LXW_ERROR_PARAMETER_VALIDATION;
7648
+ }
7649
+ else {
7650
+ return LXW_NO_ERROR;
7651
+ }
7652
+ }
7653
+
7495
7654
  /*
7496
7655
  * Write the <ignoredErrors> element.
7497
7656
  */
@@ -7844,6 +8003,9 @@ worksheet_write_formula_num(lxw_worksheet *self,
7844
8003
  if (!formula)
7845
8004
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
7846
8005
 
8006
+ if (lxw_str_is_empty(formula))
8007
+ return LXW_ERROR_PARAMETER_IS_EMPTY;
8008
+
7847
8009
  err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
7848
8010
  if (err)
7849
8011
  return err;
@@ -7879,6 +8041,9 @@ worksheet_write_formula_str(lxw_worksheet *self,
7879
8041
  if (!formula)
7880
8042
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
7881
8043
 
8044
+ if (lxw_str_is_empty(formula))
8045
+ return LXW_ERROR_PARAMETER_IS_EMPTY;
8046
+
7882
8047
  err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
7883
8048
  if (err)
7884
8049
  return err;
@@ -7944,6 +8109,9 @@ _store_array_formula(lxw_worksheet *self,
7944
8109
  if (!formula)
7945
8110
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
7946
8111
 
8112
+ if (lxw_str_is_empty(formula))
8113
+ return LXW_ERROR_PARAMETER_IS_EMPTY;
8114
+
7947
8115
  /* Check that row and col are valid and store max and min values. */
7948
8116
  err = _check_dimensions(self, first_row, first_col, LXW_FALSE, LXW_FALSE);
7949
8117
  if (err)
@@ -7964,7 +8132,7 @@ _store_array_formula(lxw_worksheet *self,
7964
8132
 
7965
8133
  /* Copy and trip leading "{=" from formula. */
7966
8134
  if (formula[0] == '{')
7967
- if (formula[1] == '=')
8135
+ if (strlen(formula) >= 2 && formula[1] == '=')
7968
8136
  formula_copy = lxw_strdup(formula + 2);
7969
8137
  else
7970
8138
  formula_copy = lxw_strdup(formula + 1);
@@ -7972,8 +8140,17 @@ _store_array_formula(lxw_worksheet *self,
7972
8140
  formula_copy = lxw_strdup_formula(formula);
7973
8141
 
7974
8142
  /* Strip trailing "}" from formula. */
7975
- if (formula_copy[strlen(formula_copy) - 1] == '}')
8143
+ if (strlen(formula_copy) > 0
8144
+ && formula_copy[strlen(formula_copy) - 1] == '}') {
7976
8145
  formula_copy[strlen(formula_copy) - 1] = '\0';
8146
+ }
8147
+
8148
+ /* Check for empty formula that started as {=}. */
8149
+ if (lxw_str_is_empty(formula_copy)) {
8150
+ free(formula_copy);
8151
+ free(range);
8152
+ return LXW_ERROR_PARAMETER_IS_EMPTY;
8153
+ }
7977
8154
 
7978
8155
  /* Create a new array formula cell object. */
7979
8156
  cell = _new_array_formula_cell(first_row, first_col,
@@ -7984,7 +8161,7 @@ _store_array_formula(lxw_worksheet *self,
7984
8161
  _insert_cell(self, first_row, first_col, cell);
7985
8162
 
7986
8163
  if (is_dynamic)
7987
- self->has_dynamic_arrays = LXW_TRUE;
8164
+ self->has_dynamic_functions = LXW_TRUE;
7988
8165
 
7989
8166
  /* Pad out the rest of the area with formatted zeroes. */
7990
8167
  if (!self->optimize) {
@@ -8159,7 +8336,12 @@ worksheet_write_datetime(lxw_worksheet *self,
8159
8336
  if (err)
8160
8337
  return err;
8161
8338
 
8162
- excel_date = lxw_datetime_to_excel_date_epoch(datetime, LXW_EPOCH_1900);
8339
+ err = lxw_datetime_validate(datetime);
8340
+ if (err)
8341
+ return err;
8342
+
8343
+ excel_date =
8344
+ lxw_datetime_to_excel_date_with_epoch(datetime, self->use_1904_epoch);
8163
8345
 
8164
8346
  cell = _new_number_cell(row_num, col_num, excel_date, format);
8165
8347
 
@@ -8185,7 +8367,8 @@ worksheet_write_unixtime(lxw_worksheet *self,
8185
8367
  if (err)
8186
8368
  return err;
8187
8369
 
8188
- excel_date = lxw_unixtime_to_excel_date_epoch(unixtime, LXW_EPOCH_1900);
8370
+ excel_date =
8371
+ lxw_unixtime_to_excel_date_with_epoch(unixtime, self->use_1904_epoch);
8189
8372
 
8190
8373
  cell = _new_number_cell(row_num, col_num, excel_date, format);
8191
8374
 
@@ -8362,9 +8545,13 @@ worksheet_write_url_opt(lxw_worksheet *self,
8362
8545
  else
8363
8546
  format = user_format;
8364
8547
 
8365
- err = worksheet_write_string(self, row_num, col_num, string_copy, format);
8366
- if (err)
8367
- goto mem_error;
8548
+ if (!self->storing_embedded_image) {
8549
+ err =
8550
+ worksheet_write_string(self, row_num, col_num, string_copy,
8551
+ format);
8552
+ if (err)
8553
+ goto mem_error;
8554
+ }
8368
8555
 
8369
8556
  /* Reset default error condition. */
8370
8557
  err = LXW_ERROR_MEMORY_MALLOC_FAILED;
@@ -8419,7 +8606,7 @@ worksheet_write_rich_string(lxw_worksheet *self,
8419
8606
  int32_t string_id;
8420
8607
  struct sst_element *sst_element;
8421
8608
  lxw_error err;
8422
- uint8_t i;
8609
+ size_t i;
8423
8610
  long file_size;
8424
8611
  char *rich_string = NULL;
8425
8612
  const char *string_copy = NULL;
@@ -8570,6 +8757,9 @@ worksheet_write_comment_opt(lxw_worksheet *self,
8570
8757
  if (!text)
8571
8758
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
8572
8759
 
8760
+ if (lxw_str_is_empty(text))
8761
+ return LXW_ERROR_PARAMETER_IS_EMPTY;
8762
+
8573
8763
  if (lxw_utf8_strlen(text) > LXW_STR_MAX)
8574
8764
  return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED;
8575
8765
 
@@ -9433,7 +9623,7 @@ worksheet_hide(lxw_worksheet *self)
9433
9623
  /*
9434
9624
  * Set which cell or cells are selected in a worksheet.
9435
9625
  */
9436
- void
9626
+ lxw_error
9437
9627
  worksheet_set_selection(lxw_worksheet *self,
9438
9628
  lxw_row_t first_row, lxw_col_t first_col,
9439
9629
  lxw_row_t last_row, lxw_col_t last_col)
@@ -9441,19 +9631,33 @@ worksheet_set_selection(lxw_worksheet *self,
9441
9631
  lxw_selection *selection;
9442
9632
  lxw_row_t tmp_row;
9443
9633
  lxw_col_t tmp_col;
9634
+ lxw_error err;
9444
9635
  char active_cell[LXW_MAX_CELL_RANGE_LENGTH];
9445
9636
  char sqref[LXW_MAX_CELL_RANGE_LENGTH];
9446
9637
 
9447
9638
  /* Only allow selection to be set once to avoid freeing/re-creating it. */
9448
9639
  if (!STAILQ_EMPTY(self->selections))
9449
- return;
9640
+ return LXW_ERROR_PARAMETER_VALIDATION;
9450
9641
 
9451
9642
  /* Excel doesn't set a selection for cell A1 since it is the default. */
9452
9643
  if (first_row == 0 && first_col == 0 && last_row == 0 && last_col == 0)
9453
- return;
9644
+ return LXW_NO_ERROR;
9454
9645
 
9455
9646
  selection = calloc(1, sizeof(lxw_selection));
9456
- RETURN_VOID_ON_MEM_ERROR(selection);
9647
+ RETURN_ON_MEM_ERROR(selection, LXW_ERROR_MEMORY_MALLOC_FAILED);
9648
+
9649
+ /* Check that row and col are valid without storing. */
9650
+ err = _check_dimensions(self, first_row, first_col, LXW_TRUE, LXW_TRUE);
9651
+ if (err) {
9652
+ free(selection);
9653
+ return err;
9654
+ }
9655
+
9656
+ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE);
9657
+ if (err) {
9658
+ free(selection);
9659
+ return err;
9660
+ }
9457
9661
 
9458
9662
  /* Set the cell range selection. Do this before swapping max/min to */
9459
9663
  /* allow the selection direction to be reversed. */
@@ -9483,6 +9687,8 @@ worksheet_set_selection(lxw_worksheet *self,
9483
9687
  lxw_strcpy(selection->sqref, sqref);
9484
9688
 
9485
9689
  STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
9690
+
9691
+ return LXW_NO_ERROR;
9486
9692
  }
9487
9693
 
9488
9694
  /*
@@ -9591,6 +9797,12 @@ worksheet_set_page_view(lxw_worksheet *self)
9591
9797
  void
9592
9798
  worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size)
9593
9799
  {
9800
+ if (paper_size > 118) {
9801
+ LXW_WARN_FORMAT1("worksheet_set_paper(): invalid paper size: %d. "
9802
+ "Valid range is 0-118", paper_size);
9803
+ return;
9804
+ }
9805
+
9594
9806
  self->paper_size = paper_size;
9595
9807
  self->page_setup_changed = LXW_TRUE;
9596
9808
  }
@@ -10094,7 +10306,7 @@ worksheet_print_black_and_white(lxw_worksheet *self)
10094
10306
  lxw_error
10095
10307
  worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[])
10096
10308
  {
10097
- uint16_t count = 0;
10309
+ size_t count = 0;
10098
10310
 
10099
10311
  if (hbreaks == NULL)
10100
10312
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
@@ -10121,7 +10333,7 @@ worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[])
10121
10333
  lxw_error
10122
10334
  worksheet_set_v_pagebreaks(lxw_worksheet *self, lxw_col_t vbreaks[])
10123
10335
  {
10124
- uint16_t count = 0;
10336
+ size_t count = 0;
10125
10337
 
10126
10338
  if (vbreaks == NULL)
10127
10339
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
@@ -10311,9 +10523,9 @@ worksheet_insert_image_opt(lxw_worksheet *self,
10311
10523
  object_props->y_offset = user_options->y_offset;
10312
10524
  object_props->x_scale = user_options->x_scale;
10313
10525
  object_props->y_scale = user_options->y_scale;
10314
- object_props->object_position = user_options->object_position;
10315
10526
  object_props->url = lxw_strdup(user_options->url);
10316
10527
  object_props->tip = lxw_strdup(user_options->tip);
10528
+ object_props->object_position = user_options->object_position;
10317
10529
  object_props->decorative = user_options->decorative;
10318
10530
 
10319
10531
  if (user_options->description)
@@ -10467,6 +10679,253 @@ worksheet_insert_image_buffer(lxw_worksheet *self,
10467
10679
  image_buffer, image_size, NULL);
10468
10680
  }
10469
10681
 
10682
+ /*
10683
+ * Embed an image with options into the worksheet.
10684
+ */
10685
+ lxw_error
10686
+ worksheet_embed_image_opt(lxw_worksheet *self,
10687
+ lxw_row_t row_num, lxw_col_t col_num,
10688
+ const char *filename,
10689
+ lxw_image_options *user_options)
10690
+ {
10691
+ FILE *image_stream;
10692
+ lxw_object_properties *object_props;
10693
+ lxw_error err;
10694
+
10695
+ if (!filename) {
10696
+ LXW_WARN("worksheet_embed_image()/_opt(): "
10697
+ "filename must be specified.");
10698
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
10699
+ }
10700
+
10701
+ /* Check that the image file exists and can be opened. */
10702
+ image_stream = lxw_fopen(filename, "rb");
10703
+ if (!image_stream) {
10704
+ LXW_WARN_FORMAT1("worksheet_embed_image()/_opt(): "
10705
+ "file doesn't exist or can't be opened: %s.",
10706
+ filename);
10707
+ return LXW_ERROR_PARAMETER_VALIDATION;
10708
+ }
10709
+
10710
+ /* Check and store the cell dimensions. */
10711
+ err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
10712
+ if (err) {
10713
+ fclose(image_stream);
10714
+ return err;
10715
+ }
10716
+
10717
+ /* Create a new object to hold the image properties. */
10718
+ object_props = calloc(1, sizeof(lxw_object_properties));
10719
+ if (!object_props) {
10720
+ fclose(image_stream);
10721
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
10722
+ }
10723
+
10724
+ /* We only copy/use a limited number of options for embedded images. */
10725
+ if (user_options) {
10726
+ if (user_options->cell_format)
10727
+ object_props->format = user_options->cell_format;
10728
+
10729
+ /* The url for embedded images is written as a cell url. */
10730
+ if (user_options->url) {
10731
+ if (!user_options->cell_format)
10732
+ object_props->format = self->default_url_format;
10733
+
10734
+ self->storing_embedded_image = LXW_TRUE;
10735
+ err = worksheet_write_url(self,
10736
+ row_num,
10737
+ col_num,
10738
+ user_options->url,
10739
+ object_props->format);
10740
+ if (err) {
10741
+ _free_object_properties(object_props);
10742
+ fclose(image_stream);
10743
+ return err;
10744
+ }
10745
+
10746
+ self->storing_embedded_image = LXW_FALSE;
10747
+ }
10748
+
10749
+ object_props->decorative = user_options->decorative;
10750
+ if (user_options->description)
10751
+ object_props->description = lxw_strdup(user_options->description);
10752
+ }
10753
+
10754
+ /* Copy other options or set defaults. */
10755
+ object_props->filename = lxw_strdup(filename);
10756
+ object_props->stream = image_stream;
10757
+ object_props->row = row_num;
10758
+ object_props->col = col_num;
10759
+
10760
+ if (object_props->x_scale == 0.0)
10761
+ object_props->x_scale = 1;
10762
+
10763
+ if (object_props->y_scale == 0.0)
10764
+ object_props->y_scale = 1;
10765
+
10766
+ if (_get_image_properties(object_props) == LXW_NO_ERROR) {
10767
+ STAILQ_INSERT_TAIL(self->embedded_image_props, object_props,
10768
+ list_pointers);
10769
+ fclose(image_stream);
10770
+
10771
+ return LXW_NO_ERROR;
10772
+ }
10773
+ else {
10774
+ _free_object_properties(object_props);
10775
+ fclose(image_stream);
10776
+ return LXW_ERROR_IMAGE_DIMENSIONS;
10777
+ }
10778
+ }
10779
+
10780
+ /*
10781
+ * Embed an image into the worksheet.
10782
+ */
10783
+ lxw_error
10784
+ worksheet_embed_image(lxw_worksheet *self,
10785
+ lxw_row_t row_num, lxw_col_t col_num,
10786
+ const char *filename)
10787
+ {
10788
+ return worksheet_embed_image_opt(self, row_num, col_num, filename, NULL);
10789
+ }
10790
+
10791
+ /*
10792
+ * Embed an image buffer, with options, into the worksheet.
10793
+ */
10794
+ lxw_error
10795
+ worksheet_embed_image_buffer_opt(lxw_worksheet *self,
10796
+ lxw_row_t row_num,
10797
+ lxw_col_t col_num,
10798
+ const unsigned char *image_buffer,
10799
+ size_t image_size,
10800
+ lxw_image_options *user_options)
10801
+ {
10802
+ FILE *image_stream;
10803
+ lxw_object_properties *object_props;
10804
+ lxw_error err;
10805
+
10806
+ if (!image_size) {
10807
+ LXW_WARN("worksheet_embed_image_buffer()/_opt(): "
10808
+ "size must be non-zero.");
10809
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
10810
+ }
10811
+
10812
+ /* Write the image buffer to a file (preferably in memory) so we can read
10813
+ * the dimensions like an ordinary file. For embedded images we really only
10814
+ * need the image type. */
10815
+ #ifdef USE_FMEMOPEN
10816
+ image_stream = fmemopen((void *) image_buffer, image_size, "rb");
10817
+
10818
+ if (!image_stream)
10819
+ return LXW_ERROR_CREATING_TMPFILE;
10820
+ #else
10821
+ image_stream = lxw_tmpfile(self->tmpdir);
10822
+
10823
+ if (!image_stream)
10824
+ return LXW_ERROR_CREATING_TMPFILE;
10825
+
10826
+ if (fwrite(image_buffer, 1, image_size, image_stream) != image_size) {
10827
+ fclose(image_stream);
10828
+ return LXW_ERROR_CREATING_TMPFILE;
10829
+ }
10830
+
10831
+ rewind(image_stream);
10832
+ #endif
10833
+
10834
+ /* Check and store the cell dimensions. */
10835
+ err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
10836
+ if (err)
10837
+ return err;
10838
+
10839
+ /* Create a new object to hold the image properties. */
10840
+ object_props = calloc(1, sizeof(lxw_object_properties));
10841
+ if (!object_props) {
10842
+ fclose(image_stream);
10843
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
10844
+ }
10845
+
10846
+ /* Store the image data in the properties structure. */
10847
+ object_props->image_buffer = calloc(1, image_size);
10848
+ if (!object_props->image_buffer) {
10849
+ _free_object_properties(object_props);
10850
+ fclose(image_stream);
10851
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
10852
+ }
10853
+ else {
10854
+ memcpy(object_props->image_buffer, image_buffer, image_size);
10855
+ object_props->image_buffer_size = image_size;
10856
+ object_props->is_image_buffer = LXW_TRUE;
10857
+ }
10858
+
10859
+ /* We only copy/use a limited number of options for embedded images. */
10860
+ if (user_options) {
10861
+ if (user_options->cell_format)
10862
+ object_props->format = user_options->cell_format;
10863
+
10864
+ /* The url for embedded images is written as a cell url. */
10865
+ if (user_options->url) {
10866
+ if (!user_options->cell_format)
10867
+ object_props->format = self->default_url_format;
10868
+
10869
+ self->storing_embedded_image = LXW_TRUE;
10870
+ err = worksheet_write_url(self,
10871
+ row_num,
10872
+ col_num,
10873
+ user_options->url,
10874
+ object_props->format);
10875
+ if (err) {
10876
+ _free_object_properties(object_props);
10877
+ fclose(image_stream);
10878
+ return err;
10879
+ }
10880
+
10881
+ self->storing_embedded_image = LXW_FALSE;
10882
+ }
10883
+
10884
+ object_props->decorative = user_options->decorative;
10885
+ if (user_options->description)
10886
+ object_props->description = lxw_strdup(user_options->description);
10887
+ }
10888
+
10889
+ /* Copy other options or set defaults. */
10890
+ object_props->filename = lxw_strdup("image_buffer");
10891
+ object_props->stream = image_stream;
10892
+ object_props->row = row_num;
10893
+ object_props->col = col_num;
10894
+
10895
+ if (object_props->x_scale == 0.0)
10896
+ object_props->x_scale = 1;
10897
+
10898
+ if (object_props->y_scale == 0.0)
10899
+ object_props->y_scale = 1;
10900
+
10901
+ if (_get_image_properties(object_props) == LXW_NO_ERROR) {
10902
+ STAILQ_INSERT_TAIL(self->embedded_image_props, object_props,
10903
+ list_pointers);
10904
+ fclose(image_stream);
10905
+
10906
+ return LXW_NO_ERROR;
10907
+ }
10908
+ else {
10909
+ _free_object_properties(object_props);
10910
+ fclose(image_stream);
10911
+ return LXW_ERROR_IMAGE_DIMENSIONS;
10912
+ }
10913
+ }
10914
+
10915
+ /*
10916
+ * Insert an image buffer into the worksheet.
10917
+ */
10918
+ lxw_error
10919
+ worksheet_embed_image_buffer(lxw_worksheet *self,
10920
+ lxw_row_t row_num,
10921
+ lxw_col_t col_num,
10922
+ const unsigned char *image_buffer,
10923
+ size_t image_size)
10924
+ {
10925
+ return worksheet_embed_image_buffer_opt(self, row_num, col_num,
10926
+ image_buffer, image_size, NULL);
10927
+ }
10928
+
10470
10929
  /*
10471
10930
  * Set an image as a worksheet background.
10472
10931
  */
@@ -10921,16 +11380,16 @@ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row,
10921
11380
  || validation->validate == LXW_VALIDATION_TYPE_TIME) {
10922
11381
  if (is_between) {
10923
11382
  copy->value_number =
10924
- lxw_datetime_to_excel_date_epoch
10925
- (&validation->minimum_datetime, LXW_EPOCH_1900);
11383
+ lxw_datetime_to_excel_date_with_epoch
11384
+ (&validation->minimum_datetime, self->use_1904_epoch);
10926
11385
  copy->maximum_number =
10927
- lxw_datetime_to_excel_date_epoch
10928
- (&validation->maximum_datetime, LXW_EPOCH_1900);
11386
+ lxw_datetime_to_excel_date_with_epoch
11387
+ (&validation->maximum_datetime, self->use_1904_epoch);
10929
11388
  }
10930
11389
  else {
10931
11390
  copy->value_number =
10932
- lxw_datetime_to_excel_date_epoch(&validation->value_datetime,
10933
- LXW_EPOCH_1900);
11391
+ lxw_datetime_to_excel_date_with_epoch
11392
+ (&validation->value_datetime, self->use_1904_epoch);
10934
11393
  }
10935
11394
  }
10936
11395
 
@@ -11064,6 +11523,11 @@ worksheet_conditional_format_range(lxw_worksheet *self, lxw_row_t first_row,
11064
11523
  cond_format->stop_if_true = user_options->stop_if_true;
11065
11524
  cond_format->type_string = lxw_strdup(type_strings[cond_format->type]);
11066
11525
 
11526
+ /* Check that the criteria matches the conditional type. */
11527
+ err = _validate_conditional_criteria(cond_format);
11528
+ if (err)
11529
+ goto error;
11530
+
11067
11531
  /* Validate the user input for various types of rules. */
11068
11532
  if (user_options->type == LXW_CONDITIONAL_TYPE_CELL
11069
11533
  || cond_format->type == LXW_CONDITIONAL_TYPE_DUPLICATE
@@ -11292,3 +11756,19 @@ worksheet_ignore_errors(lxw_worksheet *self, uint8_t type, const char *range)
11292
11756
 
11293
11757
  return LXW_NO_ERROR;
11294
11758
  }
11759
+
11760
+ /*
11761
+ * Write an error cell for versions of Excel that don't support embedded images.
11762
+ */
11763
+ void
11764
+ worksheet_set_error_cell(lxw_worksheet *self,
11765
+ lxw_object_properties *object_props, uint32_t ref_id)
11766
+ {
11767
+ lxw_row_t row_num = object_props->row;
11768
+ lxw_col_t col_num = object_props->col;
11769
+
11770
+ lxw_cell *cell =
11771
+ _new_error_cell(row_num, col_num, ref_id, object_props->format);
11772
+ _insert_cell(self, row_num, col_num, cell);
11773
+
11774
+ }