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
 
@@ -78,10 +78,30 @@ _chart_free_font(lxw_chart_font *font)
78
78
  if (!font)
79
79
  return;
80
80
 
81
- free(font->name);
81
+ free((void *) font->name);
82
82
  free(font);
83
83
  }
84
84
 
85
+ STATIC void
86
+ _chart_free_data_labels(lxw_chart_series *series)
87
+ {
88
+ uint16_t index;
89
+
90
+ for (index = 0; index < series->data_label_count; index++) {
91
+ lxw_chart_custom_label *data_label = &series->data_labels[index];
92
+
93
+ free(data_label->value);
94
+ _chart_free_range(data_label->range);
95
+ _chart_free_font(data_label->font);
96
+ free(data_label->line);
97
+ free(data_label->fill);
98
+ free(data_label->pattern);
99
+ }
100
+
101
+ series->data_label_count = 0;
102
+ free(series->data_labels);
103
+ }
104
+
85
105
  /*
86
106
  * Free a series object.
87
107
  */
@@ -96,6 +116,10 @@ _chart_series_free(lxw_chart_series *series)
96
116
  free(series->fill);
97
117
  free(series->pattern);
98
118
  free(series->label_num_format);
119
+ free(series->label_line);
120
+ free(series->label_fill);
121
+ free(series->label_pattern);
122
+
99
123
  _chart_free_font(series->label_font);
100
124
 
101
125
  if (series->marker) {
@@ -109,6 +133,7 @@ _chart_series_free(lxw_chart_series *series)
109
133
  _chart_free_range(series->values);
110
134
  _chart_free_range(series->title.range);
111
135
  _chart_free_points(series);
136
+ _chart_free_data_labels(series);
112
137
 
113
138
  if (series->x_error_bars) {
114
139
  free(series->x_error_bars->line);
@@ -331,11 +356,6 @@ _chart_convert_font_args(lxw_chart_font *user_font)
331
356
  if (font->rotation)
332
357
  font->rotation = font->rotation * 60000;
333
358
 
334
- if (font->color) {
335
- font->color = lxw_format_check_color(font->color);
336
- font->has_color = LXW_TRUE;
337
- }
338
-
339
359
  return font;
340
360
  }
341
361
 
@@ -360,11 +380,6 @@ _chart_convert_line_args(lxw_chart_line *user_line)
360
380
  line->dash_type = user_line->dash_type;
361
381
  line->transparency = user_line->transparency;
362
382
 
363
- if (line->color) {
364
- line->color = lxw_format_check_color(line->color);
365
- line->has_color = LXW_TRUE;
366
- }
367
-
368
383
  if (line->transparency > 100)
369
384
  line->transparency = 0;
370
385
 
@@ -390,11 +405,6 @@ _chart_convert_fill_args(lxw_chart_fill *user_fill)
390
405
  fill->none = user_fill->none;
391
406
  fill->transparency = user_fill->transparency;
392
407
 
393
- if (fill->color) {
394
- fill->color = lxw_format_check_color(fill->color);
395
- fill->has_color = LXW_TRUE;
396
- }
397
-
398
408
  if (fill->transparency > 100)
399
409
  fill->transparency = 0;
400
410
 
@@ -430,17 +440,9 @@ _chart_convert_pattern_args(lxw_chart_pattern *user_pattern)
430
440
  pattern->bg_color = user_pattern->bg_color;
431
441
  pattern->type = user_pattern->type;
432
442
 
433
- pattern->fg_color = lxw_format_check_color(pattern->fg_color);
434
- pattern->has_fg_color = LXW_TRUE;
435
-
436
- if (pattern->bg_color) {
437
- pattern->bg_color = lxw_format_check_color(pattern->bg_color);
438
- pattern->has_bg_color = LXW_TRUE;
439
- }
440
- else {
443
+ if (!pattern->bg_color) {
441
444
  /* Default background color in Excel is white, when unspecified. */
442
445
  pattern->bg_color = LXW_COLOR_WHITE;
443
- pattern->has_bg_color = LXW_TRUE;
444
446
  }
445
447
 
446
448
  return pattern;
@@ -464,7 +466,7 @@ _chart_set_default_marker_type(lxw_chart *self, uint8_t type)
464
466
  /*
465
467
  * Set an axis number format.
466
468
  */
467
- void
469
+ STATIC void
468
470
  _chart_axis_set_default_num_format(lxw_chart_axis *axis, char *num_format)
469
471
  {
470
472
  if (!num_format)
@@ -857,7 +859,7 @@ _chart_write_a_def_rpr(lxw_chart *self, lxw_chart_font *font)
857
859
  LXW_INIT_ATTRIBUTES();
858
860
 
859
861
  if (font) {
860
- has_color = font->color || font->has_color;
862
+ has_color = !!font->color;
861
863
  has_latin = font->name || font->pitch_family || font->charset;
862
864
  use_font_default = !(has_color || has_latin || font->baseline == -1);
863
865
 
@@ -929,7 +931,7 @@ _chart_write_a_r_pr(lxw_chart *self, lxw_chart_font *font)
929
931
  LXW_PUSH_ATTRIBUTES_STR("lang", "en-US");
930
932
 
931
933
  if (font) {
932
- has_color = font->color || font->has_color;
934
+ has_color = !!font->color;
933
935
  has_latin = font->name || font->pitch_family || font->charset;
934
936
  use_font_default = !(has_color || has_latin || font->baseline == -1);
935
937
 
@@ -1090,12 +1092,14 @@ _chart_write_a_p_pie(lxw_chart *self, lxw_chart_font *font)
1090
1092
  * Write the <a:p> element.
1091
1093
  */
1092
1094
  STATIC void
1093
- _chart_write_a_p_rich(lxw_chart *self, char *name, lxw_chart_font *font)
1095
+ _chart_write_a_p_rich(lxw_chart *self, char *name, lxw_chart_font *font,
1096
+ uint8_t ignore_rich_pr)
1094
1097
  {
1095
1098
  lxw_xml_start_tag(self->file, "a:p", NULL);
1096
1099
 
1097
1100
  /* Write the a:pPr element. */
1098
- _chart_write_a_p_pr_rich(self, font);
1101
+ if (!ignore_rich_pr)
1102
+ _chart_write_a_p_pr_rich(self, font);
1099
1103
 
1100
1104
  /* Write the a:r element. */
1101
1105
  _chart_write_a_r(self, name, font);
@@ -1127,11 +1131,27 @@ _chart_write_a_body_pr(lxw_chart *self, int32_t rotation,
1127
1131
  if (rotation == 0 && is_horizontal)
1128
1132
  rotation = -5400000;
1129
1133
 
1130
- if (rotation)
1131
- LXW_PUSH_ATTRIBUTES_INT("rot", rotation);
1132
-
1133
- if (is_horizontal)
1134
- LXW_PUSH_ATTRIBUTES_STR("vert", "horz");
1134
+ if (rotation) {
1135
+ if (rotation == 16200000) {
1136
+ /* 270 deg/stacked angle. */
1137
+ LXW_PUSH_ATTRIBUTES_STR("rot", "0");
1138
+ LXW_PUSH_ATTRIBUTES_STR("vert", "wordArtVert");
1139
+ }
1140
+ else if (rotation == 16260000) {
1141
+ /* 271 deg/East Asian vertical. */
1142
+ LXW_PUSH_ATTRIBUTES_STR("rot", "0");
1143
+ LXW_PUSH_ATTRIBUTES_STR("vert", "eaVert");
1144
+ }
1145
+ else if (rotation == 21600000) {
1146
+ /* 360 deg = 0 for y axis. */
1147
+ LXW_PUSH_ATTRIBUTES_STR("rot", "0");
1148
+ LXW_PUSH_ATTRIBUTES_STR("vert", "horz");
1149
+ }
1150
+ else {
1151
+ LXW_PUSH_ATTRIBUTES_INT("rot", rotation);
1152
+ LXW_PUSH_ATTRIBUTES_STR("vert", "horz");
1153
+ }
1154
+ }
1135
1155
 
1136
1156
  lxw_xml_empty_tag(self->file, "a:bodyPr", &attributes);
1137
1157
 
@@ -1466,8 +1486,8 @@ _chart_write_axis_font(lxw_chart *self, lxw_chart_font *font)
1466
1486
  * Write the <c:rich> element.
1467
1487
  */
1468
1488
  STATIC void
1469
- _chart_write_rich(lxw_chart *self, char *name, uint8_t is_horizontal,
1470
- lxw_chart_font *font)
1489
+ _chart_write_rich(lxw_chart *self, char *name, lxw_chart_font *font,
1490
+ uint8_t is_horizontal, uint8_t ignore_rich_pr)
1471
1491
  {
1472
1492
  int32_t rotation = 0;
1473
1493
 
@@ -1483,7 +1503,7 @@ _chart_write_rich(lxw_chart *self, char *name, uint8_t is_horizontal,
1483
1503
  _chart_write_a_lst_style(self);
1484
1504
 
1485
1505
  /* Write the a:p element. */
1486
- _chart_write_a_p_rich(self, name, font);
1506
+ _chart_write_a_p_rich(self, name, font, ignore_rich_pr);
1487
1507
 
1488
1508
  lxw_xml_end_tag(self->file, "c:rich");
1489
1509
  }
@@ -1499,7 +1519,7 @@ _chart_write_tx_rich(lxw_chart *self, char *name, uint8_t is_horizontal,
1499
1519
  lxw_xml_start_tag(self->file, "c:tx", NULL);
1500
1520
 
1501
1521
  /* Write the c:rich element. */
1502
- _chart_write_rich(self, name, is_horizontal, font);
1522
+ _chart_write_rich(self, name, font, is_horizontal, LXW_FALSE);
1503
1523
 
1504
1524
  lxw_xml_end_tag(self->file, "c:tx");
1505
1525
  }
@@ -1658,28 +1678,33 @@ _chart_write_a_ln(lxw_chart *self, lxw_chart_line *line)
1658
1678
  /* Convert to internal units. */
1659
1679
  width_int = (uint32_t) (0.5 + (12700.0 * width_flt));
1660
1680
 
1661
- if (width_int)
1681
+ if (line->width > 0.0)
1662
1682
  LXW_PUSH_ATTRIBUTES_INT("w", width_int);
1663
1683
 
1664
- lxw_xml_start_tag(self->file, "a:ln", &attributes);
1684
+ if (line->none || line->color || line->dash_type) {
1685
+ lxw_xml_start_tag(self->file, "a:ln", &attributes);
1665
1686
 
1666
- /* Write the line fill. */
1667
- if (line->none) {
1668
- /* Write the a:noFill element. */
1669
- _chart_write_a_no_fill(self);
1670
- }
1671
- else if (line->has_color) {
1672
- /* Write the a:solidFill element. */
1673
- _chart_write_a_solid_fill(self, line->color, line->transparency);
1674
- }
1687
+ /* Write the line fill. */
1688
+ if (line->none) {
1689
+ /* Write the a:noFill element. */
1690
+ _chart_write_a_no_fill(self);
1691
+ }
1692
+ else if (line->color) {
1693
+ /* Write the a:solidFill element. */
1694
+ _chart_write_a_solid_fill(self, line->color, line->transparency);
1695
+ }
1675
1696
 
1676
- /* Write the line/dash type. */
1677
- if (line->dash_type) {
1678
- /* Write the a:prstDash element. */
1679
- _chart_write_a_prst_dash(self, line->dash_type);
1680
- }
1697
+ /* Write the line/dash type. */
1698
+ if (line->dash_type) {
1699
+ /* Write the a:prstDash element. */
1700
+ _chart_write_a_prst_dash(self, line->dash_type);
1701
+ }
1681
1702
 
1682
- lxw_xml_end_tag(self->file, "a:ln");
1703
+ lxw_xml_end_tag(self->file, "a:ln");
1704
+ }
1705
+ else {
1706
+ lxw_xml_empty_tag(self->file, "a:ln", &attributes);
1707
+ }
1683
1708
 
1684
1709
  LXW_FREE_ATTRIBUTES();
1685
1710
  }
@@ -1824,10 +1849,10 @@ _chart_write_a_patt_fill(lxw_chart *self, lxw_chart_pattern *pattern)
1824
1849
 
1825
1850
  lxw_xml_start_tag(self->file, "a:pattFill", &attributes);
1826
1851
 
1827
- if (pattern->has_fg_color)
1852
+ if (pattern->fg_color)
1828
1853
  _chart_write_a_fg_clr(self, pattern->fg_color);
1829
1854
 
1830
- if (pattern->has_bg_color)
1855
+ if (pattern->bg_color)
1831
1856
  _chart_write_a_bg_clr(self, pattern->bg_color);
1832
1857
 
1833
1858
  lxw_xml_end_tag(self->file, "a:pattFill");
@@ -2268,6 +2293,141 @@ _chart_write_label_num_fmt(lxw_chart *self, char *format)
2268
2293
  LXW_FREE_ATTRIBUTES();
2269
2294
  }
2270
2295
 
2296
+ /*
2297
+ * Write parts of the <c:dLbl> elements where only formatting is changed.
2298
+ */
2299
+ STATIC void
2300
+ _chart_write_custom_label_format_only(lxw_chart *self,
2301
+ lxw_chart_custom_label *data_label)
2302
+ {
2303
+ if (data_label->line || data_label->fill || data_label->pattern) {
2304
+ _chart_write_sp_pr(self, data_label->line, data_label->fill,
2305
+ data_label->pattern);
2306
+ _chart_write_tx_pr(self, LXW_FALSE, data_label->font);
2307
+ }
2308
+ else if (data_label->font) {
2309
+ lxw_xml_empty_tag(self->file, "c:spPr", NULL);
2310
+ _chart_write_tx_pr(self, LXW_FALSE, data_label->font);
2311
+ }
2312
+ }
2313
+
2314
+ /*
2315
+ * Write parts of the <c:dLbl> elements for formula custom labels.
2316
+ */
2317
+ STATIC void
2318
+ _chart_write_custom_label_formula(lxw_chart *self, lxw_chart_series *series,
2319
+ lxw_chart_custom_label *data_label)
2320
+ {
2321
+ lxw_xml_empty_tag(self->file, "c:layout", NULL);
2322
+ lxw_xml_start_tag(self->file, "c:tx", NULL);
2323
+
2324
+ _chart_write_str_ref(self, data_label->range);
2325
+
2326
+ lxw_xml_end_tag(self->file, "c:tx");
2327
+
2328
+ _chart_write_custom_label_format_only(self, data_label);
2329
+
2330
+ /* Write the c:dLblPos element. */
2331
+ if (series->label_position)
2332
+ _chart_write_d_lbl_pos(self, series->label_position);
2333
+
2334
+ /* Write the c:showVal element. */
2335
+ if (series->show_labels_value)
2336
+ _chart_write_show_val(self);
2337
+
2338
+ /* Write the c:showCatName element. */
2339
+ if (series->show_labels_category)
2340
+ _chart_write_show_cat_name(self);
2341
+
2342
+ /* Write the c:showSerName element. */
2343
+ if (series->show_labels_name)
2344
+ _chart_write_show_ser_name(self);
2345
+
2346
+ }
2347
+
2348
+ /*
2349
+ * Write parts of the <c:dLbl> elements for string custom labels.
2350
+ */
2351
+ STATIC void
2352
+ _chart_write_custom_label_str(lxw_chart *self, lxw_chart_series *series,
2353
+ lxw_chart_custom_label *data_label)
2354
+ {
2355
+ uint8_t ignore_rich_pr = LXW_TRUE;
2356
+
2357
+ if (data_label->line || data_label->fill || data_label->pattern)
2358
+ ignore_rich_pr = LXW_FALSE;
2359
+
2360
+ lxw_xml_empty_tag(self->file, "c:layout", NULL);
2361
+ lxw_xml_start_tag(self->file, "c:tx", NULL);
2362
+
2363
+ /* Write the c:rich element. */
2364
+ _chart_write_rich(self, data_label->value, data_label->font,
2365
+ LXW_FALSE, ignore_rich_pr);
2366
+
2367
+ lxw_xml_end_tag(self->file, "c:tx");
2368
+
2369
+ /* Write the c:spPr element. */
2370
+ _chart_write_sp_pr(self, data_label->line, data_label->fill,
2371
+ data_label->pattern);
2372
+
2373
+ /* Write the c:dLblPos element. */
2374
+ if (series->label_position)
2375
+ _chart_write_d_lbl_pos(self, series->label_position);
2376
+
2377
+ /* Write the c:showVal element. */
2378
+ if (series->show_labels_value)
2379
+ _chart_write_show_val(self);
2380
+
2381
+ /* Write the c:showCatName element. */
2382
+ if (series->show_labels_category)
2383
+ _chart_write_show_cat_name(self);
2384
+
2385
+ /* Write the c:showSerName element. */
2386
+ if (series->show_labels_name)
2387
+ _chart_write_show_ser_name(self);
2388
+
2389
+ }
2390
+
2391
+ /*
2392
+ * Write the <c:dLbl> elements for custom labels.
2393
+ */
2394
+ STATIC void
2395
+ _chart_write_custom_labels(lxw_chart *self, lxw_chart_series *series)
2396
+ {
2397
+ uint16_t index = 0;
2398
+
2399
+ for (index = 0; index < series->data_label_count; index++) {
2400
+ lxw_chart_custom_label *data_label = &series->data_labels[index];
2401
+
2402
+ if (!data_label->value && !data_label->range && !data_label->hide
2403
+ && !data_label->font) {
2404
+
2405
+ continue;
2406
+ }
2407
+
2408
+ lxw_xml_start_tag(self->file, "c:dLbl", NULL);
2409
+
2410
+ /* Write the c:idx element. */
2411
+ _chart_write_idx(self, index);
2412
+
2413
+ if (data_label->hide) {
2414
+ /* Write the c:delete element. */
2415
+ _chart_write_delete(self);
2416
+ }
2417
+ else if (data_label->value) {
2418
+ _chart_write_custom_label_str(self, series, data_label);
2419
+ }
2420
+ else if (data_label->range) {
2421
+ _chart_write_custom_label_formula(self, series, data_label);
2422
+ }
2423
+ else if (data_label->font) {
2424
+ _chart_write_custom_label_format_only(self, data_label);
2425
+ }
2426
+
2427
+ lxw_xml_end_tag(self->file, "c:dLbl");
2428
+ }
2429
+ }
2430
+
2271
2431
  /*
2272
2432
  * Write the <c:dLbls> element.
2273
2433
  */
@@ -2279,10 +2439,17 @@ _chart_write_d_lbls(lxw_chart *self, lxw_chart_series *series)
2279
2439
 
2280
2440
  lxw_xml_start_tag(self->file, "c:dLbls", NULL);
2281
2441
 
2442
+ if (series->data_labels)
2443
+ _chart_write_custom_labels(self, series);
2444
+
2282
2445
  /* Write the c:numFmt element. */
2283
2446
  if (series->label_num_format)
2284
2447
  _chart_write_label_num_fmt(self, series->label_num_format);
2285
2448
 
2449
+ /* Write the c:spPr element. */
2450
+ _chart_write_sp_pr(self, series->label_line, series->label_fill,
2451
+ series->label_pattern);
2452
+
2286
2453
  if (series->label_font)
2287
2454
  _chart_write_tx_pr(self, LXW_FALSE, series->label_font);
2288
2455
 
@@ -3161,7 +3328,9 @@ _chart_write_crosses(lxw_chart *self, lxw_chart_axis *axis)
3161
3328
 
3162
3329
  LXW_INIT_ATTRIBUTES();
3163
3330
 
3164
- if (axis->crossing_max)
3331
+ if (axis->crossing_min)
3332
+ LXW_PUSH_ATTRIBUTES_STR("val", "min");
3333
+ else if (axis->crossing_max)
3165
3334
  LXW_PUSH_ATTRIBUTES_STR("val", "max");
3166
3335
  else
3167
3336
  LXW_PUSH_ATTRIBUTES_STR("val", "autoZero");
@@ -3569,7 +3738,7 @@ _chart_write_legend_entry(lxw_chart *self, uint16_t index)
3569
3738
  /* Write the c:idx element. */
3570
3739
  _chart_write_idx(self, self->delete_series[index]);
3571
3740
 
3572
- /* Write the c:delete element. */
3741
+ /* Write the c:dst_label element. */
3573
3742
  _chart_write_delete(self);
3574
3743
 
3575
3744
  lxw_xml_end_tag(self->file, "c:legendEntry");
@@ -4104,7 +4273,8 @@ _chart_write_cat_axis(lxw_chart *self)
4104
4273
  _chart_write_cross_axis(self, self->axis_id_2);
4105
4274
 
4106
4275
  /* Write the c:crosses element. */
4107
- if (!self->y_axis->has_crossing || self->y_axis->crossing_max)
4276
+ if (!self->y_axis->has_crossing || self->y_axis->crossing_min
4277
+ || self->y_axis->crossing_max)
4108
4278
  _chart_write_crosses(self, self->y_axis);
4109
4279
  else
4110
4280
  _chart_write_crosses_at(self, self->y_axis);
@@ -4185,7 +4355,8 @@ _chart_write_val_axis(lxw_chart *self)
4185
4355
  _chart_write_cross_axis(self, self->axis_id_1);
4186
4356
 
4187
4357
  /* Write the c:crosses element. */
4188
- if (!self->x_axis->has_crossing || self->x_axis->crossing_max)
4358
+ if (!self->x_axis->has_crossing || self->x_axis->crossing_min
4359
+ || self->x_axis->crossing_max)
4189
4360
  _chart_write_crosses(self, self->x_axis);
4190
4361
  else
4191
4362
  _chart_write_crosses_at(self, self->x_axis);
@@ -4263,7 +4434,8 @@ _chart_write_cat_val_axis(lxw_chart *self)
4263
4434
  _chart_write_cross_axis(self, self->axis_id_2);
4264
4435
 
4265
4436
  /* Write the c:crosses element. */
4266
- if (!self->y_axis->has_crossing || self->y_axis->crossing_max)
4437
+ if (!self->y_axis->has_crossing || self->y_axis->crossing_min
4438
+ || self->y_axis->crossing_max)
4267
4439
  _chart_write_crosses(self, self->y_axis);
4268
4440
  else
4269
4441
  _chart_write_crosses_at(self, self->y_axis);
@@ -4497,18 +4669,15 @@ _chart_write_scatter_chart(lxw_chart *self)
4497
4669
 
4498
4670
  /* Add default scatter chart formatting to the series data unless
4499
4671
  * it has already been specified by the user.*/
4500
- if (self->type == LXW_CHART_SCATTER) {
4501
- if (!series->line) {
4502
- lxw_chart_line line = {
4503
- 0x000000,
4504
- LXW_TRUE,
4505
- 2.25,
4506
- LXW_CHART_LINE_DASH_SOLID,
4507
- 0,
4508
- LXW_FALSE
4509
- };
4510
- series->line = _chart_convert_line_args(&line);
4511
- }
4672
+ if (self->type == LXW_CHART_SCATTER && !series->line) {
4673
+ lxw_chart_line line = {
4674
+ 0x000000,
4675
+ LXW_TRUE,
4676
+ 2.25,
4677
+ LXW_CHART_LINE_DASH_SOLID,
4678
+ 0
4679
+ };
4680
+ series->line = _chart_convert_line_args(&line);
4512
4681
  }
4513
4682
 
4514
4683
  /* Write the c:ser element. */
@@ -4797,7 +4966,7 @@ _chart_initialize_doughnut_chart(lxw_chart *self)
4797
4966
  * Initialize a line chart.
4798
4967
  */
4799
4968
  STATIC void
4800
- _chart_initialize_line_chart(lxw_chart *self)
4969
+ _chart_initialize_line_chart(lxw_chart *self, uint8_t type)
4801
4970
  {
4802
4971
  self->chart_group = LXW_CHART_LINE;
4803
4972
  _chart_set_default_marker_type(self, LXW_CHART_MARKER_NONE);
@@ -4806,6 +4975,17 @@ _chart_initialize_line_chart(lxw_chart *self)
4806
4975
  self->y_axis->is_value = LXW_TRUE;
4807
4976
  self->default_label_position = LXW_CHART_LABEL_POSITION_RIGHT;
4808
4977
 
4978
+ if (type == LXW_CHART_LINE_STACKED) {
4979
+ self->grouping = LXW_GROUPING_STACKED;
4980
+ self->subtype = LXW_CHART_SUBTYPE_STACKED;
4981
+ }
4982
+
4983
+ if (type == LXW_CHART_LINE_STACKED_PERCENT) {
4984
+ self->grouping = LXW_GROUPING_PERCENTSTACKED;
4985
+ _chart_axis_set_default_num_format(self->y_axis, "0%");
4986
+ self->subtype = LXW_CHART_SUBTYPE_STACKED;
4987
+ }
4988
+
4809
4989
  /* Initialize the function pointers for this chart type. */
4810
4990
  self->write_chart_type = _chart_write_line_chart;
4811
4991
  self->write_plot_area = _chart_write_plot_area;
@@ -4900,7 +5080,9 @@ _chart_initialize(lxw_chart *self, uint8_t type)
4900
5080
  break;
4901
5081
 
4902
5082
  case LXW_CHART_LINE:
4903
- _chart_initialize_line_chart(self);
5083
+ case LXW_CHART_LINE_STACKED:
5084
+ case LXW_CHART_LINE_STACKED_PERCENT:
5085
+ _chart_initialize_line_chart(self, type);
4904
5086
  break;
4905
5087
 
4906
5088
  case LXW_CHART_PIE:
@@ -4999,7 +5181,7 @@ lxw_chart_add_data_cache(lxw_series_range *range, uint8_t *data,
4999
5181
  }
5000
5182
 
5001
5183
  /*
5002
- * Insert an image into the worksheet.
5184
+ * Add a series to the chart.
5003
5185
  */
5004
5186
  lxw_chart_series *
5005
5187
  chart_add_series(lxw_chart *self, const char *categories, const char *values)
@@ -5306,7 +5488,7 @@ chart_series_set_marker_pattern(lxw_chart_series *series,
5306
5488
  }
5307
5489
 
5308
5490
  /*
5309
- * Store the horizontal page breaks on a worksheet.
5491
+ * Store the points for a chart.
5310
5492
  */
5311
5493
  lxw_error
5312
5494
  chart_series_set_points(lxw_chart_series *series, lxw_chart_point *points[])
@@ -5375,6 +5557,83 @@ chart_series_set_labels_options(lxw_chart_series *series, uint8_t show_name,
5375
5557
  series->show_labels_value = show_value;
5376
5558
  }
5377
5559
 
5560
+ /*
5561
+ * Store the custom data_labels for a chart.
5562
+ */
5563
+ lxw_error
5564
+ chart_series_set_labels_custom(lxw_chart_series *series,
5565
+ lxw_chart_data_label *data_labels[])
5566
+ {
5567
+ uint16_t i = 0;
5568
+ uint16_t data_label_count = 0;
5569
+
5570
+ if (data_labels == NULL)
5571
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
5572
+
5573
+ while (data_labels[data_label_count])
5574
+ data_label_count++;
5575
+
5576
+ if (data_label_count == 0)
5577
+ return LXW_ERROR_NULL_PARAMETER_IGNORED;
5578
+
5579
+ series->has_labels = LXW_TRUE;
5580
+
5581
+ /* Set the Value label type if no other type is set. */
5582
+ if (!series->show_labels_name && !series->show_labels_category
5583
+ && !series->show_labels_value) {
5584
+ series->show_labels_value = LXW_TRUE;
5585
+ }
5586
+
5587
+ /* Free any existing resource. */
5588
+ _chart_free_data_labels(series);
5589
+
5590
+ series->data_labels = calloc(data_label_count,
5591
+ sizeof(lxw_chart_custom_label));
5592
+ RETURN_ON_MEM_ERROR(series->data_labels, LXW_ERROR_MEMORY_MALLOC_FAILED);
5593
+
5594
+ /* Copy the user data into the array of new structs. The struct types
5595
+ * are different since the internal version has more fields. */
5596
+ for (i = 0; i < data_label_count; i++) {
5597
+ lxw_chart_data_label *user_label = data_labels[i];
5598
+ lxw_chart_custom_label *data_label = &series->data_labels[i];
5599
+ const char *src_value = user_label->value;
5600
+
5601
+ data_label->hide = user_label->hide;
5602
+ data_label->font = _chart_convert_font_args(user_label->font);
5603
+ data_label->line = _chart_convert_line_args(user_label->line);
5604
+ data_label->fill = _chart_convert_fill_args(user_label->fill);
5605
+ data_label->pattern =
5606
+ _chart_convert_pattern_args(user_label->pattern);
5607
+
5608
+ if (src_value) {
5609
+ if (*src_value == '=') {
5610
+ /* The value is a formula. Handle like other chart ranges. */
5611
+ data_label->range = calloc(1, sizeof(lxw_series_range));
5612
+ GOTO_LABEL_ON_MEM_ERROR(data_label->range, mem_error);
5613
+
5614
+ data_label->range->formula = lxw_strdup(src_value + 1);
5615
+
5616
+ /* Add the formula to the data cache to allow value to be looked
5617
+ * up and filled in when the file is closed. */
5618
+ if (_chart_init_data_cache(data_label->range) != LXW_NO_ERROR)
5619
+ goto mem_error;
5620
+ }
5621
+ else {
5622
+ /* The value is a simple string. */
5623
+ data_label->value = lxw_strdup(src_value);
5624
+ }
5625
+ }
5626
+ }
5627
+
5628
+ series->data_label_count = data_label_count;
5629
+
5630
+ return LXW_NO_ERROR;
5631
+
5632
+ mem_error:
5633
+ _chart_free_data_labels(series);
5634
+ return LXW_ERROR_MEMORY_MALLOC_FAILED;
5635
+ }
5636
+
5378
5637
  /*
5379
5638
  * Set the data labels separator for a series.
5380
5639
  */
@@ -5459,6 +5718,52 @@ chart_series_set_labels_font(lxw_chart_series *series, lxw_chart_font *font)
5459
5718
  series->label_font = _chart_convert_font_args(font);
5460
5719
  }
5461
5720
 
5721
+ /*
5722
+ * Set a line type for a series data labels.
5723
+ */
5724
+ void
5725
+ chart_series_set_labels_line(lxw_chart_series *series, lxw_chart_line *line)
5726
+ {
5727
+ if (!line)
5728
+ return;
5729
+
5730
+ /* Free any previously allocated resource. */
5731
+ free(series->label_line);
5732
+
5733
+ series->label_line = _chart_convert_line_args(line);
5734
+ }
5735
+
5736
+ /*
5737
+ * Set a fill type for a series data labels.
5738
+ */
5739
+ void
5740
+ chart_series_set_labels_fill(lxw_chart_series *series, lxw_chart_fill *fill)
5741
+ {
5742
+ if (!fill)
5743
+ return;
5744
+
5745
+ /* Free any previously allocated resource. */
5746
+ free(series->label_fill);
5747
+
5748
+ series->label_fill = _chart_convert_fill_args(fill);
5749
+ }
5750
+
5751
+ /*
5752
+ * Set a pattern type for a series data labels.
5753
+ */
5754
+ void
5755
+ chart_series_set_labels_pattern(lxw_chart_series *series,
5756
+ lxw_chart_pattern *pattern)
5757
+ {
5758
+ if (!pattern)
5759
+ return;
5760
+
5761
+ /* Free any previously allocated resource. */
5762
+ free(series->label_pattern);
5763
+
5764
+ series->label_pattern = _chart_convert_pattern_args(pattern);
5765
+ }
5766
+
5462
5767
  /*
5463
5768
  * Set the trendline for a chart series.
5464
5769
  */
@@ -5847,7 +6152,17 @@ chart_axis_set_crossing(lxw_chart_axis *axis, double value)
5847
6152
  }
5848
6153
 
5849
6154
  /*
5850
- * Set the axis crossing position as the max possible value.
6155
+ * Set the axis crossing position as the minimum possible value.
6156
+ */
6157
+ void
6158
+ chart_axis_set_crossing_min(lxw_chart_axis *axis)
6159
+ {
6160
+ axis->has_crossing = LXW_TRUE;
6161
+ axis->crossing_min = LXW_TRUE;
6162
+ }
6163
+
6164
+ /*
6165
+ * Set the axis crossing position as the maximum possible value.
5851
6166
  */
5852
6167
  void
5853
6168
  chart_axis_set_crossing_max(lxw_chart_axis *axis)