fast_excel 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -7
  3. data/CHANGELOG.md +9 -0
  4. data/Gemfile +1 -1
  5. data/examples/example.rb +2 -0
  6. data/examples/example_date_time.rb +38 -0
  7. data/fast_excel.gemspec +2 -2
  8. data/lib/fast_excel/binding/format.rb +17 -0
  9. data/lib/fast_excel/binding/workbook.rb +39 -17
  10. data/lib/fast_excel/binding/worksheet.rb +57 -13
  11. data/lib/fast_excel/binding.rb +7 -7
  12. data/lib/fast_excel.rb +27 -20
  13. data/libxlsxwriter/.github/FUNDING.yml +1 -0
  14. data/libxlsxwriter/.github/ISSUE_TEMPLATE.md +85 -0
  15. data/libxlsxwriter/.github/PULL_REQUEST_TEMPLATE.md +130 -0
  16. data/libxlsxwriter/.github/workflows/cmake_actions.yml +48 -0
  17. data/libxlsxwriter/.github/workflows/code_style.yml +23 -0
  18. data/libxlsxwriter/.github/workflows/coverity.yml +22 -0
  19. data/libxlsxwriter/.github/workflows/make_actions.yml +52 -0
  20. data/libxlsxwriter/.github/workflows/valgrind.yml +23 -0
  21. data/libxlsxwriter/.github/workflows/windows_build.yml +54 -0
  22. data/libxlsxwriter/.github/workflows/zig_build.yml +22 -0
  23. data/libxlsxwriter/.gitignore +16 -1
  24. data/libxlsxwriter/.indent.pro +24 -0
  25. data/libxlsxwriter/CMakeLists.txt +156 -56
  26. data/libxlsxwriter/CONTRIBUTING.md +2 -2
  27. data/libxlsxwriter/Changes.txt +344 -2
  28. data/libxlsxwriter/LICENSE.txt +66 -8
  29. data/libxlsxwriter/Makefile +151 -54
  30. data/libxlsxwriter/Package.swift +42 -0
  31. data/libxlsxwriter/Readme.md +4 -2
  32. data/libxlsxwriter/build.zig +324 -0
  33. data/libxlsxwriter/build.zig.zon +11 -0
  34. data/libxlsxwriter/cmake/FindMINIZIP.cmake +3 -3
  35. data/libxlsxwriter/cocoapods/libxlsxwriter-umbrella.h +6 -0
  36. data/libxlsxwriter/include/xlsxwriter/app.h +2 -1
  37. data/libxlsxwriter/include/xlsxwriter/chart.h +236 -32
  38. data/libxlsxwriter/include/xlsxwriter/chartsheet.h +7 -7
  39. data/libxlsxwriter/include/xlsxwriter/comment.h +76 -0
  40. data/libxlsxwriter/include/xlsxwriter/common.h +111 -50
  41. data/libxlsxwriter/include/xlsxwriter/content_types.h +8 -1
  42. data/libxlsxwriter/include/xlsxwriter/core.h +1 -1
  43. data/libxlsxwriter/include/xlsxwriter/custom.h +1 -1
  44. data/libxlsxwriter/include/xlsxwriter/drawing.h +11 -20
  45. data/libxlsxwriter/include/xlsxwriter/format.h +121 -8
  46. data/libxlsxwriter/include/xlsxwriter/hash_table.h +1 -1
  47. data/libxlsxwriter/include/xlsxwriter/metadata.h +49 -0
  48. data/libxlsxwriter/include/xlsxwriter/packager.h +27 -16
  49. data/libxlsxwriter/include/xlsxwriter/relationships.h +1 -1
  50. data/libxlsxwriter/include/xlsxwriter/shared_strings.h +1 -1
  51. data/libxlsxwriter/include/xlsxwriter/styles.h +13 -7
  52. data/libxlsxwriter/include/xlsxwriter/table.h +51 -0
  53. data/libxlsxwriter/include/xlsxwriter/theme.h +1 -1
  54. data/libxlsxwriter/include/xlsxwriter/third_party/emyg_dtoa.h +26 -0
  55. data/libxlsxwriter/include/xlsxwriter/third_party/ioapi.h +27 -25
  56. data/libxlsxwriter/include/xlsxwriter/third_party/md5.h +45 -0
  57. data/libxlsxwriter/include/xlsxwriter/third_party/zip.h +155 -153
  58. data/libxlsxwriter/include/xlsxwriter/utility.h +70 -8
  59. data/libxlsxwriter/include/xlsxwriter/vml.h +55 -0
  60. data/libxlsxwriter/include/xlsxwriter/workbook.h +218 -47
  61. data/libxlsxwriter/include/xlsxwriter/worksheet.h +2770 -241
  62. data/libxlsxwriter/include/xlsxwriter/xmlwriter.h +12 -8
  63. data/libxlsxwriter/include/xlsxwriter.h +4 -2
  64. data/libxlsxwriter/libxlsxwriter.podspec +8 -5
  65. data/libxlsxwriter/src/Makefile +58 -21
  66. data/libxlsxwriter/src/app.c +5 -2
  67. data/libxlsxwriter/src/chart.c +396 -81
  68. data/libxlsxwriter/src/chartsheet.c +22 -22
  69. data/libxlsxwriter/src/comment.c +443 -0
  70. data/libxlsxwriter/src/content_types.c +40 -1
  71. data/libxlsxwriter/src/core.c +2 -2
  72. data/libxlsxwriter/src/custom.c +1 -1
  73. data/libxlsxwriter/src/drawing.c +160 -40
  74. data/libxlsxwriter/src/format.c +109 -25
  75. data/libxlsxwriter/src/hash_table.c +1 -1
  76. data/libxlsxwriter/src/metadata.c +283 -0
  77. data/libxlsxwriter/src/packager.c +794 -94
  78. data/libxlsxwriter/src/relationships.c +1 -1
  79. data/libxlsxwriter/src/shared_strings.c +2 -4
  80. data/libxlsxwriter/src/styles.c +353 -58
  81. data/libxlsxwriter/src/table.c +304 -0
  82. data/libxlsxwriter/src/theme.c +1 -1
  83. data/libxlsxwriter/src/utility.c +143 -43
  84. data/libxlsxwriter/src/vml.c +1062 -0
  85. data/libxlsxwriter/src/workbook.c +567 -77
  86. data/libxlsxwriter/src/worksheet.c +6668 -1462
  87. data/libxlsxwriter/src/xmlwriter.c +95 -5
  88. data/libxlsxwriter/third_party/dtoa/Makefile +42 -0
  89. data/libxlsxwriter/third_party/dtoa/emyg_dtoa.c +461 -0
  90. data/libxlsxwriter/third_party/dtoa/emyg_dtoa.h +26 -0
  91. data/libxlsxwriter/third_party/md5/Makefile +42 -0
  92. data/libxlsxwriter/third_party/md5/md5.c +291 -0
  93. data/libxlsxwriter/third_party/md5/md5.h +45 -0
  94. data/libxlsxwriter/third_party/minizip/Makefile +3 -8
  95. data/libxlsxwriter/third_party/minizip/Makefile.orig +8 -4
  96. data/libxlsxwriter/third_party/minizip/MiniZip64_Changes.txt +1 -1
  97. data/libxlsxwriter/third_party/minizip/configure.ac +1 -1
  98. data/libxlsxwriter/third_party/minizip/crypt.h +13 -16
  99. data/libxlsxwriter/third_party/minizip/ioapi.c +31 -57
  100. data/libxlsxwriter/third_party/minizip/ioapi.h +31 -23
  101. data/libxlsxwriter/third_party/minizip/iowin32.c +29 -45
  102. data/libxlsxwriter/third_party/minizip/iowin32.h +4 -4
  103. data/libxlsxwriter/third_party/minizip/miniunz.c +29 -56
  104. data/libxlsxwriter/third_party/minizip/minizip.c +38 -49
  105. data/libxlsxwriter/third_party/minizip/mztools.c +1 -7
  106. data/libxlsxwriter/third_party/minizip/unzip.c +202 -342
  107. data/libxlsxwriter/third_party/minizip/unzip.h +74 -74
  108. data/libxlsxwriter/third_party/minizip/zip.c +165 -218
  109. data/libxlsxwriter/third_party/minizip/zip.h +164 -154
  110. data/libxlsxwriter/third_party/tmpfileplus/Makefile +3 -3
  111. data/libxlsxwriter/version.txt +1 -1
  112. data/test/auto_width_test.rb +20 -0
  113. data/test/default_format_test.rb +1 -1
  114. data/test/validations_test.rb +3 -3
  115. data/test/worksheet_test.rb +6 -1
  116. metadata +33 -7
  117. data/libxlsxwriter/.travis.yml +0 -37
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Used in conjunction with the libxlsxwriter library.
5
5
  *
6
- * Copyright 2014-2019, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
6
+ * Copyright 2014-2022, John McNamara, jmcnamara@cpan.org. See LICENSE.txt.
7
7
  *
8
8
  */
9
9
 
@@ -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)