fast_excel 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }