write_xlsx 0.81.1 → 0.83.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 (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
  #