diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | examples/borders_example.md | 9 | ||||
| -rw-r--r-- | lib/axlsx/stylesheet/border.rb | 2 | ||||
| -rw-r--r-- | lib/axlsx/stylesheet/styles.rb | 110 | ||||
| -rw-r--r-- | test/stylesheet/tc_styles.rb | 48 |
5 files changed, 152 insertions, 18 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5604fa..3e642869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ CHANGELOG --------- - **Unreleased** + - [PR #117](https://github.com/caxlsx/caxlsx/pull/117) - Allow passing an Array of border hashes to the `border` style. Change previous behaviour where `border_top`, `border_*` styles would not be applied unless `border` style was also defined. - [PR #122](https://github.com/caxlsx/caxlsx/pull/122) - Improve error messages when incorrect ranges are provided to `Worksheet#[]` - [PR #123](https://github.com/caxlsx/caxlsx/pull/123) - Fix invalid xml when pivot table created with more than one column in data field. Solves [Issue #110](https://github.com/caxlsx/caxlsx/issues/110) diff --git a/examples/borders_example.md b/examples/borders_example.md index d637681c..2607d17c 100644 --- a/examples/borders_example.md +++ b/examples/borders_example.md @@ -14,9 +14,18 @@ s = wb.styles red_border = s.add_style border: { style: :thick, color: 'FFFF0000', edges: [:left, :right] } blue_border = s.add_style border: { style: :thick, color: 'FF0000FF' } +complex_border = workbook.styles.add_style( + border: [ + { style: :thin, color: '000' }, + { style: :double, edges: [:top, :bottom] }, + { style: :thick, edges: [:left, :right] }, + ] +) + wb.add_worksheet(name: 'Custom Borders') do |sheet| sheet.add_row ['wrap', 'me', 'up in red'], style: red_border sheet.add_row [1, 2, 3], style: blue_border + sheet.add_row [4, 5, 6], style: complex_border end p.serialize 'borders_example.xlsx' diff --git a/lib/axlsx/stylesheet/border.rb b/lib/axlsx/stylesheet/border.rb index 422a4466..0a823c7a 100644 --- a/lib/axlsx/stylesheet/border.rb +++ b/lib/axlsx/stylesheet/border.rb @@ -6,6 +6,8 @@ module Axlsx include Axlsx::SerializedAttributes include Axlsx::OptionsParser + EDGES = [:left, :right, :top, :bottom].freeze + # Creates a new Border object # @option options [Boolean] diagonal_up # @option options [Boolean] diagonal_down diff --git a/lib/axlsx/stylesheet/styles.rb b/lib/axlsx/stylesheet/styles.rb index 2460ab10..d1ee1f66 100644 --- a/lib/axlsx/stylesheet/styles.rb +++ b/lib/axlsx/stylesheet/styles.rb @@ -310,33 +310,108 @@ module Axlsx # may include an :edges entry that references an array of symbols identifying which border edges # you wish to apply the style or any other valid Border initializer options. # If the :edges entity is not provided the style is applied to all edges of cells that reference this style. - # Also available :border_top, :border_right, :border_bottom and :border_left options with :style and/or :color - # key-value entries, which override :border values. + # Also available :border_top, :border_right, :border_bottom and :border_left options with :style and/or :color + # key-value entries, which override :border values. # @example # #apply a thick red border to the top and bottom # { :border => { :style => :thick, :color => "FFFF0000", :edges => [:top, :bottom] } # @return [Border|Integer] def parse_border_options(options={}) - return unless options[:border] - b_opts = options[:border] - if b_opts.is_a?(Hash) - raise ArgumentError, (ERR_INVALID_BORDER_OPTIONS % b_opts) unless b_opts.keys.include?(:style) && b_opts.keys.include?(:color) - border = Border.new b_opts - (b_opts[:edges] || [:left, :right, :top, :bottom]).each do |edge| - edge_options = options["border_#{edge}".to_sym] || {} - border_edge = b_opts.merge(edge_options) - b_options = { :name => edge, :style => border_edge[:style], :color => Color.new(:rgb => border_edge[:color]) } - border.prs << BorderPr.new(b_options) + if options[:border].nil? && Border::EDGES.all?{|x| options["border_#{x}".to_sym].nil? } + return nil + end + + if options[:border].is_a?(Integer) + if options[:border] >= borders.size + raise ArgumentError, (ERR_INVALID_BORDER_ID % options[:border]) end - options[:type] == :dxf ? border : borders << border - elsif b_opts.is_a? Integer - raise ArgumentError, (ERR_INVALID_BORDER_ID % b_opts) unless b_opts < borders.size + if options[:type] == :dxf - borders[b_opts].clone + return borders[options[:border]].clone + else + return options[:border] + end + end + + validate_border_hash = ->(val){ + if !(val.keys.include?(:style) && val.keys.include?(:color)) + raise ArgumentError, (ERR_INVALID_BORDER_OPTIONS % options[:border]) + end + } + + borders_array = [] + + if options[:border].nil? + base_border_opts = {} + else + if options[:border].is_a?(Array) + borders_array += options[:border] + + base_border_opts = {} + + options[:border].each do |b_opts| + if b_opts[:edges].nil? + base_border_opts = base_border_opts.merge(b_opts) + end + end else - border = b_opts + borders_array << options[:border] + + base_border_opts = options[:border] + + validate_border_hash.call(base_border_opts) + end + end + + Border::EDGES.each do |edge| + val = options["border_#{edge}".to_sym] + + if val + borders_array << val.merge(edges: [edge]) + end + end + + border = Border.new(base_border_opts) + + Border::EDGES.each do |edge| + edge_b_opts = base_border_opts + + skip_edge = true + + borders_array.each do |b_opts| + if b_opts[:edges] && b_opts[:edges].include?(edge) + edge_b_opts = edge_b_opts.merge(b_opts) + skip_edge = false + end + end + + if options["border_#{edge}".to_sym] + edge_b_opts = edge_b_opts.merge(options["border_#{edge}".to_sym]) + skip_edge = false + end + + if skip_edge && base_border_opts[:edges] + next + end + + if !edge_b_opts.empty? + if base_border_opts.empty? + validate_border_hash.call(edge_b_opts) + end + + border.prs << BorderPr.new({ + :name => edge, + :style => edge_b_opts[:style], + :color => Color.new(:rgb => edge_b_opts[:color]) }, + ) end end + + if options[:type] == :dxf + return border + else + return borders << border + end end # Parses Style#add_style options for number formatting. @@ -417,4 +492,3 @@ module Axlsx end end end - diff --git a/test/stylesheet/tc_styles.rb b/test/stylesheet/tc_styles.rb index 72bf1466..c46b6bdb 100644 --- a/test/stylesheet/tc_styles.rb +++ b/test/stylesheet/tc_styles.rb @@ -17,6 +17,7 @@ class TestStyles < Test::Unit::TestCase end assert(errors.size == 0) end + def test_add_style_border_hash border_count = @styles.borders.size @styles.add_style :border => {:style => :thin, :color => "FFFF0000"} @@ -26,6 +27,32 @@ class TestStyles < Test::Unit::TestCase assert_equal @styles.borders.last.prs.size, 4 end + def test_add_style_border_array + prev_border_count = @styles.borders.size + + borders_array = [ + {:style => :thin, :color => "DDDDDD"}, + {:edges => [:top], :style => :thin, :color => "000000"}, + {:edges => [:bottom], :style => :thick, :color => "FF0000"}, + {:edges => [:left], :style => :dotted, :color => "FFFF00"}, + {:edges => [:right], :style => :dashed, :color => "FFFFFF"}, + {:style => :thick, :color => "CCCCCC"}, + ] + + @styles.add_style(border: borders_array) + + assert_equal(@styles.borders.size, (prev_border_count+1)) + + current_border = @styles.borders.last + + borders_array.each do |b_opts| + if b_opts[:edges] + border_pr = current_border.prs.detect{|x| x.name == b_opts[:edges].first } + assert_equal(border_pr.color.rgb, "FF#{b_opts[:color]}") + end + end + end + def test_add_style_border_edges @styles.add_style :border => { :style => :thin, :color => "0000FFFF", :edges => [:top, :bottom] } parts = @styles.borders.last.prs @@ -258,4 +285,25 @@ class TestStyles < Test::Unit::TestCase end assert(errors.size == 0) end + + def test_border_top_without_border_regression + ### https://github.com/axlsx-styler-gem/axlsx_styler/issues/31 + + borders = { + top: { style: :double, color: '0000FF' }, + right: { style: :thick, color: 'FF0000' }, + bottom: { style: :double, color: '0000FF' }, + left: { style: :thick, color: 'FF0000' } + } + + borders.each do |edge, b_opts| + @styles.add_style("border_#{edge}".to_sym => b_opts) + + current_border = @styles.borders.last + + border_pr = current_border.prs.detect{|x| x.name == edge } + assert_equal(border_pr.color.rgb, "FF#{b_opts[:color]}") + end + + end end |
