summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--lib/axlsx/stylesheet/styles.rb119
-rw-r--r--test/stylesheet/tc_styles.rb116
2 files changed, 159 insertions, 76 deletions
diff --git a/lib/axlsx/stylesheet/styles.rb b/lib/axlsx/stylesheet/styles.rb
index f988df95..d3a7cbf5 100644
--- a/lib/axlsx/stylesheet/styles.rb
+++ b/lib/axlsx/stylesheet/styles.rb
@@ -223,88 +223,97 @@ module Axlsx
options[:type] ||= :xf
raise ArgumentError, "Type must be one of [:xf, :dxf]" unless [:xf, :dxf].include?(options[:type] )
- numFmt = parse_num_fmt_options options
- applyProtection = (options[:hidden] || options[:locked]) ? 1 : 0
- border = parse_border_options options
fill = parse_fill_options options
font = parse_font_options options
+ numFmt = parse_num_fmt_options options
+ border = parse_border_options options
alignment = parse_alignment_options options
protection = parse_protection_options options
- if options[:type] == :dxf
+ case options[:type]
+ when :dxf
style = Dxf.new :fill => fill, :font => font, :numFmt => numFmt, :border => border, :alignment => alignment, :protection => protection
else
- # Only add styles if we're adding to Xf. They're embedded inside the Dxf pieces directly
- # Default to borderId = 0 rather than no border
- borderId = border.is_a?(Border) ? borders << border : border || 0
- numFmtId = numFmt.is_a?(NumFmt) ? numFmt.numFmtId : options[:num_fmt] || 0
- numFmts << numFmt if numFmt.is_a?(NumFmt)
- style = Xf.new(:fillId=>fill || 0, :fontId=>font || 0, :applyNumberFormat => 1, :applyFill=>1, :applyFont=>1, :numFmtId=>numFmtId, :borderId=>borderId, :applyProtection=>applyProtection, :applyBorder=>1, :alignment => alignment, :protection => protection)
- style.applyAlignment = 1 if style.alignment
- style.applyProtection = 1 if style.protection
+ 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
- if style.is_a? Dxf
- dxfs << style
- else
- cellXfs << style if style.is_a? Xf
- end
+ options[:type] == :xf ? cellXfs << style : dxfs << style
end
+ # parses add_style options for protection styles
+ # noop if options hash does not include :hide or :locked key
+
+ # @option options [Boolean] hide boolean value defining cell protection attribute for hiding.
+ # @option options [Boolean] locked boolean value defining cell protection attribute for locking.
+ # @return [CellProtection]
def parse_protection_options(options={})
- CellProtection.new(options) if options[:hide] || options[:locked]
+ return if (options.keys & [:hidden, :locked]).empty?
+ CellProtection.new(options)
end
+ # parses add_style options for alignment
+ # noop if options hash does not include :alignment key
+ # @option options [Hash] alignment A hash of options to prive the CellAlignment intializer
+ # @return [CellAlignment]
+ # @see CellAlignment
def parse_alignment_options(options={})
- CellAlignment.new(options[:alignment]) if options[:alignment]
+ return unless options[:alignment]
+ CellAlignment.new options[:alignment]
end
+ # parses add_style options for fonts. If the options hash contains :type => :dxf we return a new Font object.
+ # if not, we return the index of the newly created font object in the styles.fonts collection.
+ # @note noop if none of the options described here are set on the options parameter.
+ # @option options [Symbol] type The type of style object we are working with (dxf or xf)
+ # @option options [String] fg_color The text color
+ # @option options [Integer] sz The text size
+ # @option options [Boolean] b Indicates if the text should be bold
+ # @option options [Boolean] i Indicates if the text should be italicised
+ # @option options [Boolean] u Indicates if the text should be underlined
+ # @option options [Boolean] strike Indicates if the text should be rendered with a strikethrough
+ # @option options [Boolean] outline Indicates if the text should be rendered with a shadow
+ # @option options [Integer] charset The character set to use.
+ # @option options [Integer] family The font family to use.
+ # @option options [String] font_name The name of the font to use
+ # @return [Font|Integer]
def parse_font_options(options={})
- if (options.values_at(:fg_color, :sz, :b, :i, :u, :strike, :outline, :shadow, :charset, :family, :font_name).length)
- # it would be better to pass a hash of what is allowed to the constructor and only set color and font name conditionally
- font = Font.new()
- [:b, :i, :u, :strike, :outline, :shadow, :charset, :family, :sz, :fg_color, :font_name].each do |key|
- next if options[key].nil?
- case key
- when :fg_color
- font.color = Color.new(:rgb => options[:fg_color])
- when :font_name
- font.name = options[:font_name]
- else
- font.send("#{key}=", options[key])
- end
- end
- font = fonts << font if options[:type] != :dxf
- end
+ return if (options.keys & [:fg_color, :sz, :b, :i, :u, :strike, :outline, :shadow, :charset, :family, :font_name]).empty?
+ font = Font.new(options)
+ font.color = Color.new(:rgb => options[:fg_color]) if options[:fg_color]
+ font.name = options[:font_name] if options[:font_name]
+ options[:type] == :dxf ? font : fonts << font
end
+ # parses add_style options for fills. If the options hash contains :type => :dxf we return a Fill object. If not, we return the index of the fill after being added to the fills collection.
+ # @note noop if :bg_color is not specified in options
+ # @option options [String] bg_color The rgb color to apply to the fill
+ # @return [Fill|Integer]
def parse_fill_options(options={})
- if options[:bg_color]
- color = Color.new(:rgb=>options[:bg_color])
- pattern = PatternFill.new(:patternType =>:solid, :fgColor=>color)
- fill = Fill.new(pattern)
- fill = fills << fill if options[:type] != :dxf
- fill
- end
+ return unless options[:bg_color]
+ color = Color.new(:rgb=>options[:bg_color])
+ pattern = PatternFill.new(:patternType =>:solid, :fgColor=>color)
+ fill = Fill.new(pattern)
+ options[:type] == :dxf ? fill : fills << fill
end
+
# parses Style#add_style options for borders.
- # @option options [Hash|Integer] A border style definition hash. Border style definition hashes must include :style and color: key-value entries and may include an :edges entry that references an array of symbols identifying which border edges you wish to apply the style to. If the :edges entity is not provided the style is applied to all edges of cells that reference this style.
+ # @note noop if :border is not specified in options
+ # @option options [Hash|Integer] A border style definition hash or the index of an existing border. Border style definition hashes must include :style and color: key-value entries and 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.
# @example
# #apply a thick red border to the top and bottom
# { :border => { :style => :thick, :color => "FFFF0000", :edges => [:top, :bottom] }
- # When options is an Integer it is expected to reference a predefined border entry in styles and must be a valid index withing the Style#borders list.
- # @note An error will be raised if you attempt to use an Integer border reference with a dxf style
- # @return [Border|Integer|nil]
+ # @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 & [:style, :color]).size == 2
- border = Border.new
+ raise ArgumentError, (ERR_INVALID_BORDER_OPTIONS % b_opts) unless b_opts.values_at(:style, :color).size == 2
+ border = Border.new b_opts
(b_opts[:edges] || [:left, :right, :top, :bottom]).each do |edge|
b_options = { :name => edge, :style => b_opts[:style], :color => Color.new(:rgb => b_opts[:color]) }
border.prs << BorderPr.new(b_options)
end
- border
+ 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
@@ -320,10 +329,16 @@ module Axlsx
# @option options [Hash] A hash describing the :format_code and/or :num_fmt integer for the style.
# @return [NumFmt|Integer]
def parse_num_fmt_options(options={})
- return unless options.values_at([:format_code,:num_fmt]).length
- if options[:format_code] || (options[:type] == :dxf && options[:num_fmt])
+ return if (options.keys & [:format_code, :num_fmt]).empty?
+
+ #When the user provides format_code - we always need to create a new numFmt object
+ #When the type is :dxf we always need to create a new numFmt object
+ if options[:format_code] || options[:type] == :dxf
+ #If this is a standard xf we pull from numFmts the highest current and increment for num_fmt
options[:num_fmt] ||= (@numFmts.map{ |num_fmt| num_fmt.numFmtId }.max + 1) if options[:type] != :dxf
- NumFmt.new(:numFmtId => options[:num_fmt] || 0, :formatCode=> options[:format_code].to_s)
+ #BUG? should we be pulling the max from xdfs.numFmt when the type is :dxf?
+ numFmt = NumFmt.new(:numFmtId => options[:num_fmt] || 0, :formatCode=> options[:format_code].to_s)
+ options[:type] == :dxf ? numFmt : numFmts << numFmt
else
options[:num_fmt]
end
diff --git a/test/stylesheet/tc_styles.rb b/test/stylesheet/tc_styles.rb
index b556ef0f..18235fff 100644
--- a/test/stylesheet/tc_styles.rb
+++ b/test/stylesheet/tc_styles.rb
@@ -46,36 +46,104 @@ class TestStyles < Test::Unit::TestCase
num_fmt = {:num_fmt => 5}
both = { :format_code => "#000", :num_fmt => 0 }
assert_equal(@styles.parse_num_fmt_options, nil, 'noop if neither :format_code or :num_fmt exist')
- assert_equal(@styles.parse_num_fmt_options(f_code).numFmtId, ((@styles.numFmts.map{ |nf| nf.numFmtId }).max + 1), "new numfmts gets next available id")
- assert(@styles.parse_num_fmt_options(f_code).is_a?(Axlsx::NumFmt), "Must create a NumFmt object when format_code key exists.")
- assert(@styles.parse_num_fmt_options(num_fmt).is_a?(Integer), "Should return the provided num_fmt if not dxf and format_code is not set")
- assert(@styles.parse_num_fmt_options(num_fmt.merge({:type => :dxf})).is_a?(Axlsx::NumFmt), "Makes a new NumFmt if dxf and only num_fmt specified")
- assert(@styles.parse_num_fmt_options(both).is_a?(Axlsx::NumFmt), "builds a new number format if format_code and num_fmt are specified")
+ max = @styles.numFmts.map{ |num_fmt| num_fmt.numFmtId }.max
+ @styles.parse_num_fmt_options(f_code)
+ assert_equal(@styles.numFmts.last.numFmtId, max + 1, "new numfmts gets next available id")
+ assert(@styles.parse_num_fmt_options(num_fmt).is_a?(Integer), "Should return the provided num_fmt if not dxf")
+ assert(@styles.parse_num_fmt_options(num_fmt.merge({:type => :dxf})).is_a?(Axlsx::NumFmt), "Makes a new NumFmt if dxf")
end
- def test_parse_border_options_hash
- b_opts = {:border => { :edges => [:left, :right], :color => "FFDADADA", :style => :thick } }
+ def test_parse_border_options_hash_required_keys
+ assert_raise(ArgumentError, "Require color key") { @styles.parse_border_options(:border => { :style => :thin }) }
+ assert_raise(ArgumentError, "Require style key") { @styles.parse_border_options(:border => { :color => "FF0d0d0d" }) }
+ assert_nothing_raised { @styles.parse_border_options(:border => { :style => :thin, :color => "FF000000"} ) }
+ end
+
+ def test_parse_border_basic_options
+ b_opts = {:border => { :diagonalUp => 1, :edges => [:left, :right], :color => "FFDADADA", :style => :thick } }
b = @styles.parse_border_options b_opts
- assert(b.is_a? Axlsx::Border)
- assert_raise(ArgumentError, "Require color key") { @styles.parse_border_options({:border => {:style => :thin}}) }
- assert_raise(ArgumentError, "Require style key") { @styles.parse_border_options({:border => {:color => "FF0d0d0d"}}) }
+ assert(b.is_a? Integer)
+ assert_equal(@styles.parse_border_options(b_opts.merge({:type => :dxf})).class,Axlsx::Border)
+ assert(@styles.borders.last.diagonalUp == 1, "border options are passed in to the initializer")
+ end
+
+ def test_parse_border_options_edges
+ b_opts = {:border => { :diagonalUp => 1, :edges => [:left, :right], :color => "FFDADADA", :style => :thick } }
+ @styles.parse_border_options b_opts
+ b = @styles.borders.last
left = b.prs.select { |bpr| bpr.name == :left }[0]
right = b.prs.select { |bpr| bpr.name == :right }[0]
top = b.prs.select { |bpr| bpr.name == :top }[0]
bottom = b.prs.select { |bpr| bpr.name == :bottom }[0]
- assert_equal(top, nil)
- assert_equal(bottom, nil)
- assert left.is_a? Axlsx::BorderPr
- assert right.is_a? Axlsx::BorderPr
- assert_equal(left.style,right.style)
- assert_equal(left.style, :thick)
- assert_equal(right.color.rgb,left.color.rgb)
- assert_equal(right.color.rgb,"FFDADADA")
- assert_equal(@styles.parse_border_options({}), nil)
+ assert_equal(top, nil, "unspecified top edge should not be created")
+ assert_equal(bottom, nil, "unspecified bottom edge should not be created")
+ assert(left.is_a?(Axlsx::BorderPr), "specified left edge is set")
+ assert(right.is_a?(Axlsx::BorderPr), "specified right edge is set")
+ assert_equal(left.style,right.style, "edge parts have the same style")
+ assert_equal(left.style, :thick, "the style is THICK")
+ assert_equal(right.color.rgb,left.color.rgb, "edge parts are colors are the same")
+ assert_equal(right.color.rgb,"FFDADADA", "edge color rgb is correct")
+ end
+
+ def test_parse_border_options_noop
+ assert_equal(@styles.parse_border_options({}), nil, "noop if the border key is not in options")
+ end
+
+ def test_parse_border_options_integer_xf
+ assert_equal(@styles.parse_border_options(:border => 1), 1)
+ assert_raise(ArgumentError, "unknown border index") {@styles.parse_border_options(:border => 100) }
end
- def test_parse_border_options_integer
+ def test_parse_border_options_integer_dxf
+ b_opts = { :border => { :edges => [:left, :right], :color => "FFFFFFFF", :style=> :thick } }
+ b = @styles.parse_border_options(b_opts)
+ b2 = @styles.parse_border_options(:border => b, :type => :dxf)
+ assert(b2.is_a?(Axlsx::Border), "Cloned existing border object")
+ end
+
+ def test_parse_alignment_options
+ assert_equal(@styles.parse_alignment_options {}, nil, "noop if :alignment is not set")
+ assert(@styles.parse_alignment_options(:alignment => {}).is_a?(Axlsx::CellAlignment))
+ end
+
+ def test_parse_font_options
+ options = {
+ :fg_color => "FF050505",
+ :sz => 20,
+ :b => 1,
+ :i => 1,
+ :u => 1,
+ :strike => 1,
+ :outline => 1,
+ :shadow => 1,
+ :charset => 9,
+ :family => 1,
+ :font_name => "woot font"
+ }
+ assert_equal(@styles.parse_font_options {}, nil, "noop if no font keys are set")
+ assert_equal(@styles.parse_font_options(:b=>1).class, Fixnum, "return index of font if not :dxf type")
+ assert_equal(@styles.parse_font_options(:b=>1, :type => :dxf).class, Axlsx::Font, "return font object if :dxf type")
+
+ f = @styles.parse_font_options(options.merge(:type => :dxf))
+ color = options.delete(:fg_color)
+ options[:name] = options.delete(:font_name)
+ options.each do |key, value|
+ assert_equal(f.send(key), value, "assert that #{key} was parsed")
+ end
+ assert_equal(f.color.rgb, color)
+ end
+
+ def test_parse_fill_options
+ assert_equal(@styles.parse_fill_options {}, nil, "noop if no fill keys are set")
+ assert_equal(@styles.parse_fill_options(:bg_color => "DE").class, Fixnum, "return index of fill if not :dxf type")
+ assert_equal(@styles.parse_fill_options(:bg_color => "DE", :type => :dxf).class, Axlsx::Fill, "return fill object if :dxf type")
+ f = @styles.parse_fill_options(:bg_color => "DE", :type => :dxf)
+ assert(f.fill_type.fgColor.rgb == "FFDEDEDE")
+ end
+ def test_parse_protection_options
+ assert_equal(@styles.parse_protection_options {}, nil, "noop if no protection keys are set")
+ assert_equal(@styles.parse_protection_options(:hidden => 1).class, Axlsx::CellProtection, "creates a new cell protection object")
end
def test_add_style
@@ -105,10 +173,10 @@ class TestStyles < Test::Unit::TestCase
assert_raise(ArgumentError, "should reject invalid borderId") { @styles.add_style :border => 2 }
- assert_equal(xf.applyProtection, 1, "protection applied")
- assert_equal(xf.applyBorder, 1, "border applied")
- assert_equal(xf.applyNumberFormat,1, "number format applied")
- assert_equal(xf.applyAlignment, 1, "alignment applied")
+ assert_equal(xf.applyProtection, true, "protection applied")
+ assert_equal(xf.applyBorder, true, "border applied")
+ assert_equal(xf.applyNumberFormat,true, "number format applied")
+ assert_equal(xf.applyAlignment, true, "alignment applied")
end
def test_basic_add_style_dxf