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) }