diff --git a/lib/axlsx/drawing/bar_chart.rb b/lib/axlsx/drawing/bar_chart.rb
new file mode 100644
index 00000000..2ba4fc2f
--- /dev/null
+++ b/lib/axlsx/drawing/bar_chart.rb
@@ -0,0 +1,96 @@
+module Axlsx
+
+ class BarChart < Chart
+
+ # the category axis
+ # @return [CatAxis]
+ def cat_axis
+ axes[:cat_axis]
+ end
+ alias :catAxis :cat_axis
+
+ # the value axis
+ # @return [ValAxis]
+ def val_axis
+ axes[:val_axis]
+ end
+ alias :valAxis :val_axis
+
+ # The direction of the bars in the chart
+ # must be one of [:bar, :col]
+ # @return [Symbol]
+ def bar_dir
+ @bar_dir ||= :bar
+ end
+ alias :barDir :bar_dir
+
+ #grouping for a column, line, or area chart.
+ # must be one of [:percentStacked, :clustered, :standard, :stacked]
+ # @return [Symbol]
+ def grouping
+ @grouping ||= :clustered
+ end
+
+ # space between bar or column clusters, as a percentage of the bar or column width.
+ # @return [String]
+ def gap_width
+ @gap_width ||= 150
+ end
+ alias :gapWidth :gap_width
+
+ # validation regex for gap amount percent
+ GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/
+
+ def initialize(frame, options={})
+ @gap_width = nil
+ super(frame, options)
+ @series_type = BarSeries
+ @d_lbls = nil
+ @vary_colors = true
+ end
+
+ def to_xml_string(str = '')
+ super(str) do |str_inner|
+ str_inner << ''
+ str_inner << ''
+ str_inner << ''
+ str_inner << ''
+ @series.each { |ser| ser.to_xml_string(str_inner) }
+ @d_lbls.to_xml_string(str_inner) if @d_lbls
+ str_inner << '' unless @gap_width.nil?
+ axes.to_xml_string(str_inner, :ids => true)
+ str_inner << ''
+ axes.to_xml_string(str_inner)
+ end
+ end
+
+ # The direction of the bars in the chart
+ # must be one of [:bar, :col]
+ def bar_dir=(v)
+ RestrictionValidator.validate "BarChart.bar_dir", [:bar, :col], v
+ @bar_dir = v
+ end
+ alias :barDir= :bar_dir=
+
+ #grouping for a column, line, or area chart.
+ # must be one of [:percentStacked, :clustered, :standard, :stacked]
+ def grouping=(v)
+ RestrictionValidator.validate "BarChart.grouping", [:percentStacked, :clustered, :standard, :stacked], v
+ @grouping = v
+ end
+
+ # space between bar or column clusters, as a percentage of the bar or column width.
+ def gap_width=(v)
+ RegexValidator.validate "BarChart.gap_width", GAP_AMOUNT_PERCENT, v
+ @gap_width=(v)
+ end
+ alias :gapWidth= :gap_width=
+
+ # A hash of axes used by this chart. Bar charts have a value and
+ # category axes specified via axes[:val_axes] and axes[:cat_axis]
+ # @return [Axes]
+ def axes
+ @axes ||= Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis)
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/axlsx/drawing/bar_series.rb b/lib/axlsx/drawing/bar_series.rb
index d85345c9..888e3da8 100644
--- a/lib/axlsx/drawing/bar_series.rb
+++ b/lib/axlsx/drawing/bar_series.rb
@@ -53,6 +53,7 @@ def shape=(v)
# @return [String]
def to_xml_string(str = '')
super(str) do |str_inner|
+ @legend_color = @colors.first
colors.each_with_index do |c, index|
str_inner << ''
diff --git a/lib/axlsx/drawing/chart.rb b/lib/axlsx/drawing/chart.rb
index c1d408e6..488fd1c7 100644
--- a/lib/axlsx/drawing/chart.rb
+++ b/lib/axlsx/drawing/chart.rb
@@ -14,15 +14,16 @@ class Chart
# @option options [Array|String|Cell] start_at The X, Y coordinates defining the top left corner of the chart.
# @option options [Array|String|Cell] end_at The X, Y coordinates defining the bottom right corner of the chart.
def initialize(frame, options={})
- @style = 18
- @view_3D = nil
+ @style = 18
+ @view_3D = nil
@graphic_frame=frame
@graphic_frame.anchor.drawing.worksheet.workbook.charts << self
- @series = SimpleTypedList.new Series
- @show_legend = true
+ @series = SimpleTypedList.new Series
+ @show_legend = true
@display_blanks_as = :gap
- @series_type = Series
- @title = Title.new
+ @series_type = Series
+ @title = Title.new
+ @bg_color = nil
parse_options options
start_at(*options[:start_at]) if options[:start_at]
end_at(*options[:end_at]) if options[:end_at]
@@ -53,10 +54,16 @@ def d_lbls
# Indicates that colors should be varied by datum
# @return [Boolean]
attr_reader :vary_colors
-
+
# Configures the vary_colors options for this chart
# @param [Boolean] v The value to set
- def vary_colors=(v) Axlsx::validate_boolean(v); @vary_colors = v; end
+ def vary_colors=(v)
+ Axlsx::validate_boolean(v); @vary_colors = v;
+ end
+
+ # the background color for chart
+ # @return [String]
+ attr_reader :bg_color
# The title object for the chart.
# @return [Title]
@@ -71,6 +78,7 @@ def vary_colors=(v) Axlsx::validate_boolean(v); @vary_colors = v; end
# @return [Boolean]
attr_reader :show_legend
+
# How to display blank values
# Options are
# * gap: Display nothing
@@ -98,6 +106,11 @@ def pn
"#{CHART_PN % (index+1)}"
end
+ # @see color
+ def bg_color=(v)
+ @bg_color = v
+ end
+
# The title object for the chart.
# @param [String, Cell] v
# @return [Title]
@@ -113,18 +126,24 @@ def title=(v)
# Show the legend in the chart
# @param [Boolean] v
# @return [Boolean]
- def show_legend=(v) Axlsx::validate_boolean(v); @show_legend = v; end
+ def show_legend=(v)
+ Axlsx::validate_boolean(v); @show_legend = v;
+ end
# How to display blank values
# @see display_blanks_as
# @param [Symbol] v
# @return [Symbol]
- def display_blanks_as=(v) Axlsx::validate_display_blanks_as(v); @display_blanks_as = v; end
+ def display_blanks_as=(v)
+ Axlsx::validate_display_blanks_as(v); @display_blanks_as = v;
+ end
# The style for the chart.
# see ECMA Part 1 ยง21.2.2.196
# @param [Integer] v must be between 1 and 48
- def style=(v) DataTypeValidator.validate "Chart.style", Integer, v, lambda { |arg| arg >= 1 && arg <= 48 }; @style = v; end
+ def style=(v)
+ DataTypeValidator.validate "Chart.style", Integer, v, lambda { |arg| arg >= 1 && arg <= 48 }; @style = v;
+ end
# backwards compatibility to allow chart.to and chart.from access to anchor markers
# @note This will be disconinued in version 2.0.0. Please use the end_at method
@@ -146,6 +165,7 @@ def add_series(options={})
@series.last
end
+
# Serializes the object
# @param [String] str
# @return [String]
@@ -176,6 +196,14 @@ def to_xml_string(str = '')
str << ''
str << ''
str << ''
+ # chart background
+ if @bg_color
+ str << ''
+ str << ''
+ str << ''
+ str << ''
+ str << ''
+ end
str << ''
str << ''
str << ''
@@ -224,7 +252,10 @@ def end_at(x=10, y=10)
end
# sets the view_3D object for the chart
- def view_3D=(v) DataTypeValidator.validate "#{self.class}.view_3D", View3D, v; @view_3D = v; end
+ def view_3D=(v)
+ DataTypeValidator.validate "#{self.class}.view_3D", View3D, v; @view_3D = v;
+ end
+
alias :view3D= :view_3D=
end
diff --git a/lib/axlsx/drawing/drawing.rb b/lib/axlsx/drawing/drawing.rb
index e7fcba85..349327ab 100644
--- a/lib/axlsx/drawing/drawing.rb
+++ b/lib/axlsx/drawing/drawing.rb
@@ -34,6 +34,7 @@ module Axlsx
require 'axlsx/drawing/view_3D.rb'
require 'axlsx/drawing/chart.rb'
require 'axlsx/drawing/pie_3D_chart.rb'
+ require 'axlsx/drawing/bar_chart.rb'
require 'axlsx/drawing/bar_3D_chart.rb'
require 'axlsx/drawing/line_chart.rb'
require 'axlsx/drawing/line_3D_chart.rb'
diff --git a/lib/axlsx/drawing/series.rb b/lib/axlsx/drawing/series.rb
index c9f467d5..21502d2b 100644
--- a/lib/axlsx/drawing/series.rb
+++ b/lib/axlsx/drawing/series.rb
@@ -16,12 +16,20 @@ class Series
# @return [SeriesTitle]
attr_reader :title
+ # The String of rgb color to apply to chart legend
+ # @return [String]
+ attr_reader :legend_color
+
+ #The Hash of trendline options
+ # @return [Hash]
+ attr_reader :trendline
+
# Creates a new series
# @param [Chart] chart
# @option options [Integer] order
# @option options [String] title
def initialize(chart, options={})
- @order = nil
+ @order = nil
self.chart = chart
@chart.series << self
parse_options options
@@ -40,7 +48,9 @@ def order
end
# @see order
- def order=(v) Axlsx::validate_unsigned_int(v); @order = v; end
+ def order=(v)
+ Axlsx::validate_unsigned_int(v); @order = v;
+ end
# @see title
def title=(v)
@@ -49,10 +59,36 @@ def title=(v)
@title = v
end
+ # @see trendline
+ def trendline=(v)
+ @trendline = v
+ end
+
+ # set trendline_color
+ def trendline_color
+ if @trendline[:color]
+ @trendline[:color]
+ else
+ '00000'
+ end
+ end
+
+ #set trenline_weight
+ def trendline_weight
+ if @trendline[:weight].to_s
+ @trendline[:weight].to_s
+ else
+ '1'
+ end
+
+ end
+
private
# assigns the chart for this series
- def chart=(v) DataTypeValidator.validate "Series.chart", Chart, v; @chart = v; end
+ def chart=(v)
+ DataTypeValidator.validate "Series.chart", Chart, v; @chart = v;
+ end
# Serializes the object
# @param [String] str
@@ -63,6 +99,29 @@ def to_xml_string(str = '')
str << ''
title.to_xml_string(str) unless title.nil?
yield str if block_given?
+ #set color at legend
+ if @legend_color
+ str << ''
+ str << ''
+ str << ''
+ str << ''
+ str << ''
+ end
+
+ if @trendline
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ str << ' '
+ end
str << ''
end
end
diff --git a/lib/axlsx/drawing/title.rb b/lib/axlsx/drawing/title.rb
index 5292a177..283c2b64 100644
--- a/lib/axlsx/drawing/title.rb
+++ b/lib/axlsx/drawing/title.rb
@@ -43,8 +43,8 @@ def cell=(v)
# @param [String] str
# @return [String]
def to_xml_string(str = '')
- str << ''
unless @text.empty?
+ str << ''
str << ''
if @cell.is_a?(Cell)
str << ''
@@ -68,11 +68,10 @@ def to_xml_string(str = '')
str << ''
end
str << ''
- end
str << ''
str << ''
str << ''
+ end
end
-
end
end
diff --git a/test/drawing/tc_bar_chart.rb b/test/drawing/tc_bar_chart.rb
new file mode 100644
index 00000000..b8e47ca7
--- /dev/null
+++ b/test/drawing/tc_bar_chart.rb
@@ -0,0 +1,58 @@
+require 'tc_helper.rb'
+require 'axlsx/drawing/bar_chart'
+
+class TestBarChart < Test::Unit::TestCase
+
+ def setup
+ @p = Axlsx::Package.new
+ ws = @p.workbook.add_worksheet
+ @row = ws.add_row ["one", 1, Time.new]
+ @chart = ws.add_chart Axlsx::BarChart, :title => "fishery", :bar_dir => :col
+ end
+
+ def test_initialization
+ assert_equal(@chart.series_type, Axlsx::BarSeries, "series type incorrect")
+ end
+
+ def test_chart_type
+ doc = Nokogiri::XML(@chart.to_xml_string)
+ assert_equal(1, doc.xpath('//c:barChart').size)
+ end
+
+ def test_bar_direction
+ assert_raise(ArgumentError, "require valid bar direction") { @chart.bar_dir = :left }
+ assert_nothing_raised("allow valid bar direction") { @chart.bar_dir = :col }
+ assert(@chart.bar_dir == :col)
+ end
+
+ def test_grouping
+ assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted }
+ assert_nothing_raised("allow valid grouping") { @chart.grouping = :standard }
+ assert(@chart.grouping == :standard)
+ end
+
+ def test_gapWidth
+ assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 200 }
+ assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = "200%" }
+ assert(@chart.gap_width == "200%")
+ end
+
+ def test_to_xml_string
+ schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD))
+ doc = Nokogiri::XML(@chart.to_xml_string)
+ errors = []
+ schema.validate(doc).each do |error|
+ errors.push error
+ puts error.message
+ end
+ assert(errors.empty?, "error free validation")
+ end
+
+ def test_to_xml_string_has_axes_in_correct_order
+ str = @chart.to_xml_string
+ cat_axis_position = str.index(@chart.axes[:cat_axis].id.to_s)
+ val_axis_position = str.index(@chart.axes[:val_axis].id.to_s)
+ assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML")
+ end
+
+end
\ No newline at end of file
diff --git a/test/drawing/tc_chart.rb b/test/drawing/tc_chart.rb
index 8b69e73a..26d469ad 100644
--- a/test/drawing/tc_chart.rb
+++ b/test/drawing/tc_chart.rb
@@ -6,13 +6,14 @@ def setup
@p = Axlsx::Package.new
ws = @p.workbook.add_worksheet
@row = ws.add_row ["one", 1, Time.now]
- @chart = ws.add_chart Axlsx::Bar3DChart, :title => "fishery"
+ @chart = ws.add_chart Axlsx::Bar3DChart
end
def teardown
end
def test_initialization
+ @chart.title = 'fishery'
assert_equal(@p.workbook.charts.last,@chart, "the chart is in the workbook")
assert_equal(@chart.title.text, "fishery", "the title option has been applied")
assert((@chart.series.is_a?(Axlsx::SimpleTypedList) && @chart.series.empty?), "The series is initialized and empty")
@@ -27,6 +28,18 @@ def test_title
assert_equal(@chart.title.cell, @row.cells.first)
end
+ def test_hidden_title
+ # this code must be missing
+ # fishery
+ doc = Nokogiri::XML(@chart.to_xml_string)
+ assert_equal(0, doc.xpath('//a:t').size)
+ end
+
+ def test_bg_color
+ doc = Nokogiri::XML(@chart.to_xml_string)
+ assert_equal(0, doc.xpath('//a:t', 'a:solidFill').size)
+ end
+
def test_to_from_marker_access
assert(@chart.to.is_a?(Axlsx::Marker))
assert(@chart.from.is_a?(Axlsx::Marker))
@@ -37,7 +50,7 @@ def test_style
assert_nothing_raised { @chart.style = 2 }
assert_equal(@chart.style, 2)
end
-
+
def test_vary_colors
assert_equal(true, @chart.vary_colors)
assert_raise(ArgumentError) { @chart.vary_colors = 7 }