summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNoel Peden <[email protected]>2022-10-21 04:19:01 -0700
committerGitHub <[email protected]>2022-10-21 04:19:01 -0700
commit315c3f04224ba2f4b44fe2ce633315a6e79f62cb (patch)
treed67454ea279e19f25b37b05fee0a43a80d19a158
parentbcc88ca556b3b8527ba0ad56424f93ef170b9c31 (diff)
parent449056492f8c9fd44684dbf7bbe18f401fe5e6ad (diff)
downloadcaxlsx-315c3f04224ba2f4b44fe2ce633315a6e79f62cb.tar.gz
caxlsx-315c3f04224ba2f4b44fe2ce633315a6e79f62cb.zip
Merge pull request #168 from westonganger/merge_axlsx_styler
Merge axlsx_styler gem into caxlsx
-rw-r--r--CHANGELOG.md13
-rw-r--r--README.md2
-rw-r--r--examples/add_border_example.md28
-rw-r--r--examples/append_styles_example.md31
-rw-r--r--examples/images/add_border_example.pngbin0 -> 7026 bytes
-rw-r--r--examples/images/append_styles_example.pngbin0 -> 8034 bytes
-rw-r--r--lib/axlsx.rb22
-rw-r--r--lib/axlsx/package.rb8
-rw-r--r--lib/axlsx/stylesheet/styles.rb42
-rw-r--r--lib/axlsx/workbook/workbook.rb30
-rw-r--r--lib/axlsx/workbook/worksheet/border_creator.rb76
-rw-r--r--lib/axlsx/workbook/worksheet/cell.rb26
-rw-r--r--lib/axlsx/workbook/worksheet/worksheet.rb38
-rw-r--r--test/tc_axlsx.rb8
-rw-r--r--test/tc_package.rb35
-rw-r--r--test/workbook/worksheet/tc_border_creator.rb73
-rw-r--r--test/workbook/worksheet/tc_worksheet.rb244
17 files changed, 673 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6791d637..95bb0781 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,19 @@
CHANGELOG
---------
+- **Unreleased - Axlsx Styler**
+ - [PR #xx](https://github.com/caxlsx/caxlsx/pull/xx)Merge in the gem [`axlsx_styler`](https://github.com/axlsx-styler-gem/axlsx_styler)
+ - Add ability to both apply or append to existing styles after rows have been created using `worksheet.add_style`
+ - `worksheet.add_style "A1", {b: true}`
+ - `worksheet.add_style "A1:B2", {b: true}`
+ - `worksheet.add_style ["A1", "B2:C7", "D8:E9"], {b: true}`
+ - Add ability to create borders upon specific areas of the page using `worksheet.add_border`
+ - `worksheet.add_border "A1", {style: :thin}`
+ - `worksheet.add_border "A1:B2", {style: :thin}`
+ - `worksheet.add_border ["A1", "B2:C7", "D8:E9"], {style: :thin}`
+ - Add `Axlsx::BorderCreator` the class used under the hood for `worksheet.add_border`
+ - Allow specifying `:all` in `border: {edges: :all}` which is a shortcut for `border: {edges: [:left, :right, :top, :bottom]}`
+
- **Unreleased**
- [PR #155](https://github.com/caxlsx/caxlsx/pull/155) - Add `hideDropDown` alias for `showDropDown` setting, as the latter is confusing to use (because its logic seems inverted).
- [PR #143](https://github.com/caxlsx/caxlsx/pull/143) - Add setting `sort_on_headers` for pivot tables
diff --git a/README.md b/README.md
index 3d0fb760..e8de7f92 100644
--- a/README.md
+++ b/README.md
@@ -120,8 +120,6 @@ Currently the following additional gems are available:
* Provides a `.axlsx` renderer to Rails so you can move all your spreadsheet code from your controller into view files.
- [activeadmin-caxlsx](https://github.com/caxlsx/activeadmin-caxlsx)
* An Active Admin plugin that includes DSL to create downloadable reports.
-- [axlsx_styler](https://github.com/axlsx-styler-gem/axlsx_styler)
- * Allows you to build clean and maintainable styles for your axlsx spreadsheets. Build your spreadsheeet with data and then apply styles later.
## Known Software Interoperability Issues
diff --git a/examples/add_border_example.md b/examples/add_border_example.md
new file mode 100644
index 00000000..d64b940d
--- /dev/null
+++ b/examples/add_border_example.md
@@ -0,0 +1,28 @@
+## Description
+
+Shows how to use `add_border` to add a border to four edges of the selected cell range.
+
+## Code
+
+```ruby
+p = Axlsx::Package.new
+wb = p.workbook
+
+wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ["", "Product", "Category", "Price"]
+ sheet.add_row ["", "Butter", "Dairy", 4.99]
+ sheet.add_row ["", "Bread", "Baked Goods", 3.45]
+ sheet.add_row ["", "Broccoli", "Produce", 2.99]
+
+ sheet.add_border "B2:D5"
+ sheet.add_border "B3:D3", { edges: [:top], style: :thick }
+ sheet.add_border ["C3:C4", "D3:D4"]
+end
+
+p.serialize "add_border.xlsx"
+```
+
+## Output
+
+![Output](images/add_border_example.png "Output")
diff --git a/examples/append_styles_example.md b/examples/append_styles_example.md
new file mode 100644
index 00000000..d8cb4696
--- /dev/null
+++ b/examples/append_styles_example.md
@@ -0,0 +1,31 @@
+## Description
+
+Shows how to append styles after rows have been created using `worksheet.add_style`
+
+## Code
+
+```ruby
+p = Axlsx::Package.new
+wb = p.workbook
+
+wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ["", "Product", "Category", "Price"]
+ sheet.add_row ["", "Butter", "Dairy", 4.99]
+ sheet.add_row ["", "Bread", "Baked Goods", 3.45]
+ sheet.add_row ["", "Broccoli", "Produce", 2.99]
+
+ sheet.add_style "B2:D2", b: true
+ sheet.add_style "B2:B5", b: true
+ sheet.add_style "B2:D2", bg_color: "95AFBA"
+ sheet.add_style "B3:D5", bg_color: "E2F89C"
+ sheet.add_style "D3:D5", alignment: { horizontal: :left }
+ sheet.add_style ["C3:C4", "D3:D4"], fg_color: "00FF00"
+end
+
+p.serialize "append_styles.xlsx"
+```
+
+## Output
+
+![Output](images/append_styles_example.png "Output")
diff --git a/examples/images/add_border_example.png b/examples/images/add_border_example.png
new file mode 100644
index 00000000..f18ac144
--- /dev/null
+++ b/examples/images/add_border_example.png
Binary files differ
diff --git a/examples/images/append_styles_example.png b/examples/images/append_styles_example.png
new file mode 100644
index 00000000..5b8b2d55
--- /dev/null
+++ b/examples/images/append_styles_example.png
Binary files differ
diff --git a/lib/axlsx.rb b/lib/axlsx.rb
index f2403893..23082b71 100644
--- a/lib/axlsx.rb
+++ b/lib/axlsx.rb
@@ -28,8 +28,17 @@ require 'zip'
#core dependencies
require 'bigdecimal'
+require 'set'
require 'time'
+begin
+ if Gem.loaded_specs.has_key?("axlsx_styler")
+ raise StandardError.new("Please remove `axlsx_styler` from your Gemfile, the associated functionality is now built-in to `caxlsx` directly.")
+ end
+rescue
+ # Do nothing
+end
+
# xlsx generation with charts, images, automated column width, customizable styles
# and full schema validation. Axlsx excels at helping you generate beautiful
# Office Open XML Spreadsheet documents without having to understand the entire
@@ -166,6 +175,19 @@ module Axlsx
end
end
+ # utility method for performing a deep merge on a Hash
+ # @param [Hash] Hash to merge into
+ # @param [Hash] Hash to be added
+ def self.hash_deep_merge(first_hash, second_hash)
+ first_hash.merge(second_hash) do |key, this_val, other_val|
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
+ Axlsx.hash_deep_merge(this_val, other_val)
+ else
+ other_val
+ end
+ end
+ end
+
# Instructs the serializer to not try to escape cell value input.
# This will give you a huge speed bonus, but if you content has <, > or other xml character data
# the workbook will be invalid and excel will complain.
diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb
index ccabf799..6baaa173 100644
--- a/lib/axlsx/package.rb
+++ b/lib/axlsx/package.rb
@@ -101,6 +101,10 @@ module Axlsx
# s = p.to_stream()
# File.open('example_streamed.xlsx', 'wb') { |f| f.write(s.read) }
def serialize(output, options = {}, secondary_options = nil)
+ if !workbook.styles_applied
+ workbook.apply_styles
+ end
+
confirm_valid, zip_command = parse_serialize_options(options, secondary_options)
return false unless !confirm_valid || self.validate.empty?
zip_provider = if zip_command
@@ -122,6 +126,10 @@ module Axlsx
# @param [Boolean] confirm_valid Validate the package prior to serialization.
# @return [StringIO|Boolean] False if confirm_valid and validation errors exist. rewound string IO if not.
def to_stream(confirm_valid=false)
+ if !workbook.styles_applied
+ workbook.apply_styles
+ end
+
return false unless !confirm_valid || self.validate.empty?
Relationship.initialize_ids_cache
zip = write_parts(Zip::OutputStream.new(StringIO.new.binmode, true))
diff --git a/lib/axlsx/stylesheet/styles.rb b/lib/axlsx/stylesheet/styles.rb
index fd741ac5..1ddbb788 100644
--- a/lib/axlsx/stylesheet/styles.rb
+++ b/lib/axlsx/stylesheet/styles.rb
@@ -120,6 +120,10 @@ module Axlsx
load_default_styles
end
+ def style_index
+ @style_index ||= {}
+ end
+
# Drastically simplifies style creation and management.
# @return [Integer]
# @option options [String] fg_color The text color
@@ -219,11 +223,36 @@ module Axlsx
# f = File.open('example_differential_styling', 'wb')
# p.serialize(f)
#
+ # An index for cell styles where keys are styles codes as per Axlsx::Style and values are Cell#raw_style
+ # The reason for the backward key/value ordering is that style lookup must be most efficient, while `add_style` can be less efficient
def add_style(options={})
# Default to :xf
options[:type] ||= :xf
+
raise ArgumentError, "Type must be one of [:xf, :dxf]" unless [:xf, :dxf].include?(options[:type] )
+ if options[:border].is_a?(Hash) && options[:border][:edges] == :all
+ options[:border][:edges] = Axlsx::Border::EDGES
+ end
+
+ if options[:type] == :xf
+ # Check to see if style in cache already
+
+ font_defaults = {name: @fonts.first.name, sz: @fonts.first.sz, family: @fonts.first.family}
+
+ raw_style = {type: :xf}.merge(font_defaults).merge(options)
+
+ if raw_style[:format_code]
+ raw_style.delete(:num_fmt)
+ end
+
+ xf_index = style_index.key(raw_style)
+
+ if xf_index
+ return xf_index
+ end
+ end
+
fill = parse_fill_options options
font = parse_font_options options
numFmt = parse_num_fmt_options options
@@ -238,7 +267,18 @@ module Axlsx
style = Xf.new :fillId=>fill || 0, :fontId=>font || 0, :numFmtId=>numFmt || 0, :borderId=>border || 0, :alignment => alignment, :protection => protection, :applyFill=>!fill.nil?, :applyFont=>!font.nil?, :applyNumberFormat =>!numFmt.nil?, :applyBorder=>!border.nil?, :applyAlignment => !alignment.nil?, :applyProtection => !protection.nil?
end
- options[:type] == :xf ? cellXfs << style : dxfs << style
+ if options[:type] == :xf
+ xf_index = (cellXfs << style)
+
+ # Add styles to style_index cache for re-use
+ style_index[xf_index] = raw_style
+
+ return xf_index
+ else
+ dxf_index = (dxfs << style)
+
+ return dxf_index
+ end
end
# parses add_style options for protection styles
diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb
index edf719d1..938d4aee 100644
--- a/lib/axlsx/workbook/workbook.rb
+++ b/lib/axlsx/workbook/workbook.rb
@@ -187,6 +187,36 @@ require 'axlsx/workbook/worksheet/selection.rb'
@styles
end
+ # An array that holds all cells with styles
+ # @return Set
+ def styled_cells
+ @styled_cells ||= Set.new
+ end
+
+ # Are the styles added with workbook.add_styles applied yet
+ # @return Boolean
+ attr_accessor :styles_applied
+
+ # A helper to apply styles that were added using `worksheet.add_style`
+ # @return [Boolean]
+ def apply_styles
+ return false if !styled_cells
+
+ styled_cells.each do |cell|
+ current_style = styles.style_index[cell.style]
+
+ if current_style
+ new_style = Axlsx.hash_deep_merge(current_style, cell.raw_style)
+ else
+ new_style = cell.raw_style
+ end
+
+ cell.style = styles.add_style(new_style)
+ end
+
+ self.styles_applied = true
+ end
+
# Indicates if the epoc date for serialization should be 1904. If false, 1900 is used.
@@date1904 = false
diff --git a/lib/axlsx/workbook/worksheet/border_creator.rb b/lib/axlsx/workbook/worksheet/border_creator.rb
new file mode 100644
index 00000000..a4146ee1
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/border_creator.rb
@@ -0,0 +1,76 @@
+# encoding: UTF-8
+
+module Axlsx
+ class BorderCreator
+ attr_reader :worksheet, :cells, :edges, :width, :color
+
+ def initialize(worksheet, cells, args)
+ @worksheet = worksheet
+ @cells = cells
+ if args.is_a?(Hash)
+ @edges = args[:edges] || Axlsx::Border::EDGES
+ @width = args[:style] || :thin
+ @color = args[:color] || '000000'
+ else
+ @edges = args || Axlsx::Border::Edges
+ @width = :thin
+ @color = '000000'
+ end
+
+ if @edges == :all
+ @edges = Axlsx::Border::EDGES
+ elsif @edges.is_a?(Array)
+ @edges = (@edges.map(&:to_sym).uniq & Axlsx::Border::EDGES)
+ else
+ @edges = []
+ end
+ end
+
+ def draw
+ @edges.each do |edge|
+ worksheet.add_style(
+ border_cells[edge],
+ {
+ border: {style: @width, color: @color, edges: [edge]}
+ }
+ )
+ end
+ end
+
+ private
+
+ def border_cells
+ {
+ top: "#{first_cell}:#{last_col}#{first_row}",
+ right: "#{last_col}#{first_row}:#{last_cell}",
+ bottom: "#{first_col}#{last_row}:#{last_cell}",
+ left: "#{first_cell}:#{first_col}#{last_row}",
+ }
+ end
+
+ def first_cell
+ @first_cell ||= cells.first.r
+ end
+
+ def last_cell
+ @last_cell ||= cells.last.r
+ end
+
+ def first_row
+ @first_row ||= first_cell.scan(/\d+/).first
+ end
+
+ def first_col
+ @first_col ||= first_cell.scan(/\D+/).first
+ end
+
+ def last_row
+ @last_row ||= last_cell.scan(/\d+/).first
+ end
+
+ def last_col
+ @last_col ||= last_cell.scan(/\D+/).first
+ end
+
+ end
+end
diff --git a/lib/axlsx/workbook/worksheet/cell.rb b/lib/axlsx/workbook/worksheet/cell.rb
index 3277b8c5..125ca051 100644
--- a/lib/axlsx/workbook/worksheet/cell.rb
+++ b/lib/axlsx/workbook/worksheet/cell.rb
@@ -82,6 +82,32 @@ module Axlsx
defined?(@style) ? @style : 0
end
+ attr_accessor :raw_style
+
+ # The index of the cellXfs item to be applied to this cell.
+ # @param [Hash] styles
+ # @see Axlsx::Styles
+ def add_style(style)
+ self.raw_style ||= {}
+
+ new_style = Axlsx.hash_deep_merge(raw_style, style)
+
+ all_edges = [:top, :right, :bottom, :left]
+
+ if !raw_style[:border].nil? && !style[:border].nil?
+ border_at = (raw_style[:border][:edges] || all_edges) + (style[:border][:edges] || all_edges)
+ new_style[:border][:edges] = border_at.uniq.sort
+ elsif !style[:border].nil?
+ new_style[:border] = style[:border]
+ end
+
+ self.raw_style = new_style
+
+ wb = row.worksheet.workbook
+
+ wb.styled_cells << self
+ end
+
# The row this cell belongs to.
# @return [Row]
attr_reader :row
diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb
index 921687c8..a240e8e9 100644
--- a/lib/axlsx/workbook/worksheet/worksheet.rb
+++ b/lib/axlsx/workbook/worksheet/worksheet.rb
@@ -1,4 +1,7 @@
# encoding: UTF-8
+
+require_relative "border_creator"
+
module Axlsx
# The Worksheet class represents a worksheet in the workbook.
@@ -560,6 +563,41 @@ module Axlsx
cells.each { |cell| cell.style = style }
end
+ # Set the style for cells in a specific column
+ # @param [String|Array] cell references
+ # @param [Hash] styles
+ def add_style(cell_refs, *styles)
+ if !cell_refs.is_a?(Array)
+ cell_refs = [cell_refs]
+ end
+
+ cell_refs.each do |cell_ref|
+ item = self[cell_ref]
+
+ cells = item.is_a?(Array) ? item : [item]
+
+ cells.each do |cell|
+ styles.each do |style|
+ cell.add_style(style)
+ end
+ end
+ end
+ end
+
+ # Set the style for cells in a specific column
+ # @param [String|Array] cell references
+ # @param [Hash|Array|Symbol] border options
+ def add_border(cell_refs, options = Axlsx::Border::EDGES)
+ if !cell_refs.is_a?(Array)
+ cell_refs = [cell_refs]
+ end
+
+ cell_refs.each do |cell_ref|
+ cells = self[cell_ref]
+ Axlsx::BorderCreator.new(self, cells, options).draw
+ end
+ end
+
# Returns a sheet node serialization for this sheet in the workbook.
def to_sheet_node_xml_string(str='')
add_autofilter_defined_name_to_workbook
diff --git a/test/tc_axlsx.rb b/test/tc_axlsx.rb
index e58ebafe..3903fc42 100644
--- a/test/tc_axlsx.rb
+++ b/test/tc_axlsx.rb
@@ -132,4 +132,12 @@ class TestAxlsx < Test::Unit::TestCase
nil_subject = InstanceValuesSubject.new(nil_obj: nil)
assert_equal({"nil_obj" => nil}, Axlsx.instance_values_for(nil_subject), 'should return nil ivars')
end
+
+ def test_hash_deep_merge
+ h1 = {foo: {bar: true}}
+ h2 = {foo: {baz: true}}
+ assert_equal({foo: {baz: true}}, h1.merge(h2))
+ assert_equal({foo: {bar: true, baz: true}}, Axlsx.hash_deep_merge(h1, h2))
+ end
+
end
diff --git a/test/tc_package.rb b/test/tc_package.rb
index 79fe20f5..0788d029 100644
--- a/test/tc_package.rb
+++ b/test/tc_package.rb
@@ -157,6 +157,26 @@ class TestPackage < Test::Unit::TestCase
end
end
+ def test_serialize_automatically_performs_apply_styles
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ assert_nil wb.styles_applied
+ wb.add_worksheet do |sheet|
+ sheet.add_row ['A1', 'B1']
+ sheet.add_style 'A1:B1', b: true
+ end
+
+ @fname = 'axlsx_test_serialization.xlsx'
+
+ p.serialize(@fname)
+
+ assert_equal true, wb.styles_applied
+ assert_equal 1, wb.styles.style_index.count
+
+ File.delete(@fname)
+ end
+
def assert_zip_file_matches_package(fname, package)
zf = Zip::File.open(fname)
package.send(:parts).each{ |part| zf.get_entry(part[:entry]) }
@@ -305,6 +325,21 @@ class TestPackage < Test::Unit::TestCase
assert(Axlsx::Relationship.ids_cache.empty?)
end
+ def test_to_stream_automatically_performs_apply_styles
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ assert_nil wb.styles_applied
+ wb.add_worksheet do |sheet|
+ sheet.add_row ['A1', 'B1']
+ sheet.add_style 'A1:B1', b: true
+ end
+
+ p.to_stream
+
+ assert_equal 1, wb.styles.style_index.count
+ end
+
def test_encrypt
# this is no where near close to ready yet
assert(@package.encrypt('your_mom.xlsxl', 'has a password') == false)
diff --git a/test/workbook/worksheet/tc_border_creator.rb b/test/workbook/worksheet/tc_border_creator.rb
new file mode 100644
index 00000000..86b47fc1
--- /dev/null
+++ b/test/workbook/worksheet/tc_border_creator.rb
@@ -0,0 +1,73 @@
+require 'tc_helper.rb'
+
+class TestBorderCreator < Test::Unit::TestCase
+ def setup
+ @p = Axlsx::Package.new
+ @wb = @p.workbook
+ @ws = @wb.add_worksheet
+ end
+
+ def test_defaults
+ 3.times do
+ @ws.add_row [1,2,3]
+ end
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:B2"], {})
+ assert_equal bc.instance_variable_get(:@edges), Axlsx::Border::EDGES
+ assert_equal bc.instance_variable_get(:@width), :thin
+ assert_equal bc.instance_variable_get(:@color), "000000"
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:B2"], ["top"])
+ assert_equal bc.instance_variable_get(:@edges), [:top]
+ assert_equal bc.instance_variable_get(:@width), :thin
+ assert_equal bc.instance_variable_get(:@color), "000000"
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:B2"], :all)
+ assert_equal bc.instance_variable_get(:@edges), Axlsx::Border::EDGES
+ assert_equal bc.instance_variable_get(:@width), :thin
+ assert_equal bc.instance_variable_get(:@color), "000000"
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:B2"], [:foo])
+ assert_equal bc.instance_variable_get(:@edges), []
+ assert_equal bc.instance_variable_get(:@width), :thin
+ assert_equal bc.instance_variable_get(:@color), "000000"
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:B2"], {edges: ["top"], style: :thick, color: "ffffff"})
+ assert_equal bc.instance_variable_get(:@edges), [:top]
+ assert_equal bc.instance_variable_get(:@width), :thick
+ assert_equal bc.instance_variable_get(:@color), "ffffff"
+ end
+
+ def test_draw
+ 5.times do
+ @ws.add_row [1,2,3,4,5]
+ end
+
+ bc = Axlsx::BorderCreator.new(@ws, @ws["A1:C3"], {edges: ["top", :left], style: :thick, color: "ffffff"})
+
+ bc.draw
+
+ assert_equal 2, @ws.styles.borders.size
+
+ @wb.apply_styles
+
+ assert_equal 5, @ws.styles.borders.size
+
+ assert_equal 2, @ws.styles.borders[2].prs.size
+ assert_equal ["FFFFFFFF"], @ws.styles.borders[2].prs.map(&:color).map(&:rgb).uniq
+ assert_equal [:thick], @ws.styles.borders[2].prs.map(&:style).uniq
+ assert_equal [:left, :top], @ws.styles.borders[2].prs.map(&:name)
+
+
+ assert_equal 1, @ws.styles.borders[3].prs.size
+ assert_equal ["FFFFFFFF"], @ws.styles.borders[3].prs.map(&:color).map(&:rgb).uniq
+ assert_equal [:thick], @ws.styles.borders[3].prs.map(&:style).uniq
+ assert_equal [:top], @ws.styles.borders[3].prs.map(&:name)
+
+ assert_equal 1, @ws.styles.borders[4].prs.size
+ assert_equal ["FFFFFFFF"], @ws.styles.borders[4].prs.map(&:color).map(&:rgb).uniq
+ assert_equal [:thick], @ws.styles.borders[4].prs.map(&:style).uniq
+ assert_equal [:left], @ws.styles.borders[4].prs.map(&:name)
+ end
+
+end
diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb
index c679e18c..6dbea530 100644
--- a/test/workbook/worksheet/tc_worksheet.rb
+++ b/test/workbook/worksheet/tc_worksheet.rb
@@ -629,4 +629,248 @@ class TestWorksheet < Test::Unit::TestCase
assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetPr/xmlns:outlinePr').size, 1)
assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetPr/xmlns:outlinePr[@summaryBelow=0][@summaryRight=1]').size, 1)
end
+
+ def test_merge_styles_1
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ bold = wb.styles.add_style b: true
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', '1', '2', '3'], style: [nil, bold]
+ sheet.add_row ['', '4', '5', '6'], style: bold
+ sheet.add_row ['', '7', '8', '9']
+ sheet.add_style 'B2:D4', b: true
+ sheet.add_border 'B2:D4', { style: :thin, color: '000000' }
+ end
+
+ wb.apply_styles
+
+ assert_equal 9, wb.styles.style_index.count
+ end
+
+ def test_merge_styles_2
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ bold = wb.styles.add_style b: true
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row ['A1', 'B1'], style: [nil, bold]
+ sheet.add_row ['A2', 'B2'], style: bold
+ sheet.add_row ['A3', 'B3']
+ sheet.add_style 'A1:A2', i: true
+ end
+
+ wb.apply_styles
+
+ assert_equal 3, wb.styles.style_index.count
+ end
+
+ def test_merge_styles_3
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ bold = wb.styles.add_style b: true
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row ['A1', 'B1'], style: [nil, bold]
+ sheet.add_row ['A2', 'B2']
+ sheet.add_style 'B1:B2', bg_color: 'FF0000'
+ end
+
+ wb.apply_styles
+
+ assert_equal 3, wb.styles.style_index.count
+ end
+
+ def test_table_with_borders
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', 'Product', 'Category', 'Price']
+ sheet.add_row ['', 'Butter', 'Dairy', 4.99]
+ sheet.add_row ['', 'Bread', 'Baked Goods', 3.45]
+ sheet.add_row ['', 'Broccoli', 'Produce', 2.99]
+ sheet.add_row ['', 'Pizza', 'Frozen Foods', 4.99]
+ sheet.column_widths 5, 20, 20, 20
+
+ sheet.add_style 'B2:D2', b: true
+ sheet.add_style 'B2:B6', b: true
+ sheet.add_style 'B2:D2', bg_color: '95AFBA'
+ sheet.add_style 'B3:D6', bg_color: 'E2F89C'
+ sheet.add_style 'D3:D6', alignment: { horizontal: :left }
+ sheet.add_border 'B2:D6'
+ sheet.add_border 'B3:D3', [:top]
+ sheet.add_border 'B3:D3', edges: [:bottom], style: :medium
+ sheet.add_border 'B3:D3', edges: [:bottom], style: :medium, color: '32f332'
+ end
+
+ wb.apply_styles
+
+ assert_equal true, wb.styles_applied
+ assert_equal 12, wb.styles.style_index.count
+ assert_equal 12 + 2, wb.styles.style_index.keys.max
+ end
+
+ def test_duplicate_borders
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', 'B2', 'C2', 'D2']
+ sheet.add_row ['', 'B3', 'C3', 'D3']
+ sheet.add_row ['', 'B4', 'C4', 'D4']
+
+ sheet.add_border 'B2:D4'
+ sheet.add_border 'B2:D4'
+ end
+
+ wb.apply_styles
+
+ assert_equal true, wb.styles_applied
+ assert_equal 8, wb.styles.style_index.count
+ assert_equal 8, wb.styled_cells.count
+ end
+
+ def test_multiple_style_borders_on_same_cells
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', 'B2', 'C2', 'D2']
+ sheet.add_row ['', 'B3', 'C3', 'D3']
+
+ sheet.add_border 'B2:D3', :all
+ sheet.add_border 'B2:D2', edges: [:bottom], style: :thick, color: 'ff0000'
+ end
+
+ wb.apply_styles
+
+ assert_equal true, wb.styles_applied
+ assert_equal 6, wb.styles.style_index.count
+ assert_equal 6, wb.styled_cells.count
+
+ b2_cell_style = {
+ border: {
+ style: :thick,
+ color: 'ff0000',
+ edges: [:bottom, :left, :top]
+ },
+ type: :xf,
+ name: 'Arial',
+ sz: 11,
+ family: 1
+ }
+ assert_equal b2_cell_style, wb.styles.style_index.values.find{|x| x == b2_cell_style}
+
+ d3_cell_style = {
+ border: {
+ style: :thin,
+ color: '000000',
+ edges: [:bottom, :right]
+ },
+ type: :xf,
+ name: 'Arial',
+ sz: 11,
+ family: 1
+ }
+ assert_equal d3_cell_style, wb.styles.style_index.values.find{|x| x == d3_cell_style}
+ end
+
+ def test_mixed_borders_1
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', '1', '2', '3']
+ sheet.add_row ['', '4', '5', '6']
+ sheet.add_row ['', '7', '8', '9']
+ sheet.add_style 'B2:D4', border: { style: :thin, color: '000000' }
+ sheet.add_border 'C3:D4', style: :medium
+ end
+
+ wb.apply_styles
+
+ assert_equal 9, wb.styled_cells.count
+ assert_equal 2, wb.styles.style_index.count
+ end
+
+ def test_mixed_borders_2
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row
+ sheet.add_row ['', '1', '2', '3']
+ sheet.add_row ['', '4', '5', '6']
+ sheet.add_row ['', '7', '8', '9']
+ sheet.add_border 'B2:D4', style: :medium
+ sheet.add_style 'D2:D4', border: { style: :thin, color: '000000' }
+ end
+
+ wb.apply_styles
+
+ assert_equal 8, wb.styled_cells.count
+ assert_equal 6, wb.styles.style_index.count
+ end
+
+ def test_dxf_cell
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row (1..2).to_a
+ sheet.add_style "A1:A1", { bg_color: "AA0000" }
+
+ sheet.add_row (1..2).to_a
+ sheet.add_style "B1:B1", { bg_color: "CC0000" }
+
+ sheet.add_row (1..2).to_a
+ sheet.add_style "A3:B3", { bg_color: "00FF00" }
+
+ wb.styles.add_style(bg_color: "0000FF", type: :dxf)
+ end
+
+ wb.apply_styles
+
+ assert_equal 1, wb.styles.dxfs.count
+
+ assert_equal 6, wb.styles.cellXfs.count
+ end
+
+ def test_default_font_with_style_index
+ p = Axlsx::Package.new
+ wb = p.workbook
+
+ wb.styles.fonts[0].name = 'Times New Roman'
+ wb.styles.fonts[0].sz = 12
+
+ wb.add_worksheet do |sheet|
+ sheet.add_row [1,2,3]
+ sheet.add_style "A1:C1", { color: "FFFFFF" }
+ end
+
+ wb.apply_styles
+
+ assert_equal 1, wb.styles.style_index.size
+
+ assert_equal(
+ {
+ type: :xf,
+ name: "Times New Roman",
+ sz: 12,
+ family: 1,
+ color: "FFFFFF",
+ },
+ wb.styles.style_index.values.first
+ )
+ end
+
end