write_xlsx 0.75.0 → 0.76.0

Sign up to get free protection for your applications and to get access to all the features.
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)