lib/write_xlsx/chart.rb in write_xlsx-0.81.1 vs lib/write_xlsx/chart.rb in write_xlsx-0.83.0

- old
+ new

@@ -139,11 +139,15 @@ attr_accessor :id, :name # :nodoc: attr_writer :index, :palette, :protection # :nodoc: attr_reader :embedded, :formula_ids, :formula_data # :nodoc: attr_reader :x_scale, :y_scale, :x_offset, :y_offset # :nodoc: attr_reader :width, :height # :nodoc: - attr_reader :label_positions, :label_position_default + attr_reader :label_positions, :label_position_default, :combined # :nodoc: + attr_writer :date_category, :already_inserted + attr_writer :series_index + attr_writer :writer + attr_reader :x2_axis, :y2_axis, :axis2_ids # # Factory method for returning chart objects based on their class type. # def self.factory(current_subclass, subtype = nil) # :nodoc: @@ -183,11 +187,11 @@ @subtype = subtype @sheet_type = 0x0200 @series = [] @embedded = 0 - @id = '' + @id = -1 @series_index = 0 @style_id = 2 @formula_ids = {} @formula_data = [] @protection = 0 @@ -195,10 +199,12 @@ @plotarea = ChartArea.new @title = Caption.new(self) @name = '' @table = nil set_default_properties + @combined = nil + @is_secondary = false end def set_xml_writer(filename) # :nodoc: @writer.set_xml_writer(filename) end @@ -243,10 +249,15 @@ # Set the secondary axis properties. x2_axis = params[:x2_axis] y2_axis = params[:y2_axis] + # Store secondary status for combined charts. + if ptrue?(x2_axis) || ptrue?(y2_axis) + @is_secondary = true + end + # Set the gap and overlap for Bar/Column charts. if params[:gap] if ptrue?(y2_axis) @series_gap_2 = params[:gap] else @@ -340,11 +351,11 @@ # # Set on of the 42 built-in Excel chart styles. The default style is 2. # def set_style(style_id = 2) - style_id = 2 if style_id < 0 || style_id > 42 + style_id = 2 if style_id < 0 || style_id > 48 @style_id = style_id end # # Set the option for displaying blank data in a chart. The default is 'gap'. @@ -422,10 +433,17 @@ def set_high_low_lines(params = {}) @hi_low_lines = Chartline.new(params) end # + # Add another chart to create a combined chart. + # + def combine(chart) + @combined = chart + end + + # # Setup the default configuration data for an embedded chart. # def set_embedded_config_data @embedded = 1 end @@ -516,11 +534,16 @@ # # Switch name and name_formula parameters if required. # def process_names(name = nil, name_formula = nil) # :nodoc: # Name looks like a formula, use it to set name_formula. - if name && name =~ /^=[^!]+!\$/ + if name.respond_to?(:to_ary) + cell = xl_rowcol_to_cell(name[1], name[2], 1, 1) + name_formula = "#{quote_sheetname(name[0])}!#{cell}" + name = '' + elsif + name && name =~ /^=[^!]+!\$/ name_formula = name name = '' end [name, name_formula] @@ -554,10 +577,18 @@ end id end + def already_inserted? + @already_inserted + end + + def is_secondary? + @is_secondary + end + private def axis_setup @axis_ids = [] @axis2_ids = [] @@ -661,15 +692,15 @@ @axis2_ids += ids end end def ids - chart_id = 1 + @id + chart_id = 5001 + @id axis_count = 1 + @axis2_ids.size + @axis_ids.size - id1 = sprintf('5%03d%04d', chart_id, axis_count) - id2 = sprintf('5%03d%04d', chart_id, axis_count + 1) + id1 = sprintf('%04d%04d', chart_id, axis_count) + id2 = sprintf('%04d%04d', chart_id, axis_count + 1) [id1, id2] end # @@ -830,17 +861,39 @@ # # Write the <c:plotArea> element. # def write_plot_area # :nodoc: + second_chart = @combined @writer.tag_elements('c:plotArea') do # Write the c:layout element. write_layout(@plotarea.layout, 'plot') # Write the subclass chart type elements for primary and secondary axes. write_chart_type(:primary_axes => 1) write_chart_type(:primary_axes => 0) + # Configure a combined chart if present. + if second_chart + + # Secondary axis has unique id otherwise use same as primary. + if second_chart.is_secondary? + second_chart.id = 1000 + @id + else + second_chart.id = @id + end + + # Share the same writer for writing. + second_chart.writer = @writer + + # Share series index with primary chart. + second_chart.series_index = @series_index + + # Write the subclass chart type elements for combined chart. + second_chart.write_chart_type(:primary_axes => 1) + second_chart.write_chart_type(:primary_axes => 0) + end + # Write the category and value elements for the primary axes. params = { :x_axis => @x_axis, :y_axis => @y_axis, :axis_ids => @axis_ids @@ -850,21 +903,37 @@ write_date_axis(params) else write_cat_axis(params) end - write_val_axis(params) + write_val_axis(@x_axis, @y_axis, @axis_ids) # Write the category and value elements for the secondary axes. params = { :x_axis => @x2_axis, :y_axis => @y2_axis, :axis_ids => @axis2_ids } - write_val_axis(params) + write_val_axis(@x2_axis, @y2_axis, @axis2_ids) + # Write the secondary axis for the secondary chart. + if second_chart && second_chart.is_secondary? + + params = { + :x_axis => second_chart.x2_axis, + :y_axis => second_chart.y2_axis, + :axis_ids => second_chart.axis2_ids + } + + second_chart.write_val_axis( + second_chart.x2_axis, + second_chart.y2_axis, + second_chart.axis2_ids + ) + end + if @date_category write_date_axis(params) else write_cat_axis(params) end @@ -935,43 +1004,48 @@ # # Write the <c:ser> element. # def write_ser(series) # :nodoc: - index = @series_index - @series_index += 1 - @writer.tag_elements('c:ser') do - # Write the c:idx element. - write_idx(index) - # Write the c:order element. - write_order(index) - # Write the series name. - write_series_name(series) - # Write the c:spPr element. - write_sp_pr(series) - # Write the c:marker element. - write_marker(series.marker) - # Write the c:invertIfNegative element. - write_c_invert_if_negative(series.invert_if_negative) - # Write the c:dPt element. - write_d_pt(series.points) - # Write the c:dLbls element. - write_d_lbls(series.labels) - # Write the c:trendline element. - write_trendline(series.trendline) - # Write the c:errBars element. - write_error_bars(series.error_bars) + write_ser_base(series) do + write_c_invert_if_negative(series.invert_if_negative) + end # Write the c:cat element. write_cat(series) # Write the c:val element. write_val(series) # Write the c:smooth element. write_c_smooth(series.smooth) if ptrue?(@smooth_allowed) end + @series_index += 1 end + def write_ser_base(series) + # Write the c:idx element. + write_idx(@series_index) + # Write the c:order element. + write_order(@series_index) + # Write the series name. + write_series_name(series) + # Write the c:spPr element. + write_sp_pr(series) + # Write the c:marker element. + write_marker(series.marker) + + yield if block_given? + + # Write the c:dPt element. + write_d_pt(series.points) + # Write the c:dLbls element. + write_d_lbls(series.labels) + # Write the c:trendline element. + write_trendline(series.trendline) + # Write the c:errBars element. + write_error_bars(series.error_bars) + end + # # Write the <c:idx> element. # def write_idx(val) # :nodoc: @writer.empty_tag('c:idx', [ ['val', val] ]) @@ -1175,11 +1249,11 @@ if @show_crosses || ptrue?(x_axis.visible) write_crossing(y_axis.crossing) end # Write the c:auto element. - write_auto(1) + write_auto(1) unless x_axis.text_axis # Write the c:labelAlign element. write_label_align('ctr') # Write the c:labelOffset element. write_label_offset(100) # Write the c:tickLblSkip element. @@ -1188,21 +1262,20 @@ end # # Write the <c:valAx> element. Usually the Y axis. # - def write_val_axis(params) - axis_ids = params[:axis_ids] + def write_val_axis(x_axis, y_axis, axis_ids, position = nil) return unless axis_ids && !axis_ids.empty? - x_axis = params[:x_axis] - y_axis = params[:y_axis] - axis_ids_0 = axis_ids[0] - axis_ids_1 = axis_ids[1] - position = y_axis.position || params[:position] || @val_axis_position - - write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position) + write_val_axis_base( + x_axis, y_axis, + axis_ids[0], + axis_ids[1], + y_axis.position || position || @val_axis_position + ) end + public :write_val_axis def write_val_axis_base(x_axis, y_axis, axis_ids_0, axis_ids_1, position) # :nodoc: @writer.tag_elements('c:valAx') do write_axis_id(axis_ids_1)