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

- old
+ new

@@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- require 'write_xlsx/package/xml_writer_simple' +require 'write_xlsx/gradient' require 'write_xlsx/utility' require 'write_xlsx/chart/axis' require 'write_xlsx/chart/caption' require 'write_xlsx/chart/series' @@ -35,25 +36,33 @@ end end class ChartArea include Writexlsx::Utility + include Writexlsx::Gradient - attr_reader :line, :fill, :layout + attr_reader :line, :fill, :gradient, :layout def initialize(params = {}) @layout = layout_properties(params[:layout]) # Allow 'border' as a synonym for 'line'. border = params_to_border(params) # Set the line properties for the chartarea. @line = border ? line_properties(border) : line_properties(params[:line]) + # Set the gradient fill properties for the series. + @gradient = gradient_properties(params[:gradient]) + # Map deprecated Spreadsheet::WriteExcel fill colour. fill = params[:color] ? { :color => params[:color] } : params[:fill] @fill = fill_properties(fill) + + if ptrue?(@gradient) + @fill = nil + end end private def params_to_border(params) @@ -639,10 +648,11 @@ # def get_data_type(data) # :nodoc: # Check for no data in the series. return 'none' unless data return 'none' if data.empty? + return 'multi_str' if data.first.kind_of?(Array) # If the token isn't a number assume it is a string. data.each do |token| next unless token return 'str' unless token.kind_of?(Numeric) @@ -1087,10 +1097,14 @@ type = get_data_type(data) if type == 'str' @cat_has_num_fmt = false # Write the c:strRef element. write_str_ref(formula, data, type) + elsif type == 'multi_str' + @cat_has_num_fmt = false + # Write the c:multiLvLStrRef element. + write_multi_lvl_str_ref(formula, data) else @cat_has_num_fmt = true # Write the c:numRef element. write_num_ref(formula, data, type) end @@ -1145,10 +1159,36 @@ def write_str_ref(formula, data, type) # :nodoc: write_num_or_str_ref('c:strRef', formula, data, type) end # + # Write the <c:multiLvLStrRef> element. + # + def write_multi_lvl_str_ref(formula, data) + return if data.empty? + + @writer.tag_elements('c:multiLvlStrRef') do + # Write the c:f element. + write_series_formula(formula) + + @writer.tag_elements('c:multiLvlStrCache') do + + # Write the c:ptCount element. + write_pt_count(data.last.size) + + # Write the data arrays in reverse order. + data.reverse.each do |arr| + @writer.tag_elements('c:lvl') do + # Write the c:pt element. + arr.each_with_index {|a, i| write_pt(i, a)} + end + end + end + end + end + + # # Write the <c:numLit> element for literal number list elements. # def write_num_lit(data) write_num_base('c:numLit', data) end @@ -1327,10 +1367,13 @@ # Write the c:majorUnit element. write_c_major_unit(y_axis.major_unit) # Write the c:minorUnit element. write_c_minor_unit(y_axis.minor_unit) + + # Write the c:dispUnits element. + write_disp_units(y_axis.display_units, y_axis.display_units_visible) end end # # Write the <c:dateAx> element. Usually the X axis. @@ -2043,15 +2086,16 @@ # # Write the <c:spPr> element. # def write_sp_pr(series) # :nodoc: - line = series.line - fill = series.fill + line = series.line + fill = series.fill + gradient = series.gradient if series.respond_to?(:gradient) return if (!line || !ptrue?(line[:_defined])) && - (!fill || !ptrue?(fill[:_defined])) + (!fill || !ptrue?(fill[:_defined])) && !gradient @writer.tag_elements('c:spPr') do # Write the fill elements for solid charts such as pie/doughnut and bar. if fill && fill[:_defined] != 0 if ptrue?(fill[:none]) @@ -2060,10 +2104,14 @@ else # Write the a:solidFill element. write_a_solid_fill(fill) end end + if ptrue?(gradient) + # Write the a:gradFill element. + write_a_grad_fill(gradient) + end # Write the a:ln element. write_a_ln(line) if line && ptrue?(line[:_defined]) end end @@ -2638,9 +2686,148 @@ return unless ptrue?(smooth) attributes = [ ['val', 1] ] @writer.empty_tag('c:smooth', attributes) + end + + # + # Write the <c:dispUnits> element. + # + def write_disp_units(units, display) + return unless ptrue?(units) + + attributes = [ ['val', units] ] + + @writer.tag_elements('c:dispUnits') do + @writer.empty_tag('c:builtInUnit', attributes) + if ptrue?(display) + @writer.tag_elements('c:dispUnitsLbl') do + @writer.empty_tag('c:layout') + end + end + end + end + + # + # Write the <a:gradFill> element. + # + def write_a_grad_fill(gradient) + attributes = [ + ['flip', 'none'], + ['rotWithShape', 1] + ] + attributes = [] if gradient[:type] == 'linear' + + @writer.tag_elements('a:gradFill', attributes) do + + # Write the a:gsLst element. + write_a_gs_lst(gradient) + + if gradient[:type] == 'linear' + # Write the a:lin element. + write_a_lin(gradient[:angle]) + else + # Write the a:path element. + write_a_path(gradient[:type]) + + # Write the a:tileRect element. + write_a_tile_rect(gradient[:type]) + end + end + end + + # + # Write the <a:gsLst> element. + # + def write_a_gs_lst(gradient) + positions = gradient[:positions] + colors = gradient[:colors] + + @writer.tag_elements('a:gsLst') do + (0..colors.size-1).each do |i| + pos = (positions[i] * 1000).to_i + + attributes = [ ['pos', pos] ] + @writer.tag_elements('a:gs', attributes) do + + color = color(colors[i]) + + # Write the a:srgbClr element. + # TODO: Wait for a feature request to support transparency. + write_a_srgb_clr( color ); + end + end + end + end + + # + # Write the <a:lin> element. + # + def write_a_lin(angle) + scaled = 0 + + angle = (60000 * angle).to_i + + attributes = [ + ['ang', angle], + ['scaled', scaled] + ] + + @writer.empty_tag('a:lin', attributes) + end + + # + # Write the <a:path> element. + # + def write_a_path(type) + attributes = [ ['path', type] ] + + @writer.tag_elements('a:path', attributes) do + # Write the a:fillToRect element. + write_a_fill_to_rect(type) + end + end + + # + # Write the <a:fillToRect> element. + # + def write_a_fill_to_rect(type) + attributes = [] + + if type == 'shape' + attributes = [ + ['l' , 50000], + ['t' , 50000], + ['r' , 50000], + ['b' , 50000] + ] + else + attributes = [ + ['l', 100000], + ['t', 100000] + ] + end + + @writer.empty_tag('a:fillToRect', attributes) + end + + # + # Write the <a:tileRect> element. + # + def write_a_tile_rect(type) + attributes = [] + + if type == 'shape' + attributes = [] + else + attributes = [ + ['r', -100000], + ['b', -100000] + ] + end + + @writer.empty_tag('a:tileRect', attributes) end def write_bars_base(tag, format) if format.line_defined? || format.fill_defined? @writer.tag_elements(tag) { write_sp_pr(format) }