xlsxwriter 0.2.1.pre.2 → 0.2.2

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/Rakefile +4 -3
  3. data/ext/xlsxwriter/chart.c +20 -2
  4. data/ext/xlsxwriter/extconf.rb +8 -8
  5. data/ext/xlsxwriter/libxlsxwriter/License.txt +24 -2
  6. data/ext/xlsxwriter/libxlsxwriter/Makefile +46 -12
  7. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/app.h +1 -1
  8. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chart.h +196 -30
  9. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/chartsheet.h +3 -3
  10. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/comment.h +76 -0
  11. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/common.h +24 -5
  12. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/content_types.h +5 -1
  13. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/core.h +1 -1
  14. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/custom.h +1 -1
  15. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/drawing.h +6 -17
  16. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/format.h +20 -6
  17. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  18. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/packager.h +3 -1
  19. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/relationships.h +1 -1
  20. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/shared_strings.h +1 -1
  21. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/styles.h +11 -5
  22. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/theme.h +1 -1
  23. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/third_party/md5.h +43 -0
  24. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/utility.h +42 -3
  25. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/vml.h +55 -0
  26. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/workbook.h +83 -18
  27. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/worksheet.h +1519 -109
  28. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter/xmlwriter.h +4 -2
  29. data/ext/xlsxwriter/libxlsxwriter/include/xlsxwriter.h +3 -2
  30. data/ext/xlsxwriter/libxlsxwriter/src/Makefile +25 -7
  31. data/ext/xlsxwriter/libxlsxwriter/src/app.c +1 -1
  32. data/ext/xlsxwriter/libxlsxwriter/src/chart.c +332 -48
  33. data/ext/xlsxwriter/libxlsxwriter/src/chartsheet.c +20 -19
  34. data/ext/xlsxwriter/libxlsxwriter/src/comment.c +443 -0
  35. data/ext/xlsxwriter/libxlsxwriter/src/content_types.c +20 -1
  36. data/ext/xlsxwriter/libxlsxwriter/src/core.c +2 -2
  37. data/ext/xlsxwriter/libxlsxwriter/src/custom.c +1 -1
  38. data/ext/xlsxwriter/libxlsxwriter/src/drawing.c +58 -20
  39. data/ext/xlsxwriter/libxlsxwriter/src/format.c +98 -25
  40. data/ext/xlsxwriter/libxlsxwriter/src/hash_table.c +1 -1
  41. data/ext/xlsxwriter/libxlsxwriter/src/packager.c +269 -12
  42. data/ext/xlsxwriter/libxlsxwriter/src/relationships.c +1 -1
  43. data/ext/xlsxwriter/libxlsxwriter/src/shared_strings.c +2 -4
  44. data/ext/xlsxwriter/libxlsxwriter/src/styles.c +334 -48
  45. data/ext/xlsxwriter/libxlsxwriter/src/theme.c +1 -1
  46. data/ext/xlsxwriter/libxlsxwriter/src/utility.c +71 -8
  47. data/ext/xlsxwriter/libxlsxwriter/src/vml.c +1032 -0
  48. data/ext/xlsxwriter/libxlsxwriter/src/workbook.c +343 -27
  49. data/ext/xlsxwriter/libxlsxwriter/src/worksheet.c +3759 -478
  50. data/ext/xlsxwriter/libxlsxwriter/src/xmlwriter.c +81 -2
  51. data/ext/xlsxwriter/libxlsxwriter/third_party/md5/Makefile +42 -0
  52. data/ext/xlsxwriter/libxlsxwriter/third_party/md5/md5.c +291 -0
  53. data/ext/xlsxwriter/libxlsxwriter/third_party/md5/md5.h +43 -0
  54. data/ext/xlsxwriter/shared_strings.c +65 -0
  55. data/ext/xlsxwriter/shared_strings.h +15 -0
  56. data/ext/xlsxwriter/workbook.c +56 -1
  57. data/ext/xlsxwriter/worksheet.c +31 -2
  58. data/ext/xlsxwriter/worksheet.h +1 -0
  59. data/ext/xlsxwriter/xlsxwriter.c +2 -0
  60. data/lib/xlsxwriter/rich_string.rb +0 -2
  61. data/lib/xlsxwriter/version.rb +1 -1
  62. data/lib/xlsxwriter/worksheet.rb +2 -2
  63. data/test/{run-test.rb → run_test.rb} +3 -3
  64. data/test/support/chart_test.rb +3 -3
  65. data/test/support/with_xlsx_file.rb +4 -2
  66. data/test/support/xlsx_comparable.rb +40 -26
  67. data/test/test_array_formula.rb +42 -0
  68. data/test/test_autofilter.rb +72 -0
  69. data/test/{test-chart-area.rb → test_chart_area.rb} +2 -2
  70. data/test/{test-chart-axis.rb → test_chart_axis.rb} +16 -16
  71. data/test/test_chart_bar.rb +382 -0
  72. data/test/test_chart_blank.rb +27 -0
  73. data/test/{test-chart-column.rb → test_chart_column.rb} +2 -2
  74. data/test/{test-chart-doughnut.rb → test_chart_doughnut.rb} +2 -2
  75. data/test/{test-chart-legend.rb → test_chart_legend.rb} +2 -2
  76. data/test/{test-chart-pie.rb → test_chart_pie.rb} +2 -2
  77. data/test/{test-chart-scatter.rb → test_chart_scatter.rb} +3 -4
  78. data/test/{test-chart-size.rb → test_chart_size.rb} +2 -2
  79. data/test/{test-chart-title.rb → test_chart_title.rb} +3 -3
  80. data/test/{test-chartsheet.rb → test_chartsheet.rb} +2 -2
  81. data/test/{test-data.rb → test_data.rb} +1 -1
  82. data/test/{test-data-validation.rb → test_data_validation.rb} +23 -24
  83. data/test/{test-default-row.rb → test_default_row.rb} +1 -1
  84. data/test/{test-defined-name.rb → test_defined_name.rb} +12 -12
  85. data/test/{test-escapes.rb → test_escapes.rb} +5 -2
  86. data/test/{test-fit-to-pages.rb → test_fit_to_pages.rb} +6 -6
  87. data/test/{test-formatting.rb → test_formatting.rb} +10 -10
  88. data/test/{test-gridlines.rb → test_gridlines.rb} +3 -3
  89. data/test/{test-hyperlink.rb → test_hyperlink.rb} +22 -11
  90. data/test/{test-image.rb → test_image.rb} +6 -4
  91. data/test/{test-macro.rb → test_macro.rb} +1 -1
  92. data/test/{test-merge-range.rb → test_merge_range.rb} +1 -1
  93. data/test/{test-misc.rb → test_misc.rb} +2 -2
  94. data/test/{test-optimize.rb → test_optimize.rb} +2 -4
  95. data/test/{test-outline.rb → test_outline.rb} +14 -14
  96. data/test/{test-page-breaks.rb → test_page_breaks.rb} +2 -2
  97. data/test/{test-page-setup.rb → test_page_setup.rb} +2 -2
  98. data/test/{test-panes.rb → test_panes.rb} +1 -1
  99. data/test/{test-print-area.rb → test_print_area.rb} +3 -3
  100. data/test/{test-print-options.rb → test_print_options.rb} +7 -7
  101. data/test/{test-print-scale.rb → test_print_scale.rb} +2 -2
  102. data/test/{test-properties.rb → test_properties.rb} +2 -2
  103. data/test/{test-protect.rb → test_protect.rb} +3 -3
  104. data/test/{test-repeat.rb → test_repeat.rb} +3 -3
  105. data/test/{test-rich-string.rb → test_rich_string.rb} +5 -9
  106. data/test/{test-row-col-format.rb → test_row_col_format.rb} +1 -1
  107. data/test/{test-ruby-worksheet.rb → test_ruby_worksheet.rb} +2 -2
  108. data/test/{test-set-selection.rb → test_set_selection.rb} +2 -2
  109. data/test/{test-set-start-page.rb → test_set_start_page.rb} +2 -2
  110. data/test/{test-simple.rb → test_simple.rb} +10 -10
  111. data/test/{test-types.rb → test_types.rb} +1 -1
  112. data/test/{xlsx-func-testcase.rb → xlsx_func_testcase.rb} +1 -0
  113. metadata +132 -106
  114. data/test/test-array-formula.rb +0 -35
  115. data/test/test-autofilter.rb +0 -72
  116. data/test/test-chart-bar.rb +0 -74
  117. /data/test/{test-errors.rb → test_errors.rb} +0 -0
@@ -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-2020, 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
  */
@@ -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,7 +216,36 @@ 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
+
207
247
  lxw_hash_free(workbook->used_xf_formats);
248
+ lxw_hash_free(workbook->used_dxf_formats);
208
249
  lxw_sst_free(workbook->sst);
209
250
  free(workbook->options.tmpdir);
210
251
  free(workbook->ordered_charts);
@@ -220,9 +261,15 @@ void
220
261
  lxw_workbook_set_default_xf_indices(lxw_workbook *self)
221
262
  {
222
263
  lxw_format *format;
264
+ int32_t index = 0;
223
265
 
224
266
  STAILQ_FOREACH(format, self->formats, list_pointers) {
225
- lxw_format_get_xf_index(format);
267
+
268
+ /* Skip the hyperlink format. */
269
+ if (index != 1)
270
+ lxw_format_get_xf_index(format);
271
+
272
+ index++;
226
273
  }
227
274
  }
228
275
 
@@ -258,7 +305,7 @@ _prepare_fonts(lxw_workbook *self)
258
305
  uint16_t *font_index = calloc(1, sizeof(uint16_t));
259
306
  *font_index = index;
260
307
  format->font_index = index;
261
- format->has_font = 1;
308
+ format->has_font = LXW_TRUE;
262
309
  lxw_insert_hash_element(fonts, key, font_index,
263
310
  sizeof(lxw_font));
264
311
  index++;
@@ -268,6 +315,18 @@ _prepare_fonts(lxw_workbook *self)
268
315
 
269
316
  lxw_hash_free(fonts);
270
317
 
318
+ /* For DXF formats we only need to check if the properties have changed. */
319
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
320
+ lxw_format *format = (lxw_format *) used_format_element->value;
321
+
322
+ /* The only font properties that can change for a DXF format are:
323
+ * color, bold, italic, underline and strikethrough. */
324
+ if (format->font_color || format->bold || format->italic
325
+ || format->underline || format->font_strikeout) {
326
+ format->has_dxf_font = LXW_TRUE;
327
+ }
328
+ }
329
+
271
330
  self->font_count = index;
272
331
  }
273
332
 
@@ -312,6 +371,15 @@ _prepare_borders(lxw_workbook *self)
312
371
  }
313
372
  }
314
373
 
374
+ /* For DXF formats we only need to check if the properties have changed. */
375
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
376
+ lxw_format *format = (lxw_format *) used_format_element->value;
377
+
378
+ if (format->left || format->right || format->top || format->bottom) {
379
+ format->has_dxf_border = LXW_TRUE;
380
+ }
381
+ }
382
+
315
383
  lxw_hash_free(borders);
316
384
 
317
385
  self->border_count = index;
@@ -361,6 +429,17 @@ _prepare_fills(lxw_workbook *self)
361
429
  lxw_insert_hash_element(fills, default_fill_2, fill_index2,
362
430
  sizeof(lxw_fill));
363
431
 
432
+ /* For DXF formats we only need to check if the properties have changed. */
433
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
434
+ lxw_format *format = (lxw_format *) used_format_element->value;
435
+
436
+ if (format->pattern || format->bg_color || format->fg_color) {
437
+ format->has_dxf_fill = LXW_TRUE;
438
+ format->dxf_bg_color = format->bg_color;
439
+ format->dxf_fg_color = format->fg_color;
440
+ }
441
+ }
442
+
364
443
  LXW_FOREACH_ORDERED(used_format_element, self->used_xf_formats) {
365
444
  lxw_format *format = (lxw_format *) used_format_element->value;
366
445
  lxw_fill *key = lxw_format_get_fill_key(format);
@@ -482,6 +561,41 @@ _prepare_num_formats(lxw_workbook *self)
482
561
  }
483
562
  }
484
563
 
564
+ LXW_FOREACH_ORDERED(used_format_element, self->used_dxf_formats) {
565
+ lxw_format *format = (lxw_format *) used_format_element->value;
566
+
567
+ /* Format already has a number format index. */
568
+ if (format->num_format_index)
569
+ continue;
570
+
571
+ /* Check if there is a user defined number format string. */
572
+ if (*format->num_format) {
573
+ char num_format[LXW_FORMAT_FIELD_LEN] = { 0 };
574
+ lxw_snprintf(num_format, LXW_FORMAT_FIELD_LEN, "%s",
575
+ format->num_format);
576
+
577
+ /* Look up the num_format in the hash table. */
578
+ hash_element = lxw_hash_key_exists(num_formats, num_format,
579
+ LXW_FORMAT_FIELD_LEN);
580
+
581
+ if (hash_element) {
582
+ /* Num_Format has already been used. */
583
+ format->num_format_index = *(uint16_t *) hash_element->value;
584
+ }
585
+ else {
586
+ /* This is a new num_format. */
587
+ num_format_index = calloc(1, sizeof(uint16_t));
588
+ *num_format_index = index;
589
+ format->num_format_index = index;
590
+ lxw_insert_hash_element(num_formats, format->num_format,
591
+ num_format_index,
592
+ LXW_FORMAT_FIELD_LEN);
593
+ index++;
594
+ /* Don't update num_format_count for DXF formats. */
595
+ }
596
+ }
597
+ }
598
+
485
599
  lxw_hash_free(num_formats);
486
600
 
487
601
  self->num_format_count = num_format_count;
@@ -735,7 +849,7 @@ _populate_range_data_cache(lxw_workbook *self, lxw_series_range *range)
735
849
  return;
736
850
  }
737
851
 
738
- cell_obj = lxw_worksheet_find_cell(row_obj, col_num);
852
+ cell_obj = lxw_worksheet_find_cell_in_row(row_obj, col_num);
739
853
 
740
854
  if (cell_obj) {
741
855
  if (cell_obj->type == NUMBER_CELL) {
@@ -847,6 +961,9 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
847
961
  STATIC void
848
962
  _populate_range(lxw_workbook *self, lxw_series_range *range)
849
963
  {
964
+ if (!range)
965
+ return;
966
+
850
967
  _populate_range_dimensions(self, range);
851
968
  _populate_range_data_cache(self, range);
852
969
  }
@@ -860,6 +977,7 @@ _add_chart_cache_data(lxw_workbook *self)
860
977
  {
861
978
  lxw_chart *chart;
862
979
  lxw_chart_series *series;
980
+ uint16_t i;
863
981
 
864
982
  STAILQ_FOREACH(chart, self->ordered_charts, ordered_list_pointers) {
865
983
 
@@ -874,6 +992,11 @@ _add_chart_cache_data(lxw_workbook *self)
874
992
  _populate_range(self, series->categories);
875
993
  _populate_range(self, series->values);
876
994
  _populate_range(self, series->title.range);
995
+
996
+ for (i = 0; i < series->data_label_count; i++) {
997
+ lxw_chart_custom_label *data_label = &series->data_labels[i];
998
+ _populate_range(self, data_label->range);
999
+ }
877
1000
  }
878
1001
  }
879
1002
  }
@@ -886,11 +1009,16 @@ _prepare_drawings(lxw_workbook *self)
886
1009
  {
887
1010
  lxw_sheet *sheet;
888
1011
  lxw_worksheet *worksheet;
889
- lxw_image_options *image_options;
1012
+ lxw_object_properties *object_props;
890
1013
  uint32_t chart_ref_id = 0;
891
1014
  uint32_t image_ref_id = 0;
1015
+ uint32_t ref_id = 0;
892
1016
  uint32_t drawing_id = 0;
893
1017
  uint8_t is_chartsheet;
1018
+ lxw_image_md5 tmp_image_md5;
1019
+ lxw_image_md5 *new_image_md5 = NULL;
1020
+ lxw_image_md5 *found_duplicate_image = NULL;
1021
+ uint8_t i;
894
1022
 
895
1023
  STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
896
1024
  if (sheet->is_chartsheet) {
@@ -902,42 +1030,178 @@ _prepare_drawings(lxw_workbook *self)
902
1030
  is_chartsheet = LXW_FALSE;
903
1031
  }
904
1032
 
905
- if (STAILQ_EMPTY(worksheet->image_data)
906
- && STAILQ_EMPTY(worksheet->chart_data))
1033
+ if (STAILQ_EMPTY(worksheet->image_props)
1034
+ && STAILQ_EMPTY(worksheet->chart_data)
1035
+ && !worksheet->has_header_vml) {
907
1036
  continue;
1037
+ }
908
1038
 
909
1039
  drawing_id++;
910
1040
 
911
- STAILQ_FOREACH(image_options, worksheet->chart_data, list_pointers) {
1041
+ /* Prepare worksheet images. */
1042
+ STAILQ_FOREACH(object_props, worksheet->image_props, list_pointers) {
1043
+
1044
+ if (object_props->image_type == LXW_IMAGE_PNG)
1045
+ self->has_png = LXW_TRUE;
1046
+
1047
+ if (object_props->image_type == LXW_IMAGE_JPEG)
1048
+ self->has_jpeg = LXW_TRUE;
1049
+
1050
+ if (object_props->image_type == LXW_IMAGE_BMP)
1051
+ self->has_bmp = LXW_TRUE;
1052
+
1053
+ /* Check for duplicate images and only store the first instance. */
1054
+ if (object_props->md5) {
1055
+ tmp_image_md5.md5 = object_props->md5;
1056
+ found_duplicate_image = RB_FIND(lxw_image_md5s,
1057
+ self->image_md5s,
1058
+ &tmp_image_md5);
1059
+ }
1060
+
1061
+ if (found_duplicate_image) {
1062
+ ref_id = found_duplicate_image->id;
1063
+ object_props->is_duplicate = LXW_TRUE;
1064
+ }
1065
+ else {
1066
+ image_ref_id++;
1067
+ ref_id = image_ref_id;
1068
+
1069
+ #ifndef USE_NO_MD5
1070
+ new_image_md5 = calloc(1, sizeof(lxw_image_md5));
1071
+ #endif
1072
+ if (new_image_md5 && object_props->md5) {
1073
+ new_image_md5->id = ref_id;
1074
+ new_image_md5->md5 = lxw_strdup(object_props->md5);
1075
+
1076
+ RB_INSERT(lxw_image_md5s, self->image_md5s,
1077
+ new_image_md5);
1078
+ }
1079
+ }
1080
+
1081
+ lxw_worksheet_prepare_image(worksheet, ref_id, drawing_id,
1082
+ object_props);
1083
+ }
1084
+
1085
+ /* Prepare worksheet charts. */
1086
+ STAILQ_FOREACH(object_props, worksheet->chart_data, list_pointers) {
912
1087
  chart_ref_id++;
913
1088
  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,
1089
+ object_props, is_chartsheet);
1090
+ if (object_props->chart)
1091
+ STAILQ_INSERT_TAIL(self->ordered_charts, object_props->chart,
917
1092
  ordered_list_pointers);
918
1093
  }
919
1094
 
920
- STAILQ_FOREACH(image_options, worksheet->image_data, list_pointers) {
1095
+ /* Prepare worksheet header/footer images. */
1096
+ for (i = 0; i < LXW_HEADER_FOOTER_OBJS_MAX; i++) {
1097
+
1098
+ object_props = *worksheet->header_footer_objs[i];
1099
+ if (!object_props)
1100
+ continue;
921
1101
 
922
- if (image_options->image_type == LXW_IMAGE_PNG)
1102
+ if (object_props->image_type == LXW_IMAGE_PNG)
923
1103
  self->has_png = LXW_TRUE;
924
1104
 
925
- if (image_options->image_type == LXW_IMAGE_JPEG)
1105
+ if (object_props->image_type == LXW_IMAGE_JPEG)
926
1106
  self->has_jpeg = LXW_TRUE;
927
1107
 
928
- if (image_options->image_type == LXW_IMAGE_BMP)
1108
+ if (object_props->image_type == LXW_IMAGE_BMP)
929
1109
  self->has_bmp = LXW_TRUE;
930
1110
 
931
- image_ref_id++;
1111
+ /* Check for duplicate images and only store the first instance. */
1112
+ if (object_props->md5) {
1113
+ tmp_image_md5.md5 = object_props->md5;
1114
+ found_duplicate_image = RB_FIND(lxw_image_md5s,
1115
+ self->header_image_md5s,
1116
+ &tmp_image_md5);
1117
+ }
1118
+
1119
+ if (found_duplicate_image) {
1120
+ ref_id = found_duplicate_image->id;
1121
+ object_props->is_duplicate = LXW_TRUE;
1122
+ }
1123
+ else {
1124
+ image_ref_id++;
1125
+ ref_id = image_ref_id;
1126
+
1127
+ #ifndef USE_NO_MD5
1128
+ new_image_md5 = calloc(1, sizeof(lxw_image_md5));
1129
+ #endif
1130
+ if (new_image_md5 && object_props->md5) {
1131
+ new_image_md5->id = ref_id;
1132
+ new_image_md5->md5 = lxw_strdup(object_props->md5);
1133
+
1134
+ RB_INSERT(lxw_image_md5s, self->header_image_md5s,
1135
+ new_image_md5);
1136
+ }
1137
+ }
932
1138
 
933
- lxw_worksheet_prepare_image(worksheet, image_ref_id, drawing_id,
934
- image_options);
1139
+ lxw_worksheet_prepare_header_image(worksheet, ref_id,
1140
+ object_props);
935
1141
  }
1142
+
936
1143
  }
937
1144
 
938
1145
  self->drawing_count = drawing_id;
939
1146
  }
940
1147
 
1148
+ /*
1149
+ * Iterate through the worksheets and set up the VML objects.
1150
+ */
1151
+
1152
+ STATIC void
1153
+ _prepare_vml(lxw_workbook *self)
1154
+ {
1155
+ lxw_worksheet *worksheet;
1156
+ lxw_sheet *sheet;
1157
+ uint32_t comment_id = 0;
1158
+ uint32_t vml_drawing_id = 0;
1159
+ uint32_t vml_data_id = 1;
1160
+ uint32_t vml_header_id = 0;
1161
+ uint32_t vml_shape_id = 1024;
1162
+ uint32_t comment_count = 0;
1163
+
1164
+ STAILQ_FOREACH(sheet, self->sheets, list_pointers) {
1165
+ if (sheet->is_chartsheet)
1166
+ continue;
1167
+ else
1168
+ worksheet = sheet->u.worksheet;
1169
+
1170
+ if (!worksheet->has_vml && !worksheet->has_header_vml)
1171
+ continue;
1172
+
1173
+ if (worksheet->has_vml) {
1174
+ self->has_vml = LXW_TRUE;
1175
+ if (worksheet->has_comments) {
1176
+ self->comment_count++;
1177
+ comment_id++;
1178
+ self->has_comments = LXW_TRUE;
1179
+ }
1180
+
1181
+ vml_drawing_id++;
1182
+
1183
+ comment_count = lxw_worksheet_prepare_vml_objects(worksheet,
1184
+ vml_data_id,
1185
+ vml_shape_id,
1186
+ vml_drawing_id,
1187
+ comment_id);
1188
+
1189
+ /* Each VML should start with a shape id incremented by 1024. */
1190
+ vml_data_id += 1 * ((1024 + comment_count) / 1024);
1191
+ vml_shape_id += 1024 * ((1024 + comment_count) / 1024);
1192
+ }
1193
+
1194
+ if (worksheet->has_header_vml) {
1195
+ self->has_vml = LXW_TRUE;
1196
+ vml_drawing_id++;
1197
+ vml_header_id++;
1198
+ lxw_worksheet_prepare_header_vml_objects(worksheet,
1199
+ vml_header_id,
1200
+ vml_drawing_id);
1201
+ }
1202
+ }
1203
+ }
1204
+
941
1205
  /*
942
1206
  * Iterate through the worksheets and store any defined names used for print
943
1207
  * ranges or repeat rows/columns.
@@ -1432,6 +1696,16 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1432
1696
  GOTO_LABEL_ON_MEM_ERROR(workbook->chartsheet_names, mem_error);
1433
1697
  RB_INIT(workbook->chartsheet_names);
1434
1698
 
1699
+ /* Add the image MD5 tree. */
1700
+ workbook->image_md5s = calloc(1, sizeof(struct lxw_image_md5s));
1701
+ GOTO_LABEL_ON_MEM_ERROR(workbook->image_md5s, mem_error);
1702
+ RB_INIT(workbook->image_md5s);
1703
+
1704
+ /* Add the image MD5 tree. */
1705
+ workbook->header_image_md5s = calloc(1, sizeof(struct lxw_image_md5s));
1706
+ GOTO_LABEL_ON_MEM_ERROR(workbook->header_image_md5s, mem_error);
1707
+ RB_INIT(workbook->header_image_md5s);
1708
+
1435
1709
  /* Add the charts list. */
1436
1710
  workbook->charts = calloc(1, sizeof(struct lxw_charts));
1437
1711
  GOTO_LABEL_ON_MEM_ERROR(workbook->charts, mem_error);
@@ -1464,6 +1738,10 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1464
1738
  workbook->used_xf_formats = lxw_hash_new(128, 1, 0);
1465
1739
  GOTO_LABEL_ON_MEM_ERROR(workbook->used_xf_formats, mem_error);
1466
1740
 
1741
+ /* Add a hash table to track format indices. */
1742
+ workbook->used_dxf_formats = lxw_hash_new(128, 1, 0);
1743
+ GOTO_LABEL_ON_MEM_ERROR(workbook->used_dxf_formats, mem_error);
1744
+
1467
1745
  /* Add the worksheets list. */
1468
1746
  workbook->custom_properties =
1469
1747
  calloc(1, sizeof(struct lxw_custom_properties));
@@ -1477,12 +1755,20 @@ workbook_new_opt(const char *filename, lxw_workbook_options *options)
1477
1755
  /* Initialize its index. */
1478
1756
  lxw_format_get_xf_index(format);
1479
1757
 
1758
+ /* Add the default hyperlink format. */
1759
+ format = workbook_add_format(workbook);
1760
+ GOTO_LABEL_ON_MEM_ERROR(format, mem_error);
1761
+ format_set_hyperlink(format);
1762
+ workbook->default_url_format = format;
1763
+
1480
1764
  if (options) {
1481
1765
  workbook->options.constant_memory = options->constant_memory;
1482
1766
  workbook->options.tmpdir = lxw_strdup(options->tmpdir);
1483
1767
  workbook->options.use_zip64 = options->use_zip64;
1484
1768
  }
1485
1769
 
1770
+ workbook->max_url_length = 2079;
1771
+
1486
1772
  return workbook;
1487
1773
 
1488
1774
  mem_error:
@@ -1501,7 +1787,7 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1501
1787
  lxw_worksheet *worksheet = NULL;
1502
1788
  lxw_worksheet_name *worksheet_name = NULL;
1503
1789
  lxw_error error;
1504
- lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1790
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1505
1791
  char *new_name = NULL;
1506
1792
 
1507
1793
  if (sheetname) {
@@ -1540,6 +1826,8 @@ workbook_add_worksheet(lxw_workbook *self, const char *sheetname)
1540
1826
  init_data.active_sheet = &self->active_sheet;
1541
1827
  init_data.first_sheet = &self->first_sheet;
1542
1828
  init_data.tmpdir = self->options.tmpdir;
1829
+ init_data.default_url_format = self->default_url_format;
1830
+ init_data.max_url_length = self->max_url_length;
1543
1831
 
1544
1832
  /* Create a new worksheet object. */
1545
1833
  worksheet = lxw_worksheet_new(&init_data);
@@ -1583,7 +1871,7 @@ workbook_add_chartsheet(lxw_workbook *self, const char *sheetname)
1583
1871
  lxw_chartsheet *chartsheet = NULL;
1584
1872
  lxw_chartsheet_name *chartsheet_name = NULL;
1585
1873
  lxw_error error;
1586
- lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1874
+ lxw_worksheet_init_data init_data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1587
1875
  char *new_name = NULL;
1588
1876
 
1589
1877
  if (sheetname) {
@@ -1685,6 +1973,7 @@ workbook_add_format(lxw_workbook *self)
1685
1973
  RETURN_ON_MEM_ERROR(format, NULL);
1686
1974
 
1687
1975
  format->xf_format_indices = self->used_xf_formats;
1976
+ format->dxf_format_indices = self->used_dxf_formats;
1688
1977
  format->num_xf_formats = &self->num_xf_formats;
1689
1978
 
1690
1979
  STAILQ_INSERT_TAIL(self->formats, format, list_pointers);
@@ -1702,6 +1991,7 @@ workbook_close(lxw_workbook *self)
1702
1991
  lxw_worksheet *worksheet = NULL;
1703
1992
  lxw_packager *packager = NULL;
1704
1993
  lxw_error error = LXW_NO_ERROR;
1994
+ char codename[LXW_MAX_SHEETNAME_LENGTH] = { 0 };
1705
1995
 
1706
1996
  /* Add a default worksheet if non have been added. */
1707
1997
  if (!self->num_sheets)
@@ -1739,11 +2029,18 @@ workbook_close(lxw_workbook *self)
1739
2029
  else
1740
2030
  worksheet = sheet->u.worksheet;
1741
2031
 
1742
- if (!worksheet->vba_codename)
1743
- worksheet_set_vba_name(worksheet, worksheet->name);
2032
+ if (!worksheet->vba_codename) {
2033
+ lxw_snprintf(codename, LXW_MAX_SHEETNAME_LENGTH, "Sheet%d",
2034
+ worksheet->index + 1);
2035
+
2036
+ worksheet_set_vba_name(worksheet, codename);
2037
+ }
1744
2038
  }
1745
2039
  }
1746
2040
 
2041
+ /* Prepare the worksheet VML elements such as comments. */
2042
+ _prepare_vml(self);
2043
+
1747
2044
  /* Set the defined names for the worksheets such as Print Titles. */
1748
2045
  _prepare_defined_names(self);
1749
2046
 
@@ -1903,6 +2200,8 @@ workbook_set_properties(lxw_workbook *self, lxw_doc_properties *user_props)
1903
2200
  GOTO_LABEL_ON_MEM_ERROR(doc_props->hyperlink_base, mem_error);
1904
2201
  }
1905
2202
 
2203
+ doc_props->created = user_props->created;
2204
+
1906
2205
  self->properties = doc_props;
1907
2206
 
1908
2207
  return LXW_NO_ERROR;
@@ -2150,6 +2449,27 @@ workbook_get_chartsheet_by_name(lxw_workbook *self, const char *name)
2150
2449
  return NULL;
2151
2450
  }
2152
2451
 
2452
+ /*
2453
+ * Get the default URL format.
2454
+ */
2455
+ lxw_format *
2456
+ workbook_get_default_url_format(lxw_workbook *self)
2457
+ {
2458
+ return self->default_url_format;
2459
+ }
2460
+
2461
+ /*
2462
+ * Unset the default URL format.
2463
+ */
2464
+ void
2465
+ workbook_unset_default_url_format(lxw_workbook *self)
2466
+ {
2467
+ self->default_url_format->hyperlink = LXW_FALSE;
2468
+ self->default_url_format->xf_id = 0;
2469
+ self->default_url_format->underline = LXW_UNDERLINE_NONE;
2470
+ self->default_url_format->theme = 0;
2471
+ }
2472
+
2153
2473
  /*
2154
2474
  * Validate the worksheet name based on Excel's rules.
2155
2475
  */
@@ -2168,10 +2488,6 @@ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
2168
2488
  if (sheetname[0] == '\'' || sheetname[strlen(sheetname) - 1] == '\'')
2169
2489
  return LXW_ERROR_SHEETNAME_START_END_APOSTROPHE;
2170
2490
 
2171
- /* Check that the worksheet name isn't the reserved work "History". */
2172
- if (lxw_strcasecmp(sheetname, "history") == 0)
2173
- return LXW_ERROR_SHEETNAME_RESERVED;
2174
-
2175
2491
  /* Check if the worksheet name is already in use. */
2176
2492
  if (workbook_get_worksheet_by_name(self, sheetname))
2177
2493
  return LXW_ERROR_SHEETNAME_ALREADY_USED;
@@ -2198,7 +2514,7 @@ workbook_add_vba_project(lxw_workbook *self, const char *filename)
2198
2514
  }
2199
2515
 
2200
2516
  /* Check that the vbaProject file exists and can be opened. */
2201
- filehandle = fopen(filename, "rb");
2517
+ filehandle = lxw_fopen(filename, "rb");
2202
2518
  if (!filehandle) {
2203
2519
  LXW_WARN_FORMAT1("workbook_add_vba_project(): "
2204
2520
  "file doesn't exist or can't be opened: %s.",