From 8d55f0d2ad3c5e4f4c6e9be1cf1e7b065fc25e49 Mon Sep 17 00:00:00 2001 From: Zsolt Kozaroczy Date: Mon, 26 Apr 2021 22:32:49 +0200 Subject: Add option to define a series color for the BarSeries (#81) --- test/drawing/tc_bar_series.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'test/drawing') diff --git a/test/drawing/tc_bar_series.rb b/test/drawing/tc_bar_series.rb index 483e561d..f076fae0 100644 --- a/test/drawing/tc_bar_series.rb +++ b/test/drawing/tc_bar_series.rb @@ -6,13 +6,21 @@ class TestBarSeries < Test::Unit::TestCase p = Axlsx::Package.new @ws = p.workbook.add_worksheet :name=>"hmmm" @chart = @ws.add_chart Axlsx::Bar3DChart, :title => "fishery" - @series = @chart.add_series :data=>[0,1,2], :labels=>["zero", "one", "two"], :title=>"bob", :colors => ['FF0000', '00FF00', '0000FF'], :shape => :cone + @series = @chart.add_series( + data: [0, 1, 2], + labels: ['zero', 'one', 'two'], + title: 'bob', + colors: ['FF0000', '00FF00', '0000FF'], + shape: :cone, + series_color: '5A5A5A' + ) end def test_initialize assert_equal(@series.title.text, "bob", "series title has been applied") assert_equal(@series.data.class, Axlsx::NumDataSource, "data option applied") assert_equal(@series.shape, :cone, "series shape has been applied") + assert_equal(@series.series_color, '5A5A5A', 'series color has been applied') assert(@series.data.is_a?(Axlsx::NumDataSource)) assert(@series.labels.is_a?(Axlsx::AxDataSource)) end @@ -33,5 +41,6 @@ class TestBarSeries < Test::Unit::TestCase assert_equal(doc.xpath("//c:dPt/c:idx[@val='#{index}']").size,1) assert_equal(doc.xpath("//c:dPt/c:spPr/a:solidFill/a:srgbClr[@val='#{@series.colors[index]}']").size,1) end + assert_equal(doc.xpath('//c:spPr[not(ancestor::c:dPt)]/a:solidFill/a:srgbClr').first.get_attribute('val'), '5A5A5A', 'series color has been applied') end end -- cgit v1.2.3 From 158942c16b249a269e77633dd261b87787d191af Mon Sep 17 00:00:00 2001 From: Zsolt Kozaroczy Date: Sat, 24 Jul 2021 00:31:54 +0200 Subject: Add overlap to bar charts (#107) --- CHANGELOG.md | 9 ++++--- examples/README.md | 1 + examples/images/stacked_bar_chart_example.png | Bin 0 -> 28959 bytes examples/stacked_bar_chart_example.md | 35 ++++++++++++++++++++++++++ lib/axlsx/drawing/bar_chart.rb | 14 ++++++++++- test/drawing/tc_bar_chart.rb | 14 +++++++++++ 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 examples/images/stacked_bar_chart_example.png create mode 100644 examples/stacked_bar_chart_example.md (limited to 'test/drawing') diff --git a/CHANGELOG.md b/CHANGELOG.md index c55a2673..d9c3fc45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ CHANGELOG --------- +- **Unreleased** + - [PR #107](https://github.com/caxlsx/caxlsx/pull/107) - Add overlap to bar charts + - **March.27.21**: 3.1.0 - [PR #95](https://github.com/caxlsx/caxlsx/pull/95) - Replace mimemagic with marcel - [PR #87](https://github.com/caxlsx/caxlsx/pull/87) - Implement :offset option for worksheet#add_row @@ -11,7 +14,7 @@ CHANGELOG - **January.5.21**: 3.0.4 - [PR #72](https://github.com/caxlsx/caxlsx/pull/72) - Relax Ruby dependency to allow for Ruby 3. This required Travis to be upgraded from Ubuntu Trusty to Ubuntu Bionic. rbx-3 was dropped. - [PR #71](https://github.com/caxlsx/caxlsx/pull/71) - Adds date type to validator so sheet.add_data_validation works with date type. Addresses [I #26](https://github.com/caxlsx/caxlsx/issues/26) - Date Data Validation not working - - [PR #70](https://github.com/caxlsx/caxlsx/pull/70) - Fix worksheet title length enforcement caused by switching from size to bytesize. Addresses [I #67](https://github.com/caxlsx/caxlsx/issues/67) - character length error in worksheet name when using Japanese, which was introduced by addressing [I #588](https://github.com/randym/axlsx/issues/588) in the old Axlsx repo. + - [PR #70](https://github.com/caxlsx/caxlsx/pull/70) - Fix worksheet title length enforcement caused by switching from size to bytesize. Addresses [I #67](https://github.com/caxlsx/caxlsx/issues/67) - character length error in worksheet name when using Japanese, which was introduced by addressing [I #588](https://github.com/randym/axlsx/issues/588) in the old Axlsx repo. - **December.7.20**: 3.0.3 @@ -19,7 +22,7 @@ CHANGELOG - [PR #56](https://github.com/caxlsx/caxlsx/pull/56) - Add `zip_command` option to `#serialize` for faster serialization of large Excel files by using a zip binary - [PR #54](https://github.com/caxlsx/caxlsx/pull/54) - Fix type detection for floats with out-of-rage exponents - [I #67](https://github.com/caxlsx/caxlsx/issues/67) - Fix regression in worksheet name length enforcement: Some unicode characters were counted incorrectly, so that names that previously worked fine now stopped working. (This was introduced in 3.0.2) - + - **July.16.20**: 3.0.2 - [I #51](https://github.com/caxlsx/caxlsx/issues/51) - Images do not import on Windows. IO read set explicitly to binary mode. - [PR #53](https://github.com/caxlsx/caxlsx/pull/53) - Limit column width to 255. Maximum column width limit in MS Excel is 255 characters, see https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3 @@ -152,7 +155,7 @@ CHANGELOG - added in interop requirements so that charts are properly exported to PDF from Libra Office - various readability improvements and work standardizing attribute - names to snake_case. Aliases are provided for backward compatiblity + names to snake_case. Aliases are provided for backward compatiblity - **June.11.12**: 1.1.7 - fix chart rendering issue when label offset is specified as a diff --git a/examples/README.md b/examples/README.md index eca725dc..5f53199e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -44,6 +44,7 @@ Types: * [Line chart (3D)](3d_line_chart_example.md) * [Pie chart (3D)](3d_pie_chart_example.md) * [Scatter chart](scatter_chart_example.md) +* [Stacked bar chart](stacked_bar_chart_example.md) Customizations: * [Chart colors](chart_colors_example.md) diff --git a/examples/images/stacked_bar_chart_example.png b/examples/images/stacked_bar_chart_example.png new file mode 100644 index 00000000..da046983 Binary files /dev/null and b/examples/images/stacked_bar_chart_example.png differ diff --git a/examples/stacked_bar_chart_example.md b/examples/stacked_bar_chart_example.md new file mode 100644 index 00000000..3fbd2425 --- /dev/null +++ b/examples/stacked_bar_chart_example.md @@ -0,0 +1,35 @@ +## Description + +Stacked bar chart example + +## Code + +```ruby +require 'axlsx' + +p = Axlsx::Package.new +wb = p.workbook + +wb.add_worksheet(name: 'Bar Chart') do |sheet| + sheet.add_row ['A Simple Bar Chart', 'X', 'Y'] + + sheet.add_row ['A', 3, 4] + sheet.add_row ['B', 10, 6] + sheet.add_row ['C', 7, 2] + + sheet.add_chart(Axlsx::BarChart, start_at: 'A6', end_at: 'F20') do |chart| + chart.add_series data: sheet['B2:B4'], labels: sheet['A2:A4'], title: sheet['B1'] + chart.add_series data: sheet['C2:C4'], labels: sheet['A2:A4'], title: sheet['C1'] + + chart.bar_dir = :col + chart.overlap = 100 + chart.grouping = :percentStacked + end +end + +p.serialize 'stacked_bar_chart_example.xlsx' +``` + +## Output + +![Output](images/stacked_bar_chart_example.png "Output") diff --git a/lib/axlsx/drawing/bar_chart.rb b/lib/axlsx/drawing/bar_chart.rb index e9af9400..cb596fce 100644 --- a/lib/axlsx/drawing/bar_chart.rb +++ b/lib/axlsx/drawing/bar_chart.rb @@ -49,6 +49,12 @@ module Axlsx @grouping ||= :clustered end + # Overlap between series + # @return [Integer] + def overlap + @overlap ||= 0 + end + # The shape of the bars or columns # must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax] # @return [Symbol] @@ -71,7 +77,7 @@ module Axlsx # @see Chart def initialize(frame, options={}) @vary_colors = true - @gap_width, @gap_depth, @shape = nil, nil, nil + @gap_width, @gap_depth, @overlap, @shape = nil, nil, nil, nil super(frame, options) @series_type = BarSeries @d_lbls = nil @@ -106,6 +112,11 @@ module Axlsx end alias :gapDepth= :gap_depth= + def overlap=(v) + RangeValidator.validate "BarChart.overlap", -100, 100, v + @overlap=(v) + end + # The shape of the bars or columns # must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax] def shape=(v) @@ -124,6 +135,7 @@ module Axlsx str << ('') @series.each { |ser| ser.to_xml_string(str) } @d_lbls.to_xml_string(str) if @d_lbls + str << ('') unless @overlap.nil? str << ('') unless @gap_width.nil? str << ('') unless @gap_depth.nil? str << ('') unless @shape.nil? diff --git a/test/drawing/tc_bar_chart.rb b/test/drawing/tc_bar_chart.rb index ca1ca016..d064e5be 100644 --- a/test/drawing/tc_bar_chart.rb +++ b/test/drawing/tc_bar_chart.rb @@ -45,6 +45,13 @@ class TestBarChart < Test::Unit::TestCase assert(@chart.gap_depth == "200%") end + def test_overlap + assert_raise(ArgumentError, "require valid overlap") { @chart.overlap = -101 } + assert_raise(ArgumentError, "require valid overlap") { @chart.overlap = 101 } + assert_nothing_raised("allow valid overlap") { @chart.overlap = 100 } + assert_equal(@chart.overlap, 100, 'overlap is incorrect') + end + def test_shape assert_raise(ArgumentError, "require valid shape") { @chart.shape = :star } assert_nothing_raised("allow valid shape") { @chart.shape = :cone } @@ -68,4 +75,11 @@ class TestBarChart < Test::Unit::TestCase 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 + + def test_to_xml_string_has_overlap + overlap_value = rand(-100..100) + @chart.overlap = overlap_value + doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal(doc.xpath("//c:barChart/c:overlap").first.attribute('val').value, overlap_value.to_s) + end end -- cgit v1.2.3 From c4d7279de2bd20bc98c15ea64e9074ded19124ca Mon Sep 17 00:00:00 2001 From: Zsolt Kozaroczy Date: Sun, 25 Jul 2021 01:37:29 +0200 Subject: Fix gap width validator for bar charts (#108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gap_width` and `gap_depth` now allow only integers in the range of 0–500. The previous behaviour (requiring a percentage value) was according to the current version of the OOXML spec, but Excel seems to rely on an older version, where the gap amount was required to be a simple integer. Also, `gapDepth` is only allowed in 3D bar charts, so it is now no longer available for 2D bar charts. --- CHANGELOG.md | 1 + lib/axlsx/drawing/bar_3D_chart.rb | 13 +++++-------- lib/axlsx/drawing/bar_chart.rb | 23 +++-------------------- test/drawing/tc_bar_3D_chart.rb | 37 ++++++++++++++++++++++++++----------- test/drawing/tc_bar_chart.rb | 25 +++++++++++++------------ 5 files changed, 48 insertions(+), 51 deletions(-) (limited to 'test/drawing') diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c3fc45..3558c392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ CHANGELOG - **Unreleased** - [PR #107](https://github.com/caxlsx/caxlsx/pull/107) - Add overlap to bar charts + - [PR #108](https://github.com/caxlsx/caxlsx/pull/108) - Fix gap depth and gap depth validators for bar charts and 3D bar charts - **March.27.21**: 3.1.0 - [PR #95](https://github.com/caxlsx/caxlsx/pull/95) - Replace mimemagic with marcel diff --git a/lib/axlsx/drawing/bar_3D_chart.rb b/lib/axlsx/drawing/bar_3D_chart.rb index 7d44eca6..ae6c2007 100644 --- a/lib/axlsx/drawing/bar_3D_chart.rb +++ b/lib/axlsx/drawing/bar_3D_chart.rb @@ -31,17 +31,17 @@ module Axlsx alias :barDir :bar_dir # space between bar or column clusters, as a percentage of the bar or column width. - # @return [String] + # @return [Integer] attr_reader :gap_depth alias :gapDepth :gap_depth # space between bar or column clusters, as a percentage of the bar or column width. - # @return [String] + # @return [Integer] def gap_width @gap_width ||= 150 end alias :gapWidth :gap_width - + #grouping for a column, line, or area chart. # must be one of [:percentStacked, :clustered, :standard, :stacked] # @return [Symbol] @@ -56,9 +56,6 @@ module Axlsx @shape ||= :box end - # validation regex for gap amount percent - GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/ - # Creates a new bar chart object # @param [GraphicFrame] frame The workbook that owns this chart. # @option options [Cell, String] title @@ -102,14 +99,14 @@ module Axlsx # space between bar or column clusters, as a percentage of the bar or column width. def gap_width=(v) - RegexValidator.validate "Bar3DChart.gap_width", GAP_AMOUNT_PERCENT, v + RangeValidator.validate "Bar3DChart.gap_width", 0, 500, v @gap_width=(v) end alias :gapWidth= :gap_width= # space between bar or column clusters, as a percentage of the bar or column width. def gap_depth=(v) - RegexValidator.validate "Bar3DChart.gap_didth", GAP_AMOUNT_PERCENT, v + RangeValidator.validate "Bar3DChart.gap_depth", 0, 500, v @gap_depth=(v) end alias :gapDepth= :gap_depth= diff --git a/lib/axlsx/drawing/bar_chart.rb b/lib/axlsx/drawing/bar_chart.rb index cb596fce..67787361 100644 --- a/lib/axlsx/drawing/bar_chart.rb +++ b/lib/axlsx/drawing/bar_chart.rb @@ -31,12 +31,7 @@ module Axlsx alias :barDir :bar_dir # space between bar or column clusters, as a percentage of the bar or column width. - # @return [String] - attr_reader :gap_depth - alias :gapDepth :gap_depth - - # space between bar or column clusters, as a percentage of the bar or column width. - # @return [String] + # @return [Integer] def gap_width @gap_width ||= 150 end @@ -62,9 +57,6 @@ module Axlsx @shape ||= :box end - # validation regex for gap amount percent - GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/ - # Creates a new bar chart object # @param [GraphicFrame] frame The workbook that owns this chart. # @option options [Cell, String] title @@ -72,12 +64,11 @@ module Axlsx # @option options [Symbol] bar_dir # @option options [Symbol] grouping # @option options [String] gap_width - # @option options [String] gap_depth # @option options [Symbol] shape # @see Chart def initialize(frame, options={}) @vary_colors = true - @gap_width, @gap_depth, @overlap, @shape = nil, nil, nil, nil + @gap_width, @overlap, @shape = nil, nil, nil super(frame, options) @series_type = BarSeries @d_lbls = nil @@ -100,18 +91,11 @@ module Axlsx # 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 + RangeValidator.validate "BarChart.gap_width", 0, 500, v @gap_width=(v) end alias :gapWidth= :gap_width= - # space between bar or column clusters, as a percentage of the bar or column width. - def gap_depth=(v) - RegexValidator.validate "BarChart.gap_didth", GAP_AMOUNT_PERCENT, v - @gap_depth=(v) - end - alias :gapDepth= :gap_depth= - def overlap=(v) RangeValidator.validate "BarChart.overlap", -100, 100, v @overlap=(v) @@ -137,7 +121,6 @@ module Axlsx @d_lbls.to_xml_string(str) if @d_lbls str << ('') unless @overlap.nil? str << ('') unless @gap_width.nil? - str << ('') unless @gap_depth.nil? str << ('') unless @shape.nil? axes.to_xml_string(str, :ids => true) str << '' diff --git a/test/drawing/tc_bar_3D_chart.rb b/test/drawing/tc_bar_3D_chart.rb index 0cae7af6..b7a1eca4 100644 --- a/test/drawing/tc_bar_3D_chart.rb +++ b/test/drawing/tc_bar_3D_chart.rb @@ -32,18 +32,19 @@ class TestBar3DChart < Test::Unit::TestCase assert(@chart.grouping == :standard) end + def test_gap_width + assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = -1 } + assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 501 } + assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = 200 } + assert_equal(@chart.gap_width, 200, 'gap width is incorrect') + 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_gapDepth - assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 200 } - assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = "200%" } - assert(@chart.gap_depth == "200%") - end + def test_gap_depth + assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = -1 } + assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 501 } + assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = 200 } + assert_equal(@chart.gap_depth, 200, 'gap depth is incorrect') + end def test_shape assert_raise(ArgumentError, "require valid shape") { @chart.shape = :star } @@ -68,4 +69,18 @@ class TestBar3DChart < Test::Unit::TestCase 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 + + def test_to_xml_string_has_gap_depth + gap_depth_value = rand(0..500) + @chart.gap_depth = gap_depth_value + doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal(doc.xpath("//c:bar3DChart/c:gapDepth").first.attribute('val').value, gap_depth_value.to_s) + end + + def test_to_xml_string_has_gap_width + gap_width_value = rand(0..500) + @chart.gap_width = gap_width_value + doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal(doc.xpath("//c:bar3DChart/c:gapWidth").first.attribute('val').value, gap_width_value.to_s) + end end diff --git a/test/drawing/tc_bar_chart.rb b/test/drawing/tc_bar_chart.rb index d064e5be..c8bdc8d0 100644 --- a/test/drawing/tc_bar_chart.rb +++ b/test/drawing/tc_bar_chart.rb @@ -32,18 +32,12 @@ class TestBarChart < Test::Unit::TestCase 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_gapDepth - assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 200 } - assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = "200%" } - assert(@chart.gap_depth == "200%") - end + def test_gap_width + assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = -1 } + assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 501 } + assert_nothing_raised("allow valid gap width") { @chart.gap_width = 200 } + assert_equal(@chart.gap_width, 200, 'gap width is incorrect') + end def test_overlap assert_raise(ArgumentError, "require valid overlap") { @chart.overlap = -101 } @@ -76,6 +70,13 @@ class TestBarChart < Test::Unit::TestCase assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML") end + def test_to_xml_string_has_gap_width + gap_width_value = rand(0..500) + @chart.gap_width = gap_width_value + doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal(doc.xpath("//c:barChart/c:gapWidth").first.attribute('val').value, gap_width_value.to_s) + end + def test_to_xml_string_has_overlap overlap_value = rand(-100..100) @chart.overlap = overlap_value -- cgit v1.2.3