fast_excel 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +2 -0
  3. data/.gitignore +7 -0
  4. data/.travis.yml +44 -0
  5. data/CHANGELOG.md +41 -1
  6. data/Dockerfile.test +16 -0
  7. data/Gemfile +5 -2
  8. data/Gemfile.lock +30 -23
  9. data/LICENSE +21 -0
  10. data/Makefile +13 -0
  11. data/README.md +177 -40
  12. data/Rakefile +16 -0
  13. data/appveyor.yml +25 -0
  14. data/benchmarks/1k_rows.rb +17 -4
  15. data/benchmarks/20k_rows.rb +4 -0
  16. data/benchmarks/auto_width.rb +37 -0
  17. data/benchmarks/init.rb +14 -2
  18. data/benchmarks/memory.rb +8 -0
  19. data/benchmarks/profiler.rb +27 -0
  20. data/benchmarks/write_value.rb +62 -0
  21. data/examples/example.rb +3 -4
  22. data/examples/example_align.rb +23 -0
  23. data/examples/example_auto_width.rb +26 -0
  24. data/examples/example_colors.rb +37 -0
  25. data/examples/example_filters.rb +36 -0
  26. data/examples/example_formula.rb +1 -5
  27. data/examples/example_hyperlink.rb +20 -0
  28. data/examples/example_image.rb +1 -1
  29. data/examples/example_styles.rb +27 -0
  30. data/examples/logo.png +0 -0
  31. data/ext/fast_excel/extconf.rb +3 -0
  32. data/ext/fast_excel/text_width_ext.c +460 -0
  33. data/fast_excel.gemspec +2 -3
  34. data/letters.html +114 -0
  35. data/lib/fast_excel.rb +455 -78
  36. data/lib/fast_excel/binding.rb +31 -21
  37. data/lib/fast_excel/binding/chart.rb +20 -1
  38. data/lib/fast_excel/binding/format.rb +11 -4
  39. data/lib/fast_excel/binding/workbook.rb +10 -2
  40. data/lib/fast_excel/binding/worksheet.rb +44 -27
  41. data/libxlsxwriter/.gitignore +1 -0
  42. data/libxlsxwriter/.indent.pro +8 -0
  43. data/libxlsxwriter/.travis.yml +12 -0
  44. data/libxlsxwriter/CMakeLists.txt +338 -0
  45. data/libxlsxwriter/CONTRIBUTING.md +1 -1
  46. data/libxlsxwriter/Changes.txt +162 -0
  47. data/libxlsxwriter/LICENSE.txt +65 -4
  48. data/libxlsxwriter/Makefile +33 -11
  49. data/libxlsxwriter/Readme.md +3 -1
  50. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +2 -1
  51. data/libxlsxwriter/cocoapods/libxlsxwriter.modulemap +2 -2
  52. data/libxlsxwriter/include/xlsxwriter.h +2 -2
  53. data/libxlsxwriter/include/xlsxwriter/app.h +2 -2
  54. data/libxlsxwriter/include/xlsxwriter/chart.h +164 -13
  55. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +544 -0
  56. data/libxlsxwriter/include/xlsxwriter/common.h +35 -6
  57. data/libxlsxwriter/include/xlsxwriter/content_types.h +5 -2
  58. data/libxlsxwriter/include/xlsxwriter/core.h +2 -2
  59. data/libxlsxwriter/include/xlsxwriter/custom.h +2 -2
  60. data/libxlsxwriter/include/xlsxwriter/drawing.h +3 -2
  61. data/libxlsxwriter/include/xlsxwriter/format.h +8 -8
  62. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  63. data/libxlsxwriter/include/xlsxwriter/packager.h +18 -8
  64. data/libxlsxwriter/include/xlsxwriter/relationships.h +2 -2
  65. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +5 -3
  66. data/libxlsxwriter/include/xlsxwriter/styles.h +10 -5
  67. data/libxlsxwriter/include/xlsxwriter/theme.h +2 -2
  68. data/libxlsxwriter/include/xlsxwriter/utility.h +35 -5
  69. data/libxlsxwriter/include/xlsxwriter/workbook.h +234 -57
  70. data/libxlsxwriter/include/xlsxwriter/worksheet.h +780 -91
  71. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
  72. data/libxlsxwriter/libxlsxwriter.podspec +4 -2
  73. data/libxlsxwriter/src/Makefile +31 -6
  74. data/libxlsxwriter/src/app.c +2 -2
  75. data/libxlsxwriter/src/chart.c +116 -23
  76. data/libxlsxwriter/src/chartsheet.c +508 -0
  77. data/libxlsxwriter/src/content_types.c +12 -4
  78. data/libxlsxwriter/src/core.c +11 -11
  79. data/libxlsxwriter/src/custom.c +3 -3
  80. data/libxlsxwriter/src/drawing.c +114 -17
  81. data/libxlsxwriter/src/format.c +5 -5
  82. data/libxlsxwriter/src/hash_table.c +1 -1
  83. data/libxlsxwriter/src/packager.c +378 -61
  84. data/libxlsxwriter/src/relationships.c +2 -2
  85. data/libxlsxwriter/src/shared_strings.c +18 -4
  86. data/libxlsxwriter/src/styles.c +59 -12
  87. data/libxlsxwriter/src/theme.c +2 -2
  88. data/libxlsxwriter/src/utility.c +93 -6
  89. data/libxlsxwriter/src/workbook.c +379 -61
  90. data/libxlsxwriter/src/worksheet.c +1240 -174
  91. data/libxlsxwriter/src/xmlwriter.c +18 -9
  92. data/libxlsxwriter/third_party/minizip/Makefile +6 -1
  93. data/libxlsxwriter/third_party/minizip/ioapi.c +10 -0
  94. data/libxlsxwriter/third_party/minizip/zip.c +2 -0
  95. data/libxlsxwriter/third_party/tmpfileplus/tmpfileplus.c +2 -2
  96. data/libxlsxwriter/version.txt +1 -1
  97. data/test/auto_width_test.rb +19 -0
  98. data/test/date_test.rb +34 -0
  99. data/test/format_test.rb +179 -0
  100. data/test/reopen_test.rb +22 -0
  101. data/test/test_helper.rb +23 -4
  102. data/test/text_width_test.rb +80 -0
  103. data/test/tmpfile_test.rb +1 -0
  104. data/test/validations_test.rb +47 -0
  105. data/test/worksheet_test.rb +129 -0
  106. metadata +34 -5
@@ -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
+ }