fast_excel 0.2.2 → 0.4.0

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 (100) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +2 -0
  3. data/.gitignore +7 -0
  4. data/.travis.yml +32 -9
  5. data/CHANGELOG.md +36 -1
  6. data/Dockerfile.test +17 -0
  7. data/Gemfile +2 -1
  8. data/Gemfile.lock +33 -24
  9. data/LICENSE +21 -0
  10. data/Makefile +13 -0
  11. data/README.md +177 -40
  12. data/Rakefile +11 -1
  13. data/benchmarks/1k_rows.rb +17 -4
  14. data/benchmarks/20k_rows.rb +4 -0
  15. data/benchmarks/auto_width.rb +37 -0
  16. data/benchmarks/init.rb +14 -2
  17. data/benchmarks/memory.rb +8 -0
  18. data/benchmarks/profiler.rb +27 -0
  19. data/benchmarks/write_value.rb +62 -0
  20. data/examples/example.rb +3 -3
  21. data/examples/example_auto_width.rb +26 -0
  22. data/examples/example_filters.rb +36 -0
  23. data/examples/example_formula.rb +1 -3
  24. data/examples/example_hyperlink.rb +20 -0
  25. data/ext/fast_excel/extconf.rb +3 -0
  26. data/ext/fast_excel/text_width_ext.c +460 -0
  27. data/fast_excel.gemspec +3 -4
  28. data/letters.html +114 -0
  29. data/lib/fast_excel.rb +131 -25
  30. data/lib/fast_excel/binding.rb +33 -21
  31. data/lib/fast_excel/binding/chart.rb +20 -1
  32. data/lib/fast_excel/binding/format.rb +11 -4
  33. data/lib/fast_excel/binding/workbook.rb +10 -2
  34. data/lib/fast_excel/binding/worksheet.rb +44 -27
  35. data/libxlsxwriter/.gitignore +1 -0
  36. data/libxlsxwriter/.indent.pro +8 -0
  37. data/libxlsxwriter/.travis.yml +12 -0
  38. data/libxlsxwriter/CMakeLists.txt +338 -0
  39. data/libxlsxwriter/CONTRIBUTING.md +1 -1
  40. data/libxlsxwriter/Changes.txt +162 -0
  41. data/libxlsxwriter/LICENSE.txt +65 -4
  42. data/libxlsxwriter/Makefile +33 -11
  43. data/libxlsxwriter/Readme.md +3 -1
  44. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +2 -1
  45. data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
  46. data/libxlsxwriter/include/xlsxwriter.h +2 -2
  47. data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
  48. data/libxlsxwriter/include/xlsxwriter/chart.h +164 -13
  49. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
  50. data/libxlsxwriter/include/xlsxwriter/common.h +35 -6
  51. data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
  52. data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
  53. data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
  54. data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
  55. data/libxlsxwriter/include/xlsxwriter/format.h +8 -8
  56. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  57. data/libxlsxwriter/include/xlsxwriter/packager.h +18 -8
  58. data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
  59. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
  60. data/libxlsxwriter/include/xlsxwriter/styles.h +10 -5
  61. data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
  62. data/libxlsxwriter/include/xlsxwriter/utility.h +35 -5
  63. data/libxlsxwriter/include/xlsxwriter/workbook.h +234 -57
  64. data/libxlsxwriter/include/xlsxwriter/worksheet.h +780 -91
  65. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
  66. data/libxlsxwriter/libxlsxwriter.podspec +4 -2
  67. data/libxlsxwriter/src/Makefile +31 -6
  68. data/libxlsxwriter/src/app.c +2 -2
  69. data/libxlsxwriter/src/chart.c +116 -23
  70. data/libxlsxwriter/src/chartsheet.c +508 -0
  71. data/libxlsxwriter/src/content_types.c +12 -4
  72. data/libxlsxwriter/src/core.c +11 -11
  73. data/libxlsxwriter/src/custom.c +3 -3
  74. data/libxlsxwriter/src/drawing.c +114 -17
  75. data/libxlsxwriter/src/format.c +5 -5
  76. data/libxlsxwriter/src/hash_table.c +1 -1
  77. data/libxlsxwriter/src/packager.c +378 -61
  78. data/libxlsxwriter/src/relationships.c +2 -2
  79. data/libxlsxwriter/src/shared_strings.c +18 -4
  80. data/libxlsxwriter/src/styles.c +59 -12
  81. data/libxlsxwriter/src/theme.c +2 -2
  82. data/libxlsxwriter/src/utility.c +93 -6
  83. data/libxlsxwriter/src/workbook.c +379 -61
  84. data/libxlsxwriter/src/worksheet.c +1240 -174
  85. data/libxlsxwriter/src/xmlwriter.c +18 -9
  86. data/libxlsxwriter/third_party/minizip/Makefile +6 -1
  87. data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
  88. data/libxlsxwriter/third_party/minizip/zip.c +2 -0
  89. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
  90. data/libxlsxwriter/version.txt +1 -1
  91. data/test/auto_width_test.rb +19 -0
  92. data/test/date_test.rb +34 -0
  93. data/test/format_test.rb +8 -0
  94. data/test/reopen_test.rb +22 -0
  95. data/test/test_helper.rb +8 -5
  96. data/test/text_width_test.rb +80 -0
  97. data/test/tmpfile_test.rb +1 -0
  98. data/test/validations_test.rb +47 -0
  99. data/test/worksheet_test.rb +44 -1
  100. metadata +33 -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-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -15,11 +15,11 @@
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_PRINT_ACROSS 1
21
+ #define LXW_VALIDATION_MAX_TITLE_LENGTH 32
22
+ #define LXW_VALIDATION_MAX_STRING_LENGTH 255
23
23
 
24
24
  /*
25
25
  * Forward declarations.
@@ -123,6 +123,11 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
123
123
  GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error);
124
124
  STAILQ_INIT(worksheet->selections);
125
125
 
126
+ worksheet->data_validations =
127
+ calloc(1, sizeof(struct lxw_data_validations));
128
+ GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error);
129
+ STAILQ_INIT(worksheet->data_validations);
130
+
126
131
  worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
127
132
  GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
128
133
  STAILQ_INIT(worksheet->external_hyperlinks);
@@ -187,6 +192,9 @@ lxw_worksheet_new(lxw_worksheet_init_data *init_data)
187
192
  worksheet->zoom_scale_normal = LXW_TRUE;
188
193
  worksheet->show_zeros = LXW_TRUE;
189
194
  worksheet->outline_on = LXW_TRUE;
195
+ worksheet->outline_style = LXW_TRUE;
196
+ worksheet->outline_below = LXW_TRUE;
197
+ worksheet->outline_right = LXW_FALSE;
190
198
  worksheet->tab_color = LXW_COLOR_UNSET;
191
199
 
192
200
  if (init_data) {
@@ -261,13 +269,34 @@ _free_image_options(lxw_image_options *image)
261
269
  return;
262
270
 
263
271
  free(image->filename);
264
- free(image->short_name);
272
+ free(image->description);
265
273
  free(image->extension);
266
274
  free(image->url);
267
275
  free(image->tip);
276
+ free(image->image_buffer);
268
277
  free(image);
269
278
  }
270
279
 
280
+ /*
281
+ * Free a worksheet data_validation.
282
+ */
283
+ STATIC void
284
+ _free_data_validation(lxw_data_validation *data_validation)
285
+ {
286
+ if (!data_validation)
287
+ return;
288
+
289
+ free(data_validation->value_formula);
290
+ free(data_validation->maximum_formula);
291
+ free(data_validation->input_title);
292
+ free(data_validation->input_message);
293
+ free(data_validation->error_title);
294
+ free(data_validation->error_message);
295
+ free(data_validation->minimum_formula);
296
+
297
+ free(data_validation);
298
+ }
299
+
271
300
  /*
272
301
  * Free a worksheet object.
273
302
  */
@@ -280,6 +309,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
280
309
  lxw_merged_range *merged_range;
281
310
  lxw_image_options *image_options;
282
311
  lxw_selection *selection;
312
+ lxw_data_validation *data_validation;
283
313
  lxw_rel_tuple *relationship;
284
314
 
285
315
  if (!worksheet)
@@ -309,7 +339,6 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
309
339
  }
310
340
 
311
341
  if (worksheet->hyperlinks) {
312
-
313
342
  for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row;
314
343
  row = next_row) {
315
344
 
@@ -361,6 +390,16 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
361
390
  free(worksheet->selections);
362
391
  }
363
392
 
393
+ if (worksheet->data_validations) {
394
+ while (!STAILQ_EMPTY(worksheet->data_validations)) {
395
+ data_validation = STAILQ_FIRST(worksheet->data_validations);
396
+ STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers);
397
+ _free_data_validation(data_validation);
398
+ }
399
+
400
+ free(worksheet->data_validations);
401
+ }
402
+
364
403
  /* TODO. Add function for freeing the relationship lists. */
365
404
  while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
366
405
  relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
@@ -409,6 +448,7 @@ lxw_worksheet_free(lxw_worksheet *worksheet)
409
448
  free(worksheet->vbreaks);
410
449
  free(worksheet->name);
411
450
  free(worksheet->quoted_name);
451
+ free(worksheet->vba_codename);
412
452
 
413
453
  free(worksheet);
414
454
  worksheet = NULL;
@@ -498,6 +538,26 @@ _new_inline_string_cell(lxw_row_t row_num,
498
538
  return cell;
499
539
  }
500
540
 
541
+ /*
542
+ * Create a new worksheet inline_string cell object for rich strings.
543
+ */
544
+ STATIC lxw_cell *
545
+ _new_inline_rich_string_cell(lxw_row_t row_num,
546
+ lxw_col_t col_num, char *string,
547
+ lxw_format *format)
548
+ {
549
+ lxw_cell *cell = calloc(1, sizeof(lxw_cell));
550
+ RETURN_ON_MEM_ERROR(cell, cell);
551
+
552
+ cell->row_num = row_num;
553
+ cell->col_num = col_num;
554
+ cell->type = INLINE_RICH_STRING_CELL;
555
+ cell->format = format;
556
+ cell->u.string = string;
557
+
558
+ return cell;
559
+ }
560
+
501
561
  /*
502
562
  * Create a new worksheet formula cell object.
503
563
  */
@@ -801,38 +861,6 @@ _cell_cmp(lxw_cell *cell1, lxw_cell *cell2)
801
861
  return 0;
802
862
  }
803
863
 
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
864
  /*
837
865
  * Simple replacement for libgen.h basename() for compatibility with MSVC. It
838
866
  * handles forward and back slashes. It doesn't copy exactly the return
@@ -860,6 +888,61 @@ lxw_basename(const char *path)
860
888
  return back_slash + 1;
861
889
  }
862
890
 
891
+ /* Function to count the total concatenated length of the strings in a
892
+ * validation list array, including commas. */
893
+ size_t
894
+ _validation_list_length(char **list)
895
+ {
896
+ uint8_t i = 0;
897
+ size_t length = 0;
898
+
899
+ if (!list || !list[0])
900
+ return 0;
901
+
902
+ while (list[i] && length <= LXW_VALIDATION_MAX_STRING_LENGTH) {
903
+ /* Include commas in the length. */
904
+ length += 1 + lxw_utf8_strlen(list[i]);
905
+ i++;
906
+ }
907
+
908
+ /* Adjust the count for extraneous comma at end. */
909
+ length--;
910
+
911
+ return length;
912
+ }
913
+
914
+ /* Function to convert an array of strings into a CSV string for data
915
+ * validation lists. */
916
+ char *
917
+ _validation_list_to_csv(char **list)
918
+ {
919
+ uint8_t i = 0;
920
+ char *str;
921
+
922
+ /* Create a buffer for the concatenated, and quoted, string. */
923
+ /* Add +3 for quotes and EOL. */
924
+ str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH + 3);
925
+ if (!str)
926
+ return NULL;
927
+
928
+ /* Add the start quote and first element. */
929
+ strcat(str, "\"");
930
+ strcat(str, list[0]);
931
+
932
+ /* Add the other elements preceded by a comma. */
933
+ i = 1;
934
+ while (list[i]) {
935
+ strcat(str, ",");
936
+ strcat(str, list[i]);
937
+ i++;
938
+ }
939
+
940
+ /* Add the end quote. */
941
+ strcat(str, "\"");
942
+
943
+ return str;
944
+ }
945
+
863
946
  /*****************************************************************************
864
947
  *
865
948
  * XML functions.
@@ -1372,6 +1455,12 @@ _worksheet_write_sheet_format_pr(lxw_worksheet *self)
1372
1455
  if (self->default_row_zeroed)
1373
1456
  LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1");
1374
1457
 
1458
+ if (self->outline_row_level)
1459
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevelRow", self->outline_row_level);
1460
+
1461
+ if (self->outline_col_level)
1462
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevelCol", self->outline_col_level);
1463
+
1375
1464
  lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes);
1376
1465
 
1377
1466
  LXW_FREE_ATTRIBUTES();
@@ -1608,6 +1697,9 @@ _write_row(lxw_worksheet *self, lxw_row *row, char *spans)
1608
1697
  if (height != LXW_DEF_ROW_HEIGHT)
1609
1698
  LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
1610
1699
 
1700
+ if (row->level)
1701
+ LXW_PUSH_ATTRIBUTES_INT("outlineLevel", row->level);
1702
+
1611
1703
  if (row->collapsed)
1612
1704
  LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
1613
1705
 
@@ -1649,6 +1741,9 @@ _worksheet_size_col(lxw_worksheet *self, lxw_col_t col_num)
1649
1741
  }
1650
1742
 
1651
1743
  if (col_opt) {
1744
+ if (col_opt->hidden)
1745
+ return 0;
1746
+
1652
1747
  width = col_opt->width;
1653
1748
 
1654
1749
  /* Convert to pixels. */
@@ -1684,6 +1779,9 @@ _worksheet_size_row(lxw_worksheet *self, lxw_row_t row_num)
1684
1779
  row = lxw_worksheet_find_row(self, row_num);
1685
1780
 
1686
1781
  if (row) {
1782
+ if (row->hidden)
1783
+ return 0;
1784
+
1687
1785
  height = row->height;
1688
1786
 
1689
1787
  if (height == 0)
@@ -1810,23 +1908,30 @@ _worksheet_position_object_pixels(lxw_worksheet *self,
1810
1908
  y_abs += y1;
1811
1909
 
1812
1910
  /* 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++;
1911
+ if (_worksheet_size_col(self, col_start) > 0) {
1912
+ while (x1 >= _worksheet_size_col(self, col_start)) {
1913
+ x1 -= _worksheet_size_col(self, col_start);
1914
+ col_start++;
1915
+ }
1816
1916
  }
1817
1917
 
1818
1918
  /* 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++;
1919
+ if (_worksheet_size_row(self, row_start) > 0) {
1920
+ while (y1 >= _worksheet_size_row(self, row_start)) {
1921
+ y1 -= _worksheet_size_row(self, row_start);
1922
+ row_start++;
1923
+ }
1822
1924
  }
1823
1925
 
1824
1926
  /* Initialize end cell to the same as the start cell. */
1825
1927
  col_end = col_start;
1826
1928
  row_end = row_start;
1827
1929
 
1828
- width = width + x1;
1829
- height = height + y1;
1930
+ /* Only offset the image in the cell if the row/col isn't hidden. */
1931
+ if (_worksheet_size_col(self, col_start) > 0)
1932
+ width = width + x1;
1933
+ if (_worksheet_size_row(self, row_start) > 0)
1934
+ height = height + y1;
1830
1935
 
1831
1936
  /* Subtract the underlying cell widths to find the end cell. */
1832
1937
  while (width >= _worksheet_size_col(self, col_end)) {
@@ -1888,7 +1993,7 @@ _worksheet_position_object_emus(lxw_worksheet *self,
1888
1993
  */
1889
1994
  void
1890
1995
  lxw_worksheet_prepare_image(lxw_worksheet *self,
1891
- uint16_t image_ref_id, uint16_t drawing_id,
1996
+ uint32_t image_ref_id, uint32_t drawing_id,
1892
1997
  lxw_image_options *image_data)
1893
1998
  {
1894
1999
  lxw_drawing_object *drawing_object;
@@ -1923,7 +2028,7 @@ lxw_worksheet_prepare_image(lxw_worksheet *self,
1923
2028
 
1924
2029
  drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE;
1925
2030
  drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
1926
- drawing_object->description = lxw_strdup(image_data->short_name);
2031
+ drawing_object->description = lxw_strdup(image_data->description);
1927
2032
 
1928
2033
  /* Scale to user scale. */
1929
2034
  width = image_data->width * image_data->x_scale;
@@ -1975,8 +2080,10 @@ mem_error:
1975
2080
  */
1976
2081
  void
1977
2082
  lxw_worksheet_prepare_chart(lxw_worksheet *self,
1978
- uint16_t chart_ref_id, uint16_t drawing_id,
1979
- lxw_image_options *image_data)
2083
+ uint32_t chart_ref_id,
2084
+ uint32_t drawing_id,
2085
+ lxw_image_options *image_data,
2086
+ uint8_t is_chartsheet)
1980
2087
  {
1981
2088
  lxw_drawing_object *drawing_object;
1982
2089
  lxw_rel_tuple *relationship;
@@ -1986,9 +2093,16 @@ lxw_worksheet_prepare_chart(lxw_worksheet *self,
1986
2093
 
1987
2094
  if (!self->drawing) {
1988
2095
  self->drawing = lxw_drawing_new();
1989
- self->drawing->embedded = LXW_TRUE;
1990
2096
  RETURN_VOID_ON_MEM_ERROR(self->drawing);
1991
2097
 
2098
+ if (is_chartsheet) {
2099
+ self->drawing->embedded = LXW_FALSE;
2100
+ self->drawing->orientation = self->orientation;
2101
+ }
2102
+ else {
2103
+ self->drawing->embedded = LXW_TRUE;
2104
+ }
2105
+
1992
2106
  relationship = calloc(1, sizeof(lxw_rel_tuple));
1993
2107
  GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
1994
2108
 
@@ -2155,8 +2269,7 @@ _process_png(lxw_image_options *image_options)
2155
2269
 
2156
2270
  file_error:
2157
2271
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2158
- "no size data found in file: %s.",
2159
- image_options->filename);
2272
+ "no size data found in: %s.", image_options->filename);
2160
2273
 
2161
2274
  return LXW_ERROR_IMAGE_DIMENSIONS;
2162
2275
  }
@@ -2183,8 +2296,7 @@ _process_jpeg(lxw_image_options *image_options)
2183
2296
  if (fseek_err)
2184
2297
  goto file_error;
2185
2298
 
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. */
2299
+ /* Search through the image data and read the JPEG markers. */
2188
2300
  while (!feof(stream)) {
2189
2301
 
2190
2302
  /* Read the JPEG marker and length fields for the sub-section. */
@@ -2201,7 +2313,10 @@ _process_jpeg(lxw_image_options *image_options)
2201
2313
  /* The offset for next fseek() is the field length + type length. */
2202
2314
  offset = length - 2;
2203
2315
 
2204
- if (marker == 0xFFC0 || marker == 0xFFC2) {
2316
+ /* Read the height and width in the 0xFFCn elements (except C4, C8 */
2317
+ /* and CC which aren't SOF markers). */
2318
+ if ((marker & 0xFFF0) == 0xFFC0 && marker != 0xFFC4
2319
+ && marker != 0xFFC8 && marker != 0xFFCC) {
2205
2320
  /* Skip 1 byte to height and width. */
2206
2321
  fseek_err = fseek(stream, 1, SEEK_CUR);
2207
2322
  if (fseek_err)
@@ -2219,6 +2334,7 @@ _process_jpeg(lxw_image_options *image_options)
2219
2334
  offset -= 9;
2220
2335
  }
2221
2336
 
2337
+ /* Read the DPI in the 0xFFE0 element. */
2222
2338
  if (marker == 0xFFE0) {
2223
2339
  uint16_t x_density = 0;
2224
2340
  uint16_t y_density = 0;
@@ -2279,8 +2395,7 @@ _process_jpeg(lxw_image_options *image_options)
2279
2395
 
2280
2396
  file_error:
2281
2397
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2282
- "no size data found in file: %s.",
2283
- image_options->filename);
2398
+ "no size data found in: %s.", image_options->filename);
2284
2399
 
2285
2400
  return LXW_ERROR_IMAGE_DIMENSIONS;
2286
2401
  }
@@ -2314,6 +2429,9 @@ _process_bmp(lxw_image_options *image_options)
2314
2429
  if (width == 0)
2315
2430
  goto file_error;
2316
2431
 
2432
+ height = LXW_UINT32_HOST(height);
2433
+ width = LXW_UINT32_HOST(width);
2434
+
2317
2435
  /* Set the image metadata. */
2318
2436
  image_options->image_type = LXW_IMAGE_BMP;
2319
2437
  image_options->width = width;
@@ -2326,8 +2444,7 @@ _process_bmp(lxw_image_options *image_options)
2326
2444
 
2327
2445
  file_error:
2328
2446
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2329
- "no size data found in file: %s.",
2330
- image_options->filename);
2447
+ "no size data found in: %s.", image_options->filename);
2331
2448
 
2332
2449
  return LXW_ERROR_IMAGE_DIMENSIONS;
2333
2450
  }
@@ -2344,7 +2461,7 @@ _get_image_properties(lxw_image_options *image_options)
2344
2461
  /* Read 4 bytes to look for the file header/signature. */
2345
2462
  if (fread(signature, 1, 4, image_options->stream) < 4) {
2346
2463
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2347
- "couldn't read file type for file: %s.",
2464
+ "couldn't read image type for: %s.",
2348
2465
  image_options->filename);
2349
2466
  return LXW_ERROR_IMAGE_DIMENSIONS;
2350
2467
  }
@@ -2363,7 +2480,7 @@ _get_image_properties(lxw_image_options *image_options)
2363
2480
  }
2364
2481
  else {
2365
2482
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
2366
- "unsupported image format for file: %s.",
2483
+ "unsupported image format for: %s.",
2367
2484
  image_options->filename);
2368
2485
  return LXW_ERROR_IMAGE_DIMENSIONS;
2369
2486
  }
@@ -2385,6 +2502,18 @@ STATIC void
2385
2502
  _write_number_cell(lxw_worksheet *self, char *range,
2386
2503
  int32_t style_index, lxw_cell *cell)
2387
2504
  {
2505
+ #ifdef USE_DOUBLE_FUNCTION
2506
+ char data[LXW_ATTR_32];
2507
+
2508
+ lxw_sprintf_dbl(data, cell->u.number);
2509
+
2510
+ if (style_index)
2511
+ fprintf(self->file,
2512
+ "<c r=\"%s\" s=\"%d\"><v>%s</v></c>",
2513
+ range, style_index, data);
2514
+ else
2515
+ fprintf(self->file, "<c r=\"%s\"><v>%s</v></c>", range, data);
2516
+ #else
2388
2517
  if (style_index)
2389
2518
  fprintf(self->file,
2390
2519
  "<c r=\"%s\" s=\"%d\"><v>%.16g</v></c>",
@@ -2392,6 +2521,8 @@ _write_number_cell(lxw_worksheet *self, char *range,
2392
2521
  else
2393
2522
  fprintf(self->file,
2394
2523
  "<c r=\"%s\"><v>%.16g</v></c>", range, cell->u.number);
2524
+
2525
+ #endif
2395
2526
  }
2396
2527
 
2397
2528
  /*
@@ -2402,6 +2533,7 @@ STATIC void
2402
2533
  _write_string_cell(lxw_worksheet *self, char *range,
2403
2534
  int32_t style_index, lxw_cell *cell)
2404
2535
  {
2536
+
2405
2537
  if (style_index)
2406
2538
  fprintf(self->file,
2407
2539
  "<c r=\"%s\" s=\"%d\" t=\"s\"><v>%d</v></c>",
@@ -2451,6 +2583,41 @@ _write_inline_string_cell(lxw_worksheet *self, char *range,
2451
2583
  free(string);
2452
2584
  }
2453
2585
 
2586
+ /*
2587
+ * Write out an inline rich string. Doesn't use the xml functions as an
2588
+ * optimization in the inner cell writing loop.
2589
+ */
2590
+ STATIC void
2591
+ _write_inline_rich_string_cell(lxw_worksheet *self, char *range,
2592
+ int32_t style_index, lxw_cell *cell)
2593
+ {
2594
+ char *string = cell->u.string;
2595
+
2596
+ /* Add attribute to preserve leading or trailing whitespace. */
2597
+ if (isspace((unsigned char) string[0])
2598
+ || isspace((unsigned char) string[strlen(string) - 1])) {
2599
+
2600
+ if (style_index)
2601
+ fprintf(self->file,
2602
+ "<c r=\"%s\" s=\"%d\" t=\"inlineStr\"><is>%s</is></c>",
2603
+ range, style_index, string);
2604
+ else
2605
+ fprintf(self->file,
2606
+ "<c r=\"%s\" t=\"inlineStr\"><is>%s</is></c>",
2607
+ range, string);
2608
+ }
2609
+ else {
2610
+ if (style_index)
2611
+ fprintf(self->file,
2612
+ "<c r=\"%s\" s=\"%d\" t=\"inlineStr\">"
2613
+ "<is>%s</is></c>", range, style_index, string);
2614
+ else
2615
+ fprintf(self->file,
2616
+ "<c r=\"%s\" t=\"inlineStr\">"
2617
+ "<is>%s</is></c>", range, string);
2618
+ }
2619
+ }
2620
+
2454
2621
  /*
2455
2622
  * Write out a formula worksheet cell with a numeric result.
2456
2623
  */
@@ -2459,8 +2626,7 @@ _write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
2459
2626
  {
2460
2627
  char data[LXW_ATTR_32];
2461
2628
 
2462
- lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
2463
-
2629
+ lxw_sprintf_dbl(data, cell->formula_result);
2464
2630
  lxw_xml_data_element(self->file, "f", cell->u.string, NULL);
2465
2631
  lxw_xml_data_element(self->file, "v", data, NULL);
2466
2632
  }
@@ -2479,7 +2645,7 @@ _write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
2479
2645
  LXW_PUSH_ATTRIBUTES_STR("t", "array");
2480
2646
  LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1);
2481
2647
 
2482
- lxw_snprintf(data, LXW_ATTR_32, "%.16g", cell->formula_result);
2648
+ lxw_sprintf_dbl(data, cell->formula_result);
2483
2649
 
2484
2650
  lxw_xml_data_element(self->file, "f", cell->u.string, &attributes);
2485
2651
  lxw_xml_data_element(self->file, "v", data, NULL);
@@ -2515,8 +2681,10 @@ _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell)
2515
2681
  STATIC void
2516
2682
  _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
2517
2683
  {
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;
2684
+ lxw_cell *cell_min = RB_MIN(lxw_table_cells, row->cells);
2685
+ lxw_cell *cell_max = RB_MAX(lxw_table_cells, row->cells);
2686
+ lxw_col_t span_col_min = cell_min->col_num;
2687
+ lxw_col_t span_col_max = cell_max->col_num;
2520
2688
  lxw_col_t col_min;
2521
2689
  lxw_col_t col_max;
2522
2690
  *block_num = row->row_num / 16;
@@ -2526,8 +2694,10 @@ _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
2526
2694
  while (row && (int32_t) (row->row_num / 16) == *block_num) {
2527
2695
 
2528
2696
  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;
2697
+ cell_min = RB_MIN(lxw_table_cells, row->cells);
2698
+ cell_max = RB_MAX(lxw_table_cells, row->cells);
2699
+ col_min = cell_min->col_num;
2700
+ col_max = cell_max->col_num;
2531
2701
 
2532
2702
  if (col_min < span_col_min)
2533
2703
  span_col_min = col_min;
@@ -2584,6 +2754,11 @@ _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format)
2584
2754
  return;
2585
2755
  }
2586
2756
 
2757
+ if (cell->type == INLINE_RICH_STRING_CELL) {
2758
+ _write_inline_rich_string_cell(self, range, style_index, cell);
2759
+ return;
2760
+ }
2761
+
2587
2762
  /* For other cell types use the general functions. */
2588
2763
  LXW_INIT_ATTRIBUTES();
2589
2764
  LXW_PUSH_ATTRIBUTES_STR("r", range);
@@ -2922,6 +3097,37 @@ _worksheet_write_tab_color(lxw_worksheet *self)
2922
3097
  LXW_FREE_ATTRIBUTES();
2923
3098
  }
2924
3099
 
3100
+ /*
3101
+ * Write the <outlinePr> element.
3102
+ */
3103
+ STATIC void
3104
+ _worksheet_write_outline_pr(lxw_worksheet *self)
3105
+ {
3106
+ struct xml_attribute_list attributes;
3107
+ struct xml_attribute *attribute;
3108
+
3109
+ if (!self->outline_changed)
3110
+ return;
3111
+
3112
+ LXW_INIT_ATTRIBUTES();
3113
+
3114
+ if (self->outline_style)
3115
+ LXW_PUSH_ATTRIBUTES_STR("applyStyles", "1");
3116
+
3117
+ if (!self->outline_below)
3118
+ LXW_PUSH_ATTRIBUTES_STR("summaryBelow", "0");
3119
+
3120
+ if (!self->outline_right)
3121
+ LXW_PUSH_ATTRIBUTES_STR("summaryRight", "0");
3122
+
3123
+ if (!self->outline_on)
3124
+ LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0");
3125
+
3126
+ lxw_xml_empty_tag(self->file, "outlinePr", &attributes);
3127
+
3128
+ LXW_FREE_ATTRIBUTES();
3129
+ }
3130
+
2925
3131
  /*
2926
3132
  * Write the <sheetPr> element for Sheet level properties.
2927
3133
  */
@@ -2934,14 +3140,15 @@ _worksheet_write_sheet_pr(lxw_worksheet *self)
2934
3140
  if (!self->fit_page
2935
3141
  && !self->filter_on
2936
3142
  && self->tab_color == LXW_COLOR_UNSET
2937
- && !self->outline_changed && !self->vba_codename) {
3143
+ && !self->outline_changed
3144
+ && !self->vba_codename && !self->is_chartsheet) {
2938
3145
  return;
2939
3146
  }
2940
3147
 
2941
3148
  LXW_INIT_ATTRIBUTES();
2942
3149
 
2943
3150
  if (self->vba_codename)
2944
- LXW_PUSH_ATTRIBUTES_INT("codeName", self->vba_codename);
3151
+ LXW_PUSH_ATTRIBUTES_STR("codeName", self->vba_codename);
2945
3152
 
2946
3153
  if (self->filter_on)
2947
3154
  LXW_PUSH_ATTRIBUTES_STR("filterMode", "1");
@@ -2950,7 +3157,7 @@ _worksheet_write_sheet_pr(lxw_worksheet *self)
2950
3157
  || self->outline_changed) {
2951
3158
  lxw_xml_start_tag(self->file, "sheetPr", &attributes);
2952
3159
  _worksheet_write_tab_color(self);
2953
- /* _worksheet_write_outline_pr(self); */
3160
+ _worksheet_write_outline_pr(self);
2954
3161
  _worksheet_write_page_set_up_pr(self);
2955
3162
  lxw_xml_end_tag(self->file, "sheetPr");
2956
3163
  }
@@ -3205,13 +3412,12 @@ mem_error:
3205
3412
  * Write the <sheetProtection> element.
3206
3413
  */
3207
3414
  STATIC void
3208
- _worksheet_write_sheet_protection(lxw_worksheet *self)
3415
+ _worksheet_write_sheet_protection(lxw_worksheet *self,
3416
+ lxw_protection *protect)
3209
3417
  {
3210
3418
  struct xml_attribute_list attributes;
3211
3419
  struct xml_attribute *attribute;
3212
3420
 
3213
- struct lxw_protection *protect = &self->protection;
3214
-
3215
3421
  if (!protect->is_configured)
3216
3422
  return;
3217
3423
 
@@ -3223,7 +3429,7 @@ _worksheet_write_sheet_protection(lxw_worksheet *self)
3223
3429
  if (!protect->no_sheet)
3224
3430
  LXW_PUSH_ATTRIBUTES_INT("sheet", 1);
3225
3431
 
3226
- if (protect->content)
3432
+ if (!protect->no_content)
3227
3433
  LXW_PUSH_ATTRIBUTES_INT("content", 1);
3228
3434
 
3229
3435
  if (!protect->objects)
@@ -3280,7 +3486,7 @@ _worksheet_write_sheet_protection(lxw_worksheet *self)
3280
3486
  * Write the <drawing> element.
3281
3487
  */
3282
3488
  STATIC void
3283
- _write_drawing(lxw_worksheet *self, uint16_t id)
3489
+ _worksheet_write_drawing(lxw_worksheet *self, uint16_t id)
3284
3490
  {
3285
3491
  struct xml_attribute_list attributes;
3286
3492
  struct xml_attribute *attribute;
@@ -3302,120 +3508,387 @@ _write_drawing(lxw_worksheet *self, uint16_t id)
3302
3508
  * Write the <drawing> elements.
3303
3509
  */
3304
3510
  STATIC void
3305
- _write_drawings(lxw_worksheet *self)
3511
+ _worksheet_write_drawings(lxw_worksheet *self)
3306
3512
  {
3307
3513
  if (!self->drawing)
3308
3514
  return;
3309
3515
 
3310
3516
  self->rel_count++;
3311
3517
 
3312
- _write_drawing(self, self->rel_count);
3518
+ _worksheet_write_drawing(self, self->rel_count);
3313
3519
  }
3314
3520
 
3315
3521
  /*
3316
- * Assemble and write the XML file.
3522
+ * Write the <formula1> element for numbers.
3317
3523
  */
3318
- void
3319
- lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
3524
+ STATIC void
3525
+ _worksheet_write_formula1_num(lxw_worksheet *self, double number)
3320
3526
  {
3321
- /* Write the XML declaration. */
3322
- _worksheet_xml_declaration(self);
3527
+ char data[LXW_ATTR_32];
3323
3528
 
3324
- /* Write the worksheet element. */
3325
- _worksheet_write_worksheet(self);
3529
+ lxw_sprintf_dbl(data, number);
3326
3530
 
3327
- /* Write the worksheet properties. */
3328
- _worksheet_write_sheet_pr(self);
3531
+ lxw_xml_data_element(self->file, "formula1", data, NULL);
3532
+ }
3329
3533
 
3330
- /* Write the worksheet dimensions. */
3331
- _worksheet_write_dimension(self);
3534
+ /*
3535
+ * Write the <formula1> element for strings/formulas.
3536
+ */
3537
+ STATIC void
3538
+ _worksheet_write_formula1_str(lxw_worksheet *self, char *str)
3539
+ {
3540
+ lxw_xml_data_element(self->file, "formula1", str, NULL);
3541
+ }
3332
3542
 
3333
- /* Write the sheet view properties. */
3334
- _worksheet_write_sheet_views(self);
3543
+ /*
3544
+ * Write the <formula2> element for numbers.
3545
+ */
3546
+ STATIC void
3547
+ _worksheet_write_formula2_num(lxw_worksheet *self, double number)
3548
+ {
3549
+ char data[LXW_ATTR_32];
3335
3550
 
3336
- /* Write the sheet format properties. */
3337
- _worksheet_write_sheet_format_pr(self);
3551
+ lxw_sprintf_dbl(data, number);
3338
3552
 
3339
- /* Write the sheet column info. */
3340
- _worksheet_write_cols(self);
3553
+ lxw_xml_data_element(self->file, "formula2", data, NULL);
3554
+ }
3341
3555
 
3342
- /* Write the sheetData element. */
3343
- if (!self->optimize)
3344
- _worksheet_write_sheet_data(self);
3345
- else
3346
- _worksheet_write_optimized_sheet_data(self);
3556
+ /*
3557
+ * Write the <formula2> element for strings/formulas.
3558
+ */
3559
+ STATIC void
3560
+ _worksheet_write_formula2_str(lxw_worksheet *self, char *str)
3561
+ {
3562
+ lxw_xml_data_element(self->file, "formula2", str, NULL);
3563
+ }
3347
3564
 
3348
- /* Write the sheetProtection element. */
3349
- _worksheet_write_sheet_protection(self);
3565
+ /*
3566
+ * Write the <dataValidation> element.
3567
+ */
3568
+ STATIC void
3569
+ _worksheet_write_data_validation(lxw_worksheet *self,
3570
+ lxw_data_validation *validation)
3571
+ {
3572
+ struct xml_attribute_list attributes;
3573
+ struct xml_attribute *attribute;
3574
+ uint8_t is_between = 0;
3350
3575
 
3351
- /* Write the autoFilter element. */
3352
- _worksheet_write_auto_filter(self);
3576
+ LXW_INIT_ATTRIBUTES();
3353
3577
 
3354
- /* Write the mergeCells element. */
3355
- _worksheet_write_merge_cells(self);
3578
+ switch (validation->validate) {
3579
+ case LXW_VALIDATION_TYPE_INTEGER:
3580
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3581
+ LXW_PUSH_ATTRIBUTES_STR("type", "whole");
3582
+ break;
3583
+ case LXW_VALIDATION_TYPE_DECIMAL:
3584
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3585
+ LXW_PUSH_ATTRIBUTES_STR("type", "decimal");
3586
+ break;
3587
+ case LXW_VALIDATION_TYPE_LIST:
3588
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3589
+ LXW_PUSH_ATTRIBUTES_STR("type", "list");
3590
+ break;
3591
+ case LXW_VALIDATION_TYPE_DATE:
3592
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3593
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3594
+ LXW_PUSH_ATTRIBUTES_STR("type", "date");
3595
+ break;
3596
+ case LXW_VALIDATION_TYPE_TIME:
3597
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3598
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3599
+ LXW_PUSH_ATTRIBUTES_STR("type", "time");
3600
+ break;
3601
+ case LXW_VALIDATION_TYPE_LENGTH:
3602
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3603
+ LXW_PUSH_ATTRIBUTES_STR("type", "textLength");
3604
+ break;
3605
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3606
+ LXW_PUSH_ATTRIBUTES_STR("type", "custom");
3607
+ break;
3608
+ }
3356
3609
 
3357
- /* Write the hyperlink element. */
3358
- _worksheet_write_hyperlinks(self);
3610
+ switch (validation->criteria) {
3611
+ case LXW_VALIDATION_CRITERIA_EQUAL_TO:
3612
+ LXW_PUSH_ATTRIBUTES_STR("operator", "equal");
3613
+ break;
3614
+ case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO:
3615
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual");
3616
+ break;
3617
+ case LXW_VALIDATION_CRITERIA_LESS_THAN:
3618
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan");
3619
+ break;
3620
+ case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO:
3621
+ LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual");
3622
+ break;
3623
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN:
3624
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan");
3625
+ break;
3626
+ case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO:
3627
+ LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual");
3628
+ break;
3629
+ case LXW_VALIDATION_CRITERIA_BETWEEN:
3630
+ /* Between is the default for 2 formulas and isn't added. */
3631
+ is_between = 1;
3632
+ break;
3633
+ case LXW_VALIDATION_CRITERIA_NOT_BETWEEN:
3634
+ is_between = 1;
3635
+ LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween");
3636
+ break;
3637
+ }
3359
3638
 
3360
- /* Write the printOptions element. */
3361
- _worksheet_write_print_options(self);
3639
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING)
3640
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning");
3362
3641
 
3363
- /* Write the worksheet page_margins. */
3364
- _worksheet_write_page_margins(self);
3642
+ if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION)
3643
+ LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information");
3365
3644
 
3366
- /* Write the worksheet page setup. */
3367
- _worksheet_write_page_setup(self);
3645
+ if (validation->ignore_blank)
3646
+ LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1);
3368
3647
 
3369
- /* Write the headerFooter element. */
3370
- _worksheet_write_header_footer(self);
3648
+ if (validation->dropdown == LXW_VALIDATION_OFF)
3649
+ LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1);
3371
3650
 
3372
- /* Write the rowBreaks element. */
3373
- _worksheet_write_row_breaks(self);
3651
+ if (validation->show_input)
3652
+ LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1);
3374
3653
 
3375
- /* Write the colBreaks element. */
3376
- _worksheet_write_col_breaks(self);
3654
+ if (validation->show_error)
3655
+ LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1);
3377
3656
 
3378
- /* Write the drawing element. */
3379
- _write_drawings(self);
3657
+ if (validation->error_title)
3658
+ LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title);
3380
3659
 
3381
- /* Close the worksheet tag. */
3382
- lxw_xml_end_tag(self->file, "worksheet");
3383
- }
3660
+ if (validation->error_message)
3661
+ LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message);
3384
3662
 
3385
- /*****************************************************************************
3386
- *
3387
- * Public functions.
3388
- *
3389
- ****************************************************************************/
3663
+ if (validation->input_title)
3664
+ LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title);
3390
3665
 
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;
3666
+ if (validation->input_message)
3667
+ LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message);
3401
3668
 
3402
- err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
3403
- if (err)
3404
- return err;
3669
+ LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref);
3405
3670
 
3406
- cell = _new_number_cell(row_num, col_num, value, format);
3671
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY)
3672
+ lxw_xml_empty_tag(self->file, "dataValidation", &attributes);
3673
+ else
3674
+ lxw_xml_start_tag(self->file, "dataValidation", &attributes);
3675
+
3676
+ /* Write the formula1 and formula2 elements. */
3677
+ switch (validation->validate) {
3678
+ case LXW_VALIDATION_TYPE_INTEGER:
3679
+ case LXW_VALIDATION_TYPE_DECIMAL:
3680
+ case LXW_VALIDATION_TYPE_LENGTH:
3681
+ case LXW_VALIDATION_TYPE_DATE:
3682
+ case LXW_VALIDATION_TYPE_TIME:
3683
+ case LXW_VALIDATION_TYPE_DATE_NUMBER:
3684
+ case LXW_VALIDATION_TYPE_TIME_NUMBER:
3685
+ _worksheet_write_formula1_num(self, validation->value_number);
3686
+ if (is_between)
3687
+ _worksheet_write_formula2_num(self,
3688
+ validation->maximum_number);
3689
+ break;
3690
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
3691
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
3692
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
3693
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
3694
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
3695
+ case LXW_VALIDATION_TYPE_LIST:
3696
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
3697
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
3698
+ _worksheet_write_formula1_str(self, validation->value_formula);
3699
+ if (is_between)
3700
+ _worksheet_write_formula2_str(self,
3701
+ validation->maximum_formula);
3702
+ break;
3703
+ }
3407
3704
 
3408
- _insert_cell(self, row_num, col_num, cell);
3705
+ if (validation->validate != LXW_VALIDATION_TYPE_ANY)
3706
+ lxw_xml_end_tag(self->file, "dataValidation");
3409
3707
 
3410
- return LXW_NO_ERROR;
3708
+ LXW_FREE_ATTRIBUTES();
3411
3709
  }
3412
3710
 
3413
3711
  /*
3414
- * Write a string to an Excel file.
3712
+ * Write the <dataValidations> element.
3415
3713
  */
3416
- lxw_error
3417
- worksheet_write_string(lxw_worksheet *self,
3418
- lxw_row_t row_num,
3714
+ STATIC void
3715
+ _worksheet_write_data_validations(lxw_worksheet *self)
3716
+ {
3717
+ struct xml_attribute_list attributes;
3718
+ struct xml_attribute *attribute;
3719
+ lxw_data_validation *data_validation;
3720
+
3721
+ if (self->num_validations == 0)
3722
+ return;
3723
+
3724
+ LXW_INIT_ATTRIBUTES();
3725
+ LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations);
3726
+
3727
+ lxw_xml_start_tag(self->file, "dataValidations", &attributes);
3728
+
3729
+ STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) {
3730
+ /* Write the dataValidation element. */
3731
+ _worksheet_write_data_validation(self, data_validation);
3732
+ }
3733
+
3734
+ lxw_xml_end_tag(self->file, "dataValidations");
3735
+
3736
+ LXW_FREE_ATTRIBUTES();
3737
+ }
3738
+
3739
+ /*
3740
+ * External functions to call intern XML methods shared with chartsheet.
3741
+ */
3742
+ void
3743
+ lxw_worksheet_write_sheet_views(lxw_worksheet *self)
3744
+ {
3745
+ _worksheet_write_sheet_views(self);
3746
+ }
3747
+
3748
+ void
3749
+ lxw_worksheet_write_page_margins(lxw_worksheet *self)
3750
+ {
3751
+ _worksheet_write_page_margins(self);
3752
+ }
3753
+
3754
+ void
3755
+ lxw_worksheet_write_drawings(lxw_worksheet *self)
3756
+ {
3757
+ _worksheet_write_drawings(self);
3758
+ }
3759
+
3760
+ void
3761
+ lxw_worksheet_write_sheet_protection(lxw_worksheet *self,
3762
+ lxw_protection *protect)
3763
+ {
3764
+ _worksheet_write_sheet_protection(self, protect);
3765
+ }
3766
+
3767
+ void
3768
+ lxw_worksheet_write_sheet_pr(lxw_worksheet *self)
3769
+ {
3770
+ _worksheet_write_sheet_pr(self);
3771
+ }
3772
+
3773
+ void
3774
+ lxw_worksheet_write_page_setup(lxw_worksheet *self)
3775
+ {
3776
+ _worksheet_write_page_setup(self);
3777
+ }
3778
+
3779
+ void
3780
+ lxw_worksheet_write_header_footer(lxw_worksheet *self)
3781
+ {
3782
+ _worksheet_write_header_footer(self);
3783
+ }
3784
+
3785
+ /*
3786
+ * Assemble and write the XML file.
3787
+ */
3788
+ void
3789
+ lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
3790
+ {
3791
+ /* Write the XML declaration. */
3792
+ _worksheet_xml_declaration(self);
3793
+
3794
+ /* Write the worksheet element. */
3795
+ _worksheet_write_worksheet(self);
3796
+
3797
+ /* Write the worksheet properties. */
3798
+ _worksheet_write_sheet_pr(self);
3799
+
3800
+ /* Write the worksheet dimensions. */
3801
+ _worksheet_write_dimension(self);
3802
+
3803
+ /* Write the sheet view properties. */
3804
+ _worksheet_write_sheet_views(self);
3805
+
3806
+ /* Write the sheet format properties. */
3807
+ _worksheet_write_sheet_format_pr(self);
3808
+
3809
+ /* Write the sheet column info. */
3810
+ _worksheet_write_cols(self);
3811
+
3812
+ /* Write the sheetData element. */
3813
+ if (!self->optimize)
3814
+ _worksheet_write_sheet_data(self);
3815
+ else
3816
+ _worksheet_write_optimized_sheet_data(self);
3817
+
3818
+ /* Write the sheetProtection element. */
3819
+ _worksheet_write_sheet_protection(self, &self->protection);
3820
+
3821
+ /* Write the autoFilter element. */
3822
+ _worksheet_write_auto_filter(self);
3823
+
3824
+ /* Write the mergeCells element. */
3825
+ _worksheet_write_merge_cells(self);
3826
+
3827
+ /* Write the dataValidations element. */
3828
+ _worksheet_write_data_validations(self);
3829
+
3830
+ /* Write the hyperlink element. */
3831
+ _worksheet_write_hyperlinks(self);
3832
+
3833
+ /* Write the printOptions element. */
3834
+ _worksheet_write_print_options(self);
3835
+
3836
+ /* Write the worksheet page_margins. */
3837
+ _worksheet_write_page_margins(self);
3838
+
3839
+ /* Write the worksheet page setup. */
3840
+ _worksheet_write_page_setup(self);
3841
+
3842
+ /* Write the headerFooter element. */
3843
+ _worksheet_write_header_footer(self);
3844
+
3845
+ /* Write the rowBreaks element. */
3846
+ _worksheet_write_row_breaks(self);
3847
+
3848
+ /* Write the colBreaks element. */
3849
+ _worksheet_write_col_breaks(self);
3850
+
3851
+ /* Write the drawing element. */
3852
+ _worksheet_write_drawings(self);
3853
+
3854
+ /* Close the worksheet tag. */
3855
+ lxw_xml_end_tag(self->file, "worksheet");
3856
+ }
3857
+
3858
+ /*****************************************************************************
3859
+ *
3860
+ * Public functions.
3861
+ *
3862
+ ****************************************************************************/
3863
+
3864
+ /*
3865
+ * Write a number to a cell in Excel.
3866
+ */
3867
+ lxw_error
3868
+ worksheet_write_number(lxw_worksheet *self,
3869
+ lxw_row_t row_num,
3870
+ lxw_col_t col_num, double value, lxw_format *format)
3871
+ {
3872
+ lxw_cell *cell;
3873
+ lxw_error err;
3874
+
3875
+ err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
3876
+ if (err)
3877
+ return err;
3878
+
3879
+ cell = _new_number_cell(row_num, col_num, value, format);
3880
+
3881
+ _insert_cell(self, row_num, col_num, cell);
3882
+
3883
+ return LXW_NO_ERROR;
3884
+ }
3885
+
3886
+ /*
3887
+ * Write a string to an Excel file.
3888
+ */
3889
+ lxw_error
3890
+ worksheet_write_string(lxw_worksheet *self,
3891
+ lxw_row_t row_num,
3419
3892
  lxw_col_t col_num, const char *string,
3420
3893
  lxw_format *format)
3421
3894
  {
@@ -3431,7 +3904,7 @@ worksheet_write_string(lxw_worksheet *self,
3431
3904
  if (format)
3432
3905
  return worksheet_write_blank(self, row_num, col_num, format);
3433
3906
  else
3434
- return LXW_ERROR_NULL_PARAMETER_IGNORED;
3907
+ return LXW_NO_ERROR;
3435
3908
  }
3436
3909
 
3437
3910
  err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
@@ -3443,7 +3916,7 @@ worksheet_write_string(lxw_worksheet *self,
3443
3916
 
3444
3917
  if (!self->optimize) {
3445
3918
  /* Get the SST element and string id. */
3446
- sst_element = lxw_get_sst_index(self->sst, string);
3919
+ sst_element = lxw_get_sst_index(self->sst, string, LXW_FALSE);
3447
3920
 
3448
3921
  if (!sst_element)
3449
3922
  return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND;
@@ -3918,6 +4391,156 @@ worksheet_write_url(lxw_worksheet *self,
3918
4391
  NULL);
3919
4392
  }
3920
4393
 
4394
+ /*
4395
+ * Write a rich string to an Excel file.
4396
+ *
4397
+ * Rather than duplicate several of the styles.c font xml methods of styles.c
4398
+ * and write the data to a memory buffer this function creates a temporary
4399
+ * styles object and uses it to write the data to a file. It then reads that
4400
+ * data back into memory and closes the file.
4401
+ */
4402
+ lxw_error
4403
+ worksheet_write_rich_string(lxw_worksheet *self,
4404
+ lxw_row_t row_num,
4405
+ lxw_col_t col_num,
4406
+ lxw_rich_string_tuple *rich_strings[],
4407
+ lxw_format *format)
4408
+ {
4409
+ lxw_cell *cell;
4410
+ int32_t string_id;
4411
+ struct sst_element *sst_element;
4412
+ lxw_error err;
4413
+ uint8_t i;
4414
+ long file_size;
4415
+ char *rich_string = NULL;
4416
+ char *string_copy = NULL;
4417
+ lxw_styles *styles = NULL;
4418
+ lxw_format *default_format = NULL;
4419
+ lxw_rich_string_tuple *rich_string_tuple = NULL;
4420
+ FILE *tmpfile;
4421
+
4422
+ err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
4423
+ if (err)
4424
+ return err;
4425
+
4426
+ /* Iterate through rich string fragments to check for input errors. */
4427
+ i = 0;
4428
+ err = LXW_NO_ERROR;
4429
+ while ((rich_string_tuple = rich_strings[i++]) != NULL) {
4430
+
4431
+ /* Check for NULL or empty strings. */
4432
+ if (!rich_string_tuple->string || !*rich_string_tuple->string) {
4433
+ err = LXW_ERROR_PARAMETER_VALIDATION;
4434
+ }
4435
+ }
4436
+
4437
+ /* If there are less than 2 fragments it isn't a rich string. */
4438
+ if (i <= 2)
4439
+ err = LXW_ERROR_PARAMETER_VALIDATION;
4440
+
4441
+ if (err)
4442
+ return err;
4443
+
4444
+ /* Create a tmp file for the styles object. */
4445
+ tmpfile = lxw_tmpfile(self->tmpdir);
4446
+ if (!tmpfile)
4447
+ return LXW_ERROR_CREATING_TMPFILE;
4448
+
4449
+ /* Create a temp styles object for writing the font data. */
4450
+ styles = lxw_styles_new();
4451
+ GOTO_LABEL_ON_MEM_ERROR(styles, mem_error);
4452
+ styles->file = tmpfile;
4453
+
4454
+ /* Create a default format for non-formatted text. */
4455
+ default_format = lxw_format_new();
4456
+ GOTO_LABEL_ON_MEM_ERROR(default_format, mem_error);
4457
+
4458
+ /* Iterate through the rich string fragments and write each one out. */
4459
+ i = 0;
4460
+ while ((rich_string_tuple = rich_strings[i++]) != NULL) {
4461
+ lxw_xml_start_tag(tmpfile, "r", NULL);
4462
+
4463
+ if (rich_string_tuple->format) {
4464
+ /* Write the user defined font format. */
4465
+ lxw_styles_write_rich_font(styles, rich_string_tuple->format);
4466
+ }
4467
+ else {
4468
+ /* Write a default font format. Except for the first fragment. */
4469
+ if (i > 1)
4470
+ lxw_styles_write_rich_font(styles, default_format);
4471
+ }
4472
+
4473
+ lxw_styles_write_string_fragment(styles, rich_string_tuple->string);
4474
+ lxw_xml_end_tag(tmpfile, "r");
4475
+ }
4476
+
4477
+ /* Free the temp objects. */
4478
+ lxw_styles_free(styles);
4479
+ lxw_format_free(default_format);
4480
+
4481
+ /* Flush the file and read the size to calculate the required memory. */
4482
+ fflush(tmpfile);
4483
+ file_size = ftell(tmpfile);
4484
+
4485
+ /* Allocate a buffer for the rich string xml data. */
4486
+ rich_string = calloc(file_size + 1, 1);
4487
+ GOTO_LABEL_ON_MEM_ERROR(rich_string, mem_error);
4488
+
4489
+ /* Rewind the file and read the data into the memory buffer. */
4490
+ rewind(tmpfile);
4491
+ if (fread(rich_string, file_size, 1, tmpfile) < 1) {
4492
+ fclose(tmpfile);
4493
+ free(rich_string);
4494
+ return LXW_ERROR_READING_TMPFILE;
4495
+ }
4496
+
4497
+ /* Close the temp file. */
4498
+ fclose(tmpfile);
4499
+
4500
+ if (lxw_utf8_strlen(rich_string) > LXW_STR_MAX) {
4501
+ free(rich_string);
4502
+ return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED;
4503
+ }
4504
+
4505
+ if (!self->optimize) {
4506
+ /* Get the SST element and string id. */
4507
+ sst_element = lxw_get_sst_index(self->sst, rich_string, LXW_TRUE);
4508
+ free(rich_string);
4509
+
4510
+ if (!sst_element)
4511
+ return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND;
4512
+
4513
+ string_id = sst_element->index;
4514
+ cell = _new_string_cell(row_num, col_num, string_id,
4515
+ sst_element->string, format);
4516
+ }
4517
+ else {
4518
+ /* Look for and escape control chars in the string. */
4519
+ if (strpbrk(rich_string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C"
4520
+ "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"
4521
+ "\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) {
4522
+ string_copy = lxw_escape_control_characters(rich_string);
4523
+ free(rich_string);
4524
+ }
4525
+ else {
4526
+ string_copy = rich_string;
4527
+ }
4528
+ cell = _new_inline_rich_string_cell(row_num, col_num, string_copy,
4529
+ format);
4530
+ }
4531
+
4532
+ _insert_cell(self, row_num, col_num, cell);
4533
+
4534
+ return LXW_NO_ERROR;
4535
+
4536
+ mem_error:
4537
+ lxw_styles_free(styles);
4538
+ lxw_format_free(default_format);
4539
+ fclose(tmpfile);
4540
+
4541
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
4542
+ }
4543
+
3921
4544
  /*
3922
4545
  * Set the properties of a single column or a range of columns with options.
3923
4546
  */
@@ -4010,6 +4633,14 @@ worksheet_set_column_opt(lxw_worksheet *self,
4010
4633
  copied_options = calloc(1, sizeof(lxw_col_options));
4011
4634
  RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED);
4012
4635
 
4636
+ /* Ensure the level is <= 7). */
4637
+ if (level > 7)
4638
+ level = 7;
4639
+
4640
+ if (level > self->outline_col_level)
4641
+ self->outline_col_level = level;
4642
+
4643
+ /* Set the column properties. */
4013
4644
  copied_options->firstcol = firstcol;
4014
4645
  copied_options->lastcol = lastcol;
4015
4646
  copied_options->width = width;
@@ -4082,6 +4713,14 @@ worksheet_set_row_opt(lxw_worksheet *self,
4082
4713
  height = self->default_row_height;
4083
4714
  }
4084
4715
 
4716
+ /* Ensure the level is <= 7). */
4717
+ if (level > 7)
4718
+ level = 7;
4719
+
4720
+ if (level > self->outline_row_level)
4721
+ self->outline_row_level = level;
4722
+
4723
+ /* Store the row properties. */
4085
4724
  row = _get_row(self, row_num);
4086
4725
 
4087
4726
  row->height = height;
@@ -4467,7 +5106,7 @@ worksheet_set_header_opt(lxw_worksheet *self, const char *string,
4467
5106
  lxw_header_footer_options *options)
4468
5107
  {
4469
5108
  if (options) {
4470
- if (options->margin > 0)
5109
+ if (options->margin >= 0.0)
4471
5110
  self->margin_header = options->margin;
4472
5111
  }
4473
5112
 
@@ -4491,7 +5130,7 @@ worksheet_set_footer_opt(lxw_worksheet *self, const char *string,
4491
5130
  lxw_header_footer_options *options)
4492
5131
  {
4493
5132
  if (options) {
4494
- if (options->margin > 0)
5133
+ if (options->margin >= 0.0)
4495
5134
  self->margin_footer = options->margin;
4496
5135
  }
4497
5136
 
@@ -4819,20 +5458,50 @@ worksheet_protect(lxw_worksheet *self, const char *password,
4819
5458
  struct lxw_protection *protect = &self->protection;
4820
5459
 
4821
5460
  /* 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';
5461
+ if (options) {
5462
+ protect->no_select_locked_cells = options->no_select_locked_cells;
5463
+ protect->no_select_unlocked_cells = options->no_select_unlocked_cells;
5464
+ protect->format_cells = options->format_cells;
5465
+ protect->format_columns = options->format_columns;
5466
+ protect->format_rows = options->format_rows;
5467
+ protect->insert_columns = options->insert_columns;
5468
+ protect->insert_rows = options->insert_rows;
5469
+ protect->insert_hyperlinks = options->insert_hyperlinks;
5470
+ protect->delete_columns = options->delete_columns;
5471
+ protect->delete_rows = options->delete_rows;
5472
+ protect->sort = options->sort;
5473
+ protect->autofilter = options->autofilter;
5474
+ protect->pivot_tables = options->pivot_tables;
5475
+ protect->scenarios = options->scenarios;
5476
+ protect->objects = options->objects;
5477
+ }
4827
5478
 
4828
5479
  if (password) {
4829
- uint16_t hash = _hash_password(password);
5480
+ uint16_t hash = lxw_hash_password(password);
4830
5481
  lxw_snprintf(protect->hash, 5, "%X", hash);
4831
5482
  }
4832
5483
 
5484
+ protect->no_content = LXW_TRUE;
4833
5485
  protect->is_configured = LXW_TRUE;
4834
5486
  }
4835
5487
 
5488
+ /*
5489
+ * Set the worksheet properties for outlines and grouping.
5490
+ */
5491
+ void
5492
+ worksheet_outline_settings(lxw_worksheet *self,
5493
+ uint8_t visible,
5494
+ uint8_t symbols_below,
5495
+ uint8_t symbols_right, uint8_t auto_style)
5496
+ {
5497
+ self->outline_on = visible;
5498
+ self->outline_below = symbols_below;
5499
+ self->outline_right = symbols_right;
5500
+ self->outline_style = auto_style;
5501
+
5502
+ self->outline_changed = LXW_TRUE;
5503
+ }
5504
+
4836
5505
  /*
4837
5506
  * Set the default row properties
4838
5507
  */
@@ -4864,7 +5533,7 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4864
5533
  lxw_image_options *user_options)
4865
5534
  {
4866
5535
  FILE *image_stream;
4867
- char *short_name;
5536
+ char *description;
4868
5537
  lxw_image_options *options;
4869
5538
 
4870
5539
  if (!filename) {
@@ -4882,9 +5551,9 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4882
5551
  return LXW_ERROR_PARAMETER_VALIDATION;
4883
5552
  }
4884
5553
 
4885
- /* Get the filename from the full path to add to the Drawing object. */
4886
- short_name = lxw_basename(filename);
4887
- if (!short_name) {
5554
+ /* Use the filename as the default description, like Excel. */
5555
+ description = lxw_basename(filename);
5556
+ if (!description) {
4888
5557
  LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
4889
5558
  "couldn't get basename for file: %s.", filename);
4890
5559
  fclose(image_stream);
@@ -4899,14 +5568,15 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4899
5568
  }
4900
5569
 
4901
5570
  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);
5571
+ options->x_offset = user_options->x_offset;
5572
+ options->y_offset = user_options->y_offset;
5573
+ options->x_scale = user_options->x_scale;
5574
+ options->y_scale = user_options->y_scale;
4905
5575
  }
4906
5576
 
4907
5577
  /* Copy other options or set defaults. */
4908
5578
  options->filename = lxw_strdup(filename);
4909
- options->short_name = lxw_strdup(short_name);
5579
+ options->description = lxw_strdup(description);
4910
5580
  options->stream = image_stream;
4911
5581
  options->row = row_num;
4912
5582
  options->col = col_num;
@@ -4919,10 +5589,12 @@ worksheet_insert_image_opt(lxw_worksheet *self,
4919
5589
 
4920
5590
  if (_get_image_properties(options) == LXW_NO_ERROR) {
4921
5591
  STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
5592
+ fclose(image_stream);
4922
5593
  return LXW_NO_ERROR;
4923
5594
  }
4924
5595
  else {
4925
- free(options);
5596
+ _free_image_options(options);
5597
+ fclose(image_stream);
4926
5598
  return LXW_ERROR_IMAGE_DIMENSIONS;
4927
5599
  }
4928
5600
  }
@@ -4938,6 +5610,95 @@ worksheet_insert_image(lxw_worksheet *self,
4938
5610
  return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL);
4939
5611
  }
4940
5612
 
5613
+ lxw_error
5614
+ worksheet_insert_image_buffer_opt(lxw_worksheet *self,
5615
+ lxw_row_t row_num,
5616
+ lxw_col_t col_num,
5617
+ const unsigned char *image_buffer,
5618
+ size_t image_size,
5619
+ lxw_image_options *user_options)
5620
+ {
5621
+ FILE *image_stream;
5622
+ lxw_image_options *options;
5623
+
5624
+ if (!image_size) {
5625
+ LXW_WARN("worksheet_insert_image_buffer()/_opt(): "
5626
+ "size must be non-zero.");
5627
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
5628
+ }
5629
+
5630
+ /* Write the image buffer to a temporary file so we can read the
5631
+ * dimensions like an ordinary file. */
5632
+ image_stream = lxw_tmpfile(self->tmpdir);
5633
+ if (!image_stream)
5634
+ return LXW_ERROR_CREATING_TMPFILE;
5635
+
5636
+ fwrite(image_buffer, 1, image_size, image_stream);
5637
+ rewind(image_stream);
5638
+
5639
+ /* Create a new object to hold the image options. */
5640
+ options = calloc(1, sizeof(lxw_image_options));
5641
+ if (!options) {
5642
+ fclose(image_stream);
5643
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5644
+ }
5645
+
5646
+ /* Store the image data in the options structure. */
5647
+ options->image_buffer = calloc(1, image_size);
5648
+ if (!options->image_buffer) {
5649
+ _free_image_options(options);
5650
+ fclose(image_stream);
5651
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5652
+ }
5653
+ else {
5654
+ memcpy(options->image_buffer, image_buffer, image_size);
5655
+ options->image_buffer_size = image_size;
5656
+ options->is_image_buffer = LXW_TRUE;
5657
+ }
5658
+
5659
+ if (user_options) {
5660
+ options->x_offset = user_options->x_offset;
5661
+ options->y_offset = user_options->y_offset;
5662
+ options->x_scale = user_options->x_scale;
5663
+ options->y_scale = user_options->y_scale;
5664
+ options->description = lxw_strdup(user_options->description);
5665
+ }
5666
+
5667
+ /* Copy other options or set defaults. */
5668
+ options->filename = lxw_strdup("image_buffer");
5669
+ options->stream = image_stream;
5670
+ options->row = row_num;
5671
+ options->col = col_num;
5672
+
5673
+ if (!options->x_scale)
5674
+ options->x_scale = 1;
5675
+
5676
+ if (!options->y_scale)
5677
+ options->y_scale = 1;
5678
+
5679
+ if (_get_image_properties(options) == LXW_NO_ERROR) {
5680
+ STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
5681
+ fclose(image_stream);
5682
+ return LXW_NO_ERROR;
5683
+ }
5684
+ else {
5685
+ _free_image_options(options);
5686
+ fclose(image_stream);
5687
+ return LXW_ERROR_IMAGE_DIMENSIONS;
5688
+ }
5689
+ }
5690
+
5691
+ lxw_error
5692
+ worksheet_insert_image_buffer(lxw_worksheet *self,
5693
+ lxw_row_t row_num,
5694
+ lxw_col_t col_num,
5695
+ const unsigned char *image_buffer,
5696
+ size_t image_size)
5697
+ {
5698
+ return worksheet_insert_image_buffer_opt(self, row_num, col_num,
5699
+ image_buffer, image_size, NULL);
5700
+ }
5701
+
4941
5702
  /*
4942
5703
  * Insert an chart into the worksheet.
4943
5704
  */
@@ -4984,8 +5745,12 @@ worksheet_insert_chart_opt(lxw_worksheet *self,
4984
5745
  options = calloc(1, sizeof(lxw_image_options));
4985
5746
  RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED);
4986
5747
 
4987
- if (user_options)
4988
- memcpy(options, user_options, sizeof(lxw_image_options));
5748
+ if (user_options) {
5749
+ options->x_offset = user_options->x_offset;
5750
+ options->y_offset = user_options->y_offset;
5751
+ options->x_scale = user_options->x_scale;
5752
+ options->y_scale = user_options->y_scale;
5753
+ }
4989
5754
 
4990
5755
  /* Copy other options or set defaults. */
4991
5756
  options->row = row_num;
@@ -5020,3 +5785,304 @@ worksheet_insert_chart(lxw_worksheet *self,
5020
5785
  {
5021
5786
  return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
5022
5787
  }
5788
+
5789
+ /*
5790
+ * Add a data validation to a worksheet, for a range. Ironically this requires
5791
+ * a lot of validation of the user input.
5792
+ */
5793
+ lxw_error
5794
+ worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row,
5795
+ lxw_col_t first_col,
5796
+ lxw_row_t last_row,
5797
+ lxw_col_t last_col,
5798
+ lxw_data_validation *validation)
5799
+ {
5800
+ lxw_data_validation *copy;
5801
+ uint8_t is_between = LXW_FALSE;
5802
+ uint8_t is_formula = LXW_FALSE;
5803
+ uint8_t has_criteria = LXW_TRUE;
5804
+ lxw_error err;
5805
+ lxw_row_t tmp_row;
5806
+ lxw_col_t tmp_col;
5807
+ size_t length;
5808
+
5809
+ /* No action is required for validation type 'any' unless there are
5810
+ * input messages to display.*/
5811
+ if (validation->validate == LXW_VALIDATION_TYPE_ANY
5812
+ && !(validation->input_title || validation->input_message)) {
5813
+
5814
+ return LXW_NO_ERROR;
5815
+ }
5816
+
5817
+ /* Check for formula types. */
5818
+ switch (validation->validate) {
5819
+ case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
5820
+ case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
5821
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5822
+ case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
5823
+ case LXW_VALIDATION_TYPE_DATE_FORMULA:
5824
+ case LXW_VALIDATION_TYPE_TIME_FORMULA:
5825
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5826
+ is_formula = LXW_TRUE;
5827
+ break;
5828
+ }
5829
+
5830
+ /* Check for types without a criteria. */
5831
+ switch (validation->validate) {
5832
+ case LXW_VALIDATION_TYPE_LIST:
5833
+ case LXW_VALIDATION_TYPE_LIST_FORMULA:
5834
+ case LXW_VALIDATION_TYPE_ANY:
5835
+ case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
5836
+ has_criteria = LXW_FALSE;
5837
+ break;
5838
+ }
5839
+
5840
+ /* Check that a validation parameter has been specified
5841
+ * except for 'list', 'any' and 'custom'. */
5842
+ if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) {
5843
+
5844
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5845
+ "criteria parameter must be specified.");
5846
+ return LXW_ERROR_PARAMETER_VALIDATION;
5847
+ }
5848
+
5849
+ /* Check for "between" criteria so we can do additional checks. */
5850
+ if (has_criteria
5851
+ && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN
5852
+ || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) {
5853
+
5854
+ is_between = LXW_TRUE;
5855
+ }
5856
+
5857
+ /* Check that formula values are non NULL. */
5858
+ if (is_formula) {
5859
+ if (is_between) {
5860
+ if (!validation->minimum_formula) {
5861
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5862
+ "minimum_formula parameter cannot be NULL.");
5863
+ return LXW_ERROR_PARAMETER_VALIDATION;
5864
+ }
5865
+ if (!validation->maximum_formula) {
5866
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5867
+ "maximum_formula parameter cannot be NULL.");
5868
+ return LXW_ERROR_PARAMETER_VALIDATION;
5869
+ }
5870
+ }
5871
+ else {
5872
+ if (!validation->value_formula) {
5873
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5874
+ "formula parameter cannot be NULL.");
5875
+ return LXW_ERROR_PARAMETER_VALIDATION;
5876
+ }
5877
+ }
5878
+ }
5879
+
5880
+ /* Check Excel limitations on input strings. */
5881
+ if (validation->input_title) {
5882
+ length = lxw_utf8_strlen(validation->input_title);
5883
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5884
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5885
+ "input_title length > Excel limit of %d.",
5886
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5887
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5888
+ }
5889
+ }
5890
+
5891
+ if (validation->error_title) {
5892
+ length = lxw_utf8_strlen(validation->error_title);
5893
+ if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
5894
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5895
+ "error_title length > Excel limit of %d.",
5896
+ LXW_VALIDATION_MAX_TITLE_LENGTH);
5897
+ return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
5898
+ }
5899
+ }
5900
+
5901
+ if (validation->input_message) {
5902
+ length = lxw_utf8_strlen(validation->input_message);
5903
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5904
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5905
+ "input_message length > Excel limit of %d.",
5906
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5907
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5908
+ }
5909
+ }
5910
+
5911
+ if (validation->error_message) {
5912
+ length = lxw_utf8_strlen(validation->error_message);
5913
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5914
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5915
+ "error_message length > Excel limit of %d.",
5916
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5917
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5918
+ }
5919
+ }
5920
+
5921
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
5922
+ length = _validation_list_length(validation->value_list);
5923
+
5924
+ if (length == 0) {
5925
+ LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
5926
+ "list parameters cannot be zero.");
5927
+ return LXW_ERROR_PARAMETER_VALIDATION;
5928
+ }
5929
+
5930
+ if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
5931
+ LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
5932
+ "list length with commas > Excel limit of %d.",
5933
+ LXW_VALIDATION_MAX_STRING_LENGTH);
5934
+ return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
5935
+ }
5936
+ }
5937
+
5938
+ /* Swap last row/col with first row/col as necessary */
5939
+ if (first_row > last_row) {
5940
+ tmp_row = last_row;
5941
+ last_row = first_row;
5942
+ first_row = tmp_row;
5943
+ }
5944
+ if (first_col > last_col) {
5945
+ tmp_col = last_col;
5946
+ last_col = first_col;
5947
+ first_col = tmp_col;
5948
+ }
5949
+
5950
+ /* Check that dimensions are valid but don't store them. */
5951
+ err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE);
5952
+ if (err)
5953
+ return err;
5954
+
5955
+ /* Create a copy of the parameters from the user data validation. */
5956
+ copy = calloc(1, sizeof(lxw_data_validation));
5957
+ GOTO_LABEL_ON_MEM_ERROR(copy, mem_error);
5958
+
5959
+ /* Create the data validation range. */
5960
+ if (first_row == last_row && first_col == last_col)
5961
+ lxw_rowcol_to_cell(copy->sqref, first_row, last_col);
5962
+ else
5963
+ lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row,
5964
+ last_col);
5965
+
5966
+ /* Copy the parameters from the user data validation. */
5967
+ copy->validate = validation->validate;
5968
+ copy->value_number = validation->value_number;
5969
+ copy->error_type = validation->error_type;
5970
+ copy->dropdown = validation->dropdown;
5971
+ copy->is_between = is_between;
5972
+
5973
+ if (has_criteria)
5974
+ copy->criteria = validation->criteria;
5975
+
5976
+ if (is_between) {
5977
+ copy->value_number = validation->minimum_number;
5978
+ copy->maximum_number = validation->maximum_number;
5979
+ }
5980
+
5981
+ /* Copy the input/error titles and messages. */
5982
+ if (validation->input_title) {
5983
+ copy->input_title = lxw_strdup_formula(validation->input_title);
5984
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error);
5985
+ }
5986
+
5987
+ if (validation->input_message) {
5988
+ copy->input_message = lxw_strdup_formula(validation->input_message);
5989
+ GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error);
5990
+ }
5991
+
5992
+ if (validation->error_title) {
5993
+ copy->error_title = lxw_strdup_formula(validation->error_title);
5994
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error);
5995
+ }
5996
+
5997
+ if (validation->error_message) {
5998
+ copy->error_message = lxw_strdup_formula(validation->error_message);
5999
+ GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error);
6000
+ }
6001
+
6002
+ /* Copy the formula strings. */
6003
+ if (is_formula) {
6004
+ if (is_between) {
6005
+ copy->value_formula =
6006
+ lxw_strdup_formula(validation->minimum_formula);
6007
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
6008
+ copy->maximum_formula =
6009
+ lxw_strdup_formula(validation->maximum_formula);
6010
+ GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error);
6011
+ }
6012
+ else {
6013
+ copy->value_formula =
6014
+ lxw_strdup_formula(validation->value_formula);
6015
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
6016
+ }
6017
+ }
6018
+
6019
+ /* Copy the validation list as a csv string. */
6020
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
6021
+ copy->value_formula = _validation_list_to_csv(validation->value_list);
6022
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
6023
+ }
6024
+
6025
+ if (validation->validate == LXW_VALIDATION_TYPE_LIST_FORMULA) {
6026
+ copy->value_formula = lxw_strdup_formula(validation->value_formula);
6027
+ GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
6028
+ }
6029
+
6030
+ if (validation->validate == LXW_VALIDATION_TYPE_DATE
6031
+ || validation->validate == LXW_VALIDATION_TYPE_TIME) {
6032
+ if (is_between) {
6033
+ copy->value_number =
6034
+ lxw_datetime_to_excel_date(&validation->minimum_datetime,
6035
+ LXW_EPOCH_1900);
6036
+ copy->maximum_number =
6037
+ lxw_datetime_to_excel_date(&validation->maximum_datetime,
6038
+ LXW_EPOCH_1900);
6039
+ }
6040
+ else {
6041
+ copy->value_number =
6042
+ lxw_datetime_to_excel_date(&validation->value_datetime,
6043
+ LXW_EPOCH_1900);
6044
+ }
6045
+ }
6046
+
6047
+ /* These options are on by default so we can't take plain booleans. */
6048
+ copy->ignore_blank = validation->ignore_blank ^ 1;
6049
+ copy->show_input = validation->show_input ^ 1;
6050
+ copy->show_error = validation->show_error ^ 1;
6051
+
6052
+ STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers);
6053
+
6054
+ self->num_validations++;
6055
+
6056
+ return LXW_NO_ERROR;
6057
+
6058
+ mem_error:
6059
+ _free_data_validation(copy);
6060
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
6061
+ }
6062
+
6063
+ /*
6064
+ * Add a data validation to a worksheet, for a cell.
6065
+ */
6066
+ lxw_error
6067
+ worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row,
6068
+ lxw_col_t col, lxw_data_validation *validation)
6069
+ {
6070
+ return worksheet_data_validation_range(self, row, col,
6071
+ row, col, validation);
6072
+ }
6073
+
6074
+ /*
6075
+ * Set the VBA name for the worksheet.
6076
+ */
6077
+ lxw_error
6078
+ worksheet_set_vba_name(lxw_worksheet *self, const char *name)
6079
+ {
6080
+ if (!name) {
6081
+ LXW_WARN("worksheet_set_vba_name(): " "name must be specified.");
6082
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
6083
+ }
6084
+
6085
+ self->vba_codename = lxw_strdup(name);
6086
+
6087
+ return LXW_NO_ERROR;
6088
+ }