fast_excel 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -7
  3. data/CHANGELOG.md +9 -0
  4. data/Gemfile +1 -1
  5. data/examples/example.rb +2 -0
  6. data/examples/example_date_time.rb +38 -0
  7. data/fast_excel.gemspec +2 -2
  8. data/lib/fast_excel/binding/format.rb +17 -0
  9. data/lib/fast_excel/binding/workbook.rb +39 -17
  10. data/lib/fast_excel/binding/worksheet.rb +57 -13
  11. data/lib/fast_excel/binding.rb +7 -7
  12. data/lib/fast_excel.rb +27 -20
  13. data/libxlsxwriter/.github/FUNDING.yml +1 -0
  14. data/libxlsxwriter/.github/ISSUE_TEMPLATE.md +85 -0
  15. data/libxlsxwriter/.github/PULL_REQUEST_TEMPLATE.md +130 -0
  16. data/libxlsxwriter/.github/workflows/cmake_actions.yml +48 -0
  17. data/libxlsxwriter/.github/workflows/code_style.yml +23 -0
  18. data/libxlsxwriter/.github/workflows/coverity.yml +22 -0
  19. data/libxlsxwriter/.github/workflows/make_actions.yml +52 -0
  20. data/libxlsxwriter/.github/workflows/valgrind.yml +23 -0
  21. data/libxlsxwriter/.github/workflows/windows_build.yml +54 -0
  22. data/libxlsxwriter/.github/workflows/zig_build.yml +22 -0
  23. data/libxlsxwriter/.gitignore +16 -1
  24. data/libxlsxwriter/.indent.pro +24 -0
  25. data/libxlsxwriter/CMakeLists.txt +156 -56
  26. data/libxlsxwriter/CONTRIBUTING.md +2 -2
  27. data/libxlsxwriter/Changes.txt +344 -2
  28. data/libxlsxwriter/LICENSE.txt +66 -8
  29. data/libxlsxwriter/Makefile +151 -54
  30. data/libxlsxwriter/Package.swift +42 -0
  31. data/libxlsxwriter/Readme.md +4 -2
  32. data/libxlsxwriter/build.zig +324 -0
  33. data/libxlsxwriter/build.zig.zon +11 -0
  34. data/libxlsxwriter/cmake/FindMINIZIP.cmake +3 -3
  35. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +6 -0
  36. data/libxlsxwriter/include/xlsxwriter/app.h +2 -1
  37. data/libxlsxwriter/include/xlsxwriter/chart.h +236 -32
  38. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +7 -7
  39. data/libxlsxwriter/include/xlsxwriter/comment.h +76 -0
  40. data/libxlsxwriter/include/xlsxwriter/common.h +111 -50
  41. data/libxlsxwriter/include/xlsxwriter/content_types.h +8 -1
  42. data/libxlsxwriter/include/xlsxwriter/core.h +1 -1
  43. data/libxlsxwriter/include/xlsxwriter/custom.h +1 -1
  44. data/libxlsxwriter/include/xlsxwriter/drawing.h +11 -20
  45. data/libxlsxwriter/include/xlsxwriter/format.h +121 -8
  46. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  47. data/libxlsxwriter/include/xlsxwriter/metadata.h +49 -0
  48. data/libxlsxwriter/include/xlsxwriter/packager.h +27 -16
  49. data/libxlsxwriter/include/xlsxwriter/relationships.h +1 -1
  50. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +1 -1
  51. data/libxlsxwriter/include/xlsxwriter/styles.h +13 -7
  52. data/libxlsxwriter/include/xlsxwriter/table.h +51 -0
  53. data/libxlsxwriter/include/xlsxwriter/theme.h +1 -1
  54. data/libxlsxwriter/include/xlsxwriter/third_party/emyg_dtoa.h +26 -0
  55. data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +27 -25
  56. data/libxlsxwriter/include/xlsxwriter/third_party/md5.h +45 -0
  57. data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +155 -153
  58. data/libxlsxwriter/include/xlsxwriter/utility.h +70 -8
  59. data/libxlsxwriter/include/xlsxwriter/vml.h +55 -0
  60. data/libxlsxwriter/include/xlsxwriter/workbook.h +218 -47
  61. data/libxlsxwriter/include/xlsxwriter/worksheet.h +2770 -241
  62. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +12 -8
  63. data/libxlsxwriter/include/xlsxwriter.h +4 -2
  64. data/libxlsxwriter/libxlsxwriter.podspec +8 -5
  65. data/libxlsxwriter/src/Makefile +58 -21
  66. data/libxlsxwriter/src/app.c +5 -2
  67. data/libxlsxwriter/src/chart.c +396 -81
  68. data/libxlsxwriter/src/chartsheet.c +22 -22
  69. data/libxlsxwriter/src/comment.c +443 -0
  70. data/libxlsxwriter/src/content_types.c +40 -1
  71. data/libxlsxwriter/src/core.c +2 -2
  72. data/libxlsxwriter/src/custom.c +1 -1
  73. data/libxlsxwriter/src/drawing.c +160 -40
  74. data/libxlsxwriter/src/format.c +109 -25
  75. data/libxlsxwriter/src/hash_table.c +1 -1
  76. data/libxlsxwriter/src/metadata.c +283 -0
  77. data/libxlsxwriter/src/packager.c +794 -94
  78. data/libxlsxwriter/src/relationships.c +1 -1
  79. data/libxlsxwriter/src/shared_strings.c +2 -4
  80. data/libxlsxwriter/src/styles.c +353 -58
  81. data/libxlsxwriter/src/table.c +304 -0
  82. data/libxlsxwriter/src/theme.c +1 -1
  83. data/libxlsxwriter/src/utility.c +143 -43
  84. data/libxlsxwriter/src/vml.c +1062 -0
  85. data/libxlsxwriter/src/workbook.c +567 -77
  86. data/libxlsxwriter/src/worksheet.c +6668 -1462
  87. data/libxlsxwriter/src/xmlwriter.c +95 -5
  88. data/libxlsxwriter/third_party/dtoa/Makefile +42 -0
  89. data/libxlsxwriter/third_party/dtoa/emyg_dtoa.c +461 -0
  90. data/libxlsxwriter/third_party/dtoa/emyg_dtoa.h +26 -0
  91. data/libxlsxwriter/third_party/md5/Makefile +42 -0
  92. data/libxlsxwriter/third_party/md5/md5.c +291 -0
  93. data/libxlsxwriter/third_party/md5/md5.h +45 -0
  94. data/libxlsxwriter/third_party/minizip/Makefile +3 -8
  95. data/libxlsxwriter/third_party/minizip/Makefile.orig +8 -4
  96. data/libxlsxwriter/third_party/minizip/MiniZip64_Changes.txt +1 -1
  97. data/libxlsxwriter/third_party/minizip/configure.ac +1 -1
  98. data/libxlsxwriter/third_party/minizip/crypt.h +13 -16
  99. data/libxlsxwriter/third_party/minizip/ioapi.c +31 -57
  100. data/libxlsxwriter/third_party/minizip/ioapi.h +31 -23
  101. data/libxlsxwriter/third_party/minizip/iowin32.c +29 -45
  102. data/libxlsxwriter/third_party/minizip/iowin32.h +4 -4
  103. data/libxlsxwriter/third_party/minizip/miniunz.c +29 -56
  104. data/libxlsxwriter/third_party/minizip/minizip.c +38 -49
  105. data/libxlsxwriter/third_party/minizip/mztools.c +1 -7
  106. data/libxlsxwriter/third_party/minizip/unzip.c +202 -342
  107. data/libxlsxwriter/third_party/minizip/unzip.h +74 -74
  108. data/libxlsxwriter/third_party/minizip/zip.c +165 -218
  109. data/libxlsxwriter/third_party/minizip/zip.h +164 -154
  110. data/libxlsxwriter/third_party/tmpfileplus/Makefile +3 -3
  111. data/libxlsxwriter/version.txt +1 -1
  112. data/test/auto_width_test.rb +20 -0
  113. data/test/default_format_test.rb +1 -1
  114. data/test/validations_test.rb +3 -3
  115. data/test/worksheet_test.rb +6 -1
  116. metadata +33 -7
  117. data/libxlsxwriter/.travis.yml +0 -37
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -17,11 +17,15 @@ STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1,
17
17
  lxw_worksheet_name *name2);
18
18
  STATIC int _chartsheet_name_cmp(lxw_chartsheet_name *name1,
19
19
  lxw_chartsheet_name *name2);
20
+ STATIC int _image_md5_cmp(lxw_image_md5 *tuple1, lxw_image_md5 *tuple2);
21
+
20
22
  #ifndef __clang_analyzer__
21
23
  LXW_RB_GENERATE_WORKSHEET_NAMES(lxw_worksheet_names, lxw_worksheet_name,
22
24
  tree_pointers, _worksheet_name_cmp);
23
25
  LXW_RB_GENERATE_CHARTSHEET_NAMES(lxw_chartsheet_names, lxw_chartsheet_name,
24
26
  tree_pointers, _chartsheet_name_cmp);
27
+ LXW_RB_GENERATE_IMAGE_MD5S(lxw_image_md5s, lxw_image_md5,
28
+ tree_pointers, _image_md5_cmp);
25
29
  #endif
26
30
 
27
31
  /*
@@ -49,6 +53,12 @@ _chartsheet_name_cmp(lxw_chartsheet_name *name1, lxw_chartsheet_name *name2)
49
53
  return lxw_strcasecmp(name1->name, name2->name);
50
54
  }
51
55
 
56
+ STATIC int
57
+ _image_md5_cmp(lxw_image_md5 *tuple1, lxw_image_md5 *tuple2)
58
+ {
59
+ return strcmp(tuple1->md5, tuple2->md5);
60
+ }
61
+
52
62
  /*
53
63
  * Free workbook properties.
54
64
  */
@@ -56,16 +66,16 @@ STATIC void
56
66
  _free_doc_properties(lxw_doc_properties *properties)
57
67
  {
58
68
  if (properties) {
59
- free(properties->title);
60
- free(properties->subject);
61
- free(properties->author);
62
- free(properties->manager);
63
- free(properties->company);
64
- free(properties->category);
65
- free(properties->keywords);
66
- free(properties->comments);
67
- free(properties->status);
68
- free(properties->hyperlink_base);
69
+ free((void *) properties->title);
70
+ free((void *) properties->subject);
71
+ free((void *) properties->author);
72
+ free((void *) properties->manager);
73
+ free((void *) properties->company);
74
+ free((void *) properties->category);
75
+ free((void *) properties->keywords);
76
+ free((void *) properties->comments);
77
+ free((void *) properties->status);
78
+ free((void *) properties->hyperlink_base);
69
79
  }
70
80
 
71
81
  free(properties);
@@ -97,6 +107,8 @@ lxw_workbook_free(lxw_workbook *workbook)
97
107
  struct lxw_worksheet_name *next_worksheet_name;
98
108
  struct lxw_chartsheet_name *chartsheet_name;
99
109
  struct lxw_chartsheet_name *next_chartsheet_name;
110
+ struct lxw_image_md5 *image_md5;
111
+ struct lxw_image_md5 *next_image_md5;
100
112
  lxw_chart *chart;
101
113
  lxw_format *format;
102
114
  lxw_defined_name *defined_name;
@@ -204,11 +216,55 @@ lxw_workbook_free(lxw_workbook *workbook)
204
216
  free(workbook->chartsheet_names);
205
217
  }
206
218
 
219
+ if (workbook->image_md5s) {
220
+ for (image_md5 = RB_MIN(lxw_image_md5s, workbook->image_md5s);
221
+ image_md5; image_md5 = next_image_md5) {
222
+
223
+ next_image_md5 =
224
+ RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5);
225
+ RB_REMOVE(lxw_image_md5s, workbook->image_md5s, image_md5);
226
+ free(image_md5->md5);
227
+ free(image_md5);
228
+ }
229
+
230
+ free(workbook->image_md5s);
231
+ }
232
+
233
+ if (workbook->header_image_md5s) {
234
+ for (image_md5 = RB_MIN(lxw_image_md5s, workbook->header_image_md5s);
235
+ image_md5; image_md5 = next_image_md5) {
236
+
237
+ next_image_md5 =
238
+ RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5);
239
+ RB_REMOVE(lxw_image_md5s, workbook->header_image_md5s, image_md5);
240
+ free(image_md5->md5);
241
+ free(image_md5);
242
+ }
243
+
244
+ free(workbook->header_image_md5s);
245
+ }
246
+
247
+ if (workbook->background_md5s) {
248
+ for (image_md5 = RB_MIN(lxw_image_md5s, workbook->background_md5s);
249
+ image_md5; image_md5 = next_image_md5) {
250
+
251
+ next_image_md5 =
252
+ RB_NEXT(lxw_image_md5s, workbook->image_md5, image_md5);
253
+ RB_REMOVE(lxw_image_md5s, workbook->background_md5s, image_md5);
254
+ free(image_md5->md5);
255
+ free(image_md5);
256
+ }
257
+
258
+ free(workbook->background_md5s);
259
+ }
260
+
207
261
  lxw_hash_free(workbook->used_xf_formats);
262
+ lxw_hash_free(workbook->used_dxf_formats);
208
263
  lxw_sst_free(workbook->sst);
209
- free(workbook->options.tmpdir);
264
+ free((void *) workbook->options.tmpdir);
210
265
  free(workbook->ordered_charts);
211
266
  free(workbook->vba_project);
267
+ free(workbook->vba_project_signature);
212
268
  free(workbook->vba_codename);
213
269
  free(workbook);
214
270
  }
@@ -220,9 +276,15 @@ void
220
276
  lxw_workbook_set_default_xf_indices(lxw_workbook *self)
221
277
  {
222
278
  lxw_format *format;
279
+ int32_t index = 0;
223
280
 
224
281
  STAILQ_FOREACH(format, self->formats, list_pointers) {
225
- lxw_format_get_xf_index(format);
282
+
283
+ /* Skip the hyperlink format. */
284
+ if (index != 1)
285
+ lxw_format_get_xf_index(format);
286
+
287
+ index++;
226
288
  }
227
289
  }
228
290
 
@@ -258,7 +320,7 @@ _prepare_fonts(lxw_workbook *self)
258
320
  uint16_t *font_index = calloc(1, sizeof(uint16_t));
259
321
  *font_index = index;
260
322
  format->font_index = index;
261
- format->has_font = 1;
323
+ format->has_font = LXW_TRUE;
262
324
  lxw_insert_hash_element(fonts, key, font_index,
263
325
  sizeof(lxw_font));
264
326
  index++;
@@ -268,6 +330,18 @@ _prepare_fonts(lxw_workbook *self)
268
330
 
269
331
  lxw_hash_free(fonts);
270
332
 
333
+ /* For DXF formats we only need to check if the properties have changed. */
334
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
335
+ lxw_format *format = (lxw_format *) used_format_element->value;
336
+
337
+ /* The only font properties that can change for a DXF format are:
338
+ * color, bold, italic, underline and strikethrough. */
339
+ if (format->font_color || format->bold || format->italic
340
+ || format->underline || format->font_strikeout) {
341
+ format->has_dxf_font = LXW_TRUE;
342
+ }
343
+ }
344
+
271
345
  self->font_count = index;
272
346
  }
273
347
 
@@ -312,6 +386,15 @@ _prepare_borders(lxw_workbook *self)
312
386
  }
313
387
  }
314
388
 
389
+ /* For DXF formats we only need to check if the properties have changed. */
390
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
391
+ lxw_format *format = (lxw_format *) used_format_element->value;
392
+
393
+ if (format->left || format->right || format->top || format->bottom) {
394
+ format->has_dxf_border = LXW_TRUE;
395
+ }
396
+ }
397
+
315
398
  lxw_hash_free(borders);
316
399
 
317
400
  self->border_count = index;
@@ -361,6 +444,17 @@ _prepare_fills(lxw_workbook *self)
361
444
  lxw_insert_hash_element(fills, default_fill_2, fill_index2,
362
445
  sizeof(lxw_fill));
363
446
 
447
+ /* For DXF formats we only need to check if the properties have changed. */
448
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
449
+ lxw_format *format = (lxw_format *) used_format_element->value;
450
+
451
+ if (format->pattern || format->bg_color || format->fg_color) {
452
+ format->has_dxf_fill = LXW_TRUE;
453
+ format->dxf_bg_color = format->bg_color;
454
+ format->dxf_fg_color = format->fg_color;
455
+ }
456
+ }
457
+
364
458
  LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
365
459
  lxw_format *format = (lxw_format *) used_format_element->value;
366
460
  lxw_fill *key = lxw_format_get_fill_key(format);
@@ -391,7 +485,6 @@ _prepare_fills(lxw_workbook *self)
391
485
  if (format->pattern <= LXW_PATTERN_SOLID
392
486
  && format->bg_color == LXW_COLOR_UNSET
393
487
  && format->fg_color != LXW_COLOR_UNSET) {
394
- format->bg_color = LXW_COLOR_UNSET;
395
488
  format->pattern = LXW_PATTERN_SOLID;
396
489
  }
397
490
 
@@ -482,6 +575,41 @@ _prepare_num_formats(lxw_workbook *self)
482
575
  }
483
576
  }
484
577
 
578
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
579
+ lxw_format *format = (lxw_format *) used_format_element->value;
580
+
581
+ /* Format already has a number format index. */
582
+ if (format->num_format_index)
583
+ continue;
584
+
585
+ /* Check if there is a user defined number format string. */
586
+ if (*format->num_format) {
587
+ char num_format[LXW_FORMAT_FIELD_LEN] = { 0 };
588
+ lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s",
589
+ format->num_format);
590
+
591
+ /* Look up the num_format in the hash table. */
592
+ hash_element = lxw_hash_key_exists(num_formats, num_format,
593
+ LXW_FORMAT_FIELD_LEN);
594
+
595
+ if (hash_element) {
596
+ /* Num_Format has already been used. */
597
+ format->num_format_index = *(uint16_t *) hash_element->value;
598
+ }
599
+ else {
600
+ /* This is a new num_format. */
601
+ num_format_index = calloc(1, sizeof(uint16_t));
602
+ *num_format_index = index;
603
+ format->num_format_index = index;
604
+ lxw_insert_hash_element(num_formats, format->num_format,
605
+ num_format_index,
606
+ LXW_FORMAT_FIELD_LEN);
607
+ index++;
608
+ /* Don't update num_format_count for DXF formats. */
609
+ }
610
+ }
611
+ }
612
+
485
613
  lxw_hash_free(num_formats);
486
614
 
487
615
  self->num_format_count = num_format_count;
@@ -735,7 +863,7 @@ _populate_range_data_cache(lxw_workbook *self, lxw_series_range *range)
735
863
  return;
736
864
  }
737
865
 
738
- cell_obj = lxw_worksheet_find_cell(row_obj, col_num);
866
+ cell_obj = lxw_worksheet_find_cell_in_row(row_obj, col_num);
739
867
 
740
868
  if (cell_obj) {
741
869
  if (cell_obj->type == NUMBER_CELL) {
@@ -796,7 +924,7 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
796
924
  /* Create a copy of the formula to modify and parse into parts. */
797
925
  lxw_snprintf(formula, LXW_MAX_FORMULA_RANGE_LENGTH, "%s", range->formula);
798
926
 
799
- /* Check for valid formula. TODO. This needs stronger validation. */
927
+ /* Check for valid formula. Note, This needs stronger validation. */
800
928
  tmp_str = strchr(formula, '!');
801
929
 
802
930
  if (tmp_str == NULL) {
@@ -847,6 +975,9 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
847
975
  STATIC void
848
976
  _populate_range(lxw_workbook *self, lxw_series_range *range)
849
977
  {
978
+ if (!range)
979
+ return;
980
+
850
981
  _populate_range_dimensions(self, range);
851
982
  _populate_range_data_cache(self, range);
852
983
  }
@@ -860,6 +991,7 @@ _add_chart_cache_data(lxw_workbook *self)
860
991
  {
861
992
  lxw_chart *chart;
862
993
  lxw_chart_series *series;
994
+ uint16_t i;
863
995
 
864
996
  STAILQ_FOREACH(chart, self->ordered_charts, ordered_list_pointers) {
865
997
 
@@ -874,10 +1006,34 @@ _add_chart_cache_data(lxw_workbook *self)
874
1006
  _populate_range(self, series->categories);
875
1007
  _populate_range(self, series->values);
876
1008
  _populate_range(self, series->title.range);
1009
+
1010
+ for (i = 0; i < series->data_label_count; i++) {
1011
+ lxw_chart_custom_label *data_label = &series->data_labels[i];
1012
+ _populate_range(self, data_label->range);
1013
+ }
877
1014
  }
878
1015
  }
879
1016
  }
880
1017
 
1018
+ /*
1019
+ * Store the image types used in the workbook to update the content types.
1020
+ */
1021
+ STATIC void
1022
+ _store_image_type(lxw_workbook *self, uint8_t image_type)
1023
+ {
1024
+ if (image_type == LXW_IMAGE_PNG)
1025
+ self->has_png = LXW_TRUE;
1026
+
1027
+ if (image_type == LXW_IMAGE_JPEG)
1028
+ self->has_jpeg = LXW_TRUE;
1029
+
1030
+ if (image_type == LXW_IMAGE_BMP)
1031
+ self->has_bmp = LXW_TRUE;
1032
+
1033
+ if (image_type == LXW_IMAGE_GIF)
1034
+ self->has_gif = LXW_TRUE;
1035
+ }
1036
+
881
1037
  /*
882
1038
  * Iterate through the worksheets and set up any chart or image drawings.
883
1039
  */
@@ -886,11 +1042,16 @@ _prepare_drawings(lxw_workbook *self)
886
1042
  {
887
1043
  lxw_sheet *sheet;
888
1044
  lxw_worksheet *worksheet;
889
- lxw_image_options *image_options;
1045
+ lxw_object_properties *object_props;
890
1046
  uint32_t chart_ref_id = 0;
891
1047
  uint32_t image_ref_id = 0;
1048
+ uint32_t ref_id = 0;
892
1049
  uint32_t drawing_id = 0;
893
1050
  uint8_t is_chartsheet;
1051
+ lxw_image_md5 tmp_image_md5;
1052
+ lxw_image_md5 *new_image_md5 = NULL;
1053
+ lxw_image_md5 *found_duplicate_image = NULL;
1054
+ uint8_t i;
894
1055
 
895
1056
  STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
896
1057
  if (sheet->is_chartsheet) {
@@ -902,42 +1063,205 @@ _prepare_drawings(lxw_workbook *self)
902
1063
  is_chartsheet = LXW_FALSE;
903
1064
  }
904
1065
 
905
- if (STAILQ_EMPTY(worksheet->image_data)
906
- && STAILQ_EMPTY(worksheet->chart_data))
1066
+ if (STAILQ_EMPTY(worksheet->image_props)
1067
+ && STAILQ_EMPTY(worksheet->chart_data)
1068
+ && !worksheet->has_header_vml && !worksheet->has_background_image) {
907
1069
  continue;
1070
+ }
908
1071
 
909
1072
  drawing_id++;
910
1073
 
911
- STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
1074
+ /* Prepare background images. */
1075
+ if (worksheet->has_background_image) {
1076
+
1077
+ object_props = worksheet->background_image;
1078
+
1079
+ _store_image_type(self, object_props->image_type);
1080
+
1081
+ /* Check for duplicate images and only store the first instance. */
1082
+ if (object_props->md5) {
1083
+ tmp_image_md5.md5 = object_props->md5;
1084
+ found_duplicate_image = RB_FIND(lxw_image_md5s,
1085
+ self->background_md5s,
1086
+ &tmp_image_md5);
1087
+ }
1088
+
1089
+ if (found_duplicate_image) {
1090
+ ref_id = found_duplicate_image->id;
1091
+ object_props->is_duplicate = LXW_TRUE;
1092
+ }
1093
+ else {
1094
+ image_ref_id++;
1095
+ ref_id = image_ref_id;
1096
+
1097
+ #ifndef USE_NO_MD5
1098
+ new_image_md5 = calloc(1, sizeof(lxw_image_md5));
1099
+ #endif
1100
+ if (new_image_md5 && object_props->md5) {
1101
+ new_image_md5->id = ref_id;
1102
+ new_image_md5->md5 = lxw_strdup(object_props->md5);
1103
+
1104
+ RB_INSERT(lxw_image_md5s, self->background_md5s,
1105
+ new_image_md5);
1106
+ }
1107
+ }
1108
+
1109
+ lxw_worksheet_prepare_background(worksheet, ref_id, object_props);
1110
+ }
1111
+
1112
+ /* Prepare worksheet images. */
1113
+ STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) {
1114
+
1115
+ /* Ignore background image added above. */
1116
+ if (object_props->is_background)
1117
+ continue;
1118
+
1119
+ _store_image_type(self, object_props->image_type);
1120
+
1121
+ /* Check for duplicate images and only store the first instance. */
1122
+ if (object_props->md5) {
1123
+ tmp_image_md5.md5 = object_props->md5;
1124
+ found_duplicate_image = RB_FIND(lxw_image_md5s,
1125
+ self->image_md5s,
1126
+ &tmp_image_md5);
1127
+ }
1128
+
1129
+ if (found_duplicate_image) {
1130
+ ref_id = found_duplicate_image->id;
1131
+ object_props->is_duplicate = LXW_TRUE;
1132
+ }
1133
+ else {
1134
+ image_ref_id++;
1135
+ ref_id = image_ref_id;
1136
+
1137
+ #ifndef USE_NO_MD5
1138
+ new_image_md5 = calloc(1, sizeof(lxw_image_md5));
1139
+ #endif
1140
+ if (new_image_md5 && object_props->md5) {
1141
+ new_image_md5->id = ref_id;
1142
+ new_image_md5->md5 = lxw_strdup(object_props->md5);
1143
+
1144
+ RB_INSERT(lxw_image_md5s, self->image_md5s,
1145
+ new_image_md5);
1146
+ }
1147
+ }
1148
+
1149
+ lxw_worksheet_prepare_image(worksheet, ref_id, drawing_id,
1150
+ object_props);
1151
+ }
1152
+
1153
+ /* Prepare worksheet charts. */
1154
+ STAILQ_FOREACH(object_props, worksheet->chart_data, list_pointers) {
912
1155
  chart_ref_id++;
913
1156
  lxw_worksheet_prepare_chart(worksheet, chart_ref_id, drawing_id,
914
- image_options, is_chartsheet);
915
- if (image_options->chart)
916
- STAILQ_INSERT_TAIL(self->ordered_charts, image_options->chart,
1157
+ object_props, is_chartsheet);
1158
+ if (object_props->chart)
1159
+ STAILQ_INSERT_TAIL(self->ordered_charts, object_props->chart,
917
1160
  ordered_list_pointers);
918
1161
  }
919
1162
 
920
- STAILQ_FOREACH(image_options, worksheet->image_data, list_pointers) {
1163
+ /* Prepare worksheet header/footer images. */
1164
+ for (i = 0; i < LXW_HEADER_FOOTER_OBJS_MAX; i++) {
1165
+
1166
+ object_props = *worksheet->header_footer_objs[i];
1167
+ if (!object_props)
1168
+ continue;
921
1169
 
922
- if (image_options->image_type == LXW_IMAGE_PNG)
923
- self->has_png = LXW_TRUE;
1170
+ _store_image_type(self, object_props->image_type);
924
1171
 
925
- if (image_options->image_type == LXW_IMAGE_JPEG)
926
- self->has_jpeg = LXW_TRUE;
1172
+ /* Check for duplicate images and only store the first instance. */
1173
+ if (object_props->md5) {
1174
+ tmp_image_md5.md5 = object_props->md5;
1175
+ found_duplicate_image = RB_FIND(lxw_image_md5s,
1176
+ self->header_image_md5s,
1177
+ &tmp_image_md5);
1178
+ }
1179
+
1180
+ if (found_duplicate_image) {
1181
+ ref_id = found_duplicate_image->id;
1182
+ object_props->is_duplicate = LXW_TRUE;
1183
+ }
1184
+ else {
1185
+ image_ref_id++;
1186
+ ref_id = image_ref_id;
927
1187
 
928
- if (image_options->image_type == LXW_IMAGE_BMP)
929
- self->has_bmp = LXW_TRUE;
1188
+ #ifndef USE_NO_MD5
1189
+ new_image_md5 = calloc(1, sizeof(lxw_image_md5));
1190
+ #endif
1191
+ if (new_image_md5 && object_props->md5) {
1192
+ new_image_md5->id = ref_id;
1193
+ new_image_md5->md5 = lxw_strdup(object_props->md5);
930
1194
 
931
- image_ref_id++;
1195
+ RB_INSERT(lxw_image_md5s, self->header_image_md5s,
1196
+ new_image_md5);
1197
+ }
1198
+ }
932
1199
 
933
- lxw_worksheet_prepare_image(worksheet, image_ref_id, drawing_id,
934
- image_options);
1200
+ lxw_worksheet_prepare_header_image(worksheet, ref_id,
1201
+ object_props);
935
1202
  }
1203
+
936
1204
  }
937
1205
 
938
1206
  self->drawing_count = drawing_id;
939
1207
  }
940
1208
 
1209
+ /*
1210
+ * Iterate through the worksheets and set up the VML objects.
1211
+ */
1212
+ STATIC void
1213
+ _prepare_vml(lxw_workbook *self)
1214
+ {
1215
+ lxw_worksheet *worksheet;
1216
+ lxw_sheet *sheet;
1217
+ uint32_t comment_id = 0;
1218
+ uint32_t vml_drawing_id = 0;
1219
+ uint32_t vml_data_id = 1;
1220
+ uint32_t vml_header_id = 0;
1221
+ uint32_t vml_shape_id = 1024;
1222
+ uint32_t comment_count = 0;
1223
+
1224
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1225
+ if (sheet->is_chartsheet)
1226
+ continue;
1227
+ else
1228
+ worksheet = sheet->u.worksheet;
1229
+
1230
+ if (!worksheet->has_vml && !worksheet->has_header_vml)
1231
+ continue;
1232
+
1233
+ if (worksheet->has_vml) {
1234
+ self->has_vml = LXW_TRUE;
1235
+ if (worksheet->has_comments) {
1236
+ self->comment_count++;
1237
+ comment_id++;
1238
+ self->has_comments = LXW_TRUE;
1239
+ }
1240
+
1241
+ vml_drawing_id++;
1242
+
1243
+ comment_count = lxw_worksheet_prepare_vml_objects(worksheet,
1244
+ vml_data_id,
1245
+ vml_shape_id,
1246
+ vml_drawing_id,
1247
+ comment_id);
1248
+
1249
+ /* Each VML should start with a shape id incremented by 1024. */
1250
+ vml_data_id += 1 * ((1024 + comment_count) / 1024);
1251
+ vml_shape_id += 1024 * ((1024 + comment_count) / 1024);
1252
+ }
1253
+
1254
+ if (worksheet->has_header_vml) {
1255
+ self->has_vml = LXW_TRUE;
1256
+ vml_drawing_id++;
1257
+ vml_header_id++;
1258
+ lxw_worksheet_prepare_header_vml_objects(worksheet,
1259
+ vml_header_id,
1260
+ vml_drawing_id);
1261
+ }
1262
+ }
1263
+ }
1264
+
941
1265
  /*
942
1266
  * Iterate through the worksheets and store any defined names used for print
943
1267
  * ranges or repeat rows/columns.
@@ -1085,6 +1409,34 @@ _prepare_defined_names(lxw_workbook *self)
1085
1409
  }
1086
1410
  }
1087
1411
 
1412
+ /*
1413
+ * Iterate through the worksheets and set up the table objects.
1414
+ */
1415
+ STATIC void
1416
+ _prepare_tables(lxw_workbook *self)
1417
+ {
1418
+ lxw_worksheet *worksheet;
1419
+ lxw_sheet *sheet;
1420
+ uint32_t table_id = 0;
1421
+ uint32_t table_count = 0;
1422
+
1423
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1424
+ if (sheet->is_chartsheet)
1425
+ continue;
1426
+ else
1427
+ worksheet = sheet->u.worksheet;
1428
+
1429
+ table_count = worksheet->table_count;
1430
+
1431
+ if (table_count == 0)
1432
+ continue;
1433
+
1434
+ lxw_worksheet_prepare_tables(worksheet, table_id + 1);
1435
+
1436
+ table_id += table_count;
1437
+ }
1438
+ }
1439
+
1088
1440
  /*****************************************************************************
1089
1441
  *
1090
1442
  * XML functions.
@@ -1146,6 +1498,26 @@ _write_file_version(lxw_workbook *self)
1146
1498
  LXW_FREE_ATTRIBUTES();
1147
1499
  }
1148
1500
 
1501
+ /*
1502
+ * Write the <fileSharing> element.
1503
+ */
1504
+ STATIC void
1505
+ _workbook_write_file_sharing(lxw_workbook *self)
1506
+ {
1507
+ struct xml_attribute_list attributes;
1508
+ struct xml_attribute *attribute;
1509
+
1510
+ if (self->read_only == 0)
1511
+ return;
1512
+
1513
+ LXW_INIT_ATTRIBUTES();
1514
+ LXW_PUSH_ATTRIBUTES_STR("readOnlyRecommended", "1");
1515
+
1516
+ lxw_xml_empty_tag(self->file, "fileSharing", &attributes);
1517
+
1518
+ LXW_FREE_ATTRIBUTES();
1519
+ }
1520
+
1149
1521
  /*
1150
1522
  * Write the <workbookPr> element.
1151
1523
  */
@@ -1344,6 +1716,9 @@ lxw_workbook_assemble_xml_file(lxw_workbook *self)
1344
1716
  /* Write the XLSX file version. */
1345
1717
  _write_file_version(self);
1346
1718
 
1719
+ /* Write the fileSharing element. */
1720
+ _workbook_write_file_sharing(self);
1721
+
1347
1722
  /* Write the workbook properties. */
1348
1723
  _write_workbook_pr(self);
1349
1724
 
@@ -1432,6 +1807,21 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1432
1807
  GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error);
1433
1808
  RB_INIT(workbook->chartsheet_names);
1434
1809
 
1810
+ /* Add the image MD5 tree. */
1811
+ workbook->image_md5s = calloc(1, sizeof(struct lxw_image_md5s));
1812
+ GOTO_LABEL_ON_MEM_ERROR(workbook->image_md5s, mem_error);
1813
+ RB_INIT(workbook->image_md5s);
1814
+
1815
+ /* Add the header image MD5 tree. */
1816
+ workbook->header_image_md5s = calloc(1, sizeof(struct lxw_image_md5s));
1817
+ GOTO_LABEL_ON_MEM_ERROR(workbook->header_image_md5s, mem_error);
1818
+ RB_INIT(workbook->header_image_md5s);
1819
+
1820
+ /* Add the background image MD5 tree. */
1821
+ workbook->background_md5s = calloc(1, sizeof(struct lxw_image_md5s));
1822
+ GOTO_LABEL_ON_MEM_ERROR(workbook->background_md5s, mem_error);
1823
+ RB_INIT(workbook->background_md5s);
1824
+
1435
1825
  /* Add the charts list. */
1436
1826
  workbook->charts = calloc(1, sizeof(struct lxw_charts));
1437
1827
  GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
@@ -1464,6 +1854,10 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1464
1854
  workbook->used_xf_formats = lxw_hash_new(128, 1, 0);
1465
1855
  GOTO_LABEL_ON_MEM_ERROR(workbook->used_xf_formats, mem_error);
1466
1856
 
1857
+ /* Add a hash table to track format indices. */
1858
+ workbook->used_dxf_formats = lxw_hash_new(128, 1, 0);
1859
+ GOTO_LABEL_ON_MEM_ERROR(workbook->used_dxf_formats, mem_error);
1860
+
1467
1861
  /* Add the worksheets list. */
1468
1862
  workbook->custom_properties =
1469
1863
  calloc(1, sizeof(struct lxw_custom_properties));
@@ -1477,12 +1871,22 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1477
1871
  /* Initialize its index. */
1478
1872
  lxw_format_get_xf_index(format);
1479
1873
 
1874
+ /* Add the default hyperlink format. */
1875
+ format = workbook_add_format(workbook);
1876
+ GOTO_LABEL_ON_MEM_ERROR(format, mem_error);
1877
+ format_set_hyperlink(format);
1878
+ workbook->default_url_format = format;
1879
+
1480
1880
  if (options) {
1481
1881
  workbook->options.constant_memory = options->constant_memory;
1482
1882
  workbook->options.tmpdir = lxw_strdup(options->tmpdir);
1483
1883
  workbook->options.use_zip64 = options->use_zip64;
1884
+ workbook->options.output_buffer = options->output_buffer;
1885
+ workbook->options.output_buffer_size = options->output_buffer_size;
1484
1886
  }
1485
1887
 
1888
+ workbook->max_url_length = 2079;
1889
+
1486
1890
  return workbook;
1487
1891
 
1488
1892
  mem_error:
@@ -1501,13 +1905,13 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1501
1905
  lxw_worksheet *worksheet = NULL;
1502
1906
  lxw_worksheet_name *worksheet_name = NULL;
1503
1907
  lxw_error error;
1504
- lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1908
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1505
1909
  char *new_name = NULL;
1506
1910
 
1507
1911
  if (sheetname) {
1508
1912
  /* Use the user supplied name. */
1509
1913
  init_data.name = lxw_strdup(sheetname);
1510
- init_data.quoted_name = lxw_quote_sheetname((char *) sheetname);
1914
+ init_data.quoted_name = lxw_quote_sheetname(sheetname);
1511
1915
  }
1512
1916
  else {
1513
1917
  /* Use the default SheetN name. */
@@ -1540,6 +1944,8 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1540
1944
  init_data.active_sheet = &self->active_sheet;
1541
1945
  init_data.first_sheet = &self->first_sheet;
1542
1946
  init_data.tmpdir = self->options.tmpdir;
1947
+ init_data.default_url_format = self->default_url_format;
1948
+ init_data.max_url_length = self->max_url_length;
1543
1949
 
1544
1950
  /* Create a new worksheet object. */
1545
1951
  worksheet = lxw_worksheet_new(&init_data);
@@ -1566,8 +1972,8 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1566
1972
  return worksheet;
1567
1973
 
1568
1974
  mem_error:
1569
- free(init_data.name);
1570
- free(init_data.quoted_name);
1975
+ free((void *) init_data.name);
1976
+ free((void *) init_data.quoted_name);
1571
1977
  free(worksheet_name);
1572
1978
  free(worksheet);
1573
1979
  return NULL;
@@ -1583,13 +1989,13 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
1583
1989
  lxw_chartsheet *chartsheet = NULL;
1584
1990
  lxw_chartsheet_name *chartsheet_name = NULL;
1585
1991
  lxw_error error;
1586
- lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1992
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1587
1993
  char *new_name = NULL;
1588
1994
 
1589
1995
  if (sheetname) {
1590
1996
  /* Use the user supplied name. */
1591
1997
  init_data.name = lxw_strdup(sheetname);
1592
- init_data.quoted_name = lxw_quote_sheetname((char *) sheetname);
1998
+ init_data.quoted_name = lxw_quote_sheetname(sheetname);
1593
1999
  }
1594
2000
  else {
1595
2001
  /* Use the default SheetN name. */
@@ -1650,8 +2056,8 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
1650
2056
  return chartsheet;
1651
2057
 
1652
2058
  mem_error:
1653
- free(init_data.name);
1654
- free(init_data.quoted_name);
2059
+ free((void *) init_data.name);
2060
+ free((void *) init_data.quoted_name);
1655
2061
  free(chartsheet_name);
1656
2062
  free(chartsheet);
1657
2063
  return NULL;
@@ -1694,6 +2100,7 @@ workbook_add_format(lxw_workbook *self)
1694
2100
  }
1695
2101
 
1696
2102
  format->xf_format_indices = self->used_xf_formats;
2103
+ format->dxf_format_indices = self->used_dxf_formats;
1697
2104
  format->num_xf_formats = &self->num_xf_formats;
1698
2105
 
1699
2106
  STAILQ_INSERT_TAIL(self->formats, format, list_pointers);
@@ -1721,6 +2128,7 @@ workbook_close(lxw_workbook *self)
1721
2128
  lxw_worksheet *worksheet = NULL;
1722
2129
  lxw_packager *packager = NULL;
1723
2130
  lxw_error error = LXW_NO_ERROR;
2131
+ char codename[LXW_MAX_SHEETNAME_LENGTH] = { 0 };
1724
2132
 
1725
2133
  /* Add a default worksheet if non have been added. */
1726
2134
  if (!self->num_sheets)
@@ -1731,12 +2139,12 @@ workbook_close(lxw_workbook *self)
1731
2139
  sheet = STAILQ_FIRST(self->sheets);
1732
2140
  if (!sheet->is_chartsheet) {
1733
2141
  worksheet = sheet->u.worksheet;
1734
- worksheet->selected = 1;
2142
+ worksheet->selected = LXW_TRUE;
1735
2143
  worksheet->hidden = 0;
1736
2144
  }
1737
2145
  }
1738
2146
 
1739
- /* Set the active sheet. */
2147
+ /* Set the active sheet and check if a metadata file is needed. */
1740
2148
  STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1741
2149
  if (sheet->is_chartsheet)
1742
2150
  continue;
@@ -1744,7 +2152,10 @@ workbook_close(lxw_workbook *self)
1744
2152
  worksheet = sheet->u.worksheet;
1745
2153
 
1746
2154
  if (worksheet->index == self->active_sheet)
1747
- worksheet->active = 1;
2155
+ worksheet->active = LXW_TRUE;
2156
+
2157
+ if (worksheet->has_dynamic_arrays)
2158
+ self->has_metadata = LXW_TRUE;
1748
2159
  }
1749
2160
 
1750
2161
  /* Set workbook and worksheet VBA codenames if a macro has been added. */
@@ -1758,11 +2169,18 @@ workbook_close(lxw_workbook *self)
1758
2169
  else
1759
2170
  worksheet = sheet->u.worksheet;
1760
2171
 
1761
- if (!worksheet->vba_codename)
1762
- worksheet_set_vba_name(worksheet, worksheet->name);
2172
+ if (!worksheet->vba_codename) {
2173
+ lxw_snprintf(codename, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
2174
+ worksheet->index + 1);
2175
+
2176
+ worksheet_set_vba_name(worksheet, codename);
2177
+ }
1763
2178
  }
1764
2179
  }
1765
2180
 
2181
+ /* Prepare the worksheet VML elements such as comments. */
2182
+ _prepare_vml(self);
2183
+
1766
2184
  /* Set the defined names for the worksheets such as Print Titles. */
1767
2185
  _prepare_defined_names(self);
1768
2186
 
@@ -1772,6 +2190,9 @@ workbook_close(lxw_workbook *self)
1772
2190
  /* Add cached data to charts. */
1773
2191
  _add_chart_cache_data(self);
1774
2192
 
2193
+ /* Set the table ids for the worksheet tables. */
2194
+ _prepare_tables(self);
2195
+
1775
2196
  /* Create a packager object to assemble sub-elements into a zip file. */
1776
2197
  packager = lxw_packager_new(self->filename,
1777
2198
  self->options.tmpdir,
@@ -1779,9 +2200,9 @@ workbook_close(lxw_workbook *self)
1779
2200
 
1780
2201
  /* If the packager fails it is generally due to a zip permission error. */
1781
2202
  if (packager == NULL) {
1782
- fprintf(stderr, "[ERROR] workbook_close(): "
1783
- "Error creating '%s'. "
1784
- "System error = %s\n", self->filename, strerror(errno));
2203
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2204
+ "Error creating '%s'. "
2205
+ "System error = %s\n", self->filename, strerror(errno));
1785
2206
 
1786
2207
  error = LXW_ERROR_CREATING_XLSX_FILE;
1787
2208
  goto mem_error;
@@ -1793,51 +2214,57 @@ workbook_close(lxw_workbook *self)
1793
2214
  /* Assemble all the sub-files in the xlsx package. */
1794
2215
  error = lxw_create_package(packager);
1795
2216
 
2217
+ if (!self->filename) {
2218
+ *self->options.output_buffer = packager->output_buffer;
2219
+ *self->options.output_buffer_size = packager->output_buffer_size;
2220
+ }
2221
+
1796
2222
  /* Error and non-error conditions fall through to the cleanup code. */
1797
2223
  if (error == LXW_ERROR_CREATING_TMPFILE) {
1798
- fprintf(stderr, "[ERROR] workbook_close(): "
1799
- "Error creating tmpfile(s) to assemble '%s'. "
1800
- "System error = %s\n", self->filename, strerror(errno));
2224
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2225
+ "Error creating tmpfile(s) to assemble '%s'. "
2226
+ "System error = %s\n", self->filename, strerror(errno));
1801
2227
  }
1802
2228
 
1803
2229
  /* If LXW_ERROR_ZIP_FILE_OPERATION then errno is set by zip. */
1804
2230
  if (error == LXW_ERROR_ZIP_FILE_OPERATION) {
1805
- fprintf(stderr, "[ERROR] workbook_close(): "
1806
- "Zip ZIP_ERRNO error while creating xlsx file '%s'. "
1807
- "System error = %s\n", self->filename, strerror(errno));
2231
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2232
+ "Zip ZIP_ERRNO error while creating xlsx file '%s'. "
2233
+ "System error = %s\n", self->filename, strerror(errno));
1808
2234
  }
1809
2235
 
1810
2236
  /* If LXW_ERROR_ZIP_PARAMETER_ERROR then errno is set by zip. */
1811
2237
  if (error == LXW_ERROR_ZIP_PARAMETER_ERROR) {
1812
- fprintf(stderr, "[ERROR] workbook_close(): "
1813
- "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. "
1814
- "System error = %s\n", self->filename, strerror(errno));
2238
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2239
+ "Zip ZIP_PARAMERROR error while creating xlsx file '%s'. "
2240
+ "System error = %s\n", self->filename, strerror(errno));
1815
2241
  }
1816
2242
 
1817
2243
  /* If LXW_ERROR_ZIP_BAD_ZIP_FILE then errno is set by zip. */
1818
2244
  if (error == LXW_ERROR_ZIP_BAD_ZIP_FILE) {
1819
- fprintf(stderr, "[ERROR] workbook_close(): "
1820
- "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. "
1821
- "This may require the use_zip64 option for large files. "
1822
- "System error = %s\n", self->filename, strerror(errno));
2245
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2246
+ "Zip ZIP_BADZIPFILE error while creating xlsx file '%s'. "
2247
+ "This may require the use_zip64 option for large files. "
2248
+ "System error = %s\n", self->filename, strerror(errno));
1823
2249
  }
1824
2250
 
1825
2251
  /* If LXW_ERROR_ZIP_INTERNAL_ERROR then errno is set by zip. */
1826
2252
  if (error == LXW_ERROR_ZIP_INTERNAL_ERROR) {
1827
- fprintf(stderr, "[ERROR] workbook_close(): "
1828
- "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. "
1829
- "System error = %s\n", self->filename, strerror(errno));
2253
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2254
+ "Zip ZIP_INTERNALERROR error while creating xlsx file '%s'. "
2255
+ "System error = %s\n", self->filename, strerror(errno));
1830
2256
  }
1831
2257
 
1832
2258
  /* The next 2 error conditions don't set errno. */
1833
2259
  if (error == LXW_ERROR_ZIP_FILE_ADD) {
1834
- fprintf(stderr, "[ERROR] workbook_close(): "
1835
- "Zip error adding file to xlsx file '%s'.\n", self->filename);
2260
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2261
+ "Zip error adding file to xlsx file '%s'.\n",
2262
+ self->filename);
1836
2263
  }
1837
2264
 
1838
2265
  if (error == LXW_ERROR_ZIP_CLOSE) {
1839
- fprintf(stderr, "[ERROR] workbook_close(): "
1840
- "Zip error closing xlsx file '%s'.\n", self->filename);
2266
+ LXW_PRINTF(LXW_STDERR "[ERROR] workbook_close(): "
2267
+ "Zip error closing xlsx file '%s'.\n", self->filename);
1841
2268
  }
1842
2269
 
1843
2270
  mem_error:
@@ -1922,6 +2349,8 @@ workbook_set_properties(lxw_workbook *self, lxw_doc_properties *user_props)
1922
2349
  GOTO_LABEL_ON_MEM_ERROR(doc_props->hyperlink_base, mem_error);
1923
2350
  }
1924
2351
 
2352
+ doc_props->created = user_props->created;
2353
+
1925
2354
  self->properties = doc_props;
1926
2355
 
1927
2356
  return LXW_NO_ERROR;
@@ -2169,6 +2598,27 @@ workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name)
2169
2598
  return NULL;
2170
2599
  }
2171
2600
 
2601
+ /*
2602
+ * Get the default URL format.
2603
+ */
2604
+ lxw_format *
2605
+ workbook_get_default_url_format(lxw_workbook *self)
2606
+ {
2607
+ return self->default_url_format;
2608
+ }
2609
+
2610
+ /*
2611
+ * Unset the default URL format.
2612
+ */
2613
+ void
2614
+ workbook_unset_default_url_format(lxw_workbook *self)
2615
+ {
2616
+ self->default_url_format->hyperlink = LXW_FALSE;
2617
+ self->default_url_format->xf_id = 0;
2618
+ self->default_url_format->underline = LXW_UNDERLINE_NONE;
2619
+ self->default_url_format->theme = 0;
2620
+ }
2621
+
2172
2622
  /*
2173
2623
  * Validate the worksheet name based on Excel's rules.
2174
2624
  */
@@ -2187,10 +2637,6 @@ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
2187
2637
  if (sheetname[0] == '\'' || sheetname[strlen(sheetname) - 1] == '\'')
2188
2638
  return LXW_ERROR_SHEETNAME_START_END_APOSTROPHE;
2189
2639
 
2190
- /* Check that the worksheet name isn't the reserved work "History". */
2191
- if (lxw_strcasecmp(sheetname, "history") == 0)
2192
- return LXW_ERROR_SHEETNAME_RESERVED;
2193
-
2194
2640
  /* Check if the worksheet name is already in use. */
2195
2641
  if (workbook_get_worksheet_by_name(self, sheetname))
2196
2642
  return LXW_ERROR_SHEETNAME_ALREADY_USED;
@@ -2212,15 +2658,15 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename)
2212
2658
 
2213
2659
  if (!filename) {
2214
2660
  LXW_WARN("workbook_add_vba_project(): "
2215
- "filename must be specified.");
2661
+ "project filename must be specified.");
2216
2662
  return LXW_ERROR_NULL_PARAMETER_IGNORED;
2217
2663
  }
2218
2664
 
2219
2665
  /* Check that the vbaProject file exists and can be opened. */
2220
- filehandle = fopen(filename, "rb");
2666
+ filehandle = lxw_fopen(filename, "rb");
2221
2667
  if (!filehandle) {
2222
2668
  LXW_WARN_FORMAT1("workbook_add_vba_project(): "
2223
- "file doesn't exist or can't be opened: %s.",
2669
+ "project file doesn't exist or can't be opened: %s.",
2224
2670
  filename);
2225
2671
  return LXW_ERROR_PARAMETER_VALIDATION;
2226
2672
  }
@@ -2231,6 +2677,41 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename)
2231
2677
  return LXW_NO_ERROR;
2232
2678
  }
2233
2679
 
2680
+ /*
2681
+ * Add a vbaProject binary and a vbaProjectSignature binary to the Excel workbook.
2682
+ */
2683
+ lxw_error
2684
+ workbook_add_signed_vba_project(lxw_workbook *self,
2685
+ const char *vba_project,
2686
+ const char *signature)
2687
+ {
2688
+ FILE *filehandle;
2689
+
2690
+ lxw_error error = workbook_add_vba_project(self, vba_project);
2691
+ if (error != LXW_NO_ERROR)
2692
+ return error;
2693
+
2694
+ if (!signature) {
2695
+ LXW_WARN("workbook_add_signed_vba_project(): "
2696
+ "signature filename must be specified.");
2697
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
2698
+ }
2699
+
2700
+ /* Check that the vbaProjectSignature file exists and can be opened. */
2701
+ filehandle = lxw_fopen(signature, "rb");
2702
+ if (!filehandle) {
2703
+ LXW_WARN_FORMAT1("workbook_add_signed_vba_project(): "
2704
+ "signature file doesn't exist or can't be opened: %s.",
2705
+ signature);
2706
+ return LXW_ERROR_PARAMETER_VALIDATION;
2707
+ }
2708
+ fclose(filehandle);
2709
+
2710
+ self->vba_project_signature = lxw_strdup(signature);
2711
+
2712
+ return LXW_NO_ERROR;
2713
+ }
2714
+
2234
2715
  /*
2235
2716
  * Set the VBA name for the workbook.
2236
2717
  */
@@ -2246,3 +2727,12 @@ workbook_set_vba_name(lxw_workbook *self, const char *name)
2246
2727
 
2247
2728
  return LXW_NO_ERROR;
2248
2729
  }
2730
+
2731
+ /*
2732
+ * Set the Excel "Read-only recommended" save option.
2733
+ */
2734
+ void
2735
+ workbook_read_only_recommended(lxw_workbook *self)
2736
+ {
2737
+ self->read_only = 2;
2738
+ }