write_xlsx 0.75.0 → 0.76.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +4 -0
  3. data/Gemfile +8 -2
  4. data/README.md +4 -2
  5. data/lib/write_xlsx/chart/axis.rb +69 -96
  6. data/lib/write_xlsx/chart/bar.rb +18 -21
  7. data/lib/write_xlsx/chart/caption.rb +1 -1
  8. data/lib/write_xlsx/chart/column.rb +1 -5
  9. data/lib/write_xlsx/chart/line.rb +2 -16
  10. data/lib/write_xlsx/chart/pie.rb +18 -40
  11. data/lib/write_xlsx/chart/radar.rb +2 -5
  12. data/lib/write_xlsx/chart/scatter.rb +24 -32
  13. data/lib/write_xlsx/chart/series.rb +218 -236
  14. data/lib/write_xlsx/chart/stock.rb +15 -27
  15. data/lib/write_xlsx/chart.rb +303 -392
  16. data/lib/write_xlsx/chartsheet.rb +22 -20
  17. data/lib/write_xlsx/colors.rb +9 -15
  18. data/lib/write_xlsx/drawing.rb +26 -28
  19. data/lib/write_xlsx/format.rb +15 -15
  20. data/lib/write_xlsx/package/comments.rb +1 -1
  21. data/lib/write_xlsx/package/conditional_format.rb +8 -8
  22. data/lib/write_xlsx/package/relationships.rb +4 -15
  23. data/lib/write_xlsx/package/styles.rb +9 -16
  24. data/lib/write_xlsx/shape.rb +1 -15
  25. data/lib/write_xlsx/sparkline.rb +1 -1
  26. data/lib/write_xlsx/utility.rb +69 -13
  27. data/lib/write_xlsx/version.rb +1 -1
  28. data/lib/write_xlsx/workbook.rb +19 -7
  29. data/lib/write_xlsx/worksheet/cell_data.rb +1 -1
  30. data/lib/write_xlsx/worksheet/hyperlink.rb +39 -37
  31. data/lib/write_xlsx/worksheet.rb +44 -72
  32. data/lib/write_xlsx/zip_file_utils.rb +99 -0
  33. data/test/chart/test_add_series.rb +5 -5
  34. data/test/chart/test_write_d_lbls.rb +1 -1
  35. data/test/chart/test_write_major_gridlines.rb +1 -1
  36. data/test/chart/test_write_marker.rb +1 -1
  37. data/test/chart/test_write_number_format.rb +1 -1
  38. data/test/helper.rb +7 -4
  39. data/test/regression/klt.csv +4 -0
  40. data/test/regression/test_chart_column07.rb +44 -0
  41. data/test/regression/test_chart_column08.rb +46 -0
  42. data/test/regression/test_chart_date01.rb +57 -0
  43. data/test/regression/test_chart_date02.rb +59 -0
  44. data/test/regression/test_chart_date03.rb +59 -0
  45. data/test/regression/test_chart_date04.rb +61 -0
  46. data/test/regression/test_chart_stock01.rb +1 -6
  47. data/test/regression/test_chart_title02.rb +44 -0
  48. data/test/regression/test_escapes01.rb +1 -1
  49. data/test/regression/test_escapes02.rb +1 -1
  50. data/test/regression/test_escapes03.rb +1 -1
  51. data/test/regression/test_escapes04.rb +1 -1
  52. data/test/regression/test_escapes05.rb +1 -1
  53. data/test/regression/test_escapes06.rb +1 -1
  54. data/test/regression/test_escapes07.rb +1 -1
  55. data/test/regression/test_escapes08.rb +1 -1
  56. data/test/regression/test_set_column09.rb +31 -0
  57. data/test/regression/test_shared_strings_encoding.rb +103 -0
  58. data/test/regression/xlsx_files/chart_column07.xlsx +0 -0
  59. data/test/regression/xlsx_files/chart_column08.xlsx +0 -0
  60. data/test/regression/xlsx_files/chart_date01.xlsx +0 -0
  61. data/test/regression/xlsx_files/chart_date02.xlsx +0 -0
  62. data/test/regression/xlsx_files/chart_date03.xlsx +0 -0
  63. data/test/regression/xlsx_files/chart_date04.xlsx +0 -0
  64. data/test/regression/xlsx_files/chart_title02.xlsx +0 -0
  65. data/test/regression/xlsx_files/set_column09.xlsx +0 -0
  66. data/test/regression/xlsx_files/shared_strings_encoding.xlsx +0 -0
  67. data/test/worksheet/test_write_hyperlink.rb +10 -15
  68. data/write_xlsx.gemspec +0 -3
  69. metadata +48 -39
  70. data/test/worksheet/test_set_column.rb +0 -25
@@ -35,6 +35,104 @@ module Writexlsx
35
35
  end
36
36
  end
37
37
 
38
+ class ChartArea
39
+ include Writexlsx::Utility
40
+
41
+ attr_reader :line, :fill, :layout
42
+
43
+ def initialize(params = {})
44
+ @layout = layout_properties(params[:layout])
45
+
46
+ # Allow 'border' as a synonym for 'line'.
47
+ border = params_to_border(params)
48
+
49
+ # Set the line properties for the chartarea.
50
+ @line = border ? line_properties(border) : line_properties(params[:line])
51
+
52
+ # Map deprecated Spreadsheet::WriteExcel fill colour.
53
+ fill = params[:color] ? { :color => params[:color] } : params[:fill]
54
+ @fill = fill_properties(fill)
55
+ end
56
+
57
+ private
58
+
59
+ def params_to_border(params)
60
+ line_weight = params[:line_weight]
61
+
62
+ # Map deprecated Spreadsheet::WriteExcel line_weight.
63
+ border = params[:border]
64
+ border = { :width => swe_line_weight(line_weight) } if line_weight
65
+
66
+ # Map deprecated Spreadsheet::WriteExcel line_pattern.
67
+ if params[:line_pattern]
68
+ pattern = swe_line_pattern(params[:line_pattern])
69
+ if pattern == 'none'
70
+ border = { :none => 1 }
71
+ else
72
+ border[:dash_type] = pattern
73
+ end
74
+ end
75
+
76
+ # Map deprecated Spreadsheet::WriteExcel line colour.
77
+ border[:color] = params[:line_color] if params[:line_color]
78
+ border
79
+ end
80
+
81
+ #
82
+ # Get the Spreadsheet::WriteExcel line pattern for backward compatibility.
83
+ #
84
+ def swe_line_pattern(val)
85
+ swe_line_pattern_hash[numeric_or_downcase(val)] || 'solid'
86
+ end
87
+
88
+ def swe_line_pattern_hash
89
+ {
90
+ 0 => 'solid',
91
+ 1 => 'dash',
92
+ 2 => 'dot',
93
+ 3 => 'dash_dot',
94
+ 4 => 'long_dash_dot_dot',
95
+ 5 => 'none',
96
+ 6 => 'solid',
97
+ 7 => 'solid',
98
+ 8 => 'solid',
99
+ 'solid' => 'solid',
100
+ 'dash' => 'dash',
101
+ 'dot' => 'dot',
102
+ 'dash-dot' => 'dash_dot',
103
+ 'dash-dot-dot' => 'long_dash_dot_dot',
104
+ 'none' => 'none',
105
+ 'dark-gray' => 'solid',
106
+ 'medium-gray' => 'solid',
107
+ 'light-gray' => 'solid'
108
+ }
109
+ end
110
+
111
+ #
112
+ # Get the Spreadsheet::WriteExcel line weight for backward compatibility.
113
+ #
114
+ def swe_line_weight(val)
115
+ swe_line_weight_hash[numeric_or_downcase(val)] || 1
116
+ end
117
+
118
+ def swe_line_weight_hash
119
+ {
120
+ 1 => 0.25,
121
+ 2 => 1,
122
+ 3 => 2,
123
+ 4 => 3,
124
+ 'hairline' => 0.25,
125
+ 'narrow' => 1,
126
+ 'medium' => 2,
127
+ 'wide' => 3
128
+ }
129
+ end
130
+
131
+ def numeric_or_downcase(val)
132
+ val.respond_to?(:coerce) ? val : val.downcase
133
+ end
134
+ end
135
+
38
136
  class Chart
39
137
  include Writexlsx::Utility
40
138
 
@@ -81,45 +179,19 @@ module Writexlsx
81
179
 
82
180
  @subtype = subtype
83
181
  @sheet_type = 0x0200
84
- @orientation = 0x0
85
182
  @series = []
86
183
  @embedded = 0
87
184
  @id = ''
88
185
  @series_index = 0
89
186
  @style_id = 2
90
- @axis_ids = []
91
- @axis2_ids = []
92
- @cat_has_num_fmt = false
93
- @requires_category = 0
94
- @legend_position = 'right'
95
- @cat_axis_position = 'b'
96
- @val_axis_position = 'l'
97
187
  @formula_ids = {}
98
188
  @formula_data = []
99
- @horiz_cat_axis = 0
100
- @horiz_val_axis = 1
101
189
  @protection = 0
102
- @chartarea = {}
103
- @plotarea = {}
190
+ @chartarea = ChartArea.new
191
+ @plotarea = ChartArea.new
104
192
  @title = Caption.new(self)
105
- @x_axis = Axis.new(self)
106
- @y_axis = Axis.new(self)
107
- @x2_axis = Axis.new(self)
108
- @y2_axis = Axis.new(self)
109
193
  @name = ''
110
- @show_blanks = 'gap'
111
- @show_hidden_data = false
112
- @show_crosses = true
113
- @width = 480
114
- @height = 288
115
- @x_scale = 1
116
- @y_scale = 1
117
- @x_offset = 0
118
- @y_offset = 0
119
194
  @table = nil
120
- @smooth_allowed = 0
121
- @cross_between = 'between'
122
-
123
195
  set_default_properties
124
196
  end
125
197
 
@@ -174,6 +246,7 @@ module Writexlsx
174
246
  # Set the properties of the x-axis.
175
247
  #
176
248
  def set_x_axis(params = {})
249
+ @date_category = true if ptrue?(params[:date_axis])
177
250
  @x_axis.merge_with_hash(params)
178
251
  end
179
252
 
@@ -184,6 +257,7 @@ module Writexlsx
184
257
  # The properties that can be set are the same as for set_x_axis,
185
258
  #
186
259
  def set_y_axis(params = {})
260
+ @date_category = true if ptrue?(params[:date_axis])
187
261
  @y_axis.merge_with_hash(params)
188
262
  end
189
263
 
@@ -191,6 +265,7 @@ module Writexlsx
191
265
  # Set the properties of the secondary X-axis.
192
266
  #
193
267
  def set_x2_axis(params = {})
268
+ @date_category = true if ptrue?(params[:date_axis])
194
269
  @x2_axis.merge_with_hash(params)
195
270
  end
196
271
 
@@ -198,6 +273,7 @@ module Writexlsx
198
273
  # Set the properties of the secondary Y-axis.
199
274
  #
200
275
  def set_y2_axis(params = {})
276
+ @date_category = true if ptrue?(params[:date_axis])
201
277
  @y2_axis.merge_with_hash(params)
202
278
  end
203
279
 
@@ -229,7 +305,7 @@ module Writexlsx
229
305
  #
230
306
  def set_plotarea(params)
231
307
  # Convert the user defined properties to internal properties.
232
- @plotarea = area_properties(params)
308
+ @plotarea = ChartArea.new(params)
233
309
  end
234
310
 
235
311
  #
@@ -237,7 +313,7 @@ module Writexlsx
237
313
  #
238
314
  def set_chartarea(params)
239
315
  # Convert the user defined properties to internal properties.
240
- @chartarea = area_properties(params)
316
+ @chartarea = ChartArea.new(params)
241
317
  end
242
318
 
243
319
  #
@@ -306,14 +382,8 @@ module Writexlsx
306
382
 
307
383
  # Set the up and down bar properties.
308
384
  @up_down_bars = {
309
- :_up => {
310
- :_line => line_properties(params[:up][:line]),
311
- :_fill => line_properties(params[:up][:fill])
312
- },
313
- :_down => {
314
- :_line => line_properties(params[:down][:line]),
315
- :_fill => line_properties(params[:down][:fill])
316
- }
385
+ :_up => Chartline.new(params[:up]),
386
+ :_down => Chartline.new(params[:down])
317
387
  }
318
388
  end
319
389
 
@@ -321,20 +391,14 @@ module Writexlsx
321
391
  # Set properties for the chart drop lines.
322
392
  #
323
393
  def set_drop_lines(params = {})
324
- # Set the drop line properties.
325
- line = line_properties(params[:line])
326
-
327
- @drop_lines = { :_line => line }
394
+ @drop_lines = Chartline.new(params)
328
395
  end
329
396
 
330
397
  #
331
398
  # Set properties for the chart high-low lines.
332
399
  #
333
400
  def set_high_low_lines(params = {})
334
- # Set the drop line properties.
335
- line = line_properties(params[:line])
336
-
337
- @hi_low_lines = { :_line => line }
401
+ @hi_low_lines = Chartline.new(params)
338
402
  end
339
403
 
340
404
  #
@@ -390,7 +454,21 @@ module Writexlsx
390
454
  #
391
455
  def convert_font_args(params)
392
456
  return unless params
393
- font = {
457
+ font = params_to_font(params)
458
+
459
+ # Convert font size units.
460
+ font[:_size] *= 100 if font[:_size] && font[:_size] != 0
461
+
462
+ # Convert rotation into 60,000ths of a degree.
463
+ if ptrue?(font[:_rotation])
464
+ font[:_rotation] = 60_000 * font[:_rotation].to_i
465
+ end
466
+
467
+ font
468
+ end
469
+
470
+ def params_to_font(params)
471
+ {
394
472
  :_name => params[:name],
395
473
  :_color => params[:color],
396
474
  :_size => params[:size],
@@ -402,16 +480,6 @@ module Writexlsx
402
480
  :_baseline => params[:baseline] || 0,
403
481
  :_rotation => params[:rotation]
404
482
  }
405
-
406
- # Convert font size units.
407
- font[:_size] *= 100 if font[:_size] && font[:_size] != 0
408
-
409
- # Convert rotation into 60,000ths of a degree.
410
- if ptrue?(font[:_rotation])
411
- font[:_rotation] = 60_000 * font[:_rotation].to_i
412
- end
413
-
414
- font
415
483
  end
416
484
 
417
485
  #
@@ -435,56 +503,61 @@ module Writexlsx
435
503
  # If there is no user defined data then it will be populated by the parent
436
504
  # workbook in Workbook::_add_chart_data
437
505
  #
438
- def get_data_id(formula, data) # :nodoc:
439
- # Ignore series without a range formula.
440
- return unless formula
506
+ def data_id(full_formula, data) # :nodoc:
507
+ return unless full_formula
441
508
 
442
509
  # Strip the leading '=' from the formula.
443
- formula = formula.sub(/^=/, '')
510
+ formula = full_formula.sub(/^=/, '')
444
511
 
445
512
  # Store the data id in a hash keyed by the formula and store the data
446
513
  # in a separate array with the same id.
447
- if !@formula_ids.has_key?(formula)
448
- # Haven't seen this formula before.
449
- id = @formula_data.size
450
-
451
- @formula_data << data
452
- @formula_ids[formula] = id
453
- else
514
+ if @formula_ids.has_key?(formula)
454
515
  # Formula already seen. Return existing id.
455
516
  id = @formula_ids[formula]
456
-
457
517
  # Store user defined data if it isn't already there.
458
- @formula_data[id] = data unless @formula_data[id]
518
+ @formula_data[id] ||= data
519
+ else
520
+ # Haven't seen this formula before.
521
+ id = @formula_ids[formula] = @formula_data.size
522
+ @formula_data << data
459
523
  end
460
524
 
461
525
  id
462
526
  end
463
527
 
464
- #
465
- # Convert user defined layout properties to the format required internally.
466
- #
467
- def layout_properties(args, is_text = false)
468
- return unless ptrue?(args)
469
-
470
- properties = is_text ? [:x, :y] : [:x, :y, :width, :height]
471
-
472
- # Check for valid properties.
473
- allowable = Hash.new
474
- allowable[properties.size] = nil
475
-
476
- # Set the layout properties
477
- layout = Hash.new
478
- properties.each do |property|
479
- value = args[property]
480
- # Convert to the format used by Excel for easier testing.
481
- layout[property] = sprintf("%.17g", value)
482
- end
528
+ private
483
529
 
484
- layout
530
+ def axis_setup
531
+ @axis_ids = []
532
+ @axis2_ids = []
533
+ @cat_has_num_fmt = false
534
+ @requires_category = 0
535
+ @cat_axis_position = 'b'
536
+ @val_axis_position = 'l'
537
+ @horiz_cat_axis = 0
538
+ @horiz_val_axis = 1
539
+ @x_axis = Axis.new(self)
540
+ @y_axis = Axis.new(self)
541
+ @x2_axis = Axis.new(self)
542
+ @y2_axis = Axis.new(self)
485
543
  end
486
544
 
487
- private
545
+ def display_setup
546
+ @orientation = 0x0
547
+ @width = 480
548
+ @height = 288
549
+ @x_scale = 1
550
+ @y_scale = 1
551
+ @x_offset = 0
552
+ @y_offset = 0
553
+ @legend_position = 'right'
554
+ @smooth_allowed = 0
555
+ @cross_between = 'between'
556
+ @date_category = false
557
+ @show_blanks = 'gap'
558
+ @show_hidden_data = false
559
+ @show_crosses = true
560
+ end
488
561
 
489
562
  #
490
563
  # retun primary/secondary series by :primary_axes flag
@@ -520,151 +593,15 @@ module Writexlsx
520
593
  #
521
594
  # Convert the user specified colour index or string to a rgb colour.
522
595
  #
523
- def get_color(color) # :nodoc:
524
- # Convert a HTML style #RRGGBB color.
525
- if color and color =~ /^#[0-9a-fA-F]{6}$/
526
- color = color.sub(/^#/, '')
527
- return color.upcase
528
- end
529
-
530
- index = Format.get_color(color)
531
-
532
- # Set undefined colors to black.
533
- unless index
534
- index = 0x08
535
- raise "Unknown color '#{color}' used in chart formatting."
536
- end
537
-
538
- get_palette_color(index)
539
- end
540
-
541
- #
542
- # Convert from an Excel internal colour index to a XML style #RRGGBB index
543
- # based on the default or user defined values in the Workbook palette.
544
- # Note: This version doesn't add an alpha channel.
545
- #
546
- def get_palette_color(index) # :nodoc:
547
- palette = @palette
548
-
549
- # Adjust the colour index.
550
- index -= 8
551
-
552
- # Palette is passed in from the Workbook class.
553
- rgb = palette[index]
554
-
555
- sprintf("%02X%02X%02X", *rgb)
556
- end
557
-
558
- #
559
- # Get the Spreadsheet::WriteExcel line pattern for backward compatibility.
560
- #
561
- def get_swe_line_pattern(val)
562
- value = val.downcase
563
- default = 'solid'
564
-
565
- patterns = {
566
- 0 => 'solid',
567
- 1 => 'dash',
568
- 2 => 'dot',
569
- 3 => 'dash_dot',
570
- 4 => 'long_dash_dot_dot',
571
- 5 => 'none',
572
- 6 => 'solid',
573
- 7 => 'solid',
574
- 8 => 'solid',
575
- 'solid' => 'solid',
576
- 'dash' => 'dash',
577
- 'dot' => 'dot',
578
- 'dash-dot' => 'dash_dot',
579
- 'dash-dot-dot' => 'long_dash_dot_dot',
580
- 'none' => 'none',
581
- 'dark-gray' => 'solid',
582
- 'medium-gray' => 'solid',
583
- 'light-gray' => 'solid'
584
- }
585
-
586
- patterns[value] || default
587
- end
588
-
589
- #
590
- # Get the Spreadsheet::WriteExcel line weight for backward compatibility.
591
- #
592
- def get_swe_line_weight(val)
593
- value = val.downcase
594
- default = 1
595
-
596
- weights = {
597
- 1 => 0.25,
598
- 2 => 1,
599
- 3 => 2,
600
- 4 => 3,
601
- 'hairline' => 0.25,
602
- 'narrow' => 1,
603
- 'medium' => 2,
604
- 'wide' => 3
605
- }
606
-
607
- weights[value] || default
608
- end
609
-
610
- #
611
- # Convert user defined fill properties to the structure required internally.
612
- #
613
- def fill_properties(fill) # :nodoc:
614
- return { :_defined => 0 } unless fill
615
-
616
- fill[:_defined] = 1
617
-
618
- fill
619
- end
620
-
621
- #
622
- # Convert user defined area properties to the structure required internally.
623
- #
624
- def area_properties(arg) # :nodoc:
625
- area = {}
626
-
627
- # Map deprecated Spreadsheet::WriteExcel fill colour.
628
- arg[:fill] = { :color => arg[:color] } if arg[:color]
629
-
630
- # Map deprecated Spreadsheet::WriteExcel line_weight.
631
- if arg[:line_weight]
632
- width = get_swe_line_weight(arg[:line_weight])
633
- arg[:border] = { :width => width }
634
- end
635
-
636
- # Map deprecated Spreadsheet::WriteExcel line_pattern.
637
- if arg[:line_pattern]
638
- pattern = get_swe_line_pattern(arg[:line_pattern])
639
- if pattern == 'none'
640
- arg[:border] = { :none => 1 }
641
- else
642
- arg[:border][:dash_type] = pattern
643
- end
596
+ def color(color_code) # :nodoc:
597
+ if color_code and color_code =~ /^#[0-9a-fA-F]{6}$/
598
+ # Convert a HTML style #RRGGBB color.
599
+ color_code.sub(/^#/, '').upcase
600
+ else
601
+ index = Format.color(color_code)
602
+ raise "Unknown color '#{color_code}' used in chart formatting." unless index
603
+ palette_color(index)
644
604
  end
645
-
646
- # Map deprecated Spreadsheet::WriteExcel line colour.
647
- arg[:border][:color] = arg[:line_color] if arg[:line_color]
648
-
649
- # Handle Excel::Writer::XLSX style properties.
650
-
651
- # Set the line properties for the chartarea.
652
- line = line_properties(arg[:line])
653
-
654
- # Allow 'border' as a synonym for 'line'.
655
- line = line_properties(arg[:border]) if (arg[:border])
656
-
657
- # Set the fill properties for the chartarea.
658
- fill = fill_properties(arg[:fill])
659
-
660
- # Set the plotarea layout.
661
- layout = layout_properties(arg[:layout])
662
-
663
- area[:_line] = line
664
- area[:_fill] = fill
665
- area[:_layout] = layout
666
-
667
- return area
668
605
  end
669
606
 
670
607
  #
@@ -740,38 +677,55 @@ module Writexlsx
740
677
  # Setup the default properties for a chart.
741
678
  #
742
679
  def set_default_properties # :nodoc:
743
- # Set the default axis properties.
744
- @x_axis.defaults = {
680
+ display_setup
681
+ axis_setup
682
+ set_axis_defaults
683
+
684
+ set_x_axis
685
+ set_y_axis
686
+
687
+ set_x2_axis
688
+ set_y2_axis
689
+ end
690
+
691
+ def set_axis_defaults
692
+ @x_axis.defaults = x_axis_defaults
693
+ @y_axis.defaults = y_axis_defaults
694
+ @x2_axis.defaults = x2_axis_defaults
695
+ @y2_axis.defaults = y2_axis_defaults
696
+ end
697
+
698
+ def x_axis_defaults
699
+ {
745
700
  :num_format => 'General',
746
701
  :major_gridlines => { :visible => 0 }
747
702
  }
703
+ end
748
704
 
749
- @y_axis.defaults = {
705
+ def y_axis_defaults
706
+ {
750
707
  :num_format => 'General',
751
708
  :major_gridlines => { :visible => 1 }
752
709
  }
710
+ end
753
711
 
754
- @x2_axis.defaults = {
712
+ def x2_axis_defaults
713
+ {
755
714
  :num_format => 'General',
756
715
  :label_position => 'none',
757
716
  :crossing => 'max',
758
717
  :visible => 0
759
718
  }
719
+ end
760
720
 
761
- @y2_axis.defaults = {
721
+ def y2_axis_defaults
722
+ {
762
723
  :num_format => 'General',
763
724
  :major_gridlines => { :visible => 0 },
764
725
  :position => 'right',
765
726
  :visible => 1
766
727
  }
767
-
768
- set_x_axis
769
- set_y_axis
770
-
771
- set_x2_axis
772
- set_y2_axis
773
728
  end
774
-
775
729
  #
776
730
  # Write the <c:chartSpace> element.
777
731
  #
@@ -846,36 +800,43 @@ module Writexlsx
846
800
  #
847
801
  # Write the <c:plotArea> element.
848
802
  #
849
-
850
803
  def write_plot_area # :nodoc:
851
- write_plot_area_base
852
- end
853
-
854
- def write_plot_area_base(type = nil) # :nodoc:
855
804
  @writer.tag_elements('c:plotArea') do
856
805
  # Write the c:layout element.
857
- write_layout(@plotarea[:_layout], 'plot')
806
+ write_layout(@plotarea.layout, 'plot')
858
807
  # Write the subclass chart type elements for primary and secondary axes.
859
808
  write_chart_type(:primary_axes => 1)
860
809
  write_chart_type(:primary_axes => 0)
861
810
 
862
- # Write the c:catAx elements for series using primary axes.
811
+ # Write the category and value elements for the primary axes.
863
812
  params = {
864
813
  :x_axis => @x_axis,
865
814
  :y_axis => @y_axis,
866
815
  :axis_ids => @axis_ids
867
816
  }
868
- write_cat_or_date_axis(params, type)
817
+
818
+ if @date_category
819
+ write_date_axis(params)
820
+ else
821
+ write_cat_axis(params)
822
+ end
823
+
869
824
  write_val_axis(params)
870
825
 
871
- # Write c:valAx and c:catAx elements for series using secondary axes.
826
+ # Write the category and value elements for the secondary axes.
872
827
  params = {
873
828
  :x_axis => @x2_axis,
874
829
  :y_axis => @y2_axis,
875
830
  :axis_ids => @axis2_ids
876
831
  }
832
+
877
833
  write_val_axis(params)
878
- write_cat_or_date_axis(params, type)
834
+
835
+ if @date_category
836
+ write_date_axis(params)
837
+ else
838
+ write_cat_axis(params)
839
+ end
879
840
 
880
841
  # Write the c:dTable element.
881
842
  write_d_table
@@ -885,14 +846,6 @@ module Writexlsx
885
846
  end
886
847
  end
887
848
 
888
- def write_cat_or_date_axis(params, type)
889
- if type == :stock
890
- write_date_axis(params)
891
- else
892
- write_cat_axis(params)
893
- end
894
- end
895
-
896
849
  #
897
850
  # Write the <c:layout> element.
898
851
  #
@@ -968,7 +921,7 @@ module Writexlsx
968
921
  # Write the c:marker element.
969
922
  write_marker(series.marker)
970
923
  # Write the c:invertIfNegative element.
971
- write_c_invert_if_negative(series.invert_if_neg)
924
+ write_c_invert_if_negative(series.invert_if_negative)
972
925
  # Write the c:dPt element.
973
926
  write_d_pt(series.points)
974
927
  # Write the c:dLbls element.
@@ -1203,29 +1156,20 @@ module Writexlsx
1203
1156
  #
1204
1157
  # Write the <c:valAx> element. Usually the Y axis.
1205
1158
  #
1206
- # TODO. Maybe should have a _write_cat_val_axis method as well for scatter.
1207
- #
1208
- def write_val_axis(params, cat = false) # :nodoc:
1159
+ def write_val_axis(params)
1209
1160
  axis_ids = params[:axis_ids]
1210
- position = params[:position] || @val_axis_position
1211
- horiz = @horiz_val_axis
1212
- if cat
1213
- x_axis = params[:y_axis]
1214
- y_axis = params[:x_axis]
1215
- axis_ids_0 = axis_ids[1]
1216
- axis_ids_1 = axis_ids[0]
1217
- else
1218
- x_axis = params[:x_axis]
1219
- y_axis = params[:y_axis]
1220
- axis_ids_0 = axis_ids[0]
1221
- axis_ids_1 = axis_ids[1]
1222
- end
1223
-
1224
1161
  return unless axis_ids && !axis_ids.empty?
1225
1162
 
1226
- # OVerwrite the default axis position with a user supplied value.
1227
- position = y_axis.position || position
1163
+ x_axis = params[:x_axis]
1164
+ y_axis = params[:y_axis]
1165
+ axis_ids_0 = axis_ids[0]
1166
+ axis_ids_1 = axis_ids[1]
1167
+ position = y_axis.position || params[:position] || @val_axis_position
1228
1168
 
1169
+ write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position)
1170
+ end
1171
+
1172
+ def write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position) # :nodoc:
1229
1173
  @writer.tag_elements('c:valAx') do
1230
1174
  write_axis_id(axis_ids_1)
1231
1175
 
@@ -1245,9 +1189,9 @@ module Writexlsx
1245
1189
 
1246
1190
  # Write the axis title elements.
1247
1191
  if y_axis.formula
1248
- write_title_formula(y_axis, horiz, nil, y_axis.layout)
1192
+ write_title_formula(y_axis, @horiz_val_axis, nil, y_axis.layout)
1249
1193
  elsif y_axis.name
1250
- write_title_rich(y_axis, horiz, y_axis.layout)
1194
+ write_title_rich(y_axis, @horiz_val_axis, y_axis.layout)
1251
1195
  end
1252
1196
 
1253
1197
  # Write the c:numberFormat element.
@@ -1278,16 +1222,6 @@ module Writexlsx
1278
1222
  end
1279
1223
  end
1280
1224
 
1281
- #
1282
- # Write the <c:valAx> element.
1283
- # This is for the second valAx in scatter plots.
1284
- #
1285
- # Usually the X axis.
1286
- #
1287
- def write_cat_val_axis(params) # :nodoc:
1288
- write_val_axis(params, true)
1289
- end
1290
-
1291
1225
  #
1292
1226
  # Write the <c:dateAx> element. Usually the X axis.
1293
1227
  #
@@ -1350,15 +1284,11 @@ module Writexlsx
1350
1284
  # Write the c:majorUnit element.
1351
1285
  write_c_major_unit(x_axis.major_unit)
1352
1286
  # Write the c:majorTimeUnit element.
1353
- if !x_axis.major_unit.nil?
1354
- write_c_major_time_unit(x_axis.major_unit_type)
1355
- end
1287
+ write_c_major_time_unit(x_axis.major_unit_type) if x_axis.major_unit
1356
1288
  # Write the c:minorUnit element.
1357
1289
  write_c_minor_unit(x_axis.minor_unit)
1358
1290
  # Write the c:minorTimeUnit element.
1359
- if !x_axis.minor_unit.nil?
1360
- write_c_minor_time_unit(x_axis.minor_unit_type)
1361
- end
1291
+ write_c_minor_time_unit(x_axis.minor_unit_type) if x_axis.minor_unit
1362
1292
  end
1363
1293
  end
1364
1294
 
@@ -1419,18 +1349,14 @@ module Writexlsx
1419
1349
  # Write the <c:max> element.
1420
1350
  #
1421
1351
  def write_c_max(max = nil) # :nodoc:
1422
- return if max.nil?
1423
-
1424
- @writer.empty_tag('c:max', [ ['val', max] ])
1352
+ @writer.empty_tag('c:max', [ ['val', max] ]) if max
1425
1353
  end
1426
1354
 
1427
1355
  #
1428
1356
  # Write the <c:min> element.
1429
1357
  #
1430
1358
  def write_c_min(min = nil) # :nodoc:
1431
- return if min.nil?
1432
-
1433
- @writer.empty_tag('c:min', [ ['val', min] ])
1359
+ @writer.empty_tag('c:min', [ ['val', min] ]) if min
1434
1360
  end
1435
1361
 
1436
1362
  #
@@ -1550,8 +1476,14 @@ module Writexlsx
1550
1476
  end
1551
1477
 
1552
1478
  def write_gridlines_base(tag, gridlines) # :nodoc:
1479
+ return unless gridlines
1553
1480
  return if gridlines.respond_to?(:[]) and !ptrue?(gridlines[:_visible])
1554
- write_lines_base(tag, gridlines)
1481
+
1482
+ if gridlines.line_defined?
1483
+ @writer.tag_elements(tag) { write_sp_pr(gridlines) }
1484
+ else
1485
+ @writer.empty_tag(tag)
1486
+ end
1555
1487
  end
1556
1488
 
1557
1489
  #
@@ -1603,49 +1535,34 @@ module Writexlsx
1603
1535
  # Write the <c:legend> element.
1604
1536
  #
1605
1537
  def write_legend # :nodoc:
1606
- position = @legend_position
1607
- overlay = false
1608
-
1609
- if @legend_delete_series && @legend_delete_series.kind_of?(Array)
1610
- @delete_series = @legend_delete_series
1611
- end
1612
-
1613
- if position =~ /^overlay_/
1614
- position.sub!(/^overlay_/, '')
1615
- overlay = true if position
1616
- end
1617
-
1618
- allowed = {
1619
- 'right' => 'r',
1620
- 'left' => 'l',
1621
- 'top' => 't',
1622
- 'bottom' => 'b'
1623
- }
1624
-
1625
- return if position == 'none'
1626
- return unless allowed.has_key?(position)
1627
-
1628
- position = allowed[position]
1538
+ position = @legend_position.sub(/^overlay_/, '')
1539
+ return if position == 'none' || (not position_allowed.has_key?(position))
1629
1540
 
1541
+ @delete_series = @legend_delete_series if @legend_delete_series.kind_of?(Array)
1630
1542
  @writer.tag_elements('c:legend') do
1631
1543
  # Write the c:legendPos element.
1632
- write_legend_pos(position)
1544
+ write_legend_pos(position_allowed[position])
1633
1545
  # Remove series labels from the legend.
1634
- @delete_series.each do |index|
1635
- # Write the c:legendEntry element.
1636
- write_legend_entry(index)
1637
- end if @delete_series
1546
+ # Write the c:legendEntry element.
1547
+ @delete_series.each { |i| write_legend_entry(i) } if @delete_series
1638
1548
  # Write the c:layout element.
1639
1549
  write_layout(@legend_layout, 'legend')
1640
1550
  # Write the c:txPr element.
1641
- if ptrue?(@legend_font)
1642
- write_tx_pr(nil, @legend_font)
1643
- end
1551
+ write_tx_pr(nil, @legend_font) if ptrue?(@legend_font)
1644
1552
  # Write the c:overlay element.
1645
- write_overlay if overlay
1553
+ write_overlay if @legend_position =~ /^overlay_/
1646
1554
  end
1647
1555
  end
1648
1556
 
1557
+ def position_allowed
1558
+ {
1559
+ 'right' => 'r',
1560
+ 'left' => 'l',
1561
+ 'top' => 't',
1562
+ 'bottom' => 'b'
1563
+ }
1564
+ end
1565
+
1649
1566
  #
1650
1567
  # Write the <c:legendPos> element.
1651
1568
  #
@@ -1962,13 +1879,13 @@ module Writexlsx
1962
1879
  marker ||= @default_marker
1963
1880
 
1964
1881
  return unless ptrue?(marker)
1965
- return if ptrue?(marker[:automatic])
1882
+ return if ptrue?(marker.automatic?)
1966
1883
 
1967
1884
  @writer.tag_elements('c:marker') do
1968
1885
  # Write the c:symbol element.
1969
- write_symbol(marker[:type])
1886
+ write_symbol(marker.type)
1970
1887
  # Write the c:size element.
1971
- size = marker[:size]
1888
+ size = marker.size
1972
1889
  write_marker_size(size) if ptrue?(size)
1973
1890
  # Write the c:spPr element.
1974
1891
  write_sp_pr(marker)
@@ -2002,8 +1919,8 @@ module Writexlsx
2002
1919
  # Write the <c:spPr> element.
2003
1920
  #
2004
1921
  def write_sp_pr(series) # :nodoc:
2005
- line = series.respond_to?(:line) ? series.line : series[:_line]
2006
- fill = series.respond_to?(:fill) ? series.fill : series[:_fill]
1922
+ line = series.line
1923
+ fill = series.fill
2007
1924
 
2008
1925
  return if (!line || !ptrue?(line[:_defined])) &&
2009
1926
  (!fill || !ptrue?(fill[:_defined]))
@@ -2031,7 +1948,8 @@ module Writexlsx
2031
1948
  attributes = []
2032
1949
 
2033
1950
  # Add the line width as an attribute.
2034
- if width = line[:width]
1951
+ if line[:width]
1952
+ width = line[:width]
2035
1953
  # Round width to nearest 0.25, like Excel.
2036
1954
  width = ((width + 0.125) * 4).to_i / 4.0
2037
1955
 
@@ -2051,9 +1969,9 @@ module Writexlsx
2051
1969
  write_a_solid_fill(line)
2052
1970
  end
2053
1971
  # Write the line/dash type.
2054
- if type = line[:dash_type]
1972
+ if line[:dash_type]
2055
1973
  # Write the a:prstDash element.
2056
- write_a_prst_dash(type)
1974
+ write_a_prst_dash(line[:dash_type])
2057
1975
  end
2058
1976
  end
2059
1977
  end
@@ -2070,12 +1988,8 @@ module Writexlsx
2070
1988
  #
2071
1989
  def write_a_solid_fill(line) # :nodoc:
2072
1990
  @writer.tag_elements('a:solidFill') do
2073
- if line[:color]
2074
- color = get_color(line[:color])
2075
-
2076
- # Write the a:srgbClr element.
2077
- write_a_srgb_clr(color)
2078
- end
1991
+ # Write the a:srgbClr element.
1992
+ write_a_srgb_clr(color(line[:color])) if line[:color]
2079
1993
  end
2080
1994
  end
2081
1995
 
@@ -2101,19 +2015,19 @@ module Writexlsx
2101
2015
 
2102
2016
  @writer.tag_elements('c:trendline') do
2103
2017
  # Write the c:name element.
2104
- write_name(trendline[:name])
2018
+ write_name(trendline.name)
2105
2019
  # Write the c:spPr element.
2106
2020
  write_sp_pr(trendline)
2107
2021
  # Write the c:trendlineType element.
2108
- write_trendline_type(trendline[:type])
2022
+ write_trendline_type(trendline.type)
2109
2023
  # Write the c:order element for polynomial trendlines.
2110
- write_trendline_order(trendline[:order]) if trendline[:type] == 'poly'
2024
+ write_trendline_order(trendline.order) if trendline.type == 'poly'
2111
2025
  # Write the c:period element for moving average trendlines.
2112
- write_period(trendline[:period]) if trendline[:type] == 'movingAvg'
2026
+ write_period(trendline.period) if trendline.type == 'movingAvg'
2113
2027
  # Write the c:forward element.
2114
- write_forward(trendline[:forward])
2028
+ write_forward(trendline.forward)
2115
2029
  # Write the c:backward element.
2116
- write_backward(trendline[:backward])
2030
+ write_backward(trendline.backward)
2117
2031
  end
2118
2032
  end
2119
2033
 
@@ -2169,23 +2083,20 @@ module Writexlsx
2169
2083
  # Write the <c:hiLowLines> element.
2170
2084
  #
2171
2085
  def write_hi_low_lines # :nodoc:
2172
- write_lines_base('c:hiLowLines', @hi_low_lines)
2086
+ write_lines_base(@hi_low_lines, 'c:hiLowLines')
2173
2087
  end
2174
2088
 
2175
2089
  #
2176
2090
  # Write the <c:dropLines> elent.
2177
2091
  #
2178
2092
  def write_drop_lines
2179
- write_lines_base('c:dropLines', @drop_lines)
2093
+ write_lines_base(@drop_lines, 'c:dropLines')
2180
2094
  end
2181
2095
 
2182
- #
2183
- # used from write_drop_lines and write_hi_low_lines
2184
- #
2185
- def write_lines_base(tag, lines)
2096
+ def write_lines_base(lines, tag)
2186
2097
  return unless lines
2187
2098
 
2188
- if lines[:_line] && ptrue?(lines[:_line][:_defined])
2099
+ if lines.line_defined?
2189
2100
  @writer.tag_elements(tag) { write_sp_pr(lines) }
2190
2101
  else
2191
2102
  @writer.empty_tag(tag)
@@ -2446,17 +2357,17 @@ module Writexlsx
2446
2357
  write_err_dir(direction)
2447
2358
 
2448
2359
  # Write the c:errBarType element.
2449
- write_err_bar_type(error_bars[:_direction])
2360
+ write_err_bar_type(error_bars.direction)
2450
2361
 
2451
2362
  # Write the c:errValType element.
2452
- write_err_val_type(error_bars[:_type])
2363
+ write_err_val_type(error_bars.type)
2453
2364
 
2454
- unless ptrue?(error_bars[:_endcap])
2365
+ unless ptrue?(error_bars.endcap)
2455
2366
  # Write the c:noEndCap element.
2456
2367
  write_no_end_cap
2457
2368
  end
2458
2369
 
2459
- case error_bars[:_type]
2370
+ case error_bars.type
2460
2371
  when 'stdErr'
2461
2372
  # Don't need to write a c:errValType tag.
2462
2373
  when 'cust'
@@ -2464,7 +2375,7 @@ module Writexlsx
2464
2375
  write_custom_error(error_bars)
2465
2376
  else
2466
2377
  # Write the c:val element.
2467
- write_error_val(error_bars[:_value])
2378
+ write_error_val(error_bars.value)
2468
2379
  end
2469
2380
 
2470
2381
  # Write the c:spPr element.
@@ -2511,23 +2422,23 @@ module Writexlsx
2511
2422
  # Write the custom error bars type.
2512
2423
  #
2513
2424
  def write_custom_error(error_bars)
2514
- if ptrue?(error_bars[:_plus_values])
2515
- # Write the c:plus element.
2516
- @writer.tag_elements('c:plus') do
2517
- if error_bars[:_plus_values] =~ /^=/ # '=Sheet1!$A$1:$A$5'
2518
- write_num_ref(error_bars[:_plus_values], error_bars[:_plus_data], 'num')
2519
- else # [1, 2, 3]
2520
- write_num_lit(error_bars[:_plus_values])
2521
- end
2522
- end
2523
- # Write the c:minus element.
2524
- @writer.tag_elements('c:minus') do
2525
- if error_bars[:_minus_values] =~ /^=/ # '=Sheet1!$A$1:$A$5'
2526
- write_num_ref(error_bars[:_minus_values], error_bars[:_minus_data], 'num')
2527
- else # [1, 2, 3]
2528
- write_num_lit(error_bars[:_minus_values])
2529
- end
2530
- end
2425
+ if ptrue?(error_bars.plus_values)
2426
+ write_custom_error_base('c:plus', error_bars.plus_values, error_bars.plus_data)
2427
+ write_custom_error_base('c:minus', error_bars.minus_values, error_bars.minus_data)
2428
+ end
2429
+ end
2430
+
2431
+ def write_custom_error_base(tag, values, data)
2432
+ @writer.tag_elements(tag) do
2433
+ write_num_ref_or_lit(values, data)
2434
+ end
2435
+ end
2436
+
2437
+ def write_num_ref_or_lit(values, data)
2438
+ if values =~ /^=/ # '=Sheet1!$A$1:$A$5'
2439
+ write_num_ref(values, data, 'num')
2440
+ else # [1, 2, 3]
2441
+ write_num_lit(values)
2531
2442
  end
2532
2443
  end
2533
2444
 
@@ -2584,7 +2495,7 @@ module Writexlsx
2584
2495
  end
2585
2496
 
2586
2497
  def write_bars_base(tag, format)
2587
- if ptrue?(format[:_line][:_defined]) || ptrue?(format[:_fill][:_defined])
2498
+ if format.line_defined? || format.fill_defined?
2588
2499
  @writer.tag_elements(tag) { write_sp_pr(format) }
2589
2500
  else
2590
2501
  @writer.empty_tag(tag)