write_xlsx 0.81.1 → 0.83.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -0
  3. data/Changes +20 -0
  4. data/README.md +3 -2
  5. data/examples/chart_combined.rb +107 -0
  6. data/examples/chart_data_table.rb +1 -1
  7. data/examples/chart_pareto.rb +82 -0
  8. data/lib/write_xlsx/chart.rb +115 -42
  9. data/lib/write_xlsx/chart/axis.rb +5 -1
  10. data/lib/write_xlsx/chart/bar.rb +13 -0
  11. data/lib/write_xlsx/chart/pie.rb +7 -0
  12. data/lib/write_xlsx/chart/scatter.rb +25 -57
  13. data/lib/write_xlsx/format.rb +40 -0
  14. data/lib/write_xlsx/package/app.rb +39 -1
  15. data/lib/write_xlsx/package/comments.rb +1 -5
  16. data/lib/write_xlsx/package/content_types.rb +48 -13
  17. data/lib/write_xlsx/package/core.rb +14 -12
  18. data/lib/write_xlsx/package/packager.rb +17 -41
  19. data/lib/write_xlsx/package/styles.rb +215 -256
  20. data/lib/write_xlsx/package/table.rb +5 -7
  21. data/lib/write_xlsx/package/xml_writer_simple.rb +2 -0
  22. data/lib/write_xlsx/sheets.rb +2 -2
  23. data/lib/write_xlsx/utility.rb +17 -2
  24. data/lib/write_xlsx/version.rb +1 -1
  25. data/lib/write_xlsx/workbook.rb +44 -2
  26. data/lib/write_xlsx/worksheet.rb +26 -17
  27. data/lib/write_xlsx/worksheet/page_setup.rb +12 -6
  28. data/test/chart/test_write_style.rb +2 -2
  29. data/test/helper.rb +3 -0
  30. data/test/package/app/test_app01.rb +1 -1
  31. data/test/package/app/test_app02.rb +1 -1
  32. data/test/package/app/test_app03.rb +1 -1
  33. data/test/package/content_types/test_content_types.rb +1 -1
  34. data/test/package/content_types/test_write_default.rb +1 -1
  35. data/test/package/content_types/test_write_override.rb +1 -1
  36. data/test/perl_output/chart_combined.xlsx +0 -0
  37. data/test/perl_output/chart_pareto.xlsx +0 -0
  38. data/test/regression/test_button07.rb +5 -2
  39. data/test/regression/test_button13.rb +34 -0
  40. data/test/regression/test_button14.rb +31 -0
  41. data/test/regression/test_chart_column11.rb +45 -0
  42. data/test/regression/test_chart_column12.rb +45 -0
  43. data/test/regression/test_chart_combined01.rb +37 -0
  44. data/test/regression/test_chart_combined02.rb +43 -0
  45. data/test/regression/test_chart_combined03.rb +45 -0
  46. data/test/regression/test_chart_combined04.rb +47 -0
  47. data/test/regression/test_chart_combined05.rb +49 -0
  48. data/test/regression/test_chart_combined06.rb +49 -0
  49. data/test/regression/test_chart_combined07.rb +53 -0
  50. data/test/regression/test_chart_combined08.rb +65 -0
  51. data/test/regression/test_chart_data_labels24.rb +50 -0
  52. data/test/regression/test_chart_date05.rb +57 -0
  53. data/test/regression/test_chart_format20.rb +55 -0
  54. data/test/regression/test_format11.rb +28 -0
  55. data/test/regression/test_format12.rb +41 -0
  56. data/test/regression/test_landscape01.rb +27 -0
  57. data/test/regression/test_quote_name04.rb +40 -0
  58. data/test/regression/test_set_start_page01.rb +4 -7
  59. data/test/regression/test_set_start_page02.rb +33 -0
  60. data/test/regression/test_set_start_page03.rb +33 -0
  61. data/test/regression/test_table17.rb +70 -0
  62. data/test/regression/xlsx_files/chart_column11.xlsx +0 -0
  63. data/test/regression/xlsx_files/chart_column12.xlsx +0 -0
  64. data/test/regression/xlsx_files/chart_combined01.xlsx +0 -0
  65. data/test/regression/xlsx_files/chart_combined02.xlsx +0 -0
  66. data/test/regression/xlsx_files/chart_combined03.xlsx +0 -0
  67. data/test/regression/xlsx_files/chart_combined04.xlsx +0 -0
  68. data/test/regression/xlsx_files/chart_combined05.xlsx +0 -0
  69. data/test/regression/xlsx_files/chart_combined06.xlsx +0 -0
  70. data/test/regression/xlsx_files/chart_combined07.xlsx +0 -0
  71. data/test/regression/xlsx_files/chart_combined08.xlsx +0 -0
  72. data/test/regression/xlsx_files/chart_date05.xlsx +0 -0
  73. data/test/regression/xlsx_files/chart_format20.xlsx +0 -0
  74. data/test/regression/xlsx_files/format11.xlsx +0 -0
  75. data/test/regression/xlsx_files/format12.xlsx +0 -0
  76. data/test/regression/xlsx_files/landscape01.xlsx +0 -0
  77. data/test/regression/xlsx_files/quote_name04.xlsx +0 -0
  78. data/test/regression/xlsx_files/set_start_page01.xlsx +0 -0
  79. data/test/regression/xlsx_files/set_start_page02.xlsx +0 -0
  80. data/test/regression/xlsx_files/set_start_page03.xlsx +0 -0
  81. data/test/regression/xlsx_files/table17.xlsx +0 -0
  82. data/test/test_example_match.rb +172 -0
  83. data/write_xlsx.gemspec +1 -0
  84. metadata +106 -3
@@ -14,7 +14,7 @@ class Axis < Caption
14
14
  attr_reader :log_base, :crossing, :position_axis, :label_position, :visible
15
15
  attr_reader :num_format_linked, :num_font, :layout, :interval_unit
16
16
  attr_reader :major_gridlines, :minor_gridlines, :reverse
17
- attr_reader :line, :fill
17
+ attr_reader :line, :fill, :text_axis
18
18
  #
19
19
  # Convert user defined axis values into axis instance.
20
20
  #
@@ -37,6 +37,10 @@ def merge_with_hash(params) # :nodoc:
37
37
  set_axis_name_layout(args)
38
38
  set_axis_line(args)
39
39
  set_axis_fill(args)
40
+ if ptrue?(args[:text_axis])
41
+ @chart.date_category = false
42
+ @text_axis = true
43
+ end
40
44
  end
41
45
 
42
46
  #
@@ -42,6 +42,19 @@ def initialize(subtype)
42
42
  }
43
43
  end
44
44
 
45
+ #
46
+ # Override parent method to add an extra check that is required for Bar
47
+ # charts to ensure that their combined chart is on a secondary axis.
48
+ #
49
+ def combine(chart)
50
+
51
+ unless chart.is_secondary?
52
+ raise 'Charts combined with Bar charts must be on a secondary axis'
53
+ end
54
+
55
+ super
56
+ end
57
+
45
58
  #
46
59
  # Override the virtual superclass method with a chart specific method.
47
60
  #
@@ -42,6 +42,13 @@ def initialize(subtype)
42
42
  }
43
43
  end
44
44
 
45
+ #
46
+ # Override parent method to add a warning.
47
+ #
48
+ def combine(chart)
49
+ raise "Combined chart not currently supported for Pie charts"
50
+ end
51
+
45
52
  #
46
53
  # Set the Pie/Doughnut chart rotation: the angle of the first slice.
47
54
  #
@@ -58,6 +58,13 @@ def initialize(subtype)
58
58
  }
59
59
  end
60
60
 
61
+ #
62
+ # Override parent method to add a warning.
63
+ #
64
+ def combine(chart)
65
+ raise 'Combined chart not currently supported with scatter chart as the primary chart'
66
+ end
67
+
61
68
  #
62
69
  # Override the virtual superclass method with a chart specific method.
63
70
  #
@@ -102,28 +109,8 @@ def write_scatter_chart(params)
102
109
  # Write the <c:ser> element.
103
110
  #
104
111
  def write_ser(series)
105
- index = @series_index
106
- @series_index += 1
107
-
108
112
  @writer.tag_elements('c:ser') do
109
- # Write the c:idx element.
110
- write_idx(index)
111
- # Write the c:order element.
112
- write_order(index)
113
- # Write the series name.
114
- write_series_name(series)
115
- # Write the c:spPr element.
116
- write_sp_pr(series)
117
- # Write the c:marker element.
118
- write_marker(series.marker)
119
- # Write the c:dPt element.
120
- write_d_pt(series.points)
121
- # Write the c:dLbls element.
122
- write_d_lbls(series.labels)
123
- # Write the c:trendline element.
124
- write_trendline(series.trendline)
125
- # Write the c:errBars element.
126
- write_error_bars(series.error_bars)
113
+ write_ser_base(series)
127
114
  # Write the c:xVal element.
128
115
  write_x_val(series)
129
116
  # Write the c:yVal element.
@@ -135,6 +122,7 @@ def write_ser(series)
135
122
  write_c_smooth(series.smooth)
136
123
  end
137
124
  end
125
+ @series_index += 1
138
126
  end
139
127
 
140
128
  #
@@ -148,41 +136,23 @@ def write_plot_area
148
136
  # Write the c:layout element.
149
137
  write_layout(@plotarea.layout, 'plot')
150
138
 
151
- # Write the subclass chart type elements for primary and secondary axes
139
+ # Write the subclass chart type elements for primary and secondary axes.
152
140
  write_chart_type(:primary_axes => 1)
153
141
  write_chart_type(:primary_axes => 0)
154
142
 
155
- # Write c:catAx and c:valAx elements for series using primary axes
156
- write_cat_val_axis(
157
- :x_axis => @x_axis,
158
- :y_axis => @y_axis,
159
- :axis_ids => @axis_ids,
160
- :position => 'b'
161
- )
143
+ # Write c:catAx and c:valAx elements for series using primary axes.
144
+ write_cat_val_axis(@x_axis, @y_axis, @axis_ids, 'b')
145
+
162
146
  tmp = @horiz_val_axis
163
147
  @horiz_val_axis = 1
164
- write_val_axis(
165
- :x_axis => @x_axis,
166
- :y_axis => @y_axis,
167
- :axis_ids => @axis_ids,
168
- :position => 'l'
169
- )
148
+ write_val_axis(@x_axis, @y_axis, @axis_ids, 'l')
170
149
  @horiz_val_axis = tmp
171
150
 
172
- # Write c:valAx and c:catAx elements for series using secondary axes
173
- write_cat_val_axis(
174
- :x_axis => @x2_axis,
175
- :y_axis => @y2_axis,
176
- :axis_ids => @axis2_ids,
177
- :position => 'b'
178
- )
151
+ # Write c:valAx and c:catAx elements for series using secondary axes.
152
+ write_cat_val_axis(@x2_axis, @y2_axis, @axis2_ids, 'b')
153
+
179
154
  @horiz_val_axis = 1
180
- write_val_axis(
181
- :x_axis => @x2_axis,
182
- :y_axis => @y2_axis,
183
- :axis_ids => @axis2_ids,
184
- :position => 'l'
185
- )
155
+ write_val_axis(@x2_axis, @y2_axis, @axis2_ids, 'l')
186
156
 
187
157
  # Write the c:spPr element for the plotarea formatting.
188
158
  write_sp_pr(@plotarea)
@@ -269,17 +239,15 @@ def modify_series_formatting
269
239
  #
270
240
  # Usually the X axis.
271
241
  #
272
- def write_cat_val_axis(params) # :nodoc:
273
- axis_ids = params[:axis_ids]
242
+ def write_cat_val_axis(x_axis, y_axis, axis_ids, position) # :nodoc:
274
243
  return unless axis_ids && !axis_ids.empty?
275
244
 
276
- x_axis = params[:y_axis]
277
- y_axis = params[:x_axis]
278
- axis_ids_0 = axis_ids[1]
279
- axis_ids_1 = axis_ids[0]
280
- position = y_axis.position || params[:position] || @val_axis_position
281
-
282
- write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position)
245
+ write_val_axis_base(
246
+ y_axis, x_axis,
247
+ axis_ids[1],
248
+ axis_ids[0],
249
+ x_axis.position || position || @val_axis_position
250
+ )
283
251
  end
284
252
  end
285
253
  end
@@ -784,6 +784,46 @@ def write_font_rpr(writer, worksheet) #:nodoc:
784
784
  end
785
785
  end
786
786
 
787
+ def border_attributes
788
+ attributes = []
789
+
790
+ # Diagonal borders add attributes to the <border> element.
791
+ if diag_type == 1
792
+ attributes << ['diagonalUp', 1]
793
+ elsif diag_type == 2
794
+ attributes << ['diagonalDown', 1]
795
+ elsif diag_type == 3
796
+ attributes << ['diagonalUp', 1]
797
+ attributes << ['diagonalDown', 1]
798
+ end
799
+ attributes
800
+ end
801
+
802
+ def xf_attributes
803
+ attributes = [
804
+ ['numFmtId', num_format_index],
805
+ ['fontId' , font_index],
806
+ ['fillId' , fill_index],
807
+ ['borderId', border_index],
808
+ ['xfId' , 0]
809
+ ]
810
+ attributes << ['applyNumberFormat', 1] if num_format_index > 0
811
+ # Add applyFont attribute if XF format uses a font element.
812
+ attributes << ['applyFont', 1] if font_index > 0
813
+ # Add applyFill attribute if XF format uses a fill element.
814
+ attributes << ['applyFill', 1] if fill_index > 0
815
+ # Add applyBorder attribute if XF format uses a border element.
816
+ attributes << ['applyBorder', 1] if border_index > 0
817
+
818
+ # Check if XF format has alignment properties set.
819
+ apply_align, align = get_align_properties
820
+ # We can also have applyAlignment without a sub-element.
821
+ attributes << ['applyAlignment', 1] if apply_align
822
+ attributes << ['applyProtection', 1] if get_protection_properties
823
+
824
+ attributes
825
+ end
826
+
787
827
  private
788
828
 
789
829
  def write_font_shapes(writer)
@@ -8,8 +8,9 @@ class App
8
8
 
9
9
  include Writexlsx::Utility
10
10
 
11
- def initialize
11
+ def initialize(workbook)
12
12
  @writer = Package::XMLWriterSimple.new
13
+ @workbook = workbook
13
14
  @part_names = []
14
15
  @heading_pairs = []
15
16
  @properties = {}
@@ -37,10 +38,47 @@ def assemble_xml_file
37
38
  end
38
39
  end
39
40
 
41
+ def add_worksheet_heading_pairs
42
+ add_heading_pair(
43
+ [
44
+ 'Worksheets',
45
+ @workbook.worksheets.reject {|s| s.is_chartsheet?}.count
46
+ ]
47
+ )
48
+ end
49
+
50
+ def add_chartsheet_heading_pairs
51
+ add_heading_pair(['Charts', @workbook.chartsheet_count])
52
+ end
53
+
54
+ def add_worksheet_part_names
55
+ @workbook.worksheets.
56
+ reject { |sheet| sheet.is_chartsheet? }.
57
+ each { |sheet| add_part_name(sheet.name) }
58
+ end
59
+
60
+ def add_chartsheet_part_names
61
+ @workbook.worksheets.
62
+ select { |sheet| sheet.is_chartsheet? }.
63
+ each { |sheet| add_part_name(sheet.name) }
64
+ end
65
+
40
66
  def add_part_name(part_name)
41
67
  @part_names.push(part_name)
42
68
  end
43
69
 
70
+ def add_named_range_heading_pairs
71
+ range_count = @workbook.named_ranges.size
72
+
73
+ if range_count != 0
74
+ add_heading_pair([ 'Named Ranges', range_count ])
75
+ end
76
+ end
77
+
78
+ def add_named_ranges_parts
79
+ @workbook.named_ranges.each { |named_range| add_part_name(named_range) }
80
+ end
81
+
44
82
  def add_heading_pair(heading_pair)
45
83
  return if heading_pair[1] == 0
46
84
 
@@ -311,11 +311,7 @@ def comments_visible?
311
311
  # Write the <comments> element.
312
312
  #
313
313
  def write_comments
314
- xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
315
-
316
- attributes = [
317
- ['xmlns', xmlns]
318
- ]
314
+ attributes = [ ['xmlns', XMLWriterSimple::XMLNS] ]
319
315
 
320
316
  @writer.tag_elements('comments', attributes) { yield }
321
317
  end
@@ -11,8 +11,9 @@ class ContentTypes
11
11
  App_package = 'application/vnd.openxmlformats-package.'
12
12
  App_document = 'application/vnd.openxmlformats-officedocument.'
13
13
 
14
- def initialize
14
+ def initialize(workbook)
15
15
  @writer = Package::XMLWriterSimple.new
16
+ @workbook = workbook
16
17
  @defaults = [
17
18
  [ 'rels', "#{App_package}relationships+xml" ],
18
19
  [ 'xml', 'application/xml' ]
@@ -52,6 +53,12 @@ def add_override(part_name, content_type)
52
53
  @overrides.push([part_name, content_type])
53
54
  end
54
55
 
56
+ def add_worksheet_names
57
+ @workbook.non_chartsheet_count.times do |index|
58
+ add_worksheet_name("sheet#{index+1}")
59
+ end
60
+ end
61
+
55
62
  #
56
63
  # Add the name of a worksheet to the ContentTypes overrides.
57
64
  #
@@ -61,6 +68,12 @@ def add_worksheet_name(name)
61
68
  add_override(worksheet_name, "#{App_document}spreadsheetml.worksheet+xml")
62
69
  end
63
70
 
71
+ def add_chartsheet_names
72
+ @workbook.chartsheet_count.times do |index|
73
+ add_chartsheet_name("sheet#{index+1}")
74
+ end
75
+ end
76
+
64
77
  #
65
78
  # Add the name of a chartsheet to the ContentTypes overrides.
66
79
  #
@@ -70,6 +83,10 @@ def add_chartsheet_name(name)
70
83
  add_override(chartsheet_name, "#{App_document}spreadsheetml.chartsheet+xml")
71
84
  end
72
85
 
86
+ def add_chart_names
87
+ (1 .. @workbook.charts.size).each { |i| add_chart_name("chart#{i}") }
88
+ end
89
+
73
90
  #
74
91
  # Add the name of a chart to the ContentTypes overrides.
75
92
  #
@@ -79,6 +96,12 @@ def add_chart_name(name)
79
96
  add_override(chart_name, "#{App_document}drawingml.chart+xml")
80
97
  end
81
98
 
99
+ def add_drawing_names
100
+ (1 .. @workbook.drawings.size).each do |i|
101
+ add_drawing_name("drawing#{i}")
102
+ end
103
+ end
104
+
82
105
  #
83
106
  # Add the name of a drawing to the ContentTypes overrides.
84
107
  #
@@ -95,6 +118,12 @@ def add_vml_name
95
118
  add_default('vml', "#{App_document}vmlDrawing")
96
119
  end
97
120
 
121
+ def add_comment_names
122
+ (1 .. @workbook.num_comment_files).each do |i|
123
+ add_comment_name("comments#{i}")
124
+ end
125
+ end
126
+
98
127
  #
99
128
  # Add the name of a comment to the ContentTypes overrides.
100
129
  #
@@ -121,8 +150,14 @@ def add_calc_chain
121
150
  #
122
151
  # Add the image default types.
123
152
  #
124
- def add_image_types(types)
125
- types.each_key { |type| add_default(type, "image/#{type}") }
153
+ def add_image_types
154
+ @workbook.image_types.each_key do |type|
155
+ add_default(type, "image/#{type}")
156
+ end
157
+ end
158
+
159
+ def add_table_names(table_count)
160
+ (1 .. table_count).each { |i| add_table_name("table#{i}") }
126
161
  end
127
162
 
128
163
  #
@@ -159,11 +194,7 @@ def change_the_workbook_xml_content_type_from_xlsx_to_xlsm
159
194
  #
160
195
  def write_defaults
161
196
  @defaults.each do |a|
162
- @writer.empty_tag('Default',
163
- [
164
- ['Extension', a[0]],
165
- ['ContentType', a[1]]
166
- ])
197
+ write_default_or_override('Default', 'Extension', a)
167
198
  end
168
199
  end
169
200
 
@@ -172,14 +203,18 @@ def write_defaults
172
203
  #
173
204
  def write_overrides
174
205
  @overrides.each do |a|
175
- @writer.empty_tag('Override',
176
- [
177
- ['PartName', a[0]],
178
- ['ContentType', a[1]]
179
- ])
206
+ write_default_or_override('Override', 'PartName', a)
180
207
  end
181
208
  end
182
209
 
210
+ def write_default_or_override(tag, param0, a)
211
+ @writer.empty_tag(tag,
212
+ [
213
+ [param0, a[0]],
214
+ ['ContentType', a[1]]
215
+ ])
216
+ end
217
+
183
218
  #
184
219
  # Write the <Types> element.
185
220
  #
@@ -23,18 +23,7 @@ def set_xml_writer(filename)
23
23
 
24
24
  def assemble_xml_file
25
25
  write_xml_declaration do
26
- write_cp_core_properties do
27
- write_dc_title
28
- write_dc_subject
29
- write_dc_creator
30
- write_cp_keywords
31
- write_dc_description
32
- write_cp_last_modified_by
33
- write_dcterms_created
34
- write_dcterms_modified
35
- write_cp_category
36
- write_cp_content_status
37
- end
26
+ write_cp_core_properties { write_cp_core_properties_base }
38
27
  end
39
28
  end
40
29
 
@@ -44,6 +33,19 @@ def set_properties(properties)
44
33
 
45
34
  private
46
35
 
36
+ def write_cp_core_properties_base
37
+ write_dc_title
38
+ write_dc_subject
39
+ write_dc_creator
40
+ write_cp_keywords
41
+ write_dc_description
42
+ write_cp_last_modified_by
43
+ write_dcterms_created
44
+ write_dcterms_modified
45
+ write_cp_category
46
+ write_cp_content_status
47
+ end
48
+
47
49
  #
48
50
  # Convert a localtime() date to a ISO 8601 style "2010-01-01T00:00:00Z" date.
49
51
  #