write_xlsx 1.12.3 → 1.15.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -0
  3. data/Changes +23 -0
  4. data/README.md +1 -1
  5. data/lib/write_xlsx/chart/area.rb +2 -2
  6. data/lib/write_xlsx/chart/axis.rb +55 -32
  7. data/lib/write_xlsx/chart/axis_writer.rb +528 -0
  8. data/lib/write_xlsx/chart/bar.rb +2 -2
  9. data/lib/write_xlsx/chart/caption.rb +16 -9
  10. data/lib/write_xlsx/chart/chart_area.rb +121 -0
  11. data/lib/write_xlsx/chart/column.rb +2 -2
  12. data/lib/write_xlsx/chart/d_pt_point_writer.rb +14 -0
  13. data/lib/write_xlsx/chart/doughnut.rb +0 -3
  14. data/lib/write_xlsx/chart/formatting_writer.rb +652 -0
  15. data/lib/write_xlsx/chart/initialization.rb +100 -0
  16. data/lib/write_xlsx/chart/line.rb +4 -3
  17. data/lib/write_xlsx/chart/pie.rb +6 -2
  18. data/lib/write_xlsx/chart/radar.rb +2 -2
  19. data/lib/write_xlsx/chart/scatter.rb +4 -3
  20. data/lib/write_xlsx/chart/series.rb +35 -15
  21. data/lib/write_xlsx/chart/series_data.rb +132 -0
  22. data/lib/write_xlsx/chart/series_writer.rb +318 -0
  23. data/lib/write_xlsx/chart/settings.rb +226 -0
  24. data/lib/write_xlsx/chart/stock.rb +2 -2
  25. data/lib/write_xlsx/chart/table.rb +50 -0
  26. data/lib/write_xlsx/chart/xml_writer.rb +305 -0
  27. data/lib/write_xlsx/chart.rb +286 -2477
  28. data/lib/write_xlsx/chartsheet.rb +35 -83
  29. data/lib/write_xlsx/constants.rb +11 -0
  30. data/lib/write_xlsx/drawing.rb +5 -3
  31. data/lib/write_xlsx/format/alignment_state.rb +39 -0
  32. data/lib/write_xlsx/format/alignment_style.rb +92 -0
  33. data/lib/write_xlsx/format/border_state.rb +47 -0
  34. data/lib/write_xlsx/format/border_style.rb +116 -0
  35. data/lib/write_xlsx/format/fill_state.rb +26 -0
  36. data/lib/write_xlsx/format/fill_style.rb +52 -0
  37. data/lib/write_xlsx/format/font_state.rb +74 -0
  38. data/lib/write_xlsx/format/font_style.rb +172 -0
  39. data/lib/write_xlsx/format/format_state.rb +65 -0
  40. data/lib/write_xlsx/format/number_format_state.rb +20 -0
  41. data/lib/write_xlsx/format/number_format_style.rb +28 -0
  42. data/lib/write_xlsx/format/protection_state.rb +20 -0
  43. data/lib/write_xlsx/format/protection_style.rb +28 -0
  44. data/lib/write_xlsx/format.rb +1093 -426
  45. data/lib/write_xlsx/formats.rb +0 -2
  46. data/lib/write_xlsx/image_property.rb +4 -1
  47. data/lib/write_xlsx/inserted_chart.rb +1 -1
  48. data/lib/write_xlsx/object_positioning.rb +203 -0
  49. data/lib/write_xlsx/package/app.rb +3 -3
  50. data/lib/write_xlsx/package/button.rb +6 -2
  51. data/lib/write_xlsx/package/comments.rb +11 -3
  52. data/lib/write_xlsx/package/conditional_format.rb +7 -3
  53. data/lib/write_xlsx/package/content_types.rb +2 -2
  54. data/lib/write_xlsx/package/core.rb +2 -2
  55. data/lib/write_xlsx/package/custom.rb +3 -2
  56. data/lib/write_xlsx/package/metadata.rb +2 -2
  57. data/lib/write_xlsx/package/packager.rb +0 -3
  58. data/lib/write_xlsx/package/relationships.rb +2 -2
  59. data/lib/write_xlsx/package/rich_value.rb +4 -2
  60. data/lib/write_xlsx/package/rich_value_rel.rb +2 -2
  61. data/lib/write_xlsx/package/rich_value_structure.rb +2 -2
  62. data/lib/write_xlsx/package/rich_value_types.rb +3 -3
  63. data/lib/write_xlsx/package/shared_strings.rb +2 -2
  64. data/lib/write_xlsx/package/styles.rb +13 -9
  65. data/lib/write_xlsx/package/table.rb +8 -2
  66. data/lib/write_xlsx/package/theme.rb +0 -3
  67. data/lib/write_xlsx/package/vml.rb +2 -2
  68. data/lib/write_xlsx/page_setup.rb +192 -0
  69. data/lib/write_xlsx/shape.rb +97 -100
  70. data/lib/write_xlsx/sheets.rb +9 -4
  71. data/lib/write_xlsx/sparkline.rb +2 -2
  72. data/lib/write_xlsx/utility/cell_reference.rb +124 -0
  73. data/lib/write_xlsx/utility/chart_formatting.rb +262 -0
  74. data/lib/write_xlsx/utility/common.rb +44 -0
  75. data/lib/write_xlsx/utility/date_time.rb +113 -0
  76. data/lib/write_xlsx/utility/dimensions.rb +40 -0
  77. data/lib/write_xlsx/utility/drawing.rb +136 -0
  78. data/lib/write_xlsx/utility/rich_text.rb +184 -0
  79. data/lib/write_xlsx/utility/sheetname_quoting.rb +73 -0
  80. data/lib/write_xlsx/utility/string_width.rb +45 -0
  81. data/lib/write_xlsx/utility/url.rb +27 -0
  82. data/lib/write_xlsx/utility/xml_primitives.rb +32 -0
  83. data/lib/write_xlsx/version.rb +1 -1
  84. data/lib/write_xlsx/workbook/chart_data.rb +188 -0
  85. data/lib/write_xlsx/workbook/format_preparation.rb +199 -0
  86. data/lib/write_xlsx/workbook/initialization.rb +223 -0
  87. data/lib/write_xlsx/workbook/package_preparation.rb +231 -0
  88. data/lib/write_xlsx/workbook/workbook_writer.rb +164 -0
  89. data/lib/write_xlsx/workbook.rb +143 -981
  90. data/lib/write_xlsx/worksheet/asset_manager.rb +60 -0
  91. data/lib/write_xlsx/worksheet/autofilter.rb +390 -0
  92. data/lib/write_xlsx/worksheet/cell_data.rb +13 -6
  93. data/lib/write_xlsx/worksheet/cell_data_manager.rb +47 -0
  94. data/lib/write_xlsx/worksheet/cell_data_store.rb +61 -0
  95. data/lib/write_xlsx/worksheet/columns.rb +204 -0
  96. data/lib/write_xlsx/worksheet/comments_support.rb +61 -0
  97. data/lib/write_xlsx/worksheet/conditional_formats.rb +30 -0
  98. data/lib/write_xlsx/worksheet/data_validation.rb +9 -1
  99. data/lib/write_xlsx/worksheet/data_writing.rb +1017 -0
  100. data/lib/write_xlsx/worksheet/drawing_methods.rb +308 -0
  101. data/lib/write_xlsx/worksheet/drawing_preparation.rb +290 -0
  102. data/lib/write_xlsx/worksheet/drawing_relations.rb +76 -0
  103. data/lib/write_xlsx/worksheet/drawing_xml_writer.rb +50 -0
  104. data/lib/write_xlsx/worksheet/formatting.rb +418 -0
  105. data/lib/write_xlsx/worksheet/hyperlink.rb +9 -1
  106. data/lib/write_xlsx/worksheet/initialization.rb +146 -0
  107. data/lib/write_xlsx/worksheet/panes.rb +64 -0
  108. data/lib/write_xlsx/worksheet/print_options.rb +72 -0
  109. data/lib/write_xlsx/worksheet/protection.rb +65 -0
  110. data/lib/write_xlsx/worksheet/rich_text_helpers.rb +78 -0
  111. data/lib/write_xlsx/worksheet/row_col_sizing.rb +69 -0
  112. data/lib/write_xlsx/worksheet/rows.rb +84 -0
  113. data/lib/write_xlsx/worksheet/selection.rb +41 -0
  114. data/lib/write_xlsx/worksheet/xml_writer.rb +1246 -0
  115. data/lib/write_xlsx/worksheet.rb +376 -4530
  116. metadata +66 -4
  117. data/lib/write_xlsx/utility.rb +0 -986
  118. data/lib/write_xlsx/worksheet/page_setup.rb +0 -192
@@ -1,170 +1,57 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
3
 
4
+ ###############################################################################
5
+ #
6
+ # Chart
7
+ #
8
+ # The Chart class acts as a facade coordinating chart configuration,
9
+ # data management and XML generation.
10
+ #
11
+ # Responsibilities are delegated to specialized modules:
12
+ #
13
+ # Initialization - chart setup and defaults
14
+ # Settings - public chart configuration API
15
+ # SeriesData - series and formula bookkeeping
16
+ # XmlWriter - core chart XML assembly
17
+ # AxisWriter - axis related XML
18
+ # SeriesWriter - series XML generation
19
+ # FormattingWriter - formatting and label XML
20
+ #
21
+ ###############################################################################
22
+
4
23
  require 'write_xlsx/package/xml_writer_simple'
5
24
  require 'write_xlsx/gradient'
6
25
  require 'write_xlsx/chart/legend'
7
- require 'write_xlsx/utility'
26
+ require 'write_xlsx/utility/common'
27
+ require 'write_xlsx/utility/chart_formatting'
28
+ require 'write_xlsx/utility/rich_text'
8
29
  require 'write_xlsx/chart/axis'
9
30
  require 'write_xlsx/chart/caption'
10
31
  require 'write_xlsx/chart/series'
32
+ require 'write_xlsx/chart/chart_area'
33
+ require 'write_xlsx/chart/table'
34
+ require 'write_xlsx/chart/initialization'
35
+ require 'write_xlsx/chart/settings'
36
+ require 'write_xlsx/chart/series_data'
37
+ require 'write_xlsx/chart/xml_writer'
38
+ require 'write_xlsx/chart/axis_writer'
39
+ require 'write_xlsx/chart/series_writer'
40
+ require 'write_xlsx/chart/formatting_writer'
11
41
 
12
42
  module Writexlsx
13
- class Table
14
- include Writexlsx::Utility
15
-
16
- attr_reader :horizontal, :vertical, :outline, :show_keys, :font
17
-
18
- def initialize(params = {})
19
- @horizontal = true
20
- @vertical = true
21
- @outline = true
22
- @show_keys = false
23
- @horizontal = params[:horizontal] if params.has_key?(:horizontal)
24
- @vertical = params[:vertical] if params.has_key?(:vertical)
25
- @outline = params[:outline] if params.has_key?(:outline)
26
- @show_keys = params[:show_keys] if params.has_key?(:show_keys)
27
- @font = convert_font_args(params[:font])
28
- end
29
-
30
- attr_writer :palette
31
-
32
- def write_d_table(writer)
33
- @writer = writer
34
- @writer.tag_elements('c:dTable') do
35
- @writer.empty_tag('c:showHorzBorder', attributes) if ptrue?(horizontal)
36
- @writer.empty_tag('c:showVertBorder', attributes) if ptrue?(vertical)
37
- @writer.empty_tag('c:showOutline', attributes) if ptrue?(outline)
38
- @writer.empty_tag('c:showKeys', attributes) if ptrue?(show_keys)
39
- # Write the table font.
40
- write_tx_pr(font) if ptrue?(font)
41
- end
42
- end
43
-
44
- private
45
-
46
- def attributes
47
- [['val', 1]]
48
- end
49
- end
50
-
51
- class ChartArea
52
- include Writexlsx::Utility
53
- include Writexlsx::Gradient
54
-
55
- attr_reader :line, :fill, :pattern, :gradient, :layout
56
-
57
- def initialize(params = {})
58
- @layout = layout_properties(params[:layout])
59
-
60
- # Allow 'border' as a synonym for 'line'.
61
- border = params_to_border(params)
62
-
63
- # Set the line properties for the chartarea.
64
- @line = line_properties(border || params[:line])
65
-
66
- # Set the pattern properties for the series.
67
- @pattern = pattern_properties(params[:pattern])
68
-
69
- # Set the gradient fill properties for the series.
70
- @gradient = gradient_properties(params[:gradient])
71
-
72
- # Map deprecated Spreadsheet::WriteExcel fill colour.
73
- fill = params[:color] ? { color: params[:color] } : params[:fill]
74
- @fill = fill_properties(fill)
75
-
76
- # Pattern fill overrides solid fill.
77
- @fill = nil if ptrue?(@pattern)
78
-
79
- # Gradient fill overrides solid and pattern fills.
80
- if ptrue?(@gradient)
81
- @pattern = nil
82
- @fill = nil
83
- end
84
- end
85
-
86
- private
87
-
88
- def params_to_border(params)
89
- line_weight = params[:line_weight]
90
-
91
- # Map deprecated Spreadsheet::WriteExcel line_weight.
92
- border = params[:border]
93
- border = { width: swe_line_weight(line_weight) } if line_weight
94
-
95
- # Map deprecated Spreadsheet::WriteExcel line_pattern.
96
- if params[:line_pattern]
97
- pattern = swe_line_pattern(params[:line_pattern])
98
- if pattern == 'none'
99
- border = { none: 1 }
100
- else
101
- border[:dash_type] = pattern
102
- end
103
- end
104
-
105
- # Map deprecated Spreadsheet::WriteExcel line colour.
106
- border[:color] = params[:line_color] if params[:line_color]
107
- border
108
- end
109
-
110
- #
111
- # Get the Spreadsheet::WriteExcel line pattern for backward compatibility.
112
- #
113
- def swe_line_pattern(val)
114
- swe_line_pattern_hash[numeric_or_downcase(val)] || 'solid'
115
- end
116
-
117
- def swe_line_pattern_hash
118
- {
119
- 0 => 'solid',
120
- 1 => 'dash',
121
- 2 => 'dot',
122
- 3 => 'dash_dot',
123
- 4 => 'long_dash_dot_dot',
124
- 5 => 'none',
125
- 6 => 'solid',
126
- 7 => 'solid',
127
- 8 => 'solid',
128
- 'solid' => 'solid',
129
- 'dash' => 'dash',
130
- 'dot' => 'dot',
131
- 'dash-dot' => 'dash_dot',
132
- 'dash-dot-dot' => 'long_dash_dot_dot',
133
- 'none' => 'none',
134
- 'dark-gray' => 'solid',
135
- 'medium-gray' => 'solid',
136
- 'light-gray' => 'solid'
137
- }
138
- end
139
-
140
- #
141
- # Get the Spreadsheet::WriteExcel line weight for backward compatibility.
142
- #
143
- def swe_line_weight(val)
144
- swe_line_weight_hash[numeric_or_downcase(val)] || 1
145
- end
146
-
147
- def swe_line_weight_hash
148
- {
149
- 1 => 0.25,
150
- 2 => 1,
151
- 3 => 2,
152
- 4 => 3,
153
- 'hairline' => 0.25,
154
- 'narrow' => 1,
155
- 'medium' => 2,
156
- 'wide' => 3
157
- }
158
- end
159
-
160
- def numeric_or_downcase(val)
161
- val.respond_to?(:coerce) ? val : val.downcase
162
- end
163
- end
164
-
165
43
  class Chart
166
- include Writexlsx::Utility
44
+ include Writexlsx::Utility::Common
45
+ include Writexlsx::Utility::ChartFormatting
46
+ include Writexlsx::Utility::RichText
167
47
  include Writexlsx::Gradient
48
+ include Initialization
49
+ include Settings
50
+ include SeriesData
51
+ include XmlWriter
52
+ include AxisWriter
53
+ include SeriesWriter
54
+ include FormattingWriter
168
55
 
169
56
  attr_accessor :id, :name # :nodoc:
170
57
  attr_writer :index, :palette, :protection # :nodoc:
@@ -177,6 +64,12 @@ module Writexlsx
177
64
  attr_writer :writer # :nodoc:
178
65
  attr_reader :x2_axis, :y2_axis, :axis2_ids # :nodoc:
179
66
 
67
+ ###############################################################################
68
+ #
69
+ # Factory and lifecycle
70
+ #
71
+ ###############################################################################
72
+
180
73
  #
181
74
  # Factory method for returning chart objects based on their class type.
182
75
  #
@@ -212,7 +105,7 @@ module Writexlsx
212
105
  end
213
106
  end
214
107
 
215
- def initialize(subtype) # :nodoc:
108
+ def initialize(subtype) # :nodoc:
216
109
  @writer = Package::XMLWriterSimple.new
217
110
 
218
111
  @subtype = subtype
@@ -235,240 +128,20 @@ module Writexlsx
235
128
  @is_secondary = false
236
129
  end
237
130
 
238
- def set_xml_writer(filename) # :nodoc:
131
+ def set_xml_writer(filename) # :nodoc:
239
132
  @writer.set_xml_writer(filename)
240
133
  end
241
134
 
135
+ ###############################################################################
242
136
  #
243
- # Assemble and write the XML file.
244
- #
245
- def assemble_xml_file # :nodoc:
246
- write_xml_declaration do
247
- # Write the c:chartSpace element.
248
- write_chart_space do
249
- # Write the c:lang element.
250
- write_lang
251
- # Write the c:style element.
252
- write_style
253
- # Write the c:protection element.
254
- write_protection
255
- # Write the c:chart element.
256
- write_chart
257
- # Write the c:spPr element for the chartarea formatting.
258
- write_sp_pr(@chartarea)
259
- # Write the c:printSettings element.
260
- write_print_settings if @embedded
261
- end
262
- end
263
- end
264
-
265
- #
266
- # Add a series and it's properties to a chart.
267
- #
268
- def add_series(params)
269
- # Check that the required input has been specified.
270
- raise "Must specify ':values' in add_series" unless params.has_key?(:values)
271
-
272
- raise "Must specify ':categories' in add_series for this chart type" if @requires_category != 0 && !params.has_key?(:categories)
273
-
274
- raise "The maximum number of series that can be added to an Excel Chart is 255." if @series.size == 255
275
-
276
- @series << Series.new(self, params)
277
-
278
- # Set the secondary axis properties.
279
- x2_axis = params[:x2_axis]
280
- y2_axis = params[:y2_axis]
281
-
282
- # Store secondary status for combined charts.
283
- @is_secondary = true if ptrue?(x2_axis) || ptrue?(y2_axis)
284
-
285
- # Set the gap and overlap for Bar/Column charts.
286
- if params[:gap]
287
- if ptrue?(y2_axis)
288
- @series_gap_2 = params[:gap]
289
- else
290
- @series_gap_1 = params[:gap]
291
- end
292
- end
293
-
294
- # Set the overlap for Bar/Column charts.
295
- if params[:overlap]
296
- if ptrue?(y2_axis)
297
- @series_overlap_2 = params[:overlap]
298
- else
299
- @series_overlap_1 = params[:overlap]
300
- end
301
- end
302
- end
303
-
304
- #
305
- # Set the properties of the x-axis.
306
- #
307
- def set_x_axis(params = {})
308
- @date_category = true if ptrue?(params[:date_axis])
309
- @x_axis.merge_with_hash(params)
310
- end
311
-
312
- #
313
- # Set the properties of the Y-axis.
314
- #
315
- # The set_y_axis() method is used to set properties of the Y axis.
316
- # The properties that can be set are the same as for set_x_axis,
317
- #
318
- def set_y_axis(params = {})
319
- @date_category = true if ptrue?(params[:date_axis])
320
- @y_axis.merge_with_hash(params)
321
- end
322
-
323
- #
324
- # Set the properties of the secondary X-axis.
325
- #
326
- def set_x2_axis(params = {})
327
- @date_category = true if ptrue?(params[:date_axis])
328
- @x2_axis.merge_with_hash(params)
329
- end
330
-
331
- #
332
- # Set the properties of the secondary Y-axis.
333
- #
334
- def set_y2_axis(params = {})
335
- @date_category = true if ptrue?(params[:date_axis])
336
- @y2_axis.merge_with_hash(params)
337
- end
338
-
339
- #
340
- # Set the properties of the chart title.
341
- #
342
- def set_title(params)
343
- @title.merge_with_hash(params)
344
- end
345
-
346
- #
347
- # Set the properties of the chart legend.
348
- #
349
- def set_legend(params)
350
- # Convert the user default properties to internal properties.
351
- legend_properties(params)
352
- end
353
-
354
- #
355
- # Set the properties of the chart plotarea.
356
- #
357
- def set_plotarea(params)
358
- # Convert the user defined properties to internal properties.
359
- @plotarea = ChartArea.new(params)
360
- end
361
-
362
- #
363
- # Set the properties of the chart chartarea.
364
- #
365
- def set_chartarea(params)
366
- # Convert the user defined properties to internal properties.
367
- @chartarea = ChartArea.new(params)
368
- end
369
-
370
- #
371
- # Set on of the 42 built-in Excel chart styles. The default style is 2.
372
- #
373
- def set_style(style_id = 2)
374
- style_id = 2 if style_id < 1 || style_id > 48
375
- @style_id = style_id
376
- end
377
-
378
- #
379
- # Set the option for displaying blank data in a chart. The default is 'gap'.
380
- #
381
- def show_blanks_as(option)
382
- return unless option
383
-
384
- raise "Unknown show_blanks_as() option '#{option}'\n" unless %i[gap zero span].include?(option.to_sym)
385
-
386
- @show_blanks = option
387
- end
388
-
389
- #
390
- # Display data in hidden rows or columns on the chart.
391
- #
392
- def show_hidden_data
393
- @show_hidden_data = true
394
- end
395
-
396
- #
397
- # Set dimensions for scale for the chart.
398
- #
399
- def set_size(params = {})
400
- @width = params[:width] if params[:width]
401
- @height = params[:height] if params[:height]
402
- @x_scale = params[:x_scale] if params[:x_scale]
403
- @y_scale = params[:y_scale] if params[:y_scale]
404
- @x_offset = params[:x_offset] if params[:x_offset]
405
- @y_offset = params[:y_offset] if params[:y_offset]
406
- end
407
-
408
- # Backward compatibility with poorly chosen method name.
409
- alias size set_size
410
-
411
- #
412
- # The set_table method adds a data table below the horizontal axis with the
413
- # data used to plot the chart.
414
- #
415
- def set_table(params = {})
416
- @table = Table.new(params)
417
- @table.palette = @palette
418
- end
419
-
420
- #
421
- # Set properties for the chart up-down bars.
422
- #
423
- def set_up_down_bars(params = {})
424
- # Map border to line.
425
- %i[up down].each do |up_down|
426
- if params[up_down]
427
- params[up_down][:line] = params[up_down][:border] if params[up_down][:border]
428
- else
429
- params[up_down] = {}
430
- end
431
- end
432
-
433
- # Set the up and down bar properties.
434
- @up_down_bars = {
435
- _up: Chartline.new(params[:up]),
436
- _down: Chartline.new(params[:down])
437
- }
438
- end
439
-
440
- #
441
- # Set properties for the chart drop lines.
442
- #
443
- def set_drop_lines(params = {})
444
- @drop_lines = Chartline.new(params)
445
- end
446
-
447
- #
448
- # Set properties for the chart high-low lines.
449
- #
450
- def set_high_low_lines(params = {})
451
- @hi_low_lines = Chartline.new(params)
452
- end
453
-
454
- #
455
- # Add another chart to create a combined chart.
456
- #
457
- def combine(chart)
458
- @combined = chart
459
- end
460
-
461
- #
462
- # Setup the default configuration data for an embedded chart.
137
+ # Chart type writing entry points
463
138
  #
464
- def set_embedded_config_data
465
- @embedded = true
466
- end
139
+ ###############################################################################
467
140
 
468
141
  #
469
142
  # Write the <c:barChart> element.
470
143
  #
471
- def write_bar_chart(params) # :nodoc:
144
+ def write_bar_chart(params) # :nodoc:
472
145
  series = if ptrue?(params[:primary_axes])
473
146
  get_primary_axes_series
474
147
  else
@@ -513,52 +186,11 @@ module Writexlsx
513
186
  end
514
187
  end
515
188
 
189
+ ###############################################################################
516
190
  #
517
- # Switch name and name_formula parameters if required.
518
- #
519
- def process_names(name = nil, name_formula = nil) # :nodoc:
520
- # Name looks like a formula, use it to set name_formula.
521
- if name.respond_to?(:to_ary)
522
- cell = xl_rowcol_to_cell(name[1], name[2], 1, 1)
523
- name_formula = "#{quote_sheetname(name[0])}!#{cell}"
524
- name = ''
525
- elsif name && name =~ /^=[^!]+!\$/
526
- name_formula = name
527
- name = ''
528
- end
529
-
530
- [name, name_formula]
531
- end
532
-
533
- #
534
- # Assign an id to a each unique series formula or title/axis formula. Repeated
535
- # formulas such as for categories get the same id. If the series or title
536
- # has user specified data associated with it then that is also stored. This
537
- # data is used to populate cached Excel data when creating a chart.
538
- # If there is no user defined data then it will be populated by the parent
539
- # workbook in Workbook::_add_chart_data
191
+ # Public chart state
540
192
  #
541
- def data_id(full_formula, data) # :nodoc:
542
- return unless full_formula
543
-
544
- # Strip the leading '=' from the formula.
545
- formula = full_formula.sub(/^=/, '')
546
-
547
- # Store the data id in a hash keyed by the formula and store the data
548
- # in a separate array with the same id.
549
- if @formula_ids.has_key?(formula)
550
- # Formula already seen. Return existing id.
551
- id = @formula_ids[formula]
552
- # Store user defined data if it isn't already there.
553
- @formula_data[id] ||= data
554
- else
555
- # Haven't seen this formula before.
556
- id = @formula_ids[formula] = @formula_data.size
557
- @formula_data << data
558
- end
559
-
560
- id
561
- end
193
+ ###############################################################################
562
194
 
563
195
  def already_inserted?
564
196
  @already_inserted
@@ -568,1156 +200,98 @@ module Writexlsx
568
200
  @is_secondary
569
201
  end
570
202
 
203
+ ###############################################################################
571
204
  #
572
- # Set the option for displaying #N/A as an empty cell in a chart.
205
+ # private helpers
573
206
  #
574
- def show_na_as_empty_cell
575
- @show_na_as_empty = true
576
- end
577
-
207
+ ###############################################################################
578
208
  private
579
209
 
580
- def axis_setup
581
- @axis_ids = []
582
- @axis2_ids = []
583
- @cat_has_num_fmt = false
584
- @requires_category = 0
585
- @cat_axis_position = 'b'
586
- @val_axis_position = 'l'
587
- @horiz_cat_axis = 0
588
- @horiz_val_axis = 1
589
- @x_axis = Axis.new(self)
590
- @y_axis = Axis.new(self)
591
- @x2_axis = Axis.new(self)
592
- @y2_axis = Axis.new(self)
593
- end
594
-
595
- def display_setup
596
- @orientation = 0x0
597
- @width = 480
598
- @height = 288
599
- @x_scale = 1
600
- @y_scale = 1
601
- @x_offset = 0
602
- @y_offset = 0
603
- @legend = Legend.new
604
- @smooth_allowed = 0
605
- @cross_between = 'between'
606
- @date_category = false
607
- @show_blanks = 'gap'
608
- @show_na_as_empty = false
609
- @show_hidden_data = false
610
- @show_crosses = true
611
- end
612
-
210
+ ###############################################################################
613
211
  #
614
- # retun primary/secondary series by :primary_axes flag
212
+ # Core chart XML helpers
615
213
  #
616
- def axes_series(params)
617
- if params[:primary_axes] == 0
618
- secondary_axes_series
619
- else
620
- primary_axes_series
621
- end
622
- end
214
+ ###############################################################################
623
215
 
624
216
  #
625
- # Find the overall type of the data associated with a series.
626
- #
627
- # TODO. Need to handle date type.
217
+ # Write the chart type element. This method should be overridden by the
218
+ # subclasses.
628
219
  #
629
- def get_data_type(data) # :nodoc:
630
- # Check for no data in the series.
631
- return 'none' unless data
632
- return 'none' if data.empty?
633
- return 'multi_str' if data.first.is_a?(Array)
634
-
635
- # If the token isn't a number assume it is a string.
636
- data.each do |token|
637
- next unless token
638
- return 'str' unless token.is_a?(Numeric)
639
- end
640
-
641
- # The series data was all numeric.
642
- 'num'
643
- end
220
+ def write_chart_type; end
644
221
 
645
222
  #
646
- # Returns series which use the primary axes.
223
+ # Write the <c:protection> element.
647
224
  #
648
- def get_primary_axes_series
649
- @series.reject { |s| s.y2_axis }
650
- end
651
- alias primary_axes_series get_primary_axes_series
225
+ def write_protection # :nodoc:
226
+ return if @protection == 0
652
227
 
653
- #
654
- # Returns series which use the secondary axes.
655
- #
656
- def get_secondary_axes_series
657
- @series.select { |s| s.y2_axis }
228
+ @writer.empty_tag('c:protection')
658
229
  end
659
- alias secondary_axes_series get_secondary_axes_series
660
230
 
231
+ ###############################################################################
661
232
  #
662
- # Add a unique ids for primary or secondary axis.
233
+ # Extension list helpers
663
234
  #
664
- def add_axis_ids(params) # :nodoc:
665
- if ptrue?(params[:primary_axes])
666
- @axis_ids += ids
667
- else
668
- @axis2_ids += ids
669
- end
670
- end
235
+ ###############################################################################
236
+
237
+ def write_ext_lst_inverted_fill(color)
238
+ uri = '{6F2FDCE9-48DA-4B69-8628-5D25D57E5C99}'
239
+ xmlns_c_14 =
240
+ 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'
671
241
 
672
- def ids
673
- chart_id = 5001 + @id
674
- axis_count = 1 + @axis2_ids.size + @axis_ids.size
242
+ attributes_1 = [
243
+ ['uri', uri],
244
+ ['xmlns:c14', xmlns_c_14]
245
+ ]
675
246
 
676
- id1 = sprintf('%04d%04d', chart_id, axis_count)
677
- id2 = sprintf('%04d%04d', chart_id, axis_count + 1)
247
+ attributes_2 = [
248
+ ['xmlns:c14', xmlns_c_14]
249
+ ]
678
250
 
679
- [id1, id2]
251
+ @writer.tag_elements('c:extLst') do
252
+ @writer.tag_elements('c:ext', attributes_1) do
253
+ @writer.tag_elements('c14:invertSolidFillFmt') do
254
+ @writer.tag_elements('c14:spPr', attributes_2) do
255
+ write_a_solid_fill(color: color)
256
+ end
257
+ end
258
+ end
259
+ end
680
260
  end
681
261
 
682
262
  #
683
- # Setup the default properties for a chart.
263
+ # Write the <c:extLst> element for the display N/A as empty cell option.
684
264
  #
685
- def set_default_properties # :nodoc:
686
- display_setup
687
- axis_setup
688
- set_axis_defaults
689
-
690
- set_x_axis
691
- set_y_axis
692
-
693
- set_x2_axis
694
- set_y2_axis
695
- end
696
-
697
- def set_axis_defaults
698
- @x_axis.defaults = x_axis_defaults
699
- @y_axis.defaults = y_axis_defaults
700
- @x2_axis.defaults = x2_axis_defaults
701
- @y2_axis.defaults = y2_axis_defaults
702
- end
703
-
704
- def x_axis_defaults
705
- {
706
- num_format: 'General',
707
- major_gridlines: { visible: 0 }
708
- }
709
- end
265
+ def write_ext_lst_display_na
266
+ uri = '{56B9EC1D-385E-4148-901F-78D8002777C0}'
267
+ xmlns_c_16 = 'http://schemas.microsoft.com/office/drawing/2017/03/chart'
710
268
 
711
- def y_axis_defaults
712
- {
713
- num_format: 'General',
714
- major_gridlines: { visible: 1 }
715
- }
716
- end
269
+ attributes1 = [
270
+ ['uri', uri],
271
+ ['xmlns:c16r3', xmlns_c_16]
272
+ ]
717
273
 
718
- def x2_axis_defaults
719
- {
720
- num_format: 'General',
721
- label_position: 'none',
722
- crossing: 'max',
723
- visible: 0
724
- }
725
- end
274
+ attributes2 = [
275
+ ['val', 1]
276
+ ]
726
277
 
727
- def y2_axis_defaults
728
- {
729
- num_format: 'General',
730
- major_gridlines: { visible: 0 },
731
- position: 'right',
732
- visible: 1
733
- }
278
+ @writer.tag_elements('c:extLst') do
279
+ @writer.tag_elements('c:ext', attributes1) do
280
+ @writer.tag_elements('c16r3:dataDisplayOptions16') do
281
+ @writer.empty_tag('c16r3:dispNaAsBlank', attributes2)
282
+ end
283
+ end
284
+ end
734
285
  end
735
286
 
287
+ ###############################################################################
736
288
  #
737
- # Write the <c:chartSpace> element.
289
+ # Legend and title helpers
738
290
  #
739
- def write_chart_space(&block) # :nodoc:
740
- @writer.tag_elements('c:chartSpace', chart_space_attributes, &block)
741
- end
742
-
743
- # for <c:chartSpace> element.
744
- def chart_space_attributes # :nodoc:
745
- schema = 'http://schemas.openxmlformats.org/'
746
- [
747
- ['xmlns:c', "#{schema}drawingml/2006/chart"],
748
- ['xmlns:a', "#{schema}drawingml/2006/main"],
749
- ['xmlns:r', "#{schema}officeDocument/2006/relationships"]
750
- ]
751
- end
291
+ ###############################################################################
752
292
 
753
293
  #
754
- # Write the <c:lang> element.
755
- #
756
- def write_lang # :nodoc:
757
- @writer.empty_tag('c:lang', [%w[val en-US]])
758
- end
759
-
760
- #
761
- # Write the <c:style> element.
762
- #
763
- def write_style # :nodoc:
764
- return if @style_id == 2
765
-
766
- @writer.empty_tag('c:style', [['val', @style_id]])
767
- end
768
-
769
- #
770
- # Write the <c:chart> element.
771
- #
772
- def write_chart # :nodoc:
773
- @writer.tag_elements('c:chart') do
774
- # Write the chart title elements.
775
- if @title.none
776
- # Turn off the title.
777
- write_auto_title_deleted
778
- elsif @title.formula
779
- write_title_formula(@title, nil, nil, @title.layout, @title.overlay)
780
- elsif @title.name
781
- write_title_rich(@title, nil, @title.name_font, @title.layout, @title.overlay)
782
- end
783
-
784
- # Write the c:plotArea element.
785
- write_plot_area
786
- # Write the c:legend element.
787
- write_legend
788
- # Write the c:plotVisOnly element.
789
- write_plot_vis_only
790
-
791
- # Write the c:dispBlanksAs element.
792
- write_disp_blanks_as
793
-
794
- # Write the c:extLst element.
795
- write_ext_lst_display_na if @show_na_as_empty
796
- end
797
- end
798
-
799
- #
800
- # Write the <c:dispBlanksAs> element.
801
- #
802
- def write_disp_blanks_as
803
- return if @show_blanks == 'gap'
804
-
805
- @writer.empty_tag('c:dispBlanksAs', [['val', @show_blanks]])
806
- end
807
-
808
- #
809
- # Write the <c:plotArea> element.
810
- #
811
- def write_plot_area # :nodoc:
812
- second_chart = @combined
813
- @writer.tag_elements('c:plotArea') do
814
- # Write the c:layout element.
815
- write_layout(@plotarea.layout, 'plot')
816
- # Write the subclass chart type elements for primary and secondary axes.
817
- write_chart_type(primary_axes: 1)
818
- write_chart_type(primary_axes: 0)
819
-
820
- # Configure a combined chart if present.
821
- if second_chart
822
-
823
- # Secondary axis has unique id otherwise use same as primary.
824
- second_chart.id = if second_chart.is_secondary?
825
- 1000 + @id
826
- else
827
- @id
828
- end
829
-
830
- # Share the same writer for writing.
831
- second_chart.writer = @writer
832
-
833
- # Share series index with primary chart.
834
- second_chart.series_index = @series_index
835
-
836
- # Write the subclass chart type elements for combined chart.
837
- second_chart.write_chart_type(primary_axes: 1)
838
- second_chart.write_chart_type(primary_axes: 0)
839
- end
840
-
841
- # Write the category and value elements for the primary axes.
842
- params = {
843
- x_axis: @x_axis,
844
- y_axis: @y_axis,
845
- axis_ids: @axis_ids
846
- }
847
-
848
- if @date_category
849
- write_date_axis(params)
850
- else
851
- write_cat_axis(params)
852
- end
853
-
854
- write_val_axis(@x_axis, @y_axis, @axis_ids)
855
-
856
- # Write the category and value elements for the secondary axes.
857
- params = {
858
- x_axis: @x2_axis,
859
- y_axis: @y2_axis,
860
- axis_ids: @axis2_ids
861
- }
862
-
863
- write_val_axis(@x2_axis, @y2_axis, @axis2_ids)
864
-
865
- # Write the secondary axis for the secondary chart.
866
- if second_chart && second_chart.is_secondary?
867
-
868
- params = {
869
- x_axis: second_chart.x2_axis,
870
- y_axis: second_chart.y2_axis,
871
- axis_ids: second_chart.axis2_ids
872
- }
873
-
874
- second_chart.write_val_axis(
875
- second_chart.x2_axis,
876
- second_chart.y2_axis,
877
- second_chart.axis2_ids
878
- )
879
- end
880
-
881
- if @date_category
882
- write_date_axis(params)
883
- else
884
- write_cat_axis(params)
885
- end
886
-
887
- # Write the c:dTable element.
888
- write_d_table
889
-
890
- # Write the c:spPr element for the plotarea formatting.
891
- write_sp_pr(@plotarea)
892
- end
893
- end
894
-
895
- #
896
- # Write the <c:layout> element.
897
- #
898
- def write_layout(layout = nil, type = nil) # :nodoc:
899
- tag = 'c:layout'
900
-
901
- if layout
902
- @writer.tag_elements(tag) { write_manual_layout(layout, type) }
903
- else
904
- @writer.empty_tag(tag)
905
- end
906
- end
907
-
908
- #
909
- # Write the <c:manualLayout> element.
910
- #
911
- def write_manual_layout(layout, type)
912
- @writer.tag_elements('c:manualLayout') do
913
- # Plotarea has a layoutTarget element.
914
- @writer.empty_tag('c:layoutTarget', [%w[val inner]]) if type == 'plot'
915
-
916
- # Set the x, y positions.
917
- @writer.empty_tag('c:xMode', [%w[val edge]])
918
- @writer.empty_tag('c:yMode', [%w[val edge]])
919
- @writer.empty_tag('c:x', [['val', layout[:x]]])
920
- @writer.empty_tag('c:y', [['val', layout[:y]]])
921
-
922
- # For plotarea and legend set the width and height.
923
- if type != 'text'
924
- @writer.empty_tag('c:w', [['val', layout[:width]]])
925
- @writer.empty_tag('c:h', [['val', layout[:height]]])
926
- end
927
- end
928
- end
929
-
930
- #
931
- # Write the chart type element. This method should be overridden by the
932
- # subclasses.
933
- #
934
- def write_chart_type; end
935
-
936
- #
937
- # Write the <c:grouping> element.
938
- #
939
- def write_grouping(val) # :nodoc:
940
- @writer.empty_tag('c:grouping', [['val', val]])
941
- end
942
-
943
- #
944
- # Write the series elements.
945
- #
946
- def write_series(series) # :nodoc:
947
- write_ser(series)
948
- end
949
-
950
- #
951
- # Write the <c:ser> element.
952
- #
953
- def write_ser(series) # :nodoc:
954
- @writer.tag_elements('c:ser') do
955
- write_ser_base(series) do
956
- write_c_invert_if_negative(series.invert_if_negative)
957
- end
958
- # Write the c:cat element.
959
- write_cat(series)
960
- # Write the c:val element.
961
- write_val(series)
962
- # Write the c:smooth element.
963
- write_c_smooth(series.smooth) if ptrue?(@smooth_allowed)
964
- # Write the c:extLst element.
965
- write_ext_lst_inverted_fill(series.inverted_color) if series.inverted_color
966
- end
967
- @series_index += 1
968
- end
969
-
970
- def write_ext_lst_inverted_fill(color)
971
- uri = '{6F2FDCE9-48DA-4B69-8628-5D25D57E5C99}'
972
- xmlns_c_14 =
973
- 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'
974
-
975
- attributes_1 = [
976
- ['uri', uri],
977
- ['xmlns:c14', xmlns_c_14]
978
- ]
979
-
980
- attributes_2 = [
981
- ['xmlns:c14', xmlns_c_14]
982
- ]
983
-
984
- @writer.tag_elements('c:extLst') do
985
- @writer.tag_elements('c:ext', attributes_1) do
986
- @writer.tag_elements('c14:invertSolidFillFmt') do
987
- @writer.tag_elements('c14:spPr', attributes_2) do
988
- write_a_solid_fill(color: color)
989
- end
990
- end
991
- end
992
- end
993
- end
994
-
995
- #
996
- # Write the <c:extLst> element for the display N/A as empty cell option.
997
- #
998
- def write_ext_lst_display_na
999
- uri = '{56B9EC1D-385E-4148-901F-78D8002777C0}'
1000
- xmlns_c_16 = 'http://schemas.microsoft.com/office/drawing/2017/03/chart'
1001
-
1002
- attributes1 = [
1003
- ['uri', uri],
1004
- ['xmlns:c16r3', xmlns_c_16]
1005
- ]
1006
-
1007
- attributes2 = [
1008
- ['val', 1]
1009
- ]
1010
-
1011
- @writer.tag_elements('c:extLst') do
1012
- @writer.tag_elements('c:ext', attributes1) do
1013
- @writer.tag_elements('c16r3:dataDisplayOptions16') do
1014
- @writer.empty_tag('c16r3:dispNaAsBlank', attributes2)
1015
- end
1016
- end
1017
- end
1018
- end
1019
-
1020
- def write_ser_base(series)
1021
- # Write the c:idx element.
1022
- write_idx(@series_index)
1023
- # Write the c:order element.
1024
- write_order(@series_index)
1025
- # Write the series name.
1026
- write_series_name(series)
1027
- # Write the c:spPr element.
1028
- write_sp_pr(series)
1029
- # Write the c:marker element.
1030
- write_marker(series.marker)
1031
-
1032
- yield if block_given?
1033
-
1034
- # Write the c:dPt element.
1035
- write_d_pt(series.points)
1036
- # Write the c:dLbls element.
1037
- write_d_lbls(series.labels)
1038
- # Write the c:trendline element.
1039
- write_trendline(series.trendline)
1040
- # Write the c:errBars element.
1041
- write_error_bars(series.error_bars)
1042
- end
1043
-
1044
- #
1045
- # Write the <c:idx> element.
1046
- #
1047
- def write_idx(val) # :nodoc:
1048
- @writer.empty_tag('c:idx', [['val', val]])
1049
- end
1050
-
1051
- #
1052
- # Write the <c:order> element.
1053
- #
1054
- def write_order(val) # :nodoc:
1055
- @writer.empty_tag('c:order', [['val', val]])
1056
- end
1057
-
1058
- #
1059
- # Write the series name.
1060
- #
1061
- def write_series_name(series) # :nodoc:
1062
- if series.name_formula
1063
- write_tx_formula(series.name_formula, series.name_id)
1064
- elsif series.name
1065
- write_tx_value(series.name)
1066
- end
1067
- end
1068
-
1069
- #
1070
- # Write the <c:cat> element.
1071
- #
1072
- def write_cat(series) # :nodoc:
1073
- formula = series.categories
1074
- data_id = series.cat_data_id
1075
-
1076
- data = @formula_data[data_id] if data_id
1077
-
1078
- # Ignore <c:cat> elements for charts without category values.
1079
- return unless formula
1080
-
1081
- @writer.tag_elements('c:cat') do
1082
- # Check the type of cached data.
1083
- type = get_data_type(data)
1084
- if type == 'str'
1085
- @cat_has_num_fmt = false
1086
- # Write the c:strRef element.
1087
- write_str_ref(formula, data, type)
1088
- elsif type == 'multi_str'
1089
- @cat_has_num_fmt = false
1090
- # Write the c:multiLvLStrRef element.
1091
- write_multi_lvl_str_ref(formula, data)
1092
- else
1093
- @cat_has_num_fmt = true
1094
- # Write the c:numRef element.
1095
- write_num_ref(formula, data, type)
1096
- end
1097
- end
1098
- end
1099
-
1100
- #
1101
- # Write the <c:val> element.
1102
- #
1103
- def write_val(series) # :nodoc:
1104
- write_val_base(series.values, series.val_data_id, 'c:val')
1105
- end
1106
-
1107
- def write_val_base(formula, data_id, tag) # :nodoc:
1108
- data = @formula_data[data_id]
1109
-
1110
- @writer.tag_elements(tag) do
1111
- # Unlike Cat axes data should only be numeric.
1112
-
1113
- # Write the c:numRef element.
1114
- write_num_ref(formula, data, 'num')
1115
- end
1116
- end
1117
-
1118
- #
1119
- # Write the <c:numRef> or <c:strRef> element.
1120
- #
1121
- def write_num_or_str_ref(tag, formula, data, type) # :nodoc:
1122
- @writer.tag_elements(tag) do
1123
- # Write the c:f element.
1124
- write_series_formula(formula)
1125
- if type == 'num'
1126
- # Write the c:numCache element.
1127
- write_num_cache(data)
1128
- elsif type == 'str'
1129
- # Write the c:strCache element.
1130
- write_str_cache(data)
1131
- end
1132
- end
1133
- end
1134
-
1135
- #
1136
- # Write the <c:numRef> element.
1137
- #
1138
- def write_num_ref(formula, data, type) # :nodoc:
1139
- write_num_or_str_ref('c:numRef', formula, data, type)
1140
- end
1141
-
1142
- #
1143
- # Write the <c:strRef> element.
1144
- #
1145
- def write_str_ref(formula, data, type) # :nodoc:
1146
- write_num_or_str_ref('c:strRef', formula, data, type)
1147
- end
1148
-
1149
- #
1150
- # Write the <c:multiLvLStrRef> element.
1151
- #
1152
- def write_multi_lvl_str_ref(formula, data)
1153
- return if data.empty?
1154
-
1155
- @writer.tag_elements('c:multiLvlStrRef') do
1156
- # Write the c:f element.
1157
- write_series_formula(formula)
1158
-
1159
- @writer.tag_elements('c:multiLvlStrCache') do
1160
- # Write the c:ptCount element.
1161
- write_pt_count(data.last.size)
1162
-
1163
- # Write the data arrays in reverse order.
1164
- data.reverse.each do |arr|
1165
- @writer.tag_elements('c:lvl') do
1166
- # Write the c:pt element.
1167
- arr.each_with_index { |a, i| write_pt(i, a) }
1168
- end
1169
- end
1170
- end
1171
- end
1172
- end
1173
-
1174
- #
1175
- # Write the <c:numLit> element for literal number list elements.
1176
- #
1177
- def write_num_lit(data)
1178
- write_num_base('c:numLit', data)
1179
- end
1180
-
1181
- #
1182
- # Write the <c:f> element.
1183
- #
1184
- def write_series_formula(formula) # :nodoc:
1185
- # Strip the leading '=' from the formula.
1186
- formula = formula.sub(/^=/, '')
1187
-
1188
- @writer.data_element('c:f', formula)
1189
- end
1190
-
1191
- #
1192
- # Write the <c:axId> elements for the primary or secondary axes.
1193
- #
1194
- def write_axis_ids(params)
1195
- # Generate the axis ids.
1196
- add_axis_ids(params)
1197
-
1198
- if params[:primary_axes] == 0
1199
- # Write the axis ids for the secondary axes.
1200
- write_axis_id(@axis2_ids[0])
1201
- write_axis_id(@axis2_ids[1])
1202
- else
1203
- # Write the axis ids for the primary axes.
1204
- write_axis_id(@axis_ids[0])
1205
- write_axis_id(@axis_ids[1])
1206
- end
1207
- end
1208
-
1209
- #
1210
- # Write the <c:axId> element.
1211
- #
1212
- def write_axis_id(val) # :nodoc:
1213
- @writer.empty_tag('c:axId', [['val', val]])
1214
- end
1215
-
1216
- #
1217
- # Write the <c:catAx> element. Usually the X axis.
1218
- #
1219
- def write_cat_axis(params) # :nodoc:
1220
- x_axis = params[:x_axis]
1221
- y_axis = params[:y_axis]
1222
- axis_ids = params[:axis_ids]
1223
-
1224
- # if there are no axis_ids then we don't need to write this element
1225
- return unless axis_ids
1226
- return if axis_ids.empty?
1227
-
1228
- position = @cat_axis_position
1229
- is_y_axis = @horiz_cat_axis
1230
-
1231
- # Overwrite the default axis position with a user supplied value.
1232
- position = x_axis.position || position
1233
-
1234
- @writer.tag_elements('c:catAx') do
1235
- write_axis_id(axis_ids[0])
1236
- # Write the c:scaling element.
1237
- write_scaling(x_axis.reverse)
1238
-
1239
- write_delete(1) unless ptrue?(x_axis.visible)
1240
-
1241
- # Write the c:axPos element.
1242
- write_axis_pos(position, y_axis.reverse)
1243
-
1244
- # Write the c:majorGridlines element.
1245
- write_major_gridlines(x_axis.major_gridlines)
1246
-
1247
- # Write the c:minorGridlines element.
1248
- write_minor_gridlines(x_axis.minor_gridlines)
1249
-
1250
- # Write the axis title elements.
1251
- if x_axis.formula
1252
- write_title_formula(x_axis, is_y_axis, @x_axis, x_axis.layout)
1253
- elsif x_axis.name
1254
- write_title_rich(x_axis, is_y_axis, x_axis.name_font, x_axis.layout)
1255
- end
1256
-
1257
- # Write the c:numFmt element.
1258
- write_cat_number_format(x_axis)
1259
-
1260
- # Write the c:majorTickMark element.
1261
- write_major_tick_mark(x_axis.major_tick_mark)
1262
-
1263
- # Write the c:minorTickMark element.
1264
- write_minor_tick_mark(x_axis.minor_tick_mark)
1265
-
1266
- # Write the c:tickLblPos element.
1267
- write_tick_label_pos(x_axis.label_position)
1268
-
1269
- # Write the c:spPr element for the axis line.
1270
- write_sp_pr(x_axis)
1271
-
1272
- # Write the axis font elements.
1273
- write_axis_font(x_axis.num_font)
1274
-
1275
- # Write the c:crossAx element.
1276
- write_cross_axis(axis_ids[1])
1277
-
1278
- write_crossing(y_axis.crossing) if @show_crosses || ptrue?(x_axis.visible)
1279
- # Write the c:auto element.
1280
- write_auto(1) unless x_axis.text_axis
1281
- # Write the c:labelAlign element.
1282
- write_label_align(x_axis.label_align)
1283
- # Write the c:labelOffset element.
1284
- write_label_offset(100)
1285
- # Write the c:tickLblSkip element.
1286
- write_tick_lbl_skip(x_axis.interval_unit)
1287
- # Write the c:tickMarkSkip element.
1288
- write_tick_mark_skip(x_axis.interval_tick)
1289
- end
1290
- end
1291
-
1292
- #
1293
- # Write the <c:valAx> element. Usually the Y axis.
1294
- #
1295
- def write_val_axis(x_axis, y_axis, axis_ids, position = nil)
1296
- return unless axis_ids && !axis_ids.empty?
1297
-
1298
- write_val_axis_base(
1299
- x_axis, y_axis,
1300
- axis_ids[0],
1301
- axis_ids[1],
1302
- y_axis.position || position || @val_axis_position
1303
- )
1304
- end
1305
- public :write_val_axis
1306
-
1307
- def write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position) # :nodoc:
1308
- @writer.tag_elements('c:valAx') do
1309
- write_axis_id(axis_ids_1)
1310
-
1311
- # Write the c:scaling element.
1312
- write_scaling_with_param(y_axis)
1313
-
1314
- write_delete(1) unless ptrue?(y_axis.visible)
1315
-
1316
- # Write the c:axPos element.
1317
- write_axis_pos(position, x_axis.reverse)
1318
-
1319
- # Write the c:majorGridlines element.
1320
- write_major_gridlines(y_axis.major_gridlines)
1321
-
1322
- # Write the c:minorGridlines element.
1323
- write_minor_gridlines(y_axis.minor_gridlines)
1324
-
1325
- # Write the axis title elements.
1326
- if y_axis.formula
1327
- write_title_formula(y_axis, @horiz_val_axis, nil, y_axis.layout)
1328
- elsif y_axis.name
1329
- write_title_rich(y_axis, @horiz_val_axis, y_axis.name_font, y_axis.layout)
1330
- end
1331
-
1332
- # Write the c:numberFormat element.
1333
- write_number_format(y_axis)
1334
-
1335
- # Write the c:majorTickMark element.
1336
- write_major_tick_mark(y_axis.major_tick_mark)
1337
-
1338
- # Write the c:minorTickMark element.
1339
- write_minor_tick_mark(y_axis.minor_tick_mark)
1340
-
1341
- # Write the c:tickLblPos element.
1342
- write_tick_label_pos(y_axis.label_position)
1343
-
1344
- # Write the c:spPr element for the axis line.
1345
- write_sp_pr(y_axis)
1346
-
1347
- # Write the axis font elements.
1348
- write_axis_font(y_axis.num_font)
1349
-
1350
- # Write the c:crossAx element.
1351
- write_cross_axis(axis_ids_0)
1352
-
1353
- write_crossing(x_axis.crossing)
1354
-
1355
- # Write the c:crossBetween element.
1356
- write_cross_between(x_axis.position_axis)
1357
-
1358
- # Write the c:majorUnit element.
1359
- write_c_major_unit(y_axis.major_unit)
1360
-
1361
- # Write the c:minorUnit element.
1362
- write_c_minor_unit(y_axis.minor_unit)
1363
-
1364
- # Write the c:dispUnits element.
1365
- write_disp_units(y_axis.display_units, y_axis.display_units_visible)
1366
- end
1367
- end
1368
-
1369
- #
1370
- # Write the <c:dateAx> element. Usually the X axis.
1371
- #
1372
- def write_date_axis(params) # :nodoc:
1373
- x_axis = params[:x_axis]
1374
- y_axis = params[:y_axis]
1375
- axis_ids = params[:axis_ids]
1376
-
1377
- return unless axis_ids && !axis_ids.empty?
1378
-
1379
- position = @cat_axis_position
1380
-
1381
- # Overwrite the default axis position with a user supplied value.
1382
- position = x_axis.position || position
1383
-
1384
- @writer.tag_elements('c:dateAx') do
1385
- write_axis_id(axis_ids[0])
1386
- # Write the c:scaling element.
1387
- write_scaling_with_param(x_axis)
1388
-
1389
- write_delete(1) unless ptrue?(x_axis.visible)
1390
-
1391
- # Write the c:axPos element.
1392
- write_axis_pos(position, y_axis.reverse)
1393
-
1394
- # Write the c:majorGridlines element.
1395
- write_major_gridlines(x_axis.major_gridlines)
1396
-
1397
- # Write the c:minorGridlines element.
1398
- write_minor_gridlines(x_axis.minor_gridlines)
1399
-
1400
- # Write the axis title elements.
1401
- if x_axis.formula
1402
- write_title_formula(x_axis, nil, nil, x_axis.layout)
1403
- elsif x_axis.name
1404
- write_title_rich(x_axis, nil, x_axis.name_font, x_axis.layout)
1405
- end
1406
- # Write the c:numFmt element.
1407
- write_number_format(x_axis)
1408
- # Write the c:majorTickMark element.
1409
- write_major_tick_mark(x_axis.major_tick_mark)
1410
-
1411
- # Write the c:tickLblPos element.
1412
- write_tick_label_pos(x_axis.label_position)
1413
- # Write the c:spPr element for the axis line.
1414
- write_sp_pr(x_axis)
1415
- # Write the font elements.
1416
- write_axis_font(x_axis.num_font)
1417
- # Write the c:crossAx element.
1418
- write_cross_axis(axis_ids[1])
1419
-
1420
- write_crossing(y_axis.crossing) if @show_crosses || ptrue?(x_axis.visible)
1421
-
1422
- # Write the c:auto element.
1423
- write_auto(1)
1424
- # Write the c:labelOffset element.
1425
- write_label_offset(100)
1426
- # Write the c:tickLblSkip element.
1427
- write_tick_lbl_skip(x_axis.interval_unit)
1428
- # Write the c:tickMarkSkip element.
1429
- write_tick_mark_skip(x_axis.interval_tick)
1430
- # Write the c:majorUnit element.
1431
- write_c_major_unit(x_axis.major_unit)
1432
- # Write the c:majorTimeUnit element.
1433
- write_c_major_time_unit(x_axis.major_unit_type) if x_axis.major_unit
1434
- # Write the c:minorUnit element.
1435
- write_c_minor_unit(x_axis.minor_unit)
1436
- # Write the c:minorTimeUnit element.
1437
- write_c_minor_time_unit(x_axis.minor_unit_type) if x_axis.minor_unit
1438
- end
1439
- end
1440
-
1441
- def write_crossing(crossing)
1442
- # Note, the category crossing comes from the value axis.
1443
- if [nil, 'max', 'min'].include?(crossing)
1444
- # Write the c:crosses element.
1445
- write_crosses(crossing)
1446
- else
1447
- # Write the c:crossesAt element.
1448
- write_c_crosses_at(crossing)
1449
- end
1450
- end
1451
-
1452
- def write_scaling_with_param(param)
1453
- write_scaling(
1454
- param.reverse,
1455
- param.min,
1456
- param.max,
1457
- param.log_base
1458
- )
1459
- end
1460
-
1461
- #
1462
- # Write the <c:scaling> element.
1463
- #
1464
- def write_scaling(reverse, min = nil, max = nil, log_base = nil) # :nodoc:
1465
- @writer.tag_elements('c:scaling') do
1466
- # Write the c:logBase element.
1467
- write_c_log_base(log_base)
1468
- # Write the c:orientation element.
1469
- write_orientation(reverse)
1470
- # Write the c:max element.
1471
- write_c_max(max)
1472
- # Write the c:min element.
1473
- write_c_min(min)
1474
- end
1475
- end
1476
-
1477
- #
1478
- # Write the <c:logBase> element.
1479
- #
1480
- def write_c_log_base(val) # :nodoc:
1481
- return unless ptrue?(val)
1482
-
1483
- @writer.empty_tag('c:logBase', [['val', val]])
1484
- end
1485
-
1486
- #
1487
- # Write the <c:orientation> element.
1488
- #
1489
- def write_orientation(reverse = nil) # :nodoc:
1490
- val = ptrue?(reverse) ? 'maxMin' : 'minMax'
1491
-
1492
- @writer.empty_tag('c:orientation', [['val', val]])
1493
- end
1494
-
1495
- #
1496
- # Write the <c:max> element.
1497
- #
1498
- def write_c_max(max = nil) # :nodoc:
1499
- @writer.empty_tag('c:max', [['val', max]]) if max
1500
- end
1501
-
1502
- #
1503
- # Write the <c:min> element.
1504
- #
1505
- def write_c_min(min = nil) # :nodoc:
1506
- @writer.empty_tag('c:min', [['val', min]]) if min
1507
- end
1508
-
1509
- #
1510
- # Write the <c:axPos> element.
1511
- #
1512
- def write_axis_pos(val, reverse = false) # :nodoc:
1513
- if reverse
1514
- val = 'r' if val == 'l'
1515
- val = 't' if val == 'b'
1516
- end
1517
-
1518
- @writer.empty_tag('c:axPos', [['val', val]])
1519
- end
1520
-
1521
- #
1522
- # Write the <c:numberFormat> element. Note: It is assumed that if a user
1523
- # defined number format is supplied (i.e., non-default) then the sourceLinked
1524
- # attribute is 0. The user can override this if required.
1525
- #
1526
-
1527
- def write_number_format(axis) # :nodoc:
1528
- axis.write_number_format(@writer)
1529
- end
1530
-
1531
- #
1532
- # Write the <c:numFmt> element. Special case handler for category axes which
1533
- # don't always have a number format.
1534
- #
1535
- def write_cat_number_format(axis)
1536
- axis.write_cat_number_format(@writer, @cat_has_num_fmt)
1537
- end
1538
-
1539
- #
1540
- # Write the <c:numberFormat> element for data labels.
1541
- #
1542
- def write_data_label_number_format(format_code)
1543
- source_linked = 0
1544
-
1545
- attributes = [
1546
- ['formatCode', format_code],
1547
- ['sourceLinked', source_linked]
1548
- ]
1549
-
1550
- @writer.empty_tag('c:numFmt', attributes)
1551
- end
1552
-
1553
- #
1554
- # Write the <c:majorTickMark> element.
1555
- #
1556
- def write_major_tick_mark(val)
1557
- return unless ptrue?(val)
1558
-
1559
- @writer.empty_tag('c:majorTickMark', [['val', val]])
1560
- end
1561
-
1562
- #
1563
- # Write the <c:minorTickMark> element.
1564
- #
1565
- def write_minor_tick_mark(val)
1566
- return unless ptrue?(val)
1567
-
1568
- @writer.empty_tag('c:minorTickMark', [['val', val]])
1569
- end
1570
-
1571
- #
1572
- # Write the <c:tickLblPos> element.
1573
- #
1574
- def write_tick_label_pos(val) # :nodoc:
1575
- val ||= 'nextTo'
1576
- val = 'nextTo' if val == 'next_to'
1577
-
1578
- @writer.empty_tag('c:tickLblPos', [['val', val]])
1579
- end
1580
-
1581
- #
1582
- # Write the <c:crossAx> element.
1583
- #
1584
- def write_cross_axis(val = 'autoZero') # :nodoc:
1585
- @writer.empty_tag('c:crossAx', [['val', val]])
1586
- end
1587
-
1588
- #
1589
- # Write the <c:crosses> element.
1590
- #
1591
- def write_crosses(val) # :nodoc:
1592
- val ||= 'autoZero'
1593
-
1594
- @writer.empty_tag('c:crosses', [['val', val]])
1595
- end
1596
-
1597
- #
1598
- # Write the <c:crossesAt> element.
1599
- #
1600
- def write_c_crosses_at(val) # :nodoc:
1601
- @writer.empty_tag('c:crossesAt', [['val', val]])
1602
- end
1603
-
1604
- #
1605
- # Write the <c:auto> element.
1606
- #
1607
- def write_auto(val) # :nodoc:
1608
- @writer.empty_tag('c:auto', [['val', val]])
1609
- end
1610
-
1611
- #
1612
- # Write the <c:labelAlign> element.
1613
- #
1614
- def write_label_align(val) # :nodoc:
1615
- val ||= 'ctr'
1616
- if val == 'right'
1617
- val = 'r'
1618
- elsif val == 'left'
1619
- val = 'l'
1620
- end
1621
- @writer.empty_tag('c:lblAlgn', [['val', val]])
1622
- end
1623
-
1624
- #
1625
- # Write the <c:labelOffset> element.
1626
- #
1627
- def write_label_offset(val) # :nodoc:
1628
- @writer.empty_tag('c:lblOffset', [['val', val]])
1629
- end
1630
-
1631
- #
1632
- # Write the <c:tickLblSkip> element.
1633
- #
1634
- def write_tick_lbl_skip(val) # :nodoc:
1635
- return unless val
1636
-
1637
- @writer.empty_tag('c:tickLblSkip', [['val', val]])
1638
- end
1639
-
1640
- #
1641
- # Write the <c:tickMarkSkip> element.
1642
- #
1643
- def write_tick_mark_skip(val) # :nodoc:
1644
- return unless val
1645
-
1646
- @writer.empty_tag('c:tickMarkSkip', [['val', val]])
1647
- end
1648
-
1649
- #
1650
- # Write the <c:majorGridlines> element.
1651
- #
1652
- def write_major_gridlines(gridlines) # :nodoc:
1653
- write_gridlines_base('c:majorGridlines', gridlines)
1654
- end
1655
-
1656
- #
1657
- # Write the <c:minorGridlines> element.
1658
- #
1659
- def write_minor_gridlines(gridlines) # :nodoc:
1660
- write_gridlines_base('c:minorGridlines', gridlines)
1661
- end
1662
-
1663
- def write_gridlines_base(tag, gridlines) # :nodoc:
1664
- return unless gridlines
1665
- return if gridlines.respond_to?(:[]) and !ptrue?(gridlines[:_visible])
1666
-
1667
- if gridlines.line_defined?
1668
- @writer.tag_elements(tag) { write_sp_pr(gridlines) }
1669
- else
1670
- @writer.empty_tag(tag)
1671
- end
1672
- end
1673
-
1674
- #
1675
- # Write the <c:crossBetween> element.
1676
- #
1677
- def write_cross_between(val = nil) # :nodoc:
1678
- val ||= @cross_between
1679
-
1680
- @writer.empty_tag('c:crossBetween', [['val', val]])
1681
- end
1682
-
1683
- #
1684
- # Write the <c:majorUnit> element.
1685
- #
1686
- def write_c_major_unit(val = nil) # :nodoc:
1687
- return unless val
1688
-
1689
- @writer.empty_tag('c:majorUnit', [['val', val]])
1690
- end
1691
-
1692
- #
1693
- # Write the <c:minorUnit> element.
1694
- #
1695
- def write_c_minor_unit(val = nil) # :nodoc:
1696
- return unless val
1697
-
1698
- @writer.empty_tag('c:minorUnit', [['val', val]])
1699
- end
1700
-
1701
- #
1702
- # Write the <c:majorTimeUnit> element.
1703
- #
1704
- def write_c_major_time_unit(val) # :nodoc:
1705
- val ||= 'days'
1706
-
1707
- @writer.empty_tag('c:majorTimeUnit', [['val', val]])
1708
- end
1709
-
1710
- #
1711
- # Write the <c:minorTimeUnit> element.
1712
- #
1713
- def write_c_minor_time_unit(val) # :nodoc:
1714
- val ||= 'days'
1715
-
1716
- @writer.empty_tag('c:minorTimeUnit', [['val', val]])
1717
- end
1718
-
1719
- #
1720
- # Write the <c:legend> element.
294
+ # Write the <c:legend> element.
1721
295
  #
1722
296
  def write_legend # :nodoc:
1723
297
  position = @legend.position.sub(/^overlay_/, '')
@@ -1752,373 +326,120 @@ module Writexlsx
1752
326
  end
1753
327
 
1754
328
  #
1755
- # Write the <c:legendPos> element.
1756
- #
1757
- def write_legend_pos(val) # :nodoc:
1758
- @writer.empty_tag('c:legendPos', [['val', val]])
1759
- end
1760
-
1761
- #
1762
- # Write the <c:legendEntry> element.
1763
- #
1764
- def write_legend_entry(index) # :nodoc:
1765
- @writer.tag_elements('c:legendEntry') do
1766
- # Write the c:idx element.
1767
- write_idx(index)
1768
- # Write the c:delete element.
1769
- write_delete(1)
1770
- end
1771
- end
1772
-
1773
- #
1774
- # Write the <c:overlay> element.
1775
- #
1776
- def write_overlay # :nodoc:
1777
- @writer.empty_tag('c:overlay', [['val', 1]])
1778
- end
1779
-
1780
- #
1781
- # Write the <c:plotVisOnly> element.
1782
- #
1783
- def write_plot_vis_only # :nodoc:
1784
- val = 1
1785
-
1786
- # Ignore this element if we are plotting hidden data.
1787
- return if @show_hidden_data
1788
-
1789
- @writer.empty_tag('c:plotVisOnly', [['val', val]])
1790
- end
1791
-
1792
- #
1793
- # Write the <c:printSettings> element.
1794
- #
1795
- def write_print_settings # :nodoc:
1796
- @writer.tag_elements('c:printSettings') do
1797
- # Write the c:headerFooter element.
1798
- write_header_footer
1799
- # Write the c:pageMargins element.
1800
- write_page_margins
1801
- # Write the c:pageSetup element.
1802
- write_page_setup
1803
- end
1804
- end
1805
-
1806
- #
1807
- # Write the <c:headerFooter> element.
1808
- #
1809
- def write_header_footer # :nodoc:
1810
- @writer.empty_tag('c:headerFooter')
1811
- end
1812
-
1813
- #
1814
- # Write the <c:pageMargins> element.
1815
- #
1816
- def write_page_margins # :nodoc:
1817
- attributes = [
1818
- ['b', 0.75],
1819
- ['l', 0.7],
1820
- ['r', 0.7],
1821
- ['t', 0.75],
1822
- ['header', 0.3],
1823
- ['footer', 0.3]
1824
- ]
1825
-
1826
- @writer.empty_tag('c:pageMargins', attributes)
1827
- end
1828
-
1829
- #
1830
- # Write the <c:pageSetup> element.
1831
- #
1832
- def write_page_setup # :nodoc:
1833
- @writer.empty_tag('c:pageSetup')
1834
- end
1835
-
1836
- #
1837
- # Write the <c:autoTitleDeleted> element.
1838
- #
1839
- def write_auto_title_deleted
1840
- attributes = [['val', 1]]
1841
-
1842
- @writer.empty_tag('c:autoTitleDeleted', attributes)
1843
- end
1844
-
1845
- #
1846
- # Write the <c:title> element for a rich string.
1847
- #
1848
- def write_title_rich(title, is_y_axis, font, layout, overlay = nil) # :nodoc:
1849
- @writer.tag_elements('c:title') do
1850
- # Write the c:tx element.
1851
- write_tx_rich(title, is_y_axis, font)
1852
- # Write the c:layout element.
1853
- write_layout(layout, 'text')
1854
- # Write the c:overlay element.
1855
- write_overlay if overlay
1856
- end
1857
- end
1858
-
1859
- #
1860
- # Write the <c:title> element for a rich string.
1861
- #
1862
- def write_title_formula(title, is_y_axis = nil, axis = nil, layout = nil, overlay = nil) # :nodoc:
1863
- @writer.tag_elements('c:title') do
1864
- # Write the c:tx element.
1865
- write_tx_formula(title.formula, axis ? axis.data_id : title.data_id)
1866
- # Write the c:layout element.
1867
- write_layout(layout, 'text')
1868
- # Write the c:overlay element.
1869
- write_overlay if overlay
1870
- # Write the c:txPr element.
1871
- write_tx_pr(axis ? axis.name_font : title.name_font, is_y_axis)
1872
- end
1873
- end
1874
-
1875
- #
1876
- # Write the <c:tx> element.
1877
- #
1878
- def write_tx_rich(title, is_y_axis, font) # :nodoc:
1879
- @writer.tag_elements('c:tx') do
1880
- write_rich(title, font, is_y_axis)
1881
- end
1882
- end
1883
-
1884
- #
1885
- # Write the <c:tx> element with a simple value such as for series names.
1886
- #
1887
- def write_tx_value(title) # :nodoc:
1888
- @writer.tag_elements('c:tx') { write_v(title) }
1889
- end
1890
-
1891
- #
1892
- # Write the <c:tx> element.
1893
- #
1894
- def write_tx_formula(title, data_id) # :nodoc:
1895
- data = @formula_data[data_id] if data_id
1896
-
1897
- @writer.tag_elements('c:tx') { write_str_ref(title, data, 'str') }
1898
- end
1899
-
1900
- #
1901
- # Write the <c:rich> element.
1902
- #
1903
- def write_rich(title, font, is_y_axis, ignore_rich_pr = false) # :nodoc:
1904
- rotation = nil
1905
-
1906
- rotation = font[:_rotation] if font && font[:_rotation]
1907
- @writer.tag_elements('c:rich') do
1908
- # Write the a:bodyPr element.
1909
- write_a_body_pr(rotation, is_y_axis)
1910
- # Write the a:lstStyle element.
1911
- write_a_lst_style
1912
- # Write the a:p element.
1913
- write_a_p_rich(title, font, ignore_rich_pr)
1914
- end
1915
- end
1916
-
1917
- #
1918
- # Write the <a:p> element for rich string titles.
1919
- #
1920
- def write_a_p_rich(title, font, ignore_rich_pr) # :nodoc:
1921
- @writer.tag_elements('a:p') do
1922
- # Write the a:pPr element.
1923
- write_a_p_pr_rich(font) unless ignore_rich_pr
1924
- # Write the a:r element.
1925
- write_a_r(title, font)
1926
- end
1927
- end
1928
-
1929
- #
1930
- # Write the <a:pPr> element for rich string titles.
1931
- #
1932
- def write_a_p_pr_rich(font) # :nodoc:
1933
- @writer.tag_elements('a:pPr') { write_a_def_rpr(font) }
1934
- end
1935
-
1936
- #
1937
- # Write the <a:r> element.
1938
- #
1939
- def write_a_r(title, font) # :nodoc:
1940
- @writer.tag_elements('a:r') do
1941
- # Write the a:rPr element.
1942
- write_a_r_pr(font)
1943
- # Write the a:t element.
1944
- write_a_t(title.respond_to?(:name) ? title.name : title)
1945
- end
1946
- end
1947
-
1948
- #
1949
- # Write the <a:rPr> element.
1950
- #
1951
- def write_a_r_pr(font) # :nodoc:
1952
- attributes = [%w[lang en-US]]
1953
- attr_font = get_font_style_attributes(font)
1954
- attributes += attr_font unless attr_font.empty?
1955
-
1956
- write_def_rpr_r_pr_common(font, attributes, 'a:rPr')
1957
- end
1958
-
1959
- #
1960
- # Write the <a:t> element.
1961
- #
1962
- def write_a_t(title) # :nodoc:
1963
- @writer.data_element('a:t', title)
1964
- end
1965
-
1966
- #
1967
- # Write the <c:marker> element.
1968
- #
1969
- def write_marker(marker = nil) # :nodoc:
1970
- marker ||= @default_marker
1971
-
1972
- return unless ptrue?(marker)
1973
- return if ptrue?(marker.automatic?)
1974
-
1975
- @writer.tag_elements('c:marker') do
1976
- # Write the c:symbol element.
1977
- write_symbol(marker.type)
1978
- # Write the c:size element.
1979
- size = marker.size
1980
- write_marker_size(size) if ptrue?(size)
1981
- # Write the c:spPr element.
1982
- write_sp_pr(marker)
1983
- end
1984
- end
1985
-
1986
- #
1987
- # Write the <c:marker> element without a sub-element.
329
+ # Write the <c:legendPos> element.
1988
330
  #
1989
- def write_marker_value # :nodoc:
1990
- return unless @default_marker
1991
-
1992
- @writer.empty_tag('c:marker', [['val', 1]])
331
+ def write_legend_pos(val) # :nodoc:
332
+ @writer.empty_tag('c:legendPos', [['val', val]])
1993
333
  end
1994
334
 
1995
335
  #
1996
- # Write the <c:size> element.
336
+ # Write the <c:legendEntry> element.
1997
337
  #
1998
- def write_marker_size(val) # :nodoc:
1999
- @writer.empty_tag('c:size', [['val', val]])
338
+ def write_legend_entry(index) # :nodoc:
339
+ @writer.tag_elements('c:legendEntry') do
340
+ # Write the c:idx element.
341
+ write_idx(index)
342
+ # Write the c:delete element.
343
+ write_delete(1)
344
+ end
2000
345
  end
2001
346
 
2002
347
  #
2003
- # Write the <c:symbol> element.
348
+ # Write the <c:overlay> element.
2004
349
  #
2005
- def write_symbol(val) # :nodoc:
2006
- @writer.empty_tag('c:symbol', [['val', val]])
2007
- end
2008
-
2009
- def has_fill_formatting(element)
2010
- line = series_property(element, :line)
2011
- fill = series_property(element, :fill)
2012
- pattern = series_property(element, :pattern)
2013
- gradient = series_property(element, :gradient)
2014
-
2015
- (line && ptrue?(line[:_defined])) ||
2016
- (fill && ptrue?(fill[:_defined])) || pattern || gradient
350
+ def write_overlay # :nodoc:
351
+ @writer.empty_tag('c:overlay', [['val', 1]])
2017
352
  end
2018
353
 
2019
354
  #
2020
- # Write the <c:spPr> element.
355
+ # Write the <c:plotVisOnly> element.
2021
356
  #
2022
- def write_sp_pr(series) # :nodoc:
2023
- return unless has_fill_formatting(series)
357
+ def write_plot_vis_only # :nodoc:
358
+ val = 1
2024
359
 
2025
- line = series_property(series, :line)
2026
- fill = series_property(series, :fill)
2027
- pattern = series_property(series, :pattern)
2028
- gradient = series_property(series, :gradient)
360
+ # Ignore this element if we are plotting hidden data.
361
+ return if @show_hidden_data
2029
362
 
2030
- @writer.tag_elements('c:spPr') do
2031
- # Write the fill elements for solid charts such as pie/doughnut and bar.
2032
- if fill && fill[:_defined] != 0
2033
- if ptrue?(fill[:none])
2034
- # Write the a:noFill element.
2035
- write_a_no_fill
2036
- else
2037
- # Write the a:solidFill element.
2038
- write_a_solid_fill(fill)
2039
- end
2040
- end
2041
- write_a_patt_fill(pattern) if ptrue?(pattern)
2042
- if ptrue?(gradient)
2043
- # Write the a:gradFill element.
2044
- write_a_grad_fill(gradient)
2045
- end
2046
- # Write the a:ln element.
2047
- write_a_ln(line) if line && ptrue?(line[:_defined])
2048
- end
363
+ @writer.empty_tag('c:plotVisOnly', [['val', val]])
2049
364
  end
2050
365
 
2051
- def series_property(object, property)
2052
- if object.respond_to?(property)
2053
- object.send(property)
2054
- elsif object.respond_to?(:[])
2055
- object[property]
366
+ #
367
+ # Write the <c:title> element for a rich string.
368
+ #
369
+ def write_title_rich(title, is_y_axis = nil)
370
+ @writer.tag_elements('c:title') do
371
+ # Write the c:tx element.
372
+ write_tx_rich(title.name, is_y_axis, title.font)
373
+ # Write the c:layout element.
374
+ write_layout(title.layout, 'text')
375
+ # Write the c:overlay element.
376
+ write_overlay if title.overlay
377
+ write_sp_pr(title)
2056
378
  end
2057
379
  end
2058
380
 
381
+ ###############################################################################
2059
382
  #
2060
- # Write the <a:ln> element.
383
+ # Formatting and series decoration helpers
2061
384
  #
2062
- def write_a_ln(line) # :nodoc:
2063
- attributes = []
385
+ ###############################################################################
2064
386
 
2065
- # Add the line width as an attribute.
2066
- if line[:width]
2067
- width = line[:width]
2068
- # Round width to nearest 0.25, like Excel.
2069
- width = ((width + 0.125) * 4).to_i / 4.0
387
+ #
388
+ # Write the <c:dPt> elements.
389
+ #
390
+ def write_d_pt(points = nil)
391
+ return unless ptrue?(points)
2070
392
 
2071
- # Convert to internal units.
2072
- width = (0.5 + (12700 * width)).to_i
393
+ index = -1
394
+ points.each do |point|
395
+ index += 1
396
+ next unless ptrue?(point)
2073
397
 
2074
- attributes << ['w', width]
398
+ write_d_pt_point(index, point)
2075
399
  end
400
+ end
2076
401
 
2077
- if ptrue?(line[:none]) || ptrue?(line[:color]) || line[:dash_type]
2078
- @writer.tag_elements('a:ln', attributes) do
2079
- # Write the line fill.
2080
- if ptrue?(line[:none])
2081
- # Write the a:noFill element.
2082
- write_a_no_fill
2083
- elsif ptrue?(line[:color])
2084
- # Write the a:solidFill element.
2085
- write_a_solid_fill(line)
2086
- end
2087
-
2088
- # Write the line/dash type.
2089
- if line[:dash_type]
2090
- # Write the a:prstDash element.
2091
- write_a_prst_dash(line[:dash_type])
2092
- end
2093
- end
2094
- else
2095
- @writer.empty_tag('a:ln', attributes)
402
+ #
403
+ # Write an individual <c:dPt> element.
404
+ #
405
+ def write_d_pt_point(index, point)
406
+ @writer.tag_elements('c:dPt') do
407
+ # Write the c:idx element.
408
+ write_idx(index)
409
+ # Write the c:spPr element.
410
+ write_sp_pr(point)
2096
411
  end
2097
412
  end
2098
413
 
2099
414
  #
2100
- # Write the <a:noFill> element.
415
+ # Write the <c:delete> element.
2101
416
  #
2102
- def write_a_no_fill # :nodoc:
2103
- @writer.empty_tag('a:noFill')
417
+ def write_delete(val) # :nodoc:
418
+ @writer.empty_tag('c:delete', [['val', val]])
2104
419
  end
2105
420
 
2106
421
  #
2107
- # Write the <a:alpha> element.
422
+ # Write the <c:invertIfNegative> element.
2108
423
  #
2109
- def write_a_alpha(val)
2110
- val = (100 - val.to_i) * 1000
424
+ def write_c_invert_if_negative(invert = nil) # :nodoc:
425
+ return unless ptrue?(invert)
2111
426
 
2112
- @writer.empty_tag('a:alpha', [['val', val]])
427
+ @writer.empty_tag('c:invertIfNegative', [['val', 1]])
2113
428
  end
2114
429
 
2115
430
  #
2116
- # Write the <a:prstDash> element.
431
+ # Write the <c:dTable> element.
2117
432
  #
2118
- def write_a_prst_dash(val) # :nodoc:
2119
- @writer.empty_tag('a:prstDash', [['val', val]])
433
+ def write_d_table
434
+ @table.write_d_table(@writer) if @table
2120
435
  end
2121
436
 
437
+ ###############################################################################
438
+ #
439
+ # Trendline helpers
440
+ #
441
+ ###############################################################################
442
+
2122
443
  #
2123
444
  # Write the <c:trendline> element.
2124
445
  #
@@ -2184,475 +505,176 @@ module Writexlsx
2184
505
  # Write the <c:period> element.
2185
506
  #
2186
507
  def write_period(val = 2) # :nodoc:
2187
- @writer.empty_tag('c:period', [['val', val]])
2188
- end
2189
-
2190
- #
2191
- # Write the <c:forward> element.
2192
- #
2193
- def write_forward(val) # :nodoc:
2194
- return unless val
2195
-
2196
- @writer.empty_tag('c:forward', [['val', val]])
2197
- end
2198
-
2199
- #
2200
- # Write the <c:backward> element.
2201
- #
2202
- def write_backward(val) # :nodoc:
2203
- return unless val
2204
-
2205
- @writer.empty_tag('c:backward', [['val', val]])
2206
- end
2207
-
2208
- #
2209
- # Write the <c:intercept> element.
2210
- #
2211
- def write_intercept(val)
2212
- @writer.empty_tag('c:intercept', [['val', val]])
2213
- end
2214
-
2215
- #
2216
- # Write the <c:dispEq> element.
2217
- #
2218
- def write_disp_eq
2219
- @writer.empty_tag('c:dispEq', [['val', 1]])
2220
- end
2221
-
2222
- #
2223
- # Write the <c:dispRSqr> element.
2224
- #
2225
- def write_disp_rsqr
2226
- @writer.empty_tag('c:dispRSqr', [['val', 1]])
2227
- end
2228
-
2229
- #
2230
- # Write the <c:trendlineLbl> element.
2231
- #
2232
- def write_trendline_lbl(trendline)
2233
- @writer.tag_elements('c:trendlineLbl') do
2234
- # Write the c:layout element.
2235
- write_layout
2236
- # Write the c:numFmt element.
2237
- write_trendline_num_fmt
2238
- # Write the c:spPr element for the label formatting.
2239
- write_sp_pr(trendline.label)
2240
- # Write the data label font elements.
2241
- if trendline.label && ptrue?(trendline.label[:font])
2242
- write_axis_font(trendline.label[:font])
2243
- end
2244
- end
2245
- end
2246
-
2247
- #
2248
- # Write the <c:numFmt> element.
2249
- #
2250
- def write_trendline_num_fmt
2251
- format_code = 'General'
2252
- source_linked = 0
2253
-
2254
- attributes = [
2255
- ['formatCode', format_code],
2256
- ['sourceLinked', source_linked]
2257
- ]
2258
-
2259
- @writer.empty_tag('c:numFmt', attributes)
2260
- end
2261
-
2262
- #
2263
- # Write the <c:hiLowLines> element.
2264
- #
2265
- def write_hi_low_lines # :nodoc:
2266
- write_lines_base(@hi_low_lines, 'c:hiLowLines')
2267
- end
2268
-
2269
- #
2270
- # Write the <c:dropLines> elent.
2271
- #
2272
- def write_drop_lines
2273
- write_lines_base(@drop_lines, 'c:dropLines')
2274
- end
2275
-
2276
- def write_lines_base(lines, tag)
2277
- return unless lines
2278
-
2279
- if lines.line_defined?
2280
- @writer.tag_elements(tag) { write_sp_pr(lines) }
2281
- else
2282
- @writer.empty_tag(tag)
2283
- end
2284
- end
2285
-
2286
- #
2287
- # Write the <c:overlap> element.
2288
- #
2289
- def write_overlap(val = nil) # :nodoc:
2290
- return unless val
2291
-
2292
- @writer.empty_tag('c:overlap', [['val', val]])
2293
- end
2294
-
2295
- #
2296
- # Write the <c:numCache> element.
2297
- #
2298
- def write_num_cache(data) # :nodoc:
2299
- write_num_base('c:numCache', data)
2300
- end
2301
-
2302
- def write_num_base(tag, data)
2303
- @writer.tag_elements(tag) do
2304
- # Write the c:formatCode element.
2305
- write_format_code('General')
2306
-
2307
- # Write the c:ptCount element.
2308
- count = if data
2309
- data.size
2310
- else
2311
- 0
2312
- end
2313
- write_pt_count(count)
2314
-
2315
- data.each_with_index do |token, i|
2316
- # Write non-numeric data as 0.
2317
- if token &&
2318
- !(token.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
2319
- token = 0
2320
- end
2321
-
2322
- # Write the c:pt element.
2323
- write_pt(i, token)
2324
- end
2325
- end
2326
- end
2327
-
2328
- #
2329
- # Write the <c:strCache> element.
2330
- #
2331
- def write_str_cache(data) # :nodoc:
2332
- @writer.tag_elements('c:strCache') do
2333
- write_pt_count(data.size)
2334
- write_pts(data)
2335
- end
2336
- end
2337
-
2338
- def write_pts(data)
2339
- data.each_index { |i| write_pt(i, data[i]) }
2340
- end
2341
-
2342
- #
2343
- # Write the <c:formatCode> element.
2344
- #
2345
- def write_format_code(data) # :nodoc:
2346
- @writer.data_element('c:formatCode', data)
2347
- end
2348
-
2349
- #
2350
- # Write the <c:ptCount> element.
2351
- #
2352
- def write_pt_count(val) # :nodoc:
2353
- @writer.empty_tag('c:ptCount', [['val', val]])
2354
- end
2355
-
2356
- #
2357
- # Write the <c:pt> element.
2358
- #
2359
- def write_pt(idx, value) # :nodoc:
2360
- return unless value
2361
-
2362
- attributes = [['idx', idx]]
2363
-
2364
- @writer.tag_elements('c:pt', attributes) { write_v(value) }
2365
- end
2366
-
2367
- #
2368
- # Write the <c:v> element.
2369
- #
2370
- def write_v(data) # :nodoc:
2371
- @writer.data_element('c:v', data)
2372
- end
2373
-
2374
- #
2375
- # Write the <c:protection> element.
2376
- #
2377
- def write_protection # :nodoc:
2378
- return if @protection == 0
2379
-
2380
- @writer.empty_tag('c:protection')
2381
- end
2382
-
2383
- #
2384
- # Write the <c:dPt> elements.
2385
- #
2386
- def write_d_pt(points = nil)
2387
- return unless ptrue?(points)
2388
-
2389
- index = -1
2390
- points.each do |point|
2391
- index += 1
2392
- next unless ptrue?(point)
2393
-
2394
- write_d_pt_point(index, point)
2395
- end
2396
- end
2397
-
2398
- #
2399
- # Write an individual <c:dPt> element.
2400
- #
2401
- def write_d_pt_point(index, point)
2402
- @writer.tag_elements('c:dPt') do
2403
- # Write the c:idx element.
2404
- write_idx(index)
2405
- # Write the c:spPr element.
2406
- write_sp_pr(point)
2407
- end
2408
- end
2409
-
2410
- #
2411
- # Write the <c:dLbls> element.
2412
- #
2413
- def write_d_lbls(labels) # :nodoc:
2414
- return unless labels
2415
-
2416
- @writer.tag_elements('c:dLbls') do
2417
- # Write the custom c:dLbl elements.
2418
- write_custom_labels(labels, labels[:custom]) if labels[:custom]
2419
- # Write the c:numFmt element.
2420
- write_data_label_number_format(labels[:num_format]) if labels[:num_format]
2421
- # Write the c:spPr element.
2422
- write_sp_pr(labels)
2423
- # Write the data label font elements.
2424
- write_axis_font(labels[:font]) if labels[:font]
2425
- # Write the c:dLblPos element.
2426
- write_d_lbl_pos(labels[:position]) if ptrue?(labels[:position])
2427
- # Write the c:showLegendKey element.
2428
- write_show_legend_key if labels[:legend_key]
2429
- # Write the c:showVal element.
2430
- write_show_val if labels[:value]
2431
- # Write the c:showCatName element.
2432
- write_show_cat_name if labels[:category]
2433
- # Write the c:showSerName element.
2434
- write_show_ser_name if labels[:series_name]
2435
- # Write the c:showPercent element.
2436
- write_show_percent if labels[:percentage]
2437
- # Write the c:separator element.
2438
- write_separator(labels[:separator]) if labels[:separator]
2439
- # Write the c:showLeaderLines element.
2440
- write_show_leader_lines if labels[:leader_lines]
2441
- end
508
+ @writer.empty_tag('c:period', [['val', val]])
2442
509
  end
2443
510
 
2444
511
  #
2445
- # Write the <c:dLbl> element.
512
+ # Write the <c:forward> element.
2446
513
  #
2447
- def write_custom_labels(parent, labels)
2448
- index = 0
514
+ def write_forward(val) # :nodoc:
515
+ return unless val
2449
516
 
2450
- labels.each do |label|
2451
- index += 1
2452
- next unless ptrue?(label)
2453
-
2454
- @writer.tag_elements('c:dLbl') do
2455
- # Write the c:idx element.
2456
- write_idx(index - 1)
2457
-
2458
- if label[:delete]
2459
- write_delete(1)
2460
- elsif label[:formula]
2461
- write_custom_label_formula(label)
2462
-
2463
- write_d_lbl_pos(parent[:position]) if parent[:position]
2464
- write_show_val if parent[:value]
2465
- write_show_cat_name if parent[:category]
2466
- write_show_ser_name if parent[:series_name]
2467
- elsif label[:value]
2468
- write_custom_label_str(label)
2469
-
2470
- write_d_lbl_pos(parent[:position]) if parent[:position]
2471
- write_show_val if parent[:value]
2472
- write_show_cat_name if parent[:category]
2473
- write_show_ser_name if parent[:series_name]
2474
- else
2475
- write_custom_label_format_only(label)
2476
- end
2477
- end
2478
- end
517
+ @writer.empty_tag('c:forward', [['val', val]])
2479
518
  end
2480
519
 
2481
520
  #
2482
- # Write parts of the <c:dLbl> element for strings.
521
+ # Write the <c:backward> element.
2483
522
  #
2484
- def write_custom_label_str(label)
2485
- value = label[:value]
2486
- font = label[:font]
2487
- is_y_axis = 0
2488
- has_formatting = has_fill_formatting(label)
2489
-
2490
- # Write the c:layout element.
2491
- write_layout
2492
-
2493
- @writer.tag_elements('c:tx') do
2494
- # Write the c:rich element.
2495
- write_rich(value, font, is_y_axis, !has_formatting)
2496
- end
523
+ def write_backward(val) # :nodoc:
524
+ return unless val
2497
525
 
2498
- # Write the c:cpPr element.
2499
- write_sp_pr(label)
526
+ @writer.empty_tag('c:backward', [['val', val]])
2500
527
  end
2501
528
 
2502
529
  #
2503
- # Write parts of the <c:dLbl> element for formulas.
530
+ # Write the <c:intercept> element.
2504
531
  #
2505
- def write_custom_label_formula(label)
2506
- formula = label[:formula]
2507
- data_id = label[:data_id]
2508
-
2509
- data = @formula_data[data_id] if data_id
2510
-
2511
- # Write the c:layout element.
2512
- write_layout
2513
-
2514
- @writer.tag_elements('c:tx') do
2515
- # Write the c:strRef element.
2516
- write_str_ref(formula, data, 'str')
2517
- end
2518
-
2519
- # Write the data label formatting, if any.
2520
- write_custom_label_format_only(label)
532
+ def write_intercept(val)
533
+ @writer.empty_tag('c:intercept', [['val', val]])
2521
534
  end
2522
535
 
2523
536
  #
2524
- # Write parts of the <c:dLbl> element for labels where only the formatting has
2525
- # changed.
537
+ # Write the <c:dispEq> element.
2526
538
  #
2527
- def write_custom_label_format_only(label)
2528
- font = label[:font]
2529
- has_formatting = has_fill_formatting(label)
2530
-
2531
- if has_formatting
2532
- # Write the c:spPr element.
2533
- write_sp_pr(label)
2534
- write_tx_pr(font)
2535
- elsif font
2536
- @writer.empty_tag('c:spPr')
2537
- write_tx_pr(font)
2538
- end
539
+ def write_disp_eq
540
+ @writer.empty_tag('c:dispEq', [['val', 1]])
2539
541
  end
2540
542
 
2541
543
  #
2542
- # Write the <c:showLegendKey> element.
544
+ # Write the <c:dispRSqr> element.
2543
545
  #
2544
- def write_show_legend_key
2545
- @writer.empty_tag('c:showLegendKey', [['val', 1]])
546
+ def write_disp_rsqr
547
+ @writer.empty_tag('c:dispRSqr', [['val', 1]])
2546
548
  end
2547
549
 
2548
550
  #
2549
- # Write the <c:showVal> element.
551
+ # Write the <c:trendlineLbl> element.
2550
552
  #
2551
- def write_show_val # :nodoc:
2552
- @writer.empty_tag('c:showVal', [['val', 1]])
553
+ def write_trendline_lbl(trendline)
554
+ @writer.tag_elements('c:trendlineLbl') do
555
+ # Write the c:layout element.
556
+ write_layout
557
+ # Write the c:numFmt element.
558
+ write_trendline_num_fmt
559
+ # Write the c:spPr element for the label formatting.
560
+ write_sp_pr(trendline.label)
561
+ # Write the data label font elements.
562
+ if trendline.label && ptrue?(trendline.label[:font])
563
+ write_axis_font(trendline.label[:font])
564
+ end
565
+ end
2553
566
  end
2554
567
 
2555
568
  #
2556
- # Write the <c:showCatName> element.
569
+ # Write the <c:numFmt> element.
2557
570
  #
2558
- def write_show_cat_name # :nodoc:
2559
- @writer.empty_tag('c:showCatName', [['val', 1]])
571
+ def write_trendline_num_fmt
572
+ format_code = 'General'
573
+ source_linked = 0
574
+
575
+ attributes = [
576
+ ['formatCode', format_code],
577
+ ['sourceLinked', source_linked]
578
+ ]
579
+
580
+ @writer.empty_tag('c:numFmt', attributes)
2560
581
  end
2561
582
 
583
+ ###############################################################################
2562
584
  #
2563
- # Write the <c:showSerName> element.
585
+ # Line and bar helpers
2564
586
  #
2565
- def write_show_ser_name # :nodoc:
2566
- @writer.empty_tag('c:showSerName', [['val', 1]])
2567
- end
587
+ ###############################################################################
2568
588
 
2569
589
  #
2570
- # Write the <c:showPercent> element.
590
+ # Write the <c:hiLowLines> element.
2571
591
  #
2572
- def write_show_percent
2573
- @writer.empty_tag('c:showPercent', [['val', 1]])
592
+ def write_hi_low_lines # :nodoc:
593
+ write_lines_base(@hi_low_lines, 'c:hiLowLines')
2574
594
  end
2575
595
 
2576
596
  #
2577
- # Write the <c:separator> element.
597
+ # Write the <c:dropLines> elent.
2578
598
  #
2579
- def write_separator(data)
2580
- @writer.data_element('c:separator', data)
599
+ def write_drop_lines
600
+ write_lines_base(@drop_lines, 'c:dropLines')
2581
601
  end
2582
602
 
2583
- # Write the <c:showLeaderLines> element. This is different for Pie/Doughnut
2584
- # charts. Other chart types only supported leader lines after Excel 2015 via
2585
- # an extension element.
2586
- def write_show_leader_lines
2587
- uri = '{CE6537A1-D6FC-4f65-9D91-7224C49458BB}'
2588
- xmlns_c_15 = 'http://schemas.microsoft.com/office/drawing/2012/chart'
603
+ def write_lines_base(lines, tag)
604
+ return unless lines
2589
605
 
2590
- attributes1 = [
2591
- ['uri', uri],
2592
- ['xmlns:c15', xmlns_c_15]
2593
- ]
606
+ if lines.line_defined?
607
+ @writer.tag_elements(tag) { write_sp_pr(lines) }
608
+ else
609
+ @writer.empty_tag(tag)
610
+ end
611
+ end
612
+
613
+ #
614
+ # Write the <c:upDownBars> element.
615
+ #
616
+ def write_up_down_bars
617
+ return unless ptrue?(@up_down_bars)
2594
618
 
2595
- attributes2 = [['val', 1]]
619
+ @writer.tag_elements('c:upDownBars') do
620
+ # Write the c:gapWidth element.
621
+ write_gap_width(150)
2596
622
 
2597
- @writer.tag_elements('c:extLst') do
2598
- @writer.tag_elements('c:ext', attributes1) do
2599
- @writer.empty_tag('c15:showLeaderLines', attributes2)
2600
- end
623
+ # Write the c:upBars element.
624
+ write_up_bars(@up_down_bars[:_up])
625
+
626
+ # Write the c:downBars element.
627
+ write_down_bars(@up_down_bars[:_down])
2601
628
  end
2602
629
  end
2603
630
 
2604
631
  #
2605
- # Write the <c:dLblPos> element.
632
+ # Write the <c:gapWidth> element.
2606
633
  #
2607
- def write_d_lbl_pos(val)
2608
- @writer.empty_tag('c:dLblPos', [['val', val]])
634
+ def write_gap_width(val = nil)
635
+ return unless val
636
+
637
+ @writer.empty_tag('c:gapWidth', [['val', val]])
2609
638
  end
2610
639
 
2611
640
  #
2612
- # Write the <c:delete> element.
641
+ # Write the <c:upBars> element.
2613
642
  #
2614
- def write_delete(val) # :nodoc:
2615
- @writer.empty_tag('c:delete', [['val', val]])
643
+ def write_up_bars(format)
644
+ write_bars_base('c:upBars', format)
2616
645
  end
2617
646
 
2618
647
  #
2619
- # Write the <c:invertIfNegative> element.
648
+ # Write the <c:upBars> element.
2620
649
  #
2621
- def write_c_invert_if_negative(invert = nil) # :nodoc:
2622
- return unless ptrue?(invert)
2623
-
2624
- @writer.empty_tag('c:invertIfNegative', [['val', 1]])
650
+ def write_down_bars(format)
651
+ write_bars_base('c:downBars', format)
2625
652
  end
2626
653
 
2627
654
  #
2628
- # Write the axis font elements.
655
+ # Write the <c:smooth> element.
2629
656
  #
2630
- def write_axis_font(font) # :nodoc:
2631
- return unless font
657
+ def write_c_smooth(smooth)
658
+ return unless ptrue?(smooth)
2632
659
 
2633
- @writer.tag_elements('c:txPr') do
2634
- write_a_body_pr(font[:_rotation])
2635
- write_a_lst_style
2636
- @writer.tag_elements('a:p') do
2637
- write_a_p_pr_rich(font)
2638
- write_a_end_para_rpr
2639
- end
2640
- end
660
+ attributes = [['val', 1]]
661
+
662
+ @writer.empty_tag('c:smooth', attributes)
2641
663
  end
2642
664
 
2643
- #
2644
- # Write the <a:latin> element.
2645
- #
2646
- def write_a_latin(args) # :nodoc:
2647
- @writer.empty_tag('a:latin', args)
665
+ def write_bars_base(tag, format)
666
+ if format.line_defined? || format.fill_defined?
667
+ @writer.tag_elements(tag) { write_sp_pr(format) }
668
+ else
669
+ @writer.empty_tag(tag)
670
+ end
2648
671
  end
2649
672
 
673
+ ###############################################################################
2650
674
  #
2651
- # Write the <c:dTable> element.
675
+ # Error bar helpers
2652
676
  #
2653
- def write_d_table
2654
- @table.write_d_table(@writer) if @table
2655
- end
677
+ ###############################################################################
2656
678
 
2657
679
  #
2658
680
  # Write the X and Y error bars.
@@ -2759,218 +781,5 @@ module Writexlsx
2759
781
  write_num_lit(values)
2760
782
  end
2761
783
  end
2762
-
2763
- #
2764
- # Write the <c:upDownBars> element.
2765
- #
2766
- def write_up_down_bars
2767
- return unless ptrue?(@up_down_bars)
2768
-
2769
- @writer.tag_elements('c:upDownBars') do
2770
- # Write the c:gapWidth element.
2771
- write_gap_width(150)
2772
-
2773
- # Write the c:upBars element.
2774
- write_up_bars(@up_down_bars[:_up])
2775
-
2776
- # Write the c:downBars element.
2777
- write_down_bars(@up_down_bars[:_down])
2778
- end
2779
- end
2780
-
2781
- #
2782
- # Write the <c:gapWidth> element.
2783
- #
2784
- def write_gap_width(val = nil)
2785
- return unless val
2786
-
2787
- @writer.empty_tag('c:gapWidth', [['val', val]])
2788
- end
2789
-
2790
- #
2791
- # Write the <c:upBars> element.
2792
- #
2793
- def write_up_bars(format)
2794
- write_bars_base('c:upBars', format)
2795
- end
2796
-
2797
- #
2798
- # Write the <c:upBars> element.
2799
- #
2800
- def write_down_bars(format)
2801
- write_bars_base('c:downBars', format)
2802
- end
2803
-
2804
- #
2805
- # Write the <c:smooth> element.
2806
- #
2807
- def write_c_smooth(smooth)
2808
- return unless ptrue?(smooth)
2809
-
2810
- attributes = [['val', 1]]
2811
-
2812
- @writer.empty_tag('c:smooth', attributes)
2813
- end
2814
-
2815
- #
2816
- # Write the <c:dispUnits> element.
2817
- #
2818
- def write_disp_units(units, display)
2819
- return unless ptrue?(units)
2820
-
2821
- attributes = [['val', units]]
2822
-
2823
- @writer.tag_elements('c:dispUnits') do
2824
- @writer.empty_tag('c:builtInUnit', attributes)
2825
- if ptrue?(display)
2826
- @writer.tag_elements('c:dispUnitsLbl') do
2827
- @writer.empty_tag('c:layout')
2828
- end
2829
- end
2830
- end
2831
- end
2832
-
2833
- #
2834
- # Write the <a:gradFill> element.
2835
- #
2836
- def write_a_grad_fill(gradient)
2837
- attributes = [
2838
- %w[flip none],
2839
- ['rotWithShape', 1]
2840
- ]
2841
- attributes = [] if gradient[:type] == 'linear'
2842
-
2843
- @writer.tag_elements('a:gradFill', attributes) do
2844
- # Write the a:gsLst element.
2845
- write_a_gs_lst(gradient)
2846
-
2847
- if gradient[:type] == 'linear'
2848
- # Write the a:lin element.
2849
- write_a_lin(gradient[:angle])
2850
- else
2851
- # Write the a:path element.
2852
- write_a_path(gradient[:type])
2853
-
2854
- # Write the a:tileRect element.
2855
- write_a_tile_rect(gradient[:type])
2856
- end
2857
- end
2858
- end
2859
-
2860
- #
2861
- # Write the <a:gsLst> element.
2862
- #
2863
- def write_a_gs_lst(gradient)
2864
- positions = gradient[:positions]
2865
- colors = gradient[:colors]
2866
-
2867
- @writer.tag_elements('a:gsLst') do
2868
- (0..(colors.size - 1)).each do |i|
2869
- pos = (positions[i] * 1000).to_i
2870
-
2871
- attributes = [['pos', pos]]
2872
- @writer.tag_elements('a:gs', attributes) do
2873
- color = color(colors[i])
2874
-
2875
- # Write the a:srgbClr element.
2876
- # TODO: Wait for a feature request to support transparency.
2877
- write_a_srgb_clr(color)
2878
- end
2879
- end
2880
- end
2881
- end
2882
-
2883
- #
2884
- # Write the <a:lin> element.
2885
- #
2886
- def write_a_lin(angle)
2887
- scaled = 0
2888
-
2889
- angle = (60000 * angle).to_i
2890
-
2891
- attributes = [
2892
- ['ang', angle],
2893
- ['scaled', scaled]
2894
- ]
2895
-
2896
- @writer.empty_tag('a:lin', attributes)
2897
- end
2898
-
2899
- #
2900
- # Write the <a:path> element.
2901
- #
2902
- def write_a_path(type)
2903
- attributes = [['path', type]]
2904
-
2905
- @writer.tag_elements('a:path', attributes) do
2906
- # Write the a:fillToRect element.
2907
- write_a_fill_to_rect(type)
2908
- end
2909
- end
2910
-
2911
- #
2912
- # Write the <a:fillToRect> element.
2913
- #
2914
- def write_a_fill_to_rect(type)
2915
- attributes = if type == 'shape'
2916
- [
2917
- ['l', 50000],
2918
- ['t', 50000],
2919
- ['r', 50000],
2920
- ['b', 50000]
2921
- ]
2922
- else
2923
- [
2924
- ['l', 100000],
2925
- ['t', 100000]
2926
- ]
2927
- end
2928
-
2929
- @writer.empty_tag('a:fillToRect', attributes)
2930
- end
2931
-
2932
- #
2933
- # Write the <a:tileRect> element.
2934
- #
2935
- def write_a_tile_rect(type)
2936
- attributes = if type == 'shape'
2937
- []
2938
- else
2939
- [
2940
- ['r', -100000],
2941
- ['b', -100000]
2942
- ]
2943
- end
2944
-
2945
- @writer.empty_tag('a:tileRect', attributes)
2946
- end
2947
-
2948
- #
2949
- # Write the <a:pattFill> element.
2950
- #
2951
- def write_a_patt_fill(pattern)
2952
- attributes = [['prst', pattern[:pattern]]]
2953
-
2954
- @writer.tag_elements('a:pattFill', attributes) do
2955
- write_a_fg_clr(pattern[:fg_color])
2956
- write_a_bg_clr(pattern[:bg_color])
2957
- end
2958
- end
2959
-
2960
- def write_a_fg_clr(color)
2961
- @writer.tag_elements('a:fgClr') { write_a_srgb_clr(color(color)) }
2962
- end
2963
-
2964
- def write_a_bg_clr(color)
2965
- @writer.tag_elements('a:bgClr') { write_a_srgb_clr(color(color)) }
2966
- end
2967
-
2968
- def write_bars_base(tag, format)
2969
- if format.line_defined? || format.fill_defined?
2970
- @writer.tag_elements(tag) { write_sp_pr(format) }
2971
- else
2972
- @writer.empty_tag(tag)
2973
- end
2974
- end
2975
784
  end
2976
785
  end